(965c31410) v0.10.4.0

This commit is contained in:
Joonas Rikkonen
2020-07-30 13:00:09 +03:00
parent eeac247a8e
commit 4978af3d60
539 changed files with 45803 additions and 25359 deletions
@@ -7,86 +7,70 @@ namespace Steamworks
{
internal static class Helpers
{
public const int MaxStringSize = 1024 * 32;
public const int MemoryBufferSize = 1024 * 32;
private static IntPtr[] MemoryPool = new IntPtr[]
{
Marshal.AllocHGlobal( MemoryBufferSize ),
Marshal.AllocHGlobal( MemoryBufferSize ),
Marshal.AllocHGlobal( MemoryBufferSize ),
Marshal.AllocHGlobal( MemoryBufferSize )
};
private static object mutex = new object();
private static IntPtr[] MemoryPool;
private static int MemoryPoolIndex;
public static unsafe IntPtr TakeMemory()
{
IntPtr take = IntPtr.Zero;
lock (mutex)
lock ( MemoryPool )
{
if (MemoryPool == null)
{
//
// The pool has 5 items. This should be safe because we shouldn't really
// ever be using more than 2 memory pools
//
MemoryPool = new IntPtr[5];
for (int i = 0; i < MemoryPool.Length; i++)
MemoryPool[i] = Marshal.AllocHGlobal(MaxStringSize);
}
MemoryPoolIndex++;
if (MemoryPoolIndex >= MemoryPool.Length)
if ( MemoryPoolIndex >= MemoryPool.Length )
MemoryPoolIndex = 0;
take = MemoryPool[MemoryPoolIndex];
var take = MemoryPool[MemoryPoolIndex];
((byte*)take)[0] = 0;
}
return take;
return take;
}
}
private static byte[][] BufferPool;
private static byte[][] BufferPool = new byte[4][];
private static int BufferPoolIndex;
private static object BufferMutex = new object();
/// <summary>
/// Returns a buffer. This will get returned and reused later on.
/// We shouldn't really be using this anymore.
/// </summary>
public static byte[] TakeBuffer( int minSize )
{
int bufferPoolIndex;
lock (BufferMutex)
lock ( BufferPool )
{
if (BufferPool == null)
{
//
// The pool has 8 items.
//
BufferPool = new byte[8][];
for (int i = 0; i < BufferPool.Length; i++)
BufferPool[i] = new byte[1024 * 128];
}
BufferPoolIndex++;
if (BufferPoolIndex < 0 || BufferPoolIndex >= BufferPool.Length)
if ( BufferPoolIndex >= BufferPool.Length )
BufferPoolIndex = 0;
bufferPoolIndex = BufferPoolIndex;
}
if ( BufferPool[BufferPoolIndex] == null )
BufferPool[BufferPoolIndex] = new byte[1024 * 256];
if ( BufferPool[bufferPoolIndex].Length < minSize )
{
BufferPool[bufferPoolIndex] = new byte[minSize + 1024];
}
if ( BufferPool[BufferPoolIndex].Length < minSize )
{
BufferPool[BufferPoolIndex] = new byte[minSize + 1024];
}
return BufferPool[bufferPoolIndex];
return BufferPool[BufferPoolIndex];
}
}
internal unsafe static string MemoryToString( IntPtr ptr )
{
var len = 0;
for( len = 0; len < MaxStringSize; len++ )
for( len = 0; len < MemoryBufferSize; len++ )
{
if ( ((byte*)ptr)[len] == 0 )
break;
@@ -13,32 +13,18 @@ namespace Steamworks
#if PLATFORM_WIN64
public const int StructPlatformPackSize = 8;
public const string LibraryName = "steam_api64";
public const CallingConvention MemberConvention = CallingConvention.Cdecl;
#elif PLATFORM_WIN32
public const int StructPlatformPackSize = 8;
public const string LibraryName = "steam_api";
public const CallingConvention MemberConvention = CallingConvention.ThisCall;
#elif PLATFORM_POSIX32
public const int StructPlatformPackSize = 4;
public const string LibraryName = "libsteam_api";
public const CallingConvention MemberConvention = CallingConvention.Cdecl;
#elif PLATFORM_POSIX64
public const int StructPlatformPackSize = 4;
public const string LibraryName = "libsteam_api64";
public const CallingConvention MemberConvention = CallingConvention.Cdecl;
#elif PLATFORM_POSIX
public const int StructPlatformPackSize = 4;
public const string LibraryName = "libsteam_api";
#endif
public const CallingConvention CC = CallingConvention.Cdecl;
public const int StructPackSize = 4;
public static int MemoryOffset( int memLocation )
{
#if PLATFORM_64
return memLocation;
#else
return memLocation / 2;
#endif
}
}
}
@@ -13,27 +13,82 @@ namespace Steamworks
{
private static readonly byte[] A2S_SERVERQUERY_GETCHALLENGE = { 0x55, 0xFF, 0xFF, 0xFF, 0xFF };
// private static readonly byte A2S_PLAYER = 0x55;
private static readonly byte A2S_RULES = 0x56;
private const byte A2S_RULES = 0x56;
internal static async Task<Dictionary<string, string>> GetRules( ServerInfo server )
{
try
private static readonly Dictionary<IPEndPoint, Task<Dictionary<string, string>>> PendingQueries =
new Dictionary<IPEndPoint, Task<Dictionary<string, string>>>();
private static HashSet<int> activeRequests = new HashSet<int>();
private static int lastRequestId = 0;
internal static Task<Dictionary<string, string>> GetRules( ServerInfo server )
{
var endpoint = new IPEndPoint(server.Address, server.QueryPort);
lock (PendingQueries)
{
if (PendingQueries.TryGetValue(endpoint, out var pending))
return pending;
var task = GetRulesImpl( endpoint )
.ContinueWith(t =>
{
lock (PendingQueries)
{
PendingQueries.Remove(endpoint);
}
return t;
})
.Unwrap();
PendingQueries.Add(endpoint, task);
return task;
}
}
private static async Task<Dictionary<string, string>> GetRulesImpl( IPEndPoint endpoint )
{
int currId;
lock (activeRequests)
{
var endpoint = new IPEndPoint( server.Address, server.QueryPort );
using ( var client = new UdpClient() )
{
client.Client.SendTimeout = 3000;
client.Client.ReceiveTimeout = 3000;
client.Connect( endpoint );
return await GetRules( client );
}
lastRequestId++;
currId = lastRequestId;
activeRequests.Add(currId);
}
catch ( System.Exception e )
try
{
await Task.Yield();
while (true)
{
lock (activeRequests)
{
if (!activeRequests.Any() || (currId - activeRequests.Min()) < 25) { break; }
}
await Task.Delay(25);
}
using (var client = new UdpClient())
{
client.Client.SendTimeout = 3000;
client.Client.ReceiveTimeout = 3000;
client.Connect(endpoint);
return await GetRules(client);
}
}
catch (System.Exception)
{
//Console.Error.WriteLine( e.Message );
return null;
}
finally
{
Console.Error.WriteLine( e.Message );
return null;
lock (activeRequests)
{
activeRequests.Remove(currId);
}
}
}
@@ -54,14 +109,14 @@ namespace Steamworks
var numRules = br.ReadUInt16();
for ( int index = 0; index < numRules; index++ )
{
rules.Add( br.ReadNullTerminatedUTF8String( readBuffer ), br.ReadNullTerminatedUTF8String( readBuffer ) );
rules.Add( br.ReadNullTerminatedUTF8String(), br.ReadNullTerminatedUTF8String() );
}
}
return rules;
}
static byte[] readBuffer = new byte[1024 * 8];
static async Task<byte[]> Receive( UdpClient client )
{
@@ -70,8 +125,13 @@ namespace Steamworks
do
{
var result = await client.ReceiveAsync();
var buffer = result.Buffer;
Task<UdpReceiveResult> result = client.ReceiveAsync();
await Task.WhenAny(result, Task.Delay(3000));
if (!result.IsCompleted)
{
throw new Exception("Receive timed out");
}
var buffer = result.Result.Buffer;
using ( var br = new BinaryReader( new MemoryStream( buffer ) ) )
{
@@ -120,10 +180,10 @@ namespace Steamworks
return challengeData;
}
static byte[] sendBuffer = new byte[1024];
static async Task Send( UdpClient client, byte[] message )
{
var sendBuffer = new byte[message.Length + 4];
sendBuffer[0] = 0xFF;
sendBuffer[1] = 0xFF;
sendBuffer[2] = 0xFF;
@@ -11,103 +11,136 @@ namespace Steamworks
{
internal abstract class SteamInterface
{
public virtual IntPtr GetUserInterfacePointer() => IntPtr.Zero;
public virtual IntPtr GetServerInterfacePointer() => IntPtr.Zero;
public virtual IntPtr GetGlobalInterfacePointer() => IntPtr.Zero;
public IntPtr Self;
public IntPtr VTable;
public IntPtr SelfGlobal;
public IntPtr SelfServer;
public IntPtr SelfClient;
public virtual string InterfaceName => null;
public bool IsValid => Self != IntPtr.Zero && VTable != IntPtr.Zero;
public bool IsValid => Self != IntPtr.Zero;
public bool IsServer { get; private set; }
public void Init()
internal void SetupInterface( bool gameServer )
{
if ( SteamClient.IsValid )
{
InitClient();
if ( Self != IntPtr.Zero )
return;
}
if ( SteamServer.IsValid )
{
InitServer();
IsServer = gameServer;
SelfGlobal = GetGlobalInterfacePointer();
Self = SelfGlobal;
if ( Self != IntPtr.Zero )
return;
}
throw new System.Exception( "Trying to initialize Steam Interface but Steam not initialized" );
}
public void InitClient()
{
//
// There's an issue for us using FindOrCreateUserInterface on Rust.
// We have a different appid for our staging branch, but we use Rust's
// appid so we can still test with the live data/setup. The issue is
// if we run the staging branch and get interfaces using FindOrCreate
// then some callbacks don't work. I assume this is because these interfaces
// have already been initialized using the old appid pipe, but since I
// can't see inside Steam this is just a gut feeling. Either way using
// CreateInterface doesn't seem to have caused any fires, so we'll just use that.
//
//
// var pipe = SteamAPI.GetHSteamPipe();
//
Self = SteamInternal.CreateInterface( InterfaceName );
if ( Self == IntPtr.Zero )
if ( gameServer )
{
var user = SteamAPI.GetHSteamUser();
Self = SteamInternal.FindOrCreateUserInterface( user, InterfaceName );
SelfServer = GetServerInterfacePointer();
Self = SelfServer;
}
else
{
SelfClient = GetUserInterfacePointer();
Self = SelfClient;
}
if ( Self == IntPtr.Zero )
throw new System.Exception( $"Couldn't find interface {InterfaceName}" );
VTable = Marshal.ReadIntPtr( Self, 0 );
if ( Self == IntPtr.Zero )
throw new System.Exception( $"Invalid VTable for {InterfaceName}" );
InitInternals();
SteamClient.WatchInterface( this );
}
public void InitServer()
{
var user = SteamGameServer.GetHSteamUser();
Self = SteamInternal.FindOrCreateGameServerInterface( user, InterfaceName );
if ( Self == IntPtr.Zero )
throw new System.Exception( $"Couldn't find server interface {InterfaceName}" );
VTable = Marshal.ReadIntPtr( Self, 0 );
if ( Self == IntPtr.Zero )
throw new System.Exception( $"Invalid VTable for server {InterfaceName}" );
InitInternals();
SteamServer.WatchInterface( this );
}
public virtual void InitUserless()
{
Self = SteamInternal.FindOrCreateUserInterface( 0, InterfaceName );
if ( Self == IntPtr.Zero )
throw new System.Exception( $"Couldn't find interface {InterfaceName}" );
VTable = Marshal.ReadIntPtr( Self, 0 );
if ( Self == IntPtr.Zero )
throw new System.Exception( $"Invalid VTable for {InterfaceName}" );
InitInternals();
}
internal virtual void Shutdown()
internal void ShutdownInterface()
{
Self = IntPtr.Zero;
VTable = IntPtr.Zero;
}
}
public abstract class SteamClass
{
internal abstract void InitializeInterface( bool server );
internal abstract void DestroyInterface( bool server );
}
public class SteamSharedClass<T> : SteamClass
{
internal static SteamInterface Interface => InterfaceClient ?? InterfaceServer;
internal static SteamInterface InterfaceClient;
internal static SteamInterface InterfaceServer;
internal override void InitializeInterface( bool server )
{
}
public abstract void InitInternals();
internal virtual void SetInterface( bool server, SteamInterface iface )
{
if ( server )
{
InterfaceServer = iface;
}
if ( !server )
{
InterfaceClient = iface;
}
}
internal override void DestroyInterface( bool server )
{
if ( !server )
{
InterfaceClient = null;
}
if ( server )
{
InterfaceServer = null;
}
}
}
public class SteamClientClass<T> : SteamClass
{
internal static SteamInterface Interface;
internal override void InitializeInterface( bool server )
{
}
internal virtual void SetInterface( bool server, SteamInterface iface )
{
if ( server )
throw new System.NotSupportedException();
Interface = iface;
}
internal override void DestroyInterface( bool server )
{
Interface = null;
}
}
public class SteamServerClass<T> : SteamClass
{
internal static SteamInterface Interface;
internal override void InitializeInterface( bool server )
{
}
internal virtual void SetInterface( bool server, SteamInterface iface )
{
if ( !server )
throw new System.NotSupportedException();
Interface = iface;
}
internal override void DestroyInterface( bool server )
{
Interface = null;
}
}
}
@@ -44,7 +44,9 @@ namespace Steamworks
internal struct Utf8StringPointer
{
#pragma warning disable 649
internal IntPtr ptr;
#pragma warning restore 649
public unsafe static implicit operator string( Utf8StringPointer p )
{
@@ -3,12 +3,29 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
namespace Steamworks
{
public static partial class Utility
{
static internal T ToType<T>( this IntPtr ptr )
{
if ( ptr == IntPtr.Zero )
return default;
return (T)Marshal.PtrToStructure( ptr, typeof( T ) );
}
static internal object ToType( this IntPtr ptr, System.Type t )
{
if ( ptr == IntPtr.Zero )
return default;
return Marshal.PtrToStructure( ptr, t );
}
static internal uint Swap( uint x )
{
return ((x & 0x000000ff) << 24) +
@@ -80,20 +97,22 @@ namespace Steamworks
}
}
public static string ReadNullTerminatedUTF8String( this BinaryReader br, byte[] buffer = null )
static readonly byte[] readBuffer = new byte[1024 * 8];
public static string ReadNullTerminatedUTF8String( this BinaryReader br )
{
if ( buffer == null )
buffer = new byte[1024];
byte chr;
int i = 0;
while ( (chr = br.ReadByte()) != 0 && i < buffer.Length )
lock ( readBuffer )
{
buffer[i] = chr;
i++;
}
byte chr;
int i = 0;
while ( (chr = br.ReadByte()) != 0 && i < readBuffer.Length )
{
readBuffer[i] = chr;
i++;
}
return Encoding.UTF8.GetString( buffer, 0, i );
return Encoding.UTF8.GetString( readBuffer, 0, i );
}
}
}
}