This commit is contained in:
Garry Newman 2020-02-19 17:00:26 +00:00
parent 0d98fd9de9
commit 0ea03e1499
19 changed files with 353 additions and 440 deletions

View File

@ -8,6 +8,9 @@ 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;
@ -15,14 +18,21 @@ namespace Steamworks
public CallResult( SteamAPICall_t call )
{
this.call = call;
Console.WriteLine( $"{this.GetType().ToString()} == {call.Value}" );
}
/// <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 )
@ -34,7 +44,7 @@ namespace Steamworks
try
{
if ( !SteamUtils.Internal.GetAPICallResult( call, ptr, size, t.CallbackId, ref failed ) || failed )
if ( !SteamUtils.Internal.GetAPICallResult( call, ptr, size, (int) t.CallbackType, ref failed ) || failed )
return null;
return ((T)Marshal.PtrToStructure( ptr, typeof( T ) ));
@ -45,6 +55,9 @@ namespace Steamworks
}
}
/// <summary>
/// Return true if complete or failed
/// </summary>
public bool IsCompleted
{
get
@ -56,6 +69,9 @@ namespace Steamworks
}
}
/// <summary>
/// This is what makes this struct awaitable
/// </summary>
internal CallResult<T> GetAwaiter()
{
return this;

View File

@ -6,12 +6,12 @@ using System.Threading.Tasks;
namespace Steamworks
{
//
// Created on registration of a callback
//
/// <summary>
/// Gives us a generic way to get the CallbackId of structs
/// </summary>
internal interface ICallbackData
{
int CallbackId { get; }
CallbackType CallbackType { get; }
int DataSize { get; }
}
}

View File

@ -7,6 +7,10 @@ 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
@ -38,20 +42,19 @@ namespace Steamworks
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();
}
public static void Frame()
{
if ( ClientPipe != 0 )
Frame( ClientPipe );
if ( ServerPipe != 0)
Frame( ServerPipe );
}
/// <summary>
/// Calls RunFrame and processes events from this Steam Pipe
/// </summary>
public static void Frame( HSteamPipe pipe )
{
SteamAPI_ManualDispatch_RunFrame( pipe );
@ -71,16 +74,18 @@ namespace Steamworks
}
}
/// <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;
}
Console.WriteLine( $"Callback: {msg.Type}" );
if ( Callbacks.TryGetValue( msg.Type, out var list ) )
{
foreach ( var item in list )
@ -90,12 +95,13 @@ namespace Steamworks
}
}
/// <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>();
Console.WriteLine( $"Result: {result.AsyncCall} / {result.Callback}" );
//
// Do we have an entry added via OnCallComplete
//
@ -105,6 +111,7 @@ namespace Steamworks
return;
}
// Remove it before we do anything, incase the continuation throws exceptions
ResultCallbacks.Remove( result.AsyncCall );
// At this point whatever async routine called this
@ -112,6 +119,11 @@ namespace Steamworks
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 )
@ -119,10 +131,13 @@ namespace Steamworks
Frame( ClientPipe );
await Task.Delay( 16 );
}
Console.WriteLine( $"Exiting ClientPipe: {ClientPipe}" );
}
/// <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 )
@ -130,8 +145,6 @@ namespace Steamworks
Frame( ServerPipe );
await Task.Delay( 32 );
}
Console.WriteLine( $"Exiting ServerPipe: {ServerPipe}" );
}
struct ResultCallback
@ -160,10 +173,13 @@ namespace Steamworks
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 = (CallbackType)t.CallbackId;
var type = t.CallbackType;
if ( !Callbacks.TryGetValue( type, out var list ) )
{

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

@ -17,9 +17,6 @@ namespace Steamworks
[DllImport( Platform.LibraryName, EntryPoint = "SteamGameServer_Shutdown", CallingConvention = CallingConvention.Cdecl )]
public static extern void SteamGameServer_Shutdown();
[DllImport( Platform.LibraryName, EntryPoint = "SteamGameServer_GetHSteamUser", CallingConvention = CallingConvention.Cdecl )]
public static extern HSteamUser SteamGameServer_GetHSteamUser();
[DllImport( Platform.LibraryName, EntryPoint = "SteamGameServer_GetHSteamPipe", CallingConvention = CallingConvention.Cdecl )]
public static extern HSteamPipe SteamGameServer_GetHSteamPipe();
@ -34,11 +31,6 @@ namespace Steamworks
Native.SteamGameServer_Shutdown();
}
static internal HSteamUser GetHSteamUser()
{
return Native.SteamGameServer_GetHSteamUser();
}
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,98 +0,0 @@
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_RunCallbacks", CallingConvention = CallingConvention.Cdecl )]
public static extern void SteamAPI_RunCallbacks();
[DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_RegisterCallback", CallingConvention = CallingConvention.Cdecl )]
public static extern void SteamAPI_RegisterCallback( IntPtr pCallback, int callback );
[DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_UnregisterCallback", CallingConvention = CallingConvention.Cdecl )]
public static extern void SteamAPI_UnregisterCallback( IntPtr pCallback );
[DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_RegisterCallResult", CallingConvention = CallingConvention.Cdecl )]
public static extern void SteamAPI_RegisterCallResult( IntPtr pCallback, SteamAPICall_t callback );
[DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_UnregisterCallResult", CallingConvention = CallingConvention.Cdecl )]
public static extern void SteamAPI_UnregisterCallResult( IntPtr pCallback, SteamAPICall_t callback );
[DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_Shutdown", CallingConvention = CallingConvention.Cdecl )]
public static extern void SteamAPI_Shutdown();
[DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_GetHSteamUser", CallingConvention = CallingConvention.Cdecl )]
public static extern HSteamUser SteamAPI_GetHSteamUser();
[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 RunCallbacks()
{
Native.SteamAPI_RunCallbacks();
}
static internal void RegisterCallback( IntPtr pCallback, int callback )
{
Native.SteamAPI_RegisterCallback( pCallback, callback );
}
static internal void UnregisterCallback( IntPtr pCallback )
{
Native.SteamAPI_UnregisterCallback( pCallback );
}
static internal void RegisterCallResult( IntPtr pCallback, SteamAPICall_t callback )
{
Native.SteamAPI_RegisterCallResult( pCallback, callback );
}
static internal void UnregisterCallResult( IntPtr pCallback, SteamAPICall_t callback )
{
Native.SteamAPI_UnregisterCallResult( pCallback, callback );
}
static internal void Shutdown()
{
Native.SteamAPI_Shutdown();
}
static internal HSteamUser GetHSteamUser()
{
return Native.SteamAPI_GetHSteamUser();
}
static internal HSteamPipe GetHSteamPipe()
{
return Native.SteamAPI_GetHSteamPipe();
}
static internal bool RestartAppIfNecessary( uint unOwnAppID )
{
return Native.SteamAPI_RestartAppIfNecessary( unOwnAppID );
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,49 +0,0 @@
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 );
[DllImport( Platform.LibraryName, EntryPoint = "SteamInternal_FindOrCreateUserInterface", CallingConvention = CallingConvention.Cdecl )]
public static extern IntPtr SteamInternal_FindOrCreateUserInterface( int steamuser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string versionname );
[DllImport( Platform.LibraryName, EntryPoint = "SteamInternal_FindOrCreateGameServerInterface", CallingConvention = CallingConvention.Cdecl )]
public static extern IntPtr SteamInternal_FindOrCreateGameServerInterface( int steamuser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string versionname );
[DllImport( Platform.LibraryName, EntryPoint = "SteamInternal_CreateInterface", CallingConvention = CallingConvention.Cdecl )]
public static extern IntPtr SteamInternal_CreateInterface( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string version );
}
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 );
}
static internal IntPtr FindOrCreateUserInterface( int steamuser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string versionname )
{
return Native.SteamInternal_FindOrCreateUserInterface( steamuser, versionname );
}
static internal IntPtr FindOrCreateGameServerInterface( int steamuser, [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string versionname )
{
return Native.SteamInternal_FindOrCreateGameServerInterface( steamuser, versionname );
}
static internal IntPtr CreateInterface( [MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] string version )
{
return Native.SteamInternal_CreateInterface( version );
}
}
}

View File

@ -109,22 +109,12 @@ namespace Steamworks
ShutdownInterfaces();
}
internal static void RegisterCallback( IntPtr intPtr, int callbackId )
{
SteamAPI.RegisterCallback( intPtr, callbackId );
}
public static void RunCallbacks()
{
if ( Dispatch.ClientPipe != 0 )
Dispatch.Frame( Dispatch.ClientPipe );
}
internal static void UnregisterCallback( IntPtr intPtr )
{
SteamAPI.UnregisterCallback( intPtr );
}
/// <summary>
/// Checks if the current user's Steam client is connected to the Steam servers.
/// If it's not then no real-time services provided by the Steamworks API will be enabled. The Steam

View File

@ -26,7 +26,7 @@ namespace Steamworks
internal static void InstallEvents()
{
Dispatch.Install<FriendStateChange_t>( x => OnPersonaStateChange?.Invoke( new Friend( x.SteamID ) ) );
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() );

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

@ -19,7 +19,6 @@ public static class Cleanup
type = type.Replace( "CSteamID", "SteamId" );
type = type.Replace( "CGameID", "GameId" );
type = type.Replace( "PersonaState", "FriendState" );
type = type.Replace( "AudioPlayback_Status", "MusicStatus" );
type = type.Replace( "AuthSessionResponse", "AuthResponse" );
type = type.Replace( "FriendRelationship", "Relationship" );
@ -58,6 +57,10 @@ public static class Cleanup
type = type.Replace( "::", "." );
if ( type == "EPersonaState" ) return "EFriendState";
if ( type == "PersonaState" ) return "FriendState";
return type;
}
@ -86,6 +89,13 @@ public static class Cleanup
if ( type == "NetKeyValue" ) return false;
if ( type == "SteamIPAddress" ) return false;
if ( type == "PingLocation" ) return false;
if ( type == "CSteamID" ) return false;
if ( type == "CSteamAPIContext" ) return false;
if ( type == "CCallResult" ) return false;
if ( type == "CCallback" ) return false;
if ( type == "ValvePackingSentinel_t" ) return false;
if ( type == "CCallbackBase" ) return false;
if ( type == "CSteamGameServerAPIContext" ) return false;
return true;
}

View File

@ -76,7 +76,7 @@ namespace Generator
foreach ( var iface in def.Interfaces )
{
GenerateVTableClass( iface, $"{folder}../Generated/Interfaces/" );
GenerateInterface( iface, $"{folder}../Generated/Interfaces/" );
}
}

View File

@ -8,7 +8,7 @@ namespace Generator
{
public partial class CodeWriter
{
public void GenerateVTableClass( SteamApiDefinition.Interface iface, string folder )
public void GenerateInterface( SteamApiDefinition.Interface iface, string folder )
{
sb = new StringBuilder();

View File

@ -17,20 +17,6 @@ namespace Generator
private Dictionary<string, TypeDef> TypeDefs = new Dictionary<string, TypeDef>();
//
// Don't give a fuck about these classes, they just cause us trouble
//
public readonly static string[] SkipStructs = new string[]
{
"CSteamID",
"CSteamAPIContext",
"CCallResult",
"CCallback",
"ValvePackingSentinel_t",
"CCallbackBase",
"CSteamGameServerAPIContext"
};
public readonly static string[] ForceLargePackStructs = new string[]
{
"LeaderboardEntry_t"
@ -38,15 +24,10 @@ namespace Generator
void Structs()
{
var callbackList = new List<SteamApiDefinition.StructDef>();
foreach ( var c in def.structs )
{
var name = Cleanup.ConvertType( c.Name );
if ( SkipStructs.Contains( c.Name ) )
continue;
if ( !Cleanup.ShouldCreate( name ) )
continue;

View File

@ -16,9 +16,6 @@ namespace Generator
{
var name = Cleanup.ConvertType( c.Name );
if ( SkipStructs.Contains( c.Name ) )
continue;
if ( !Cleanup.ShouldCreate( name ) )
continue;
@ -51,7 +48,7 @@ namespace Generator
WriteLine( $"public static int _datasize = System.Runtime.InteropServices.Marshal.SizeOf( typeof({name}) );" );
WriteLine( $"public int DataSize => _datasize;" );
WriteLine( $"public int CallbackId => {c.CallbackId};" );
WriteLine( $"public CallbackType CallbackType => CallbackType.{name.Replace( "_t", "" )};" );
}
WriteLine( "#endregion" );
}

View File

@ -47,7 +47,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="CodeWriter\ClassVTable.cs" />
<Compile Include="CodeWriter\Interface.cs" />
<Compile Include="CodeWriter\Constants.cs" />
<Compile Include="CodeWriter\CustomEnums.cs" />
<Compile Include="CodeWriter\StructCallbacks.cs" />

View File

@ -16,28 +16,12 @@ namespace Generator
var content = System.IO.File.ReadAllText( "steam_api.json" );
var def = Newtonsoft.Json.JsonConvert.DeserializeObject<SteamApiDefinition>( content );
// AddMissing( def );
Definitions = def;
var generator = new CodeWriter( def );
generator.ToFolder( "../Facepunch.Steamworks/Generated/" );
}
private static void AddMissing( SteamApiDefinition output )
{
var content = System.IO.File.ReadAllText( "steam_api_missing.json" );
var missing = Newtonsoft.Json.JsonConvert.DeserializeObject<SteamApiDefinition>( content );
output.structs.AddRange( missing.structs );
// output.methods.AddRange( missing.methods );
foreach ( var s in output.structs )
{
if ( s.Fields == null ) s.Fields = new SteamApiDefinition.StructDef.StructFields[0];
}
}
}
}