Updated Implementation to use flat api

This commit is contained in:
Garry Newman 2020-02-22 20:23:19 +00:00
parent 92b45c7596
commit d8e5a48f65
57 changed files with 969 additions and 1168 deletions

View File

@ -0,0 +1,80 @@
using Steamworks.Data;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace Steamworks
{
/// <summary>
/// An awaitable version of a SteamAPICall_t
/// </summary>
internal struct CallResult<T> : INotifyCompletion where T : struct, ICallbackData
{
SteamAPICall_t call;
public CallResult( SteamAPICall_t call )
{
this.call = call;
}
/// <summary>
/// This gets called if IsComplete returned false on the first call.
/// The Action "continues" the async call. We pass it to the Dispatch
/// to be called when the callback returns.
/// </summary>
public void OnCompleted( Action continuation )
{
Dispatch.OnCallComplete( call, continuation );
}
/// <summary>
/// Gets the result. This is called internally by the async shit.
/// </summary>
public T? GetResult()
{
if ( !SteamUtils.IsCallComplete( call, out var failed ) || failed )
return null;
var t = default( T );
var size = t.DataSize;
var ptr = Marshal.AllocHGlobal( size );
try
{
if ( !SteamUtils.Internal.GetAPICallResult( call, ptr, size, (int) t.CallbackType, ref failed ) || failed )
return null;
return ((T)Marshal.PtrToStructure( ptr, typeof( T ) ));
}
finally
{
Marshal.FreeHGlobal( ptr );
}
}
/// <summary>
/// Return true if complete or failed
/// </summary>
public bool IsCompleted
{
get
{
if ( SteamUtils.IsCallComplete( call, out var failed ) || failed )
return true;
return false;
}
}
/// <summary>
/// This is what makes this struct awaitable
/// </summary>
internal CallResult<T> GetAwaiter()
{
return this;
}
}
}

View File

@ -1,43 +0,0 @@
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using Steamworks.Data;
namespace Steamworks
{
[StructLayout( LayoutKind.Sequential )]
internal partial class Callback
{
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
public delegate void Run( IntPtr thisptr, IntPtr pvParam );
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
public delegate void RunCall( IntPtr thisptr, IntPtr pvParam, bool bIOFailure, SteamAPICall_t hSteamAPICall );
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
public delegate int GetCallbackSizeBytes( IntPtr thisptr );
internal enum Flags : byte
{
Registered = 0x01,
GameServer = 0x02
}
public IntPtr vTablePtr;
public byte CallbackFlags;
public int CallbackId;
//
// These are functions that are on CCallback but are never called
// We could just send a IntPtr.Zero but it's probably safer to throw a
// big apeshit message if steam changes its behaviour at some point
//
[MonoPInvokeCallback]
internal static void RunStub( IntPtr self, IntPtr param, bool failure, SteamAPICall_t call ) =>
throw new System.Exception( "Something changed in the Steam API and now CCallbackBack is calling the CallResult function [Run( void *pvParam, bool bIOFailure, SteamAPICall_t hSteamAPICall )]" );
[MonoPInvokeCallback]
internal static int SizeStub( IntPtr self ) =>
throw new System.Exception( "Something changed in the Steam API and now CCallbackBack is calling the GetSize function [GetCallbackSizeBytes()]" );
};
}

View File

@ -1,135 +0,0 @@
using Steamworks.Data;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Steamworks
{
//
// Created on registration of a callback
//
internal class Event : IDisposable
{
internal static List<IDisposable> AllClient = new List<IDisposable>();
internal static List<IDisposable> AllServer = new List<IDisposable>();
internal static void DisposeAllClient()
{
foreach ( var a in AllClient.ToArray() )
{
a.Dispose();
}
AllClient.Clear();
}
internal static void DisposeAllServer()
{
foreach ( var a in AllServer.ToArray() )
{
a.Dispose();
}
AllServer.Clear();
}
internal static void Register( Callback.Run func, int size, int callbackId, bool gameserver )
{
var r = new Event();
r.vTablePtr = BuildVTable( func, r.Allocations );
//
// Create the callback object
//
var cb = new Callback();
cb.vTablePtr = r.vTablePtr;
cb.CallbackFlags = gameserver ? (byte)0x02 : (byte)0;
cb.CallbackId = callbackId;
//
// Pin the callback, so it doesn't get garbage collected and we can pass the pointer to native
//
r.PinnedCallback = GCHandle.Alloc( cb, GCHandleType.Pinned );
//
// Register the callback with Steam
//
SteamClient.RegisterCallback( r.PinnedCallback.AddrOfPinnedObject(), cb.CallbackId );
r.IsAllocated = true;
if ( gameserver )
Event.AllServer.Add( r );
else
Event.AllClient.Add( r );
}
static IntPtr BuildVTable( Callback.Run run, List<GCHandle> allocations )
{
var RunStub = (Callback.RunCall)Callback.RunStub;
var SizeStub = (Callback.GetCallbackSizeBytes)Callback.SizeStub;
allocations.Add( GCHandle.Alloc( run ) );
allocations.Add( GCHandle.Alloc( RunStub ) );
allocations.Add( GCHandle.Alloc( SizeStub ) );
var a = Marshal.GetFunctionPointerForDelegate<Callback.Run>( run );
var b = Marshal.GetFunctionPointerForDelegate<Callback.RunCall>( RunStub );
var c = Marshal.GetFunctionPointerForDelegate<Callback.GetCallbackSizeBytes>( SizeStub );
var vt = Marshal.AllocHGlobal( IntPtr.Size * 3 );
// Windows switches the function positions
#if PLATFORM_WIN
Marshal.WriteIntPtr( vt, IntPtr.Size * 0, b );
Marshal.WriteIntPtr( vt, IntPtr.Size * 1, a );
Marshal.WriteIntPtr( vt, IntPtr.Size * 2, c );
#else
Marshal.WriteIntPtr( vt, IntPtr.Size * 0, a );
Marshal.WriteIntPtr( vt, IntPtr.Size * 1, b );
Marshal.WriteIntPtr( vt, IntPtr.Size * 2, c );
#endif
return vt;
}
bool IsAllocated;
List<GCHandle> Allocations = new List<GCHandle>();
internal IntPtr vTablePtr;
internal GCHandle PinnedCallback;
public void Dispose()
{
if ( !IsAllocated ) return;
IsAllocated = false;
if ( !PinnedCallback.IsAllocated )
throw new System.Exception( "Callback isn't allocated!?" );
SteamClient.UnregisterCallback( PinnedCallback.AddrOfPinnedObject() );
foreach ( var a in Allocations )
{
if ( a.IsAllocated )
a.Free();
}
Allocations = null;
PinnedCallback.Free();
if ( vTablePtr != IntPtr.Zero )
{
Marshal.FreeHGlobal( vTablePtr );
vTablePtr = IntPtr.Zero;
}
}
~Event()
{
Dispose();
}
}
}

View File

@ -0,0 +1,17 @@
using Steamworks.Data;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace Steamworks
{
/// <summary>
/// Gives us a generic way to get the CallbackId of structs
/// </summary>
internal interface ICallbackData
{
CallbackType CallbackType { get; }
int DataSize { get; }
}
}

View File

@ -0,0 +1,205 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Steamworks.Data;
using Steamworks;
namespace Steamworks
{
/// <summary>
/// Manually pumps Steam's message queue and dispatches those
/// events to any waiting callbacks/callresults.
/// </summary>
internal static class Dispatch
{
#region interop
[DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ManualDispatch_Init", CallingConvention = CallingConvention.Cdecl )]
internal static extern void SteamAPI_ManualDispatch_Init();
[DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ManualDispatch_RunFrame", CallingConvention = CallingConvention.Cdecl )]
internal static extern void SteamAPI_ManualDispatch_RunFrame( HSteamPipe pipe );
[DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ManualDispatch_GetNextCallback", CallingConvention = CallingConvention.Cdecl )]
[return: MarshalAs( UnmanagedType.I1 )]
internal static extern bool SteamAPI_ManualDispatch_GetNextCallback( HSteamPipe pipe, [In, Out] ref CallbackMsg_t msg );
[DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ManualDispatch_FreeLastCallback", CallingConvention = CallingConvention.Cdecl )]
[return: MarshalAs( UnmanagedType.I1 )]
internal static extern bool SteamAPI_ManualDispatch_FreeLastCallback( HSteamPipe pipe );
[StructLayout( LayoutKind.Sequential, Pack = Platform.StructPlatformPackSize )]
internal struct CallbackMsg_t
{
public HSteamUser m_hSteamUser; // Specific user to whom this callback applies.
public CallbackType Type; // Callback identifier. (Corresponds to the k_iCallback enum in the callback structure.)
public IntPtr Data; // Points to the callback structure
public int DataSize; // Size of the data pointed to by m_pubParam
};
#endregion
internal static HSteamPipe ClientPipe { get; set; }
internal static HSteamPipe ServerPipe { get; set; }
/// <summary>
/// This gets called from Client/Server Init
/// It's important to switch to the manual dipatcher
/// </summary>
public static void Init()
{
SteamAPI_ManualDispatch_Init();
}
/// <summary>
/// Calls RunFrame and processes events from this Steam Pipe
/// </summary>
public static void Frame( HSteamPipe pipe )
{
SteamAPI_ManualDispatch_RunFrame( pipe );
CallbackMsg_t msg = default;
while ( SteamAPI_ManualDispatch_GetNextCallback( pipe, ref msg ) )
{
try
{
ProcessCallback( msg );
}
finally
{
SteamAPI_ManualDispatch_FreeLastCallback( pipe );
}
}
}
/// <summary>
/// A callback is a general global message
/// </summary>
private static void ProcessCallback( CallbackMsg_t msg )
{
// Is this a special callback telling us that the call result is ready?
if ( msg.Type == CallbackType.SteamAPICallCompleted )
{
ProcessResult( msg );
return;
}
if ( Callbacks.TryGetValue( msg.Type, out var list ) )
{
foreach ( var item in list )
{
item.action( msg.Data );
}
}
}
/// <summary>
/// A result is a reply to a specific command
/// </summary>
private static void ProcessResult( CallbackMsg_t msg )
{
var result = msg.Data.ToType<SteamAPICallCompleted_t>();
//
// Do we have an entry added via OnCallComplete
//
if ( !ResultCallbacks.TryGetValue( result.AsyncCall, out var callbackInfo ) )
{
// Do we care? Should we throw errors?
return;
}
// Remove it before we do anything, incase the continuation throws exceptions
ResultCallbacks.Remove( result.AsyncCall );
// At this point whatever async routine called this
// continues running.
callbackInfo.continuation();
}
/// <summary>
/// Pumps the queue in an async loop so we don't
/// have to think about it. This has the advantage that
/// you can call .Wait() on async shit and it still works.
/// </summary>
public static async void LoopClientAsync()
{
while ( ClientPipe != 0 )
{
Frame( ClientPipe );
await Task.Delay( 16 );
}
}
/// <summary>
/// Pumps the queue in an async loop so we don't
/// have to think about it. This has the advantage that
/// you can call .Wait() on async shit and it still works.
/// </summary>
public static async void LoopServerAsync()
{
while ( ServerPipe != 0 )
{
Frame( ServerPipe );
await Task.Delay( 32 );
}
}
struct ResultCallback
{
public Action continuation;
}
static Dictionary<ulong, ResultCallback> ResultCallbacks = new Dictionary<ulong, ResultCallback>();
/// <summary>
/// Watch for a steam api call
/// </summary>
internal static void OnCallComplete( SteamAPICall_t call, Action continuation )
{
ResultCallbacks[call.Value] = new ResultCallback
{
continuation = continuation
};
}
struct Callback
{
public Action<IntPtr> action;
public bool server;
}
static Dictionary<CallbackType, List<Callback>> Callbacks = new Dictionary<CallbackType, List<Callback>>();
/// <summary>
/// Install a global callback. The passed function will get called if it's all good.
/// </summary>
internal static void Install<T>( Action<T> p, bool server = false ) where T : ICallbackData
{
var t = default( T );
var type = t.CallbackType;
if ( !Callbacks.TryGetValue( type, out var list ) )
{
list = new List<Callback>();
Callbacks[type] = list;
}
list.Add( new Callback
{
action = x => p( x.ToType<T>() ),
server = server
} );
}
internal static void Wipe()
{
Callbacks = new Dictionary<CallbackType, List<Callback>>();
ResultCallbacks = new Dictionary<ulong, ResultCallback>();
ClientPipe = 0;
ServerPipe = 0;
}
}
}

View File

@ -11,10 +11,28 @@ namespace Steamworks
public List<Connection> Connected = new List<Connection>();
public Socket Socket { get; internal set; }
public bool Close() => Socket.Close();
public override string ToString() => Socket.ToString();
internal HSteamNetPollGroup pollGroup;
internal void Initialize()
{
pollGroup = SteamNetworkingSockets.Internal.CreatePollGroup();
}
public bool Close()
{
if ( SteamNetworkingSockets.Internal.IsValid )
{
SteamNetworkingSockets.Internal.DestroyPollGroup( pollGroup );
Socket.Close();
}
pollGroup = 0;
Socket = 0;
return true;
}
public virtual void OnConnectionChanged( Connection connection, ConnectionInfo data )
{
switch ( data.State )
@ -40,6 +58,8 @@ namespace Steamworks
{
connection.Accept();
Connecting.Add( connection );
SteamNetworkingSockets.Internal.SetConnectionPollGroup( connection, pollGroup );
}
/// <summary>
@ -67,9 +87,10 @@ namespace Steamworks
int processed = 0;
IntPtr messageBuffer = Marshal.AllocHGlobal( IntPtr.Size * bufferSize );
try
{
processed = SteamNetworkingSockets.Internal.ReceiveMessagesOnListenSocket( Socket, messageBuffer, bufferSize );
processed = SteamNetworkingSockets.Internal.ReceiveMessagesOnPollGroup( pollGroup, messageBuffer, bufferSize );
for ( int i = 0; i < processed; i++ )
{
@ -80,6 +101,7 @@ namespace Steamworks
{
Marshal.FreeHGlobal( messageBuffer );
}
//
// Overwhelmed our buffer, keep going

View File

@ -0,0 +1,50 @@
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Steamworks.Data;
namespace Steamworks
{
internal static class SteamAPI
{
internal static class Native
{
[DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_Init", CallingConvention = CallingConvention.Cdecl )]
[return: MarshalAs( UnmanagedType.I1 )]
public static extern bool SteamAPI_Init();
[DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_Shutdown", CallingConvention = CallingConvention.Cdecl )]
public static extern void SteamAPI_Shutdown();
[DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_GetHSteamPipe", CallingConvention = CallingConvention.Cdecl )]
public static extern HSteamPipe SteamAPI_GetHSteamPipe();
[DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_RestartAppIfNecessary", CallingConvention = CallingConvention.Cdecl )]
[return: MarshalAs( UnmanagedType.I1 )]
public static extern bool SteamAPI_RestartAppIfNecessary( uint unOwnAppID );
}
static internal bool Init()
{
return Native.SteamAPI_Init();
}
static internal void Shutdown()
{
Native.SteamAPI_Shutdown();
}
static internal HSteamPipe GetHSteamPipe()
{
return Native.SteamAPI_GetHSteamPipe();
}
static internal bool RestartAppIfNecessary( uint unOwnAppID )
{
return Native.SteamAPI_RestartAppIfNecessary( unOwnAppID );
}
}
}

View File

@ -0,0 +1,40 @@
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Steamworks.Data;
namespace Steamworks
{
internal static class SteamGameServer
{
internal static class Native
{
[DllImport( Platform.LibraryName, EntryPoint = "SteamGameServer_RunCallbacks", CallingConvention = CallingConvention.Cdecl )]
public static extern void SteamGameServer_RunCallbacks();
[DllImport( Platform.LibraryName, EntryPoint = "SteamGameServer_Shutdown", CallingConvention = CallingConvention.Cdecl )]
public static extern void SteamGameServer_Shutdown();
[DllImport( Platform.LibraryName, EntryPoint = "SteamGameServer_GetHSteamPipe", CallingConvention = CallingConvention.Cdecl )]
public static extern HSteamPipe SteamGameServer_GetHSteamPipe();
}
static internal void RunCallbacks()
{
Native.SteamGameServer_RunCallbacks();
}
static internal void Shutdown()
{
Native.SteamGameServer_Shutdown();
}
static internal HSteamPipe GetHSteamPipe()
{
return Native.SteamGameServer_GetHSteamPipe();
}
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Steamworks.Data;
namespace Steamworks
{
internal static class SteamInternal
{
internal static class Native
{
[DllImport( Platform.LibraryName, EntryPoint = "SteamInternal_GameServer_Init", CallingConvention = CallingConvention.Cdecl )]
[return: MarshalAs( UnmanagedType.I1 )]
public static extern bool SteamInternal_GameServer_Init( uint unIP, ushort usPort, ushort usGamePort, ushort usQueryPort, int eServerMode, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersionString );
}
static internal bool GameServer_Init( uint unIP, ushort usPort, ushort usGamePort, ushort usQueryPort, int eServerMode, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string pchVersionString )
{
return Native.SteamInternal_GameServer_Init( unIP, usPort, usGamePort, usQueryPort, eServerMode, pchVersionString );
}
}
}

View File

@ -1,107 +0,0 @@
namespace Steamworks.Data
{
/// High level connection status
public enum ConnectionState
{
/// Dummy value used to indicate an error condition in the API.
/// Specified connection doesn't exist or has already been closed.
None = 0,
/// We are trying to establish whether peers can talk to each other,
/// whether they WANT to talk to each other, perform basic auth,
/// and exchange crypt keys.
///
/// - For connections on the "client" side (initiated locally):
/// We're in the process of trying to establish a connection.
/// Depending on the connection type, we might not know who they are.
/// Note that it is not possible to tell if we are waiting on the
/// network to complete handshake packets, or for the application layer
/// to accept the connection.
///
/// - For connections on the "server" side (accepted through listen socket):
/// We have completed some basic handshake and the client has presented
/// some proof of identity. The connection is ready to be accepted
/// using AcceptConnection().
///
/// In either case, any unreliable packets sent now are almost certain
/// to be dropped. Attempts to receive packets are guaranteed to fail.
/// You may send messages if the send mode allows for them to be queued.
/// but if you close the connection before the connection is actually
/// established, any queued messages will be discarded immediately.
/// (We will not attempt to flush the queue and confirm delivery to the
/// remote host, which ordinarily happens when a connection is closed.)
Connecting = 1,
/// Some connection types use a back channel or trusted 3rd party
/// for earliest communication. If the server accepts the connection,
/// then these connections switch into the rendezvous state. During this
/// state, we still have not yet established an end-to-end route (through
/// the relay network), and so if you send any messages unreliable, they
/// are going to be discarded.
FindingRoute = 2,
/// We've received communications from our peer (and we know
/// who they are) and are all good. If you close the connection now,
/// we will make our best effort to flush out any reliable sent data that
/// has not been acknowledged by the peer. (But note that this happens
/// from within the application process, so unlike a TCP connection, you are
/// not totally handing it off to the operating system to deal with it.)
Connected = 3,
/// Connection has been closed by our peer, but not closed locally.
/// The connection still exists from an API perspective. You must close the
/// handle to free up resources. If there are any messages in the inbound queue,
/// you may retrieve them. Otherwise, nothing may be done with the connection
/// except to close it.
///
/// This stats is similar to CLOSE_WAIT in the TCP state machine.
ClosedByPeer = 4,
/// A disruption in the connection has been detected locally. (E.g. timeout,
/// local internet connection disrupted, etc.)
///
/// The connection still exists from an API perspective. You must close the
/// handle to free up resources.
///
/// Attempts to send further messages will fail. Any remaining received messages
/// in the queue are available.
ProblemDetectedLocally = 5,
//
// The following values are used internally and will not be returned by any API.
// We document them here to provide a little insight into the state machine that is used
// under the hood.
//
/// We've disconnected on our side, and from an API perspective the connection is closed.
/// No more data may be sent or received. All reliable data has been flushed, or else
/// we've given up and discarded it. We do not yet know for sure that the peer knows
/// the connection has been closed, however, so we're just hanging around so that if we do
/// get a packet from them, we can send them the appropriate packets so that they can
/// know why the connection was closed (and not have to rely on a timeout, which makes
/// it appear as if something is wrong).
FinWait = -1,
/// We've disconnected on our side, and from an API perspective the connection is closed.
/// No more data may be sent or received. From a network perspective, however, on the wire,
/// we have not yet given any indication to the peer that the connection is closed.
/// We are in the process of flushing out the last bit of reliable data. Once that is done,
/// we will inform the peer that the connection has been closed, and transition to the
/// FinWait state.
///
/// Note that no indication is given to the remote host that we have closed the connection,
/// until the data has been flushed. If the remote host attempts to send us data, we will
/// do whatever is necessary to keep the connection alive until it can be closed properly.
/// But in fact the data will be discarded, since there is no way for the application to
/// read it back. Typically this is not a problem, as application protocols that utilize
/// the lingering functionality are designed for the remote host to wait for the response
/// before sending any more data.
Linger = -2,
/// Connection is completely inactive and ready to be destroyed
Dead = -3,
Force32Bit = 0x7fffffff
};
}

View File

@ -1,57 +0,0 @@
namespace Steamworks.Data
{
internal enum NetConfig : int
{
Invalid = 0,
FakePacketLoss_Send = 2,
FakePacketLoss_Recv = 3,
FakePacketLag_Send = 4,
FakePacketLag_Recv = 5,
FakePacketReorder_Send = 6,
FakePacketReorder_Recv = 7,
FakePacketReorder_Time = 8,
FakePacketDup_Send = 26,
FakePacketDup_Recv = 27,
FakePacketDup_TimeMax = 28,
TimeoutInitial = 24,
TimeoutConnected = 25,
SendBufferSize = 9,
SendRateMin = 10,
SendRateMax = 11,
NagleTime = 12,
IP_AllowWithoutAuth = 23,
SDRClient_ConsecutitivePingTimeoutsFailInitial = 19,
SDRClient_ConsecutitivePingTimeoutsFail = 20,
SDRClient_MinPingsBeforePingAccurate = 21,
SDRClient_SingleSocket = 22,
SDRClient_ForceRelayCluster = 29,
SDRClient_DebugTicketAddress = 30,
SDRClient_ForceProxyAddr = 31,
LogLevel_AckRTT = 13,
LogLevel_PacketDecode = 14,
LogLevel_Message = 15,
LogLevel_PacketGaps = 16,
LogLevel_P2PRendezvous = 17,
LogLevel_SDRRelayPings = 18,
Force32Bit = 0x7fffffff
}
}

View File

@ -1,13 +0,0 @@
namespace Steamworks.Data
{
enum NetConfigResult
{
BadValue = -1, // No such configuration value
BadScopeObj = -2, // Bad connection handle, etc
BufferTooSmall = -3, // Couldn't fit the result in your buffer
OK = 1,
OKInherited = 2, // A value was not set at this level, but the effective (inherited) value was returned.
Force32Bit = 0x7fffffff
};
}

View File

@ -1,13 +0,0 @@
namespace Steamworks.Data
{
enum NetConfigType
{
Int32 = 1,
Int64 = 2,
Float = 3,
String = 4,
FunctionPtr = 5, // NOTE: When setting callbacks, you should put the pointer into a variable and pass a pointer to that variable.
Force32Bit = 0x7fffffff
};
}

View File

@ -1,12 +0,0 @@
namespace Steamworks.Data
{
internal enum NetScope : int
{
Global = 1,
SocketsInterface = 2,
ListenSocket = 3,
Connection = 4,
Force32Bit = 0x7fffffff
}
}

View File

@ -11,27 +11,7 @@ namespace Steamworks.ServerList
{
#region ISteamMatchmakingServers
static ISteamMatchmakingServers _internal;
internal static ISteamMatchmakingServers Internal
{
get
{
if ( _internal == null )
{
_internal = new ISteamMatchmakingServers();
_internal.Init();
}
return _internal;
}
}
internal static void Shutdown()
{
_internal = null;
}
internal static ISteamMatchmakingServers Internal => SteamMatchmakingServers.Internal;
#endregion

View File

@ -11,32 +11,21 @@ namespace Steamworks
/// <summary>
/// Exposes a wide range of information and actions for applications and Downloadable Content (DLC).
/// </summary>
public static class SteamApps
public class SteamApps : SteamClass
{
static ISteamApps _internal;
internal static ISteamApps Internal
{
get
{
if ( _internal == null )
{
_internal = new ISteamApps();
_internal.Init();
}
internal static ISteamApps Internal;
internal override SteamInterface Interface => Internal;
return _internal;
}
}
internal static void Shutdown()
internal override void InitializeInterface( bool server )
{
_internal = null;
Internal = new ISteamApps( server );
}
internal static void InstallEvents()
{
DlcInstalled_t.Install( x => OnDlcInstalled?.Invoke( x.AppID ) );
NewUrlLaunchParameters_t.Install( x => OnNewLaunchParameters?.Invoke() );
Dispatch.Install<DlcInstalled_t>( x => OnDlcInstalled?.Invoke( x.AppID ) );
Dispatch.Install<NewUrlLaunchParameters_t>( x => OnNewLaunchParameters?.Invoke() );
}
/// <summary>

View File

@ -29,76 +29,72 @@ namespace Steamworks
initialized = true;
SteamApps.InstallEvents();
SteamUtils.InstallEvents();
SteamParental.InstallEvents();
SteamMusic.InstallEvents();
SteamVideo.InstallEvents();
SteamUser.InstallEvents();
SteamFriends.InstallEvents();
SteamScreenshots.InstallEvents();
SteamUserStats.InstallEvents();
SteamInventory.InstallEvents();
SteamNetworking.InstallEvents();
SteamMatchmaking.InstallEvents();
SteamParties.InstallEvents();
SteamNetworkingSockets.InstallEvents();
SteamInput.InstallEvents();
SteamUGC.InstallEvents();
//
// Dispatch is responsible for pumping the
// event loop.
//
Dispatch.Init();
Dispatch.ClientPipe = SteamAPI.GetHSteamPipe();
Console.WriteLine( $"Dispatch.ClientPipe = {Dispatch.ClientPipe.Value}" );
AddInterface<SteamApps>();
AddInterface<SteamFriends>();
AddInterface<SteamInput>();
AddInterface<SteamInventory>();
AddInterface<SteamMatchmaking>();
AddInterface<SteamMatchmakingServers>();
AddInterface<SteamMusic>();
AddInterface<SteamNetworking>();
AddInterface<SteamNetworkingSockets>();
AddInterface<SteamNetworkingUtils>();
AddInterface<SteamParental>();
AddInterface<SteamParties>();
AddInterface<SteamRemoteStorage>();
AddInterface<SteamScreenshots>();
AddInterface<SteamUGC>();
AddInterface<SteamUser>();
AddInterface<SteamUserStats>();
AddInterface<SteamUtils>();
AddInterface<SteamVideo>();
if ( asyncCallbacks )
{
RunCallbacksAsync();
//
// This will keep looping in the background every 16 ms
// until we shut down.
//
Dispatch.LoopClientAsync();
}
}
static List<SteamInterface> openIterfaces = new List<SteamInterface>();
internal static void WatchInterface( SteamInterface steamInterface )
internal static void AddInterface<T>() where T : SteamClass, new()
{
if ( openIterfaces.Contains( steamInterface ) )
throw new System.Exception( "openIterfaces already contains interface!" );
openIterfaces.Add( steamInterface );
var t = new T();
t.InitializeInterface( false );
openInterfaces.Add( t );
}
static List<SteamClass> openInterfaces = new List<SteamClass>();
internal static void ShutdownInterfaces()
{
foreach ( var e in openIterfaces )
foreach ( var e in openInterfaces )
{
e.Shutdown();
e.DestroyInterface();
}
openIterfaces.Clear();
openInterfaces.Clear();
}
public static Action<Exception> OnCallbackException;
public static bool IsValid => initialized;
internal static async void RunCallbacksAsync()
{
while ( IsValid )
{
await Task.Delay( 16 );
try
{
RunCallbacks();
}
catch ( System.Exception e )
{
OnCallbackException?.Invoke( e );
}
}
}
public static void Shutdown()
{
if ( !IsValid ) return;
SteamInput.Shutdown();
Cleanup();
SteamAPI.Shutdown();
@ -106,45 +102,17 @@ namespace Steamworks
internal static void Cleanup()
{
Dispatch.Wipe();
initialized = false;
Event.DisposeAllClient();
ShutdownInterfaces();
SteamInput.Shutdown();
SteamApps.Shutdown();
SteamUtils.Shutdown();
SteamParental.Shutdown();
SteamMusic.Shutdown();
SteamVideo.Shutdown();
SteamUser.Shutdown();
SteamFriends.Shutdown();
SteamScreenshots.Shutdown();
SteamUserStats.Shutdown();
SteamInventory.Shutdown();
SteamNetworking.Shutdown();
SteamMatchmaking.Shutdown();
SteamParties.Shutdown();
SteamNetworkingUtils.Shutdown();
SteamNetworkingSockets.Shutdown();
ServerList.Base.Shutdown();
}
internal static void RegisterCallback( IntPtr intPtr, int callbackId )
{
SteamAPI.RegisterCallback( intPtr, callbackId );
}
public static void RunCallbacks()
{
if ( !IsValid ) return;
SteamAPI.RunCallbacks();
}
internal static void UnregisterCallback( IntPtr intPtr )
{
SteamAPI.UnregisterCallback( intPtr );
if ( Dispatch.ClientPipe != 0 )
Dispatch.Frame( Dispatch.ClientPipe );
}
/// <summary>

View File

@ -10,42 +10,29 @@ namespace Steamworks
/// <summary>
/// Undocumented Parental Settings
/// </summary>
public static class SteamFriends
public class SteamFriends : SteamClass
{
static ISteamFriends _internal;
internal static ISteamFriends Internal
internal static ISteamFriends Internal;
internal override SteamInterface Interface => Internal;
internal override void InitializeInterface( bool server )
{
get
{
SteamClient.ValidCheck();
Internal = new ISteamFriends( server );
if ( _internal == null )
{
_internal = new ISteamFriends();
_internal.Init();
richPresence = new Dictionary<string, string>();
}
return _internal;
}
}
internal static void Shutdown()
{
_internal = null;
richPresence = new Dictionary<string, string>();
}
static Dictionary<string, string> richPresence;
internal static void InstallEvents()
{
FriendStateChange_t.Install( x => OnPersonaStateChange?.Invoke( new Friend( x.SteamID ) ) );
GameRichPresenceJoinRequested_t.Install( x => OnGameRichPresenceJoinRequested?.Invoke( new Friend( x.SteamIDFriend), x.ConnectUTF8() ) );
GameConnectedFriendChatMsg_t.Install( OnFriendChatMessage );
GameOverlayActivated_t.Install( x => OnGameOverlayActivated?.Invoke() );
GameServerChangeRequested_t.Install( x => OnGameServerChangeRequested?.Invoke( x.ServerUTF8(), x.PasswordUTF8() ) );
GameLobbyJoinRequested_t.Install( x => OnGameLobbyJoinRequested?.Invoke( new Lobby( x.SteamIDLobby ), x.SteamIDFriend ) );
FriendRichPresenceUpdate_t.Install( x => OnFriendRichPresenceUpdate?.Invoke( new Friend( x.SteamIDFriend ) ) );
Dispatch.Install<PersonaStateChange_t>( x => OnPersonaStateChange?.Invoke( new Friend( x.SteamID ) ) );
Dispatch.Install<GameRichPresenceJoinRequested_t>( x => OnGameRichPresenceJoinRequested?.Invoke( new Friend( x.SteamIDFriend), x.ConnectUTF8() ) );
Dispatch.Install<GameConnectedFriendChatMsg_t>( OnFriendChatMessage );
Dispatch.Install<GameOverlayActivated_t>( x => OnGameOverlayActivated?.Invoke() );
Dispatch.Install<GameServerChangeRequested_t>( x => OnGameServerChangeRequested?.Invoke( x.ServerUTF8(), x.PasswordUTF8() ) );
Dispatch.Install<GameLobbyJoinRequested_t>( x => OnGameLobbyJoinRequested?.Invoke( new Lobby( x.SteamIDLobby ), x.SteamIDFriend ) );
Dispatch.Install<FriendRichPresenceUpdate_t>( x => OnFriendRichPresenceUpdate?.Invoke( new Friend( x.SteamIDFriend ) ) );
}
/// <summary>
@ -319,7 +306,7 @@ namespace Steamworks
do
{
if ((result = await Internal.EnumerateFollowingList((uint)resultCount)) != null)
if ( (result = await Internal.EnumerateFollowingList((uint)resultCount)) != null)
{
resultCount += result.Value.ResultsReturned;

View File

@ -3,44 +3,18 @@ using System.Collections.Generic;
namespace Steamworks
{
public static class SteamInput
public class SteamInput : SteamClass
{
internal static ISteamInput Internal;
internal override SteamInterface Interface => Internal;
internal override void InitializeInterface( bool server )
{
Internal = new ISteamInput( server );
}
internal const int STEAM_CONTROLLER_MAX_COUNT = 16;
static ISteamInput _internal;
internal static ISteamInput Internal
{
get
{
SteamClient.ValidCheck();
if ( _internal == null )
{
_internal = new ISteamInput();
_internal.Init();
}
return _internal;
}
}
internal static void Shutdown()
{
if ( _internal != null && _internal.IsValid )
{
_internal.DoShutdown();
}
_internal = null;
}
internal static void InstallEvents()
{
Internal.DoInit();
Internal.RunFrame();
// None?
}
/// <summary>
/// You shouldn't really need to call this because it get called by RunCallbacks on SteamClient

View File

@ -12,32 +12,23 @@ namespace Steamworks
/// <summary>
/// Undocumented Parental Settings
/// </summary>
public static class SteamInventory
public class SteamInventory : SteamClass
{
static ISteamInventory _internal;
internal static ISteamInventory Internal
{
get
{
if ( _internal == null )
{
_internal = new ISteamInventory();
_internal.Init();
}
internal static ISteamInventory Internal;
internal override SteamInterface Interface => Internal;
return _internal;
}
}
internal static void Shutdown()
internal override void InitializeInterface( bool server )
{
_internal = null;
}
Internal = new ISteamInventory( server );
InstallEvents();
}
internal static void InstallEvents()
{
SteamInventoryFullUpdate_t.Install( x => InventoryUpdated( x ) );
SteamInventoryDefinitionUpdate_t.Install( x => LoadDefinitions() );
SteamInventoryDefinitionUpdate_t.Install( x => LoadDefinitions(), true );
Dispatch.Install<SteamInventoryFullUpdate_t>( x => InventoryUpdated( x ) );
Dispatch.Install<SteamInventoryDefinitionUpdate_t>( x => LoadDefinitions() );
Dispatch.Install<SteamInventoryDefinitionUpdate_t>( x => LoadDefinitions(), true );
}
private static void InventoryUpdated( SteamInventoryFullUpdate_t x )

View File

@ -10,48 +10,36 @@ namespace Steamworks
/// <summary>
/// Functions for clients to access matchmaking services, favorites, and to operate on game lobbies
/// </summary>
public static class SteamMatchmaking
public class SteamMatchmaking : SteamClass
{
internal static ISteamMatchmaking Internal;
internal override SteamInterface Interface => Internal;
internal override void InitializeInterface( bool server )
{
Internal = new ISteamMatchmaking( server );
InstallEvents();
}
/// <summary>
/// Maximum number of characters a lobby metadata key can be
/// </summary>
internal static int MaxLobbyKeyLength => 255;
static ISteamMatchmaking _internal;
internal static ISteamMatchmaking Internal
{
get
{
SteamClient.ValidCheck();
if ( _internal == null )
{
_internal = new ISteamMatchmaking();
_internal.Init();
}
return _internal;
}
}
internal static void Shutdown()
{
_internal = null;
}
internal static void InstallEvents()
{
LobbyInvite_t.Install( x => OnLobbyInvite?.Invoke( new Friend( x.SteamIDUser ), new Lobby( x.SteamIDLobby ) ) );
Dispatch.Install<LobbyInvite_t>( x => OnLobbyInvite?.Invoke( new Friend( x.SteamIDUser ), new Lobby( x.SteamIDLobby ) ) );
LobbyEnter_t.Install( x => OnLobbyEntered?.Invoke( new Lobby( x.SteamIDLobby ) ) );
Dispatch.Install<LobbyEnter_t>( x => OnLobbyEntered?.Invoke( new Lobby( x.SteamIDLobby ) ) );
LobbyCreated_t.Install( x => OnLobbyCreated?.Invoke( x.Result, new Lobby( x.SteamIDLobby ) ) );
Dispatch.Install<LobbyCreated_t>( x => OnLobbyCreated?.Invoke( x.Result, new Lobby( x.SteamIDLobby ) ) );
LobbyGameCreated_t.Install( x => OnLobbyGameCreated?.Invoke( new Lobby( x.SteamIDLobby ), x.IP, x.Port, x.SteamIDGameServer ) );
Dispatch.Install<LobbyGameCreated_t>( x => OnLobbyGameCreated?.Invoke( new Lobby( x.SteamIDLobby ), x.IP, x.Port, x.SteamIDGameServer ) );
LobbyDataUpdate_t.Install( x =>
Dispatch.Install<LobbyDataUpdate_t>( x =>
{
if ( x.Success == 0 ) return;
@ -61,7 +49,7 @@ namespace Steamworks
OnLobbyMemberDataChanged?.Invoke( new Lobby( x.SteamIDLobby ), new Friend( x.SteamIDMember ) );
} );
LobbyChatUpdate_t.Install( x =>
Dispatch.Install<LobbyChatUpdate_t>( x =>
{
if ( (x.GfChatMemberStateChange & (int)ChatMemberStateChange.Entered) != 0 )
OnLobbyMemberJoined?.Invoke( new Lobby( x.SteamIDLobby ), new Friend( x.SteamIDUserChanged ) );
@ -79,7 +67,7 @@ namespace Steamworks
OnLobbyMemberBanned?.Invoke( new Lobby( x.SteamIDLobby ), new Friend( x.SteamIDUserChanged ), new Friend( x.SteamIDMakingChange ) );
} );
LobbyChatMsg_t.Install( OnLobbyChatMessageRecievedAPI );
Dispatch.Install<LobbyChatMsg_t>( OnLobbyChatMessageRecievedAPI );
}
static private unsafe void OnLobbyChatMessageRecievedAPI( LobbyChatMsg_t callback )

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Steamworks.Data;
namespace Steamworks
{
/// <summary>
/// Functions for clients to access matchmaking services, favorites, and to operate on game lobbies
/// </summary>
public class SteamMatchmakingServers : SteamClass
{
internal static ISteamMatchmakingServers Internal;
internal override SteamInterface Interface => Internal;
internal override void InitializeInterface( bool server )
{
Internal = new ISteamMatchmakingServers( server );
}
}
}

View File

@ -13,33 +13,22 @@ namespace Steamworks
/// when an important cut scene is shown, and start playing afterwards.
/// Nothing uses Steam Music though so this can probably get fucked
/// </summary>
public static class SteamMusic
public class SteamMusic : SteamClass
{
static ISteamMusic _internal;
internal static ISteamMusic Internal
{
get
{
SteamClient.ValidCheck();
internal static ISteamMusic Internal;
internal override SteamInterface Interface => Internal;
if ( _internal == null )
{
_internal = new ISteamMusic();
_internal.Init();
}
return _internal;
}
}
internal static void Shutdown()
internal override void InitializeInterface( bool server )
{
_internal = null;
Internal = new ISteamMusic( server );
InstallEvents();
}
internal static void InstallEvents()
{
PlaybackStatusHasChanged_t.Install( x => OnPlaybackChanged?.Invoke() );
VolumeHasChanged_t.Install( x => OnVolumeChanged?.Invoke( x.NewVolume ) );
Dispatch.Install<PlaybackStatusHasChanged_t>( x => OnPlaybackChanged?.Invoke() );
Dispatch.Install<VolumeHasChanged_t>( x => OnVolumeChanged?.Invoke( x.NewVolume ) );
}
/// <summary>

View File

@ -8,32 +8,22 @@ using Steamworks.Data;
namespace Steamworks
{
public static class SteamNetworking
public class SteamNetworking : SteamClass
{
static ISteamNetworking _internal;
internal static ISteamNetworking Internal
{
get
{
if ( _internal == null )
{
_internal = new ISteamNetworking();
_internal.Init();
}
internal static ISteamNetworking Internal;
internal override SteamInterface Interface => Internal;
return _internal;
}
}
internal static void Shutdown()
internal override void InitializeInterface( bool server )
{
_internal = null;
Internal = new ISteamNetworking( server );
InstallEvents();
}
internal static void InstallEvents()
{
P2PSessionRequest_t.Install( x => OnP2PSessionRequest?.Invoke( x.SteamIDRemote ) );
P2PSessionConnectFail_t.Install( x => OnP2PConnectionFailed?.Invoke( x.SteamIDRemote, (P2PSessionError) x.P2PSessionError ) );
Dispatch.Install<P2PSessionRequest_t>( x => OnP2PSessionRequest?.Invoke( x.SteamIDRemote ) );
Dispatch.Install<P2PSessionConnectFail_t>( x => OnP2PConnectionFailed?.Invoke( x.SteamIDRemote, (P2PSessionError) x.P2PSessionError ) );
}
/// <summary>

View File

@ -8,27 +8,22 @@ using Steamworks.Data;
namespace Steamworks
{
public static class SteamNetworkingSockets
public class SteamNetworkingSockets : SteamClass
{
static ISteamNetworkingSockets _internal;
internal static ISteamNetworkingSockets Internal
internal static ISteamNetworkingSockets Internal;
internal override SteamInterface Interface => Internal;
internal override void InitializeInterface( bool server )
{
get
{
if ( _internal == null )
{
_internal = new ISteamNetworkingSockets();
_internal.Init();
Internal = new ISteamNetworkingSockets( server );
SocketInterfaces = new Dictionary<uint, SocketInterface>();
ConnectionInterfaces = new Dictionary<uint, ConnectionInterface>();
}
SocketInterfaces = new Dictionary<uint, SocketInterface>();
ConnectionInterfaces = new Dictionary<uint, ConnectionInterface>();
return _internal;
}
InstallEvents();
}
#region SocketInterface
#region SocketInterface
static Dictionary<uint, SocketInterface> SocketInterfaces;
@ -50,9 +45,9 @@ namespace Steamworks
Console.WriteLine( $"Installing Socket For {id}" );
SocketInterfaces[id] = iface;
}
#endregion
#endregion
#region ConnectionInterface
#region ConnectionInterface
static Dictionary<uint, ConnectionInterface> ConnectionInterfaces;
@ -72,20 +67,16 @@ namespace Steamworks
if ( id == 0 ) throw new System.ArgumentException( "Invalid Connection" );
ConnectionInterfaces[id] = iface;
}
#endregion
#endregion
internal static void Shutdown()
{
_internal = null;
SocketInterfaces = null;
ConnectionInterfaces = null;
}
internal static void InstallEvents( bool server = false )
{
SteamNetConnectionStatusChangedCallback_t.Install( x => ConnectionStatusChanged( x ), server );
Dispatch.Install<SteamNetConnectionStatusChangedCallback_t>( ConnectionStatusChanged, server );
}
private static void ConnectionStatusChanged( SteamNetConnectionStatusChangedCallback_t data )
{
//
@ -115,7 +106,10 @@ namespace Steamworks
public static T CreateNormalSocket<T>( NetAddress address ) where T : SocketInterface, new()
{
var t = new T();
t.Socket = Internal.CreateListenSocketIP( ref address );
var options = new NetKeyValue[0];
t.Socket = Internal.CreateListenSocketIP( ref address, options.Length, options );
t.Initialize();
SetSocketInterface( t.Socket.Id, t );
return t;
}
@ -126,7 +120,8 @@ namespace Steamworks
public static T ConnectNormal<T>( NetAddress address ) where T : ConnectionInterface, new()
{
var t = new T();
t.Connection = Internal.ConnectByIPAddress( ref address );
var options = new NetKeyValue[0];
t.Connection = Internal.ConnectByIPAddress( ref address, options.Length, options );
SetConnectionInterface( t.Connection.Id, t );
return t;
}
@ -137,7 +132,8 @@ namespace Steamworks
public static T CreateRelaySocket<T>( int virtualport = 0 ) where T : SocketInterface, new()
{
var t = new T();
t.Socket = Internal.CreateListenSocketP2P( virtualport );
var options = new NetKeyValue[0];
t.Socket = Internal.CreateListenSocketP2P( virtualport, options.Length, options );
SetSocketInterface( t.Socket.Id, t );
return t;
}
@ -149,7 +145,8 @@ namespace Steamworks
{
var t = new T();
NetIdentity identity = serverId;
t.Connection = Internal.ConnectP2P( ref identity, virtualport );
var options = new NetKeyValue[0];
t.Connection = Internal.ConnectP2P( ref identity, virtualport, options.Length, options );
SetConnectionInterface( t.Connection.Id, t );
return t;
}

View File

@ -10,26 +10,51 @@ namespace Steamworks
/// <summary>
/// Undocumented Parental Settings
/// </summary>
public static class SteamNetworkingUtils
public class SteamNetworkingUtils : SteamClass
{
static ISteamNetworkingUtils _internal;
internal static ISteamNetworkingUtils Internal
{
get
{
if ( _internal == null )
{
_internal = new ISteamNetworkingUtils();
_internal.InitUserless();
}
internal static ISteamNetworkingUtils Internal;
internal override SteamInterface Interface => Internal;
return _internal;
}
internal override void InitializeInterface( bool server )
{
Internal = new ISteamNetworkingUtils( server );
}
internal static void Shutdown()
static void InstallCallbacks()
{
_internal = null;
Dispatch.Install<SteamRelayNetworkStatus_t>( x =>
{
Status = x.Avail;
} );
}
public static SteamNetworkingAvailability Status { get; private set; }
/// <summary>
/// If you know that you are going to be using the relay network (for example,
/// because you anticipate making P2P connections), call this to initialize the
/// relay network. If you do not call this, the initialization will
/// be delayed until the first time you use a feature that requires access
/// to the relay network, which will delay that first access.
///
/// You can also call this to force a retry if the previous attempt has failed.
/// Performing any action that requires access to the relay network will also
/// trigger a retry, and so calling this function is never strictly necessary,
/// but it can be useful to call it a program launch time, if access to the
/// relay network is anticipated.
///
/// Use GetRelayNetworkStatus or listen for SteamRelayNetworkStatus_t
/// callbacks to know when initialization has completed.
/// Typically initialization completes in a few seconds.
///
/// Note: dedicated servers hosted in known data centers do *not* need
/// to call this, since they do not make routing decisions. However, if
/// the dedicated server will be using P2P functionality, it will act as
/// a "client" and this should be called.
/// </summary>
public static void InitRelayNetworkAccess()
{
Internal.InitRelayNetworkAccess();
}
/// <summary>
@ -70,10 +95,12 @@ namespace Steamworks
/// </summary>
public static async Task WaitForPingDataAsync( float maxAgeInSeconds = 60 * 5 )
{
if ( Internal.CheckPingDataUpToDate( 60.0f ) )
if ( Internal.CheckPingDataUpToDate( 120.0f ) )
return;
while ( Internal.IsPingMeasurementInProgress() )
SteamRelayNetworkStatus_t status = default;
while ( Internal.GetRelayNetworkStatus( ref status ) != SteamNetworkingAvailability.Current )
{
await Task.Delay( 10 );
}
@ -118,12 +145,12 @@ namespace Steamworks
set => SetConfigFloat( NetConfig.FakePacketLag_Recv, value );
}
#region Config Internals
#region Config Internals
internal unsafe static bool GetConfigInt( NetConfig type, int value )
{
int* ptr = &value;
return Internal.SetConfigValue( type, NetScope.Global, 0, NetConfigType.Int32, (IntPtr)ptr );
return Internal.SetConfigValue( type, NetConfigScope.Global, IntPtr.Zero, NetConfigType.Int32, (IntPtr)ptr );
}
internal unsafe static int GetConfigInt( NetConfig type )
@ -131,8 +158,8 @@ namespace Steamworks
int value = 0;
NetConfigType dtype = NetConfigType.Int32;
int* ptr = &value;
ulong size = sizeof( int );
var result = Internal.GetConfigValue( type, NetScope.Global, 0, ref dtype, (IntPtr) ptr, ref size );
UIntPtr size = new UIntPtr( sizeof( int ) );
var result = Internal.GetConfigValue( type, NetConfigScope.Global, IntPtr.Zero, ref dtype, (IntPtr) ptr, ref size );
if ( result != NetConfigResult.OK )
return 0;
@ -142,7 +169,7 @@ namespace Steamworks
internal unsafe static bool SetConfigFloat( NetConfig type, float value )
{
float* ptr = &value;
return Internal.SetConfigValue( type, NetScope.Global, 0, NetConfigType.Float, (IntPtr)ptr );
return Internal.SetConfigValue( type, NetConfigScope.Global, IntPtr.Zero, NetConfigType.Float, (IntPtr)ptr );
}
internal unsafe static float GetConfigFloat( NetConfig type )
@ -150,8 +177,8 @@ namespace Steamworks
float value = 0;
NetConfigType dtype = NetConfigType.Float;
float* ptr = &value;
ulong size = sizeof( float );
var result = Internal.GetConfigValue( type, NetScope.Global, 0, ref dtype, (IntPtr)ptr, ref size );
UIntPtr size = new UIntPtr( sizeof( float ) );
var result = Internal.GetConfigValue( type, NetConfigScope.Global, IntPtr.Zero, ref dtype, (IntPtr)ptr, ref size );
if ( result != NetConfigResult.OK )
return 0;
@ -164,7 +191,7 @@ namespace Steamworks
fixed ( byte* ptr = bytes )
{
return Internal.SetConfigValue( type, NetScope.Global, 0, NetConfigType.String, (IntPtr)ptr );
return Internal.SetConfigValue( type, NetConfigScope.Global, IntPtr.Zero, NetConfigType.String, (IntPtr)ptr );
}
}
@ -211,6 +238,6 @@ namespace Steamworks
}
}*/
#endregion
#endregion
}
}
}

View File

@ -10,32 +10,21 @@ namespace Steamworks
/// <summary>
/// Undocumented Parental Settings
/// </summary>
public static class SteamParental
public class SteamParental : SteamClass
{
static ISteamParentalSettings _internal;
internal static ISteamParentalSettings Internal
{
get
{
SteamClient.ValidCheck();
internal static ISteamParentalSettings Internal;
internal override SteamInterface Interface => Internal;
if ( _internal == null )
{
_internal = new ISteamParentalSettings();
_internal.Init();
}
return _internal;
}
}
internal static void Shutdown()
internal override void InitializeInterface( bool server )
{
_internal = null;
Internal = new ISteamParentalSettings( server );
InstallEvents();
}
internal static void InstallEvents()
{
SteamParentalSettingsChanged_t.Install( x => OnSettingsChanged?.Invoke() );
Dispatch.Install<SteamParentalSettingsChanged_t>( x => OnSettingsChanged?.Invoke() );
}
/// <summary>

View File

@ -7,31 +7,21 @@ using Steamworks.Data;
namespace Steamworks
{
public static class SteamParties
public class SteamParties : SteamClass
{
static ISteamParties _internal;
internal static ISteamParties Internal
{
get
{
if ( _internal == null )
{
_internal = new ISteamParties();
_internal.Init();
}
internal static ISteamParties Internal;
internal override SteamInterface Interface => Internal;
return _internal;
}
}
internal static void Shutdown()
internal override void InitializeInterface( bool server )
{
_internal = null;
Internal = new ISteamParties( server );
InstallEvents();
}
internal static void InstallEvents()
{
AvailableBeaconLocationsUpdated_t.Install( x => OnBeaconLocationsUpdated?.Invoke() );
ActiveBeaconsUpdated_t.Install( x => OnActiveBeaconsUpdated?.Invoke() );
Dispatch.Install<AvailableBeaconLocationsUpdated_t>( x => OnBeaconLocationsUpdated?.Invoke() );
Dispatch.Install<ActiveBeaconsUpdated_t>( x => OnActiveBeaconsUpdated?.Invoke() );
}
/// <summary>

View File

@ -10,26 +10,14 @@ namespace Steamworks
/// <summary>
/// Undocumented Parental Settings
/// </summary>
public static class SteamRemoteStorage
public class SteamRemoteStorage : SteamClass
{
static ISteamRemoteStorage _internal;
internal static ISteamRemoteStorage Internal
{
get
{
if ( _internal == null )
{
_internal = new ISteamRemoteStorage();
_internal.Init();
}
internal static ISteamRemoteStorage Internal;
internal override SteamInterface Interface => Internal;
return _internal;
}
}
internal static void Shutdown()
internal override void InitializeInterface( bool server )
{
_internal = null;
Internal = new ISteamRemoteStorage( server );
}
/// <summary>

View File

@ -10,33 +10,21 @@ namespace Steamworks
/// <summary>
/// Undocumented Parental Settings
/// </summary>
public static class SteamScreenshots
public class SteamScreenshots : SteamClass
{
static ISteamScreenshots _internal;
internal static ISteamScreenshots Internal
{
get
{
SteamClient.ValidCheck();
internal static ISteamScreenshots Internal;
internal override SteamInterface Interface => Internal;
if ( _internal == null )
{
_internal = new ISteamScreenshots();
_internal.Init();
}
return _internal;
}
}
internal static void Shutdown()
internal override void InitializeInterface( bool server )
{
_internal = null;
Internal = new ISteamScreenshots( server );
InstallEvents();
}
internal static void InstallEvents()
{
ScreenshotRequested_t.Install( x => OnScreenshotRequested?.Invoke() );
ScreenshotReady_t.Install( x =>
Dispatch.Install<ScreenshotRequested_t>( x => OnScreenshotRequested?.Invoke() );
Dispatch.Install<ScreenshotReady_t>( x =>
{
if ( x.Result != Result.OK )
OnScreenshotFailed?.Invoke( x.Result );

View File

@ -10,26 +10,18 @@ namespace Steamworks
/// <summary>
/// Provides the core of the Steam Game Servers API
/// </summary>
public static partial class SteamServer
public partial class SteamServer : SteamClass
{
static bool initialized;
internal static ISteamGameServer Internal;
internal override SteamInterface Interface => Internal;
static ISteamGameServer _internal;
internal static ISteamGameServer Internal
internal override void InitializeInterface( bool server )
{
get
{
if ( _internal == null )
{
_internal = new ISteamGameServer( );
_internal.InitServer();
}
return _internal;
}
Internal = new ISteamGameServer( server );
InstallEvents();
}
public static bool IsValid => initialized;
public static bool IsValid => Internal != null && Internal.IsValid;
public static Action<Exception> OnCallbackException;
@ -37,12 +29,12 @@ namespace Steamworks
internal static void InstallEvents()
{
SteamInventory.InstallEvents();
SteamNetworkingSockets.InstallEvents(true);
//SteamNetworkingSockets.InstallEvents(true);
ValidateAuthTicketResponse_t.Install( x => OnValidateAuthTicketResponse?.Invoke( x.SteamID, x.OwnerSteamID, x.AuthSessionResponse ), true );
SteamServersConnected_t.Install( x => OnSteamServersConnected?.Invoke(), true );
SteamServerConnectFailure_t.Install( x => OnSteamServerConnectFailure?.Invoke( x.Result, x.StillRetrying ), true );
SteamServersDisconnected_t.Install( x => OnSteamServersDisconnected?.Invoke( x.Result ), true );
Dispatch.Install<ValidateAuthTicketResponse_t>( x => OnValidateAuthTicketResponse?.Invoke( x.SteamID, x.OwnerSteamID, x.AuthSessionResponse ), true );
Dispatch.Install<SteamServersConnected_t>( x => OnSteamServersConnected?.Invoke(), true );
Dispatch.Install<SteamServerConnectFailure_t>( x => OnSteamServerConnectFailure?.Invoke( x.Result, x.StillRetrying ), true );
Dispatch.Install<SteamServersDisconnected_t>( x => OnSteamServersDisconnected?.Invoke( x.Result ), true );
}
/// <summary>
@ -93,7 +85,17 @@ namespace Steamworks
throw new System.Exception( $"InitGameServer returned false ({ipaddress},{init.SteamPort},{init.GamePort},{init.QueryPort},{secure},\"{init.VersionString}\")" );
}
initialized = true;
//
// Dispatch is responsible for pumping the
// event loop.
//
Dispatch.Init();
Dispatch.ServerPipe = SteamGameServer.GetHSteamPipe();
Console.WriteLine( $"Dispatch.ServerPipe = {Dispatch.ServerPipe.Value}" );
AddInterface<SteamServer>();
AddInterface<SteamNetworkingUtils>();
AddInterface<SteamNetworkingSockets>();
//
// Initial settings
@ -111,71 +113,50 @@ namespace Steamworks
if ( asyncCallbacks )
{
RunCallbacksAsync();
//
// This will keep looping in the background every 16 ms
// until we shut down.
//
Dispatch.LoopServerAsync();
}
}
static List<SteamInterface> openIterfaces = new List<SteamInterface>();
internal static void WatchInterface( SteamInterface steamInterface )
internal static void AddInterface<T>() where T : SteamClass, new()
{
if ( openIterfaces.Contains( steamInterface ) )
throw new System.Exception( "openIterfaces already contains interface!" );
openIterfaces.Add( steamInterface );
var t = new T();
t.InitializeInterface( true );
openInterfaces.Add( t );
}
static List<SteamClass> openInterfaces = new List<SteamClass>();
internal static void ShutdownInterfaces()
{
foreach ( var e in openIterfaces )
foreach ( var e in openInterfaces )
{
e.Shutdown();
e.DestroyInterface();
}
openIterfaces.Clear();
openInterfaces.Clear();
}
public static void Shutdown()
{
Event.DisposeAllServer();
initialized = false;
_internal = null;
Internal = null;
ShutdownInterfaces();
SteamNetworkingUtils.Shutdown();
SteamNetworkingSockets.Shutdown();
SteamInventory.Shutdown();
SteamServerStats.Shutdown();
SteamGameServer.Shutdown();
}
internal static async void RunCallbacksAsync()
{
while ( IsValid )
{
try
{
RunCallbacks();
}
catch ( System.Exception e )
{
OnCallbackException?.Invoke( e );
}
await Task.Delay( 16 );
}
}
/// <summary>
/// Run the callbacks. This is also called in Async callbacks.
/// </summary>
public static void RunCallbacks()
{
SteamGameServer.RunCallbacks();
if ( Dispatch.ServerPipe != 0 )
{
Dispatch.Frame( Dispatch.ServerPipe );
}
}
/// <summary>
@ -315,16 +296,7 @@ namespace Steamworks
/// current public ip address. Be aware that this is likely to return
/// null for the first few seconds after initialization.
/// </summary>
public static System.Net.IPAddress PublicIp
{
get
{
var ip = Internal.GetPublicIP();
if ( ip == 0 ) return null;
return Utility.Int32ToIp( ip );
}
}
public static System.Net.IPAddress PublicIp => Internal.GetPublicIP();
/// <summary>
/// Enable or disable heartbeats, which are sent regularly to the master server.

View File

@ -7,26 +7,14 @@ using Steamworks.Data;
namespace Steamworks
{
public static class SteamServerStats
public class SteamServerStats : SteamClass
{
static ISteamGameServerStats _internal;
internal static ISteamGameServerStats Internal
{
get
{
if ( _internal == null )
{
_internal = new ISteamGameServerStats();
_internal.InitServer();
}
internal static ISteamGameServerStats Internal;
internal override SteamInterface Interface => Internal;
return _internal;
}
}
internal static void Shutdown()
internal override void InitializeInterface( bool server )
{
_internal = null;
Internal = new ISteamGameServerStats( server );
}
/// <summary>
@ -47,7 +35,7 @@ namespace Steamworks
/// </summary>
public static bool SetInt( SteamId steamid, string name, int stat )
{
return Internal.SetUserStat1( steamid, name, stat );
return Internal.SetUserStat( steamid, name, stat );
}
/// <summary>
@ -56,7 +44,7 @@ namespace Steamworks
/// </summary>
public static bool SetFloat( SteamId steamid, string name, float stat )
{
return Internal.SetUserStat2( steamid, name, stat );
return Internal.SetUserStat( steamid, name, stat );
}
/// <summary>
@ -68,7 +56,7 @@ namespace Steamworks
{
int data = defaultValue;
if ( !Internal.GetUserStat1( steamid, name, ref data ) )
if ( !Internal.GetUserStat( steamid, name, ref data ) )
return defaultValue;
return data;
@ -83,7 +71,7 @@ namespace Steamworks
{
float data = defaultValue;
if ( !Internal.GetUserStat2( steamid, name, ref data ) )
if ( !Internal.GetUserStat( steamid, name, ref data ) )
return defaultValue;
return data;

View File

@ -12,26 +12,20 @@ namespace Steamworks
/// Functions for accessing and manipulating Steam user information.
/// This is also where the APIs for Steam Voice are exposed.
/// </summary>
public static class SteamUGC
public class SteamUGC : SteamClass
{
static ISteamUGC _internal;
internal static ISteamUGC Internal
{
get
{
if ( _internal == null )
{
_internal = new ISteamUGC();
_internal.Init();
}
internal static ISteamUGC Internal;
internal override SteamInterface Interface => Internal;
return _internal;
}
internal override void InitializeInterface( bool server )
{
Internal = new ISteamUGC( server );
InstallEvents();
}
internal static void InstallEvents()
{
DownloadItemResult_t.Install( x => OnDownloadItemResult?.Invoke( x.Result ) );
Dispatch.Install<DownloadItemResult_t>( x => OnDownloadItemResult?.Invoke( x.Result ) );
}
/// <summary>
@ -39,11 +33,6 @@ namespace Steamworks
/// </summary>
public static event Action<Result> OnDownloadItemResult;
internal static void Shutdown()
{
_internal = null;
}
public static async Task<bool> DeleteFileAsync( PublishedFileId fileId )
{
var r = await Internal.DeleteItem( fileId );

View File

@ -13,46 +13,33 @@ namespace Steamworks
/// Functions for accessing and manipulating Steam user information.
/// This is also where the APIs for Steam Voice are exposed.
/// </summary>
public static class SteamUser
public class SteamUser : SteamClass
{
static ISteamUser _internal;
internal static ISteamUser Internal
internal static ISteamUser Internal;
internal override SteamInterface Interface => Internal;
internal override void InitializeInterface( bool server )
{
get
{
SteamClient.ValidCheck();
Internal = new ISteamUser( server );
InstallEvents();
if ( _internal == null )
{
_internal = new ISteamUser();
_internal.Init();
richPresence = new Dictionary<string, string>();
SampleRate = OptimalSampleRate;
}
return _internal;
}
}
internal static void Shutdown()
{
_internal = null;
richPresence = new Dictionary<string, string>();
SampleRate = OptimalSampleRate;
}
static Dictionary<string, string> richPresence;
internal static void InstallEvents()
{
SteamServersConnected_t.Install( x => OnSteamServersConnected?.Invoke() );
SteamServerConnectFailure_t.Install( x => OnSteamServerConnectFailure?.Invoke() );
SteamServersDisconnected_t.Install( x => OnSteamServersDisconnected?.Invoke() );
ClientGameServerDeny_t.Install( x => OnClientGameServerDeny?.Invoke() );
LicensesUpdated_t.Install( x => OnLicensesUpdated?.Invoke() );
ValidateAuthTicketResponse_t.Install( x => OnValidateAuthTicketResponse?.Invoke( x.SteamID, x.OwnerSteamID, x.AuthSessionResponse ) );
MicroTxnAuthorizationResponse_t.Install( x => OnMicroTxnAuthorizationResponse?.Invoke( x.AppID, x.OrderID, x.Authorized != 0 ) );
GameWebCallback_t.Install( x => OnGameWebCallback?.Invoke( x.URLUTF8() ) );
GetAuthSessionTicketResponse_t.Install( x => OnGetAuthSessionTicketResponse?.Invoke( x ) );
Dispatch.Install<SteamServersConnected_t>( x => OnSteamServersConnected?.Invoke() );
Dispatch.Install<SteamServerConnectFailure_t>( x => OnSteamServerConnectFailure?.Invoke() );
Dispatch.Install<SteamServersDisconnected_t>( x => OnSteamServersDisconnected?.Invoke() );
Dispatch.Install<ClientGameServerDeny_t>( x => OnClientGameServerDeny?.Invoke() );
Dispatch.Install<LicensesUpdated_t>( x => OnLicensesUpdated?.Invoke() );
Dispatch.Install<ValidateAuthTicketResponse_t>( x => OnValidateAuthTicketResponse?.Invoke( x.SteamID, x.OwnerSteamID, x.AuthSessionResponse ) );
Dispatch.Install<MicroTxnAuthorizationResponse_t>( x => OnMicroTxnAuthorizationResponse?.Invoke( x.AppID, x.OrderID, x.Authorized != 0 ) );
Dispatch.Install<GameWebCallback_t>( x => OnGameWebCallback?.Invoke( x.URLUTF8() ) );
Dispatch.Install<GetAuthSessionTicketResponse_t>( x => OnGetAuthSessionTicketResponse?.Invoke( x ) );
}
/// <summary>
@ -424,6 +411,7 @@ namespace Steamworks
/// Requests an application ticket encrypted with the secret "encrypted app ticket key".
/// The encryption key can be obtained from the Encrypted App Ticket Key page on the App Admin for your app.
/// There can only be one call pending, and this call is subject to a 60 second rate limit.
/// If you get a null result from this it's probably because you're calling it too often.
/// This can fail if you don't have an encrypted ticket set for your app here https://partner.steamgames.com/apps/sdkauth/
/// </summary>
public static async Task<byte[]> RequestEncryptedAppTicketAsync( byte[] dataToInclude )

View File

@ -7,36 +7,24 @@ using Steamworks.Data;
namespace Steamworks
{
public static class SteamUserStats
public class SteamUserStats : SteamClass
{
static ISteamUserStats _internal;
internal static ISteamUserStats Internal
internal static ISteamUserStats Internal;
internal override SteamInterface Interface => Internal;
internal override void InitializeInterface( bool server )
{
get
{
SteamClient.ValidCheck();
Internal = new ISteamUserStats( server );
if ( _internal == null )
{
_internal = new ISteamUserStats();
_internal.Init();
RequestCurrentStats();
}
return _internal;
}
}
internal static void Shutdown()
{
_internal = null;
InstallEvents();
RequestCurrentStats();
}
public static bool StatsRecieved { get; internal set; }
internal static void InstallEvents()
{
UserStatsReceived_t.Install( x =>
Dispatch.Install<UserStatsReceived_t>( x =>
{
if ( x.SteamIDUser == SteamClient.SteamId )
StatsRecieved = true;
@ -44,10 +32,10 @@ namespace Steamworks
OnUserStatsReceived?.Invoke( x.SteamIDUser, x.Result );
} );
UserStatsStored_t.Install( x => OnUserStatsStored?.Invoke( x.Result ) );
UserAchievementStored_t.Install( x => OnAchievementProgress?.Invoke( new Achievement( x.AchievementNameUTF8() ), (int) x.CurProgress, (int)x.MaxProgress ) );
UserStatsUnloaded_t.Install( x => OnUserStatsUnloaded?.Invoke( x.SteamIDUser ) );
UserAchievementIconFetched_t.Install( x => OnAchievementIconFetched?.Invoke( x.AchievementNameUTF8(), x.IconHandle ) );
Dispatch.Install<UserStatsStored_t>( x => OnUserStatsStored?.Invoke( x.Result ) );
Dispatch.Install<UserAchievementStored_t>( x => OnAchievementProgress?.Invoke( new Achievement( x.AchievementNameUTF8() ), (int) x.CurProgress, (int)x.MaxProgress ) );
Dispatch.Install<UserStatsUnloaded_t>( x => OnUserStatsUnloaded?.Invoke( x.SteamIDUser ) );
Dispatch.Install<UserAchievementIconFetched_t>( x => OnAchievementIconFetched?.Invoke( x.AchievementNameUTF8(), x.IconHandle ) );
}
@ -209,7 +197,7 @@ namespace Steamworks
/// </summary>
public static bool SetStat( string name, int value )
{
return Internal.SetStat1( name, value );
return Internal.SetStat( name, value );
}
/// <summary>
@ -218,7 +206,7 @@ namespace Steamworks
/// </summary>
public static bool SetStat( string name, float value )
{
return Internal.SetStat2( name, value );
return Internal.SetStat( name, value );
}
/// <summary>
@ -227,7 +215,7 @@ namespace Steamworks
public static int GetStatInt( string name )
{
int data = 0;
Internal.GetStat1( name, ref data );
Internal.GetStat( name, ref data );
return data;
}
@ -237,7 +225,7 @@ namespace Steamworks
public static float GetStatFloat( string name )
{
float data = 0;
Internal.GetStat2( name, ref data );
Internal.GetStat( name, ref data );
return data;
}

View File

@ -10,34 +10,24 @@ namespace Steamworks
/// <summary>
/// Interface which provides access to a range of miscellaneous utility functions
/// </summary>
public static class SteamUtils
public class SteamUtils : SteamClass
{
static ISteamUtils _internal;
internal static ISteamUtils Internal
{
get
{
if ( _internal == null )
{
_internal = new ISteamUtils();
_internal.Init();
}
internal static ISteamUtils Internal;
internal override SteamInterface Interface => Internal;
return _internal;
}
}
internal static void Shutdown()
internal override void InitializeInterface( bool server )
{
_internal = null;
Internal = new ISteamUtils( server );
InstallEvents();
}
internal static void InstallEvents()
{
IPCountry_t.Install( x => OnIpCountryChanged?.Invoke() );
LowBatteryPower_t.Install( x => OnLowBatteryPower?.Invoke( x.MinutesBatteryLeft ) );
SteamShutdown_t.Install( x => SteamClosed() );
GamepadTextInputDismissed_t.Install( x => OnGamepadTextInputDismissed?.Invoke( x.Submitted ) );
Dispatch.Install<IPCountry_t>( x => OnIpCountryChanged?.Invoke() );
Dispatch.Install<LowBatteryPower_t>( x => OnLowBatteryPower?.Invoke( x.MinutesBatteryLeft ) );
Dispatch.Install<SteamShutdown_t>( x => SteamClosed() );
Dispatch.Install<GamepadTextInputDismissed_t>( x => OnGamepadTextInputDismissed?.Invoke( x.Submitted ) );
}
private static void SteamClosed()

View File

@ -10,34 +10,22 @@ namespace Steamworks
/// <summary>
/// Undocumented Parental Settings
/// </summary>
public static class SteamVideo
public class SteamVideo : SteamClass
{
static ISteamVideo _internal;
internal static ISteamVideo Internal
internal static ISteamVideo Internal;
internal override SteamInterface Interface => Internal;
internal override void InitializeInterface( bool server )
{
get
{
SteamClient.ValidCheck();
Internal = new ISteamVideo( server );
if ( _internal == null )
{
_internal = new ISteamVideo();
_internal.Init();
}
return _internal;
}
}
internal static void Shutdown()
{
_internal = null;
InstallEvents();
}
internal static void InstallEvents()
{
BroadcastUploadStart_t.Install( x => OnBroadcastStarted?.Invoke() );
BroadcastUploadStop_t.Install( x => OnBroadcastStopped?.Invoke( x.Result ) );
Dispatch.Install<BroadcastUploadStart_t>( x => OnBroadcastStarted?.Invoke() );
Dispatch.Install<BroadcastUploadStop_t>( x => OnBroadcastStopped?.Invoke( x.Result ) );
}
public static event Action OnBroadcastStarted;

View File

@ -5,9 +5,11 @@ namespace Steamworks.Data
{
public struct Connection
{
public uint Id { get; }
public uint Id { get; set; }
public override string ToString() => Id.ToString();
public static implicit operator Connection( uint value ) => new Connection() { Id = value };
public static implicit operator uint( Connection value ) => value.Id;
/// <summary>
/// Accept an incoming connection that has been received on a listen socket.
@ -54,7 +56,8 @@ namespace Steamworks.Data
public Result SendMessage( IntPtr ptr, int size, SendType sendType = SendType.Reliable )
{
return SteamNetworkingSockets.Internal.SendMessageToConnection( this, ptr, (uint) size, (int)sendType );
long messageNumber = 0;
return SteamNetworkingSockets.Internal.SendMessageToConnection( this, ptr, (uint) size, (int)sendType, ref messageNumber );
}
public unsafe Result SendMessage( byte[] data, SendType sendType = SendType.Reliable )
@ -91,27 +94,5 @@ namespace Steamworks.Data
return strVal;
}
/*
[ThreadStatic]
private static SteamNetworkingMessage_t[] messageBuffer;
public IEnumerable<SteamNetworkingMessage_t> Messages
{
get
{
if ( messageBuffer == null )
messageBuffer = new SteamNetworkingMessage_t[128];
var num = SteamNetworkingSockets.Internal.ReceiveMessagesOnConnection( this, ref messageBuffer, messageBuffer.Length );
for ( int i = 0; i < num; i++)
{
yield return messageBuffer[i];
messageBuffer[i].Release();
}
}
}*/
}
}
}

View File

@ -130,17 +130,20 @@ namespace Steamworks
{
if ( Properties == null ) return DateTime.UtcNow;
var str = Properties["acquired"];
if ( Properties.TryGetValue( "acquired", out var str ) )
{
var y = int.Parse( str.Substring( 0, 4 ) );
var m = int.Parse( str.Substring( 4, 2 ) );
var d = int.Parse( str.Substring( 6, 2 ) );
var y = int.Parse( str.Substring( 0, 4 ) );
var m = int.Parse( str.Substring( 4, 2 ) );
var d = int.Parse( str.Substring( 6, 2 ) );
var h = int.Parse( str.Substring( 9, 2 ) );
var mn = int.Parse( str.Substring( 11, 2 ) );
var s = int.Parse( str.Substring( 13, 2 ) );
var h = int.Parse( str.Substring( 9, 2 ) );
var mn = int.Parse( str.Substring( 11, 2 ) );
var s = int.Parse( str.Substring( 13, 2 ) );
return new DateTime( y, m, d, h, mn, s, DateTimeKind.Utc );
}
return new DateTime( y, m, d, h, mn, s, DateTimeKind.Utc );
return DateTime.UtcNow;
}
}
@ -153,7 +156,11 @@ namespace Steamworks
get
{
if ( Properties == null ) return null;
return Properties["origin"];
if ( Properties.TryGetValue( "origin", out var str ) )
return str;
return null;
}
}

View File

@ -24,7 +24,12 @@ namespace Steamworks.Data
return value.steamID;
}
public override string ToString() => $"{type};{m_cbSize};{steamID}";
public override string ToString()
{
var id = this;
SteamNetworkingUtils.Internal.SteamNetworkingIdentity_ToString( ref id, out var str );
return str;
}
internal enum IdentityType
{

View File

@ -0,0 +1,31 @@
using Steamworks.Data;
using System;
using System.Runtime.InteropServices;
namespace Steamworks.Data
{
[StructLayout( LayoutKind.Explicit, Pack = Platform.StructPlatformPackSize )]
internal struct NetKeyValue
{
[FieldOffset(0)]
internal NetConfig Value; // m_eValue ESteamNetworkingConfigValue
[FieldOffset( 4 )]
internal NetConfigType DataType; // m_eDataType ESteamNetworkingConfigDataType
[FieldOffset( 8 )]
internal long Int64Value; // m_int64 int64_t
[FieldOffset( 8 )]
internal int Int32Value; // m_val_int32 int32_t
[FieldOffset( 8 )]
internal float FloatValue; // m_val_float float
[FieldOffset( 8 )]
internal IntPtr PointerValue; // m_val_functionPtr void *
// TODO - support strings, maybe
}
}

View File

@ -1,9 +1,15 @@
namespace Steamworks.Data

using System.Runtime.InteropServices;
namespace Steamworks.Data
{
[StructLayout( LayoutKind.Sequential )]
public struct Socket
{
internal uint Id;
public override string ToString() => Id.ToString();
public static implicit operator Socket( uint value ) => new Socket() { Id = value };
public static implicit operator uint( Socket value ) => value.Id;
/// <summary>
/// Destroy a listen socket. All the connections that were accepting on the listen
@ -11,7 +17,7 @@
/// </summary>
public bool Close()
{
return SteamNetworkingSockets.Internal.CloseListenSocket( this );
return SteamNetworkingSockets.Internal.CloseListenSocket( Id );
}
public SocketInterface Interface

View File

@ -36,7 +36,7 @@ namespace Steamworks.Data
{
double val = 0.0;
if ( SteamUserStats.Internal.GetGlobalStat2( Name, ref val ) )
if ( SteamUserStats.Internal.GetGlobalStat( Name, ref val ) )
return val;
return 0;
@ -45,7 +45,7 @@ namespace Steamworks.Data
public long GetGlobalInt()
{
long val = 0;
SteamUserStats.Internal.GetGlobalStat1( Name, ref val );
SteamUserStats.Internal.GetGlobalStat( Name, ref val );
return val;
}
@ -56,7 +56,7 @@ namespace Steamworks.Data
var r = new long[days];
var rows = SteamUserStats.Internal.GetGlobalStatHistory1( Name, r, (uint) r.Length * sizeof(long) );
var rows = SteamUserStats.Internal.GetGlobalStatHistory( Name, r, (uint) r.Length * sizeof(long) );
if ( days != rows )
r = r.Take( rows ).ToArray();
@ -71,7 +71,7 @@ namespace Steamworks.Data
var r = new double[days];
var rows = SteamUserStats.Internal.GetGlobalStatHistory2( Name, r, (uint)r.Length * sizeof( double ) );
var rows = SteamUserStats.Internal.GetGlobalStatHistory( Name, r, (uint)r.Length * sizeof( double ) );
if ( days != rows )
r = r.Take( rows ).ToArray();
@ -85,11 +85,11 @@ namespace Steamworks.Data
if ( UserId > 0 )
{
SteamUserStats.Internal.GetUserStat2( UserId, Name, ref val );
SteamUserStats.Internal.GetUserStat( UserId, Name, ref val );
}
else
{
SteamUserStats.Internal.GetStat2( Name, ref val );
SteamUserStats.Internal.GetStat( Name, ref val );
}
return 0;
@ -101,11 +101,11 @@ namespace Steamworks.Data
if ( UserId > 0 )
{
SteamUserStats.Internal.GetUserStat1( UserId, Name, ref val );
SteamUserStats.Internal.GetUserStat( UserId, Name, ref val );
}
else
{
SteamUserStats.Internal.GetStat1( Name, ref val );
SteamUserStats.Internal.GetStat( Name, ref val );
}
return val;
@ -114,13 +114,13 @@ namespace Steamworks.Data
public bool Set( int val )
{
LocalUserOnly();
return SteamUserStats.Internal.SetStat1( Name, val );
return SteamUserStats.Internal.SetStat( Name, val );
}
public bool Set( float val )
{
LocalUserOnly();
return SteamUserStats.Internal.SetStat2( Name, val );
return SteamUserStats.Internal.SetStat( Name, val );
}
public bool Add( int val )

View File

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace Steamworks.Data
{
[StructLayout( LayoutKind.Explicit, Pack = Platform.StructPlatformPackSize )]
internal struct SteamIPAddress
{
[FieldOffset( 0 )]
public uint Ip4Address; // Host Order
[FieldOffset( 16 )]
internal SteamIPType Type; // m_eType ESteamIPType
public static implicit operator System.Net.IPAddress( SteamIPAddress value )
{
if ( value.Type == SteamIPType.Type4 )
return Utility.Int32ToIp( value.Ip4Address );
throw new System.Exception( $"Oops - can't convert SteamIPAddress to System.Net.IPAddress because no-one coded support for {value.Type} yet" );
}
}
}

View File

@ -7,23 +7,6 @@ namespace Steamworks.Data
{
delegate void FSteamNetworkingSocketsDebugOutput (DebugOutputType nType, string pszMsg );
public struct SteamNetworkingPOPID
{
public uint Value;
public static implicit operator SteamNetworkingPOPID( uint value )
{
return new SteamNetworkingPOPID { Value = value };
}
public static implicit operator uint( SteamNetworkingPOPID value )
{
return value.Value;
}
public override string ToString() => Value.ToString();
}
[StructLayout( LayoutKind.Sequential )]
public struct SteamNetworkingQuickConnectionStatus
{
@ -49,9 +32,4 @@ namespace Steamworks.Data
{
// Not implemented
};
struct SteamDatagramHostedAddress
{
// Not implemented
}
}

View File

@ -0,0 +1,11 @@
using Steamworks.Data;
namespace Steamworks.Data
{
internal unsafe struct SteamNetworkingErrMsg
{
public fixed char Value[1024];
}
}

View File

@ -204,7 +204,7 @@ namespace Steamworks.Ugc
progress?.Report( 1 );
var updated = updating.Result;
var updated = updating.GetResult();
if ( !updated.HasValue ) return result;

View File

@ -124,7 +124,7 @@ namespace Steamworks.Ugc
}
else
{
handle = SteamUGC.Internal.CreateQueryAllUGCRequest1( queryType, matchingType, creatorApp.Value, consumerApp.Value, (uint)page );
handle = SteamUGC.Internal.CreateQueryAllUGCRequest( queryType, matchingType, creatorApp.Value, consumerApp.Value, (uint)page );
}
ApplyReturns(handle);

View File

@ -13,32 +13,21 @@ namespace Steamworks
#if PLATFORM_WIN64
public const int StructPlatformPackSize = 8;
public const string LibraryName = "steam_api64";
public const CallingConvention MemberConvention = CallingConvention.Cdecl;
public const CallingConvention CC = CallingConvention.Cdecl;
#elif PLATFORM_WIN32
public const int StructPlatformPackSize = 8;
public const string LibraryName = "steam_api";
public const CallingConvention MemberConvention = CallingConvention.ThisCall;
public const CallingConvention CC = CallingConvention.Cdecl;
#elif PLATFORM_POSIX32
public const int StructPlatformPackSize = 4;
public const string LibraryName = "libsteam_api";
public const CallingConvention MemberConvention = CallingConvention.Cdecl;
public const CallingConvention CC = CallingConvention.Cdecl;
#elif PLATFORM_POSIX64
public const int StructPlatformPackSize = 4;
public const string LibraryName = "libsteam_api";
public const CallingConvention MemberConvention = CallingConvention.Cdecl;
public const CallingConvention CC = CallingConvention.Cdecl;
#endif
public const int StructPackSize = 4;
public static int MemoryOffset( int memLocation )
{
#if PLATFORM_64
return memLocation;
#else
return memLocation / 2;
#endif
}
}
}

View File

@ -57,7 +57,7 @@ namespace Steamworks
return await GetRules(client);
}
}
catch (System.Exception e)
catch (System.Exception)
{
//Console.Error.WriteLine( e.Message );
return null;

View File

@ -11,103 +11,42 @@ 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 virtual string InterfaceName => null;
public bool IsValid => Self != IntPtr.Zero && VTable != IntPtr.Zero;
public bool IsValid => Self != IntPtr.Zero;
public void Init()
internal void SetupInterface( bool gameServer )
{
if ( SteamClient.IsValid )
{
InitClient();
Self = GetGlobalInterfacePointer();
if ( Self != IntPtr.Zero )
return;
}
if ( SteamServer.IsValid )
{
InitServer();
return;
}
throw new System.Exception( "Trying to initialize Steam Interface but Steam not initialized" );
if ( gameServer )
Self = GetServerInterfacePointer();
else
Self = GetUserInterfacePointer();
}
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 )
{
var user = SteamAPI.GetHSteamUser();
Self = SteamInternal.FindOrCreateUserInterface( user, 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();
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 virtual void DestroyInterface()
{
Interface.ShutdownInterface();
}
public abstract void InitInternals();
internal abstract SteamInterface Interface { get; }
}
}

View File

@ -3,12 +3,21 @@ 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 uint Swap( uint x )
{
return ((x & 0x000000ff) << 24) +

Binary file not shown.

Binary file not shown.

Binary file not shown.