Server Auth callbacks

This commit is contained in:
Garry Newman 2016-10-03 15:25:19 +01:00
parent c2270c6840
commit c3a1bf0bcf
8 changed files with 346 additions and 21 deletions

View File

@ -24,40 +24,88 @@ public void Init()
public void AuthCallback()
{
using ( var client = new Facepunch.Steamworks.Client( 252490 ) )
{
using ( var server = new Facepunch.Steamworks.Server( 252490, 0, 30001, 30002, 30003, true, "VersionString" ) )
{
Assert.IsTrue( client.Valid );
var ticket = client.Auth.GetAuthSessionTicket();
var ticketBinary = ticket.Data;
using ( var server = new Facepunch.Steamworks.Server( 252490, 30000, 30001, 30002, 30003, false, "VersionString" ) )
{
Assert.IsTrue( server.Valid );
var auth = server.Auth;
var Authed = false;
server.Auth.OnAuthChange = ( steamid, ownerid, status ) =>
{
Authed = status == ServerAuth.Status.OK;
Assert.AreEqual( steamid, client.SteamId );
Console.WriteLine( "steamid: {0}", steamid );
Console.WriteLine( "ownerid: {0}", ownerid );
Console.WriteLine( "status: {0}", status );
};
for ( int i = 0; i < 16; i++ )
{
System.Threading.Thread.Sleep( 10 );
GC.Collect();
server.Update();
GC.Collect();
client.Update();
GC.Collect();
}
GC.Collect();
if ( !server.Auth.StartSession( ticketBinary, client.SteamId ) )
{
Assert.Fail( "Start Session returned false" );
}
GC.Collect();
for( int i = 0; i<16; i++ )
//
// Server should receive a ServerAuth.Status.OK
// message via the OnAuthChange callback
//
for ( int i = 0; i< 100; i++ )
{
System.Threading.Thread.Sleep( 10 );
GC.Collect();
System.Threading.Thread.Sleep( 100 );
GC.Collect();
server.Update();
client.Update();
if ( Authed )
break;
}
Assert.IsTrue( Authed );
//
// Client cancels ticket
//
ticket.Cancel();
//
// Server should receive a ticket cancelled message
//
for ( int i = 0; i < 100; i++ )
{
System.Threading.Thread.Sleep( 100 );
server.Update();
client.Update();
if ( !Authed )
break;
}
Assert.IsTrue( !Authed );
}
}
}

View File

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace Facepunch.Steamworks.Callbacks.User
{
[StructLayout( LayoutKind.Sequential )]
internal class ValidateAuthTicketResponse
{
public enum Response : int
{
Okay = 0, // Steam has verified the user is online, the ticket is valid and ticket has not been reused.
UserNotConnectedToSteam = 1, // The user in question is not connected to steam
NoLicenseOrExpired = 2, // The license has expired.
VACBanned = 3, // The user is VAC banned for this game.
LoggedInElseWhere = 4, // The user account has logged in elsewhere and the session containing the game instance has been disconnected.
VACCheckTimedOut = 5, // VAC has been unable to perform anti-cheat checks on this user
AuthTicketCanceled = 6, // The ticket has been canceled by the issuer
AuthTicketInvalidAlreadyUsed = 7, // This ticket has already been used, it is not valid.
AuthTicketInvalid = 8, // This ticket is not from a user instance currently connected to steam.
PublisherIssuedBan = 9, // The user is banned for this game. The ban came via the web api and not VAC
};
public ulong SteamID;
public Response AuthResponse;
public ulong OwnerSteamID;
public const int CallbackId = Index.User + 43;
};
internal static class Index
{
internal const int User = 100;
}
}

View File

@ -16,6 +16,6 @@ public static class Config
/// for releasing his shit open source under the MIT license so we can all learn and iterate.
///
/// </summary>
public static bool UseThisCall { get; set; }
public static bool UseThisCall { get; set; } = true;
}
}

View File

@ -113,6 +113,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Callbacks\User.cs" />
<Compile Include="Client\Networking.cs" />
<Compile Include="Client\Overlay.cs" />
<Compile Include="Client\ServerList.cs" />
@ -128,13 +129,16 @@
<Compile Include="Client\Stats.cs" />
<Compile Include="Client\Voice.cs" />
<Compile Include="Config.cs" />
<Compile Include="Interop\Callback.cs" />
<Compile Include="Interop\ServerRules.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Interop\steam_api_interop.cs" />
<Compile Include="Server.cs" />
<Compile Include="Server\Auth.cs" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Compile Include="Interop\Callback.This.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>

View File

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace Facepunch.Steamworks.Interop.VTable.This
{
[StructLayout( LayoutKind.Sequential )]
internal struct Callback
{
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
public delegate void Result( IntPtr thisptr, IntPtr pvParam );
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
public delegate int GetSize( IntPtr thisptr );
[MarshalAs(UnmanagedType.FunctionPtr)]
public Result m_RunCallback;
[MarshalAs(UnmanagedType.FunctionPtr)]
public Result m_RunCallResult;
[MarshalAs(UnmanagedType.FunctionPtr)]
public GetSize m_GetCallbackSizeBytes;
internal static IntPtr Get( Action<IntPtr> onRunCallback, Func<int> getSize )
{
var size = Marshal.SizeOf( typeof( Callback ) );
var ptr = Marshal.AllocHGlobal( size );
Callback.Result da = ( _, p ) => onRunCallback( p );
Callback.GetSize dc = ( _ ) => getSize();
var a = GCHandle.Alloc( da );
var c = GCHandle.Alloc( dc );
var table = new Callback()
{
m_RunCallResult = da,
m_RunCallback = da,
m_GetCallbackSizeBytes = dc
};
Marshal.StructureToPtr( table, ptr, false );
return ptr;
}
}
}

View File

@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
//
// THANSK AGAIN TO STEAMWORKS.NET
//
// https://github.com/rlabrecque/Steamworks.NET/blob/97935154cf08f60da92c55e2c73ee60a8f456e03/Plugins/Steamworks.NET/CallbackDispatcher.cs
//
// Calling Conventions:
// Unity x86 Windows - StdCall (No this pointer)
// Unity x86 Linux - Cdecl
// Unity x86 OSX - Cdecl
// Unity x64 Windows - Cdecl
// Unity x64 Linux - Cdecl
// Unity x64 OSX - Cdecl
// Microsoft x86 Windows - ThisCall
// Microsoft x64 Windows - ThisCall
// Mono x86 Linux - Cdecl
// Mono x86 OSX - Cdecl
// Mono x64 Linux - Cdecl
// Mono x64 OSX - Cdecl
// Mono on Windows is probably not supported.
namespace Facepunch.Steamworks.Interop
{
internal partial class Callback<T> : IDisposable
{
public int CallbackId = 0;
public bool GameServer = false;
public Action<T> Function;
private IntPtr vTablePtr = IntPtr.Zero;
private GCHandle callbackPin;
private readonly int m_size = Marshal.SizeOf(typeof(T));
public Callback( bool gameserver, int callbackid, Action<T> func )
{
GameServer = gameserver;
CallbackId = callbackid;
Function = func;
BuildVTable();
Valve.Steamworks.SteamAPI.RegisterCallback( callbackPin.AddrOfPinnedObject(), CallbackId );
}
public virtual void Dispose()
{
if ( callbackPin.IsAllocated )
{
Valve.Steamworks.SteamAPI.UnregisterCallback( callbackPin.AddrOfPinnedObject() );
callbackPin.Free();
}
if ( vTablePtr != IntPtr.Zero )
{
Marshal.FreeHGlobal( vTablePtr );
vTablePtr = IntPtr.Zero;
}
}
private void OnRunCallback( IntPtr ptr )
{
T data = (T) Marshal.PtrToStructure( ptr, typeof(T) );
Function( data );
}
private int GetSize()
{
throw new System.NotImplementedException();
}
// Steamworks.NET Specific
private void BuildVTable()
{
InitVTable();
var callbackBase = new CallbackBase()
{
vTablePtr = vTablePtr,
CallbackFlags = GameServer ? (byte) CallbackBase.Flags.GameServer : (byte) 0,
CallbackId = CallbackId
};
callbackPin = GCHandle.Alloc( callbackBase, GCHandleType.Pinned );
}
void InitVTable()
{
if ( Config.UseThisCall )
{
vTablePtr = VTable.This.Callback.Get( OnRunCallback, GetSize );
return;
}
//vTablePtr = VTable.CDecl.Callback.Get( OnRunCallback, GetSize );
}
}
[StructLayout( LayoutKind.Sequential )]
internal class CallbackBase
{
internal enum Flags : byte
{
Registered = 0x01,
GameServer = 0x02
}
public IntPtr vTablePtr;
public byte CallbackFlags;
public int CallbackId;
};
}

View File

@ -1,6 +1,7 @@

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Facepunch.Steamworks
@ -119,7 +120,9 @@ public Server( uint appId, uint IpAddress, ushort SteamPort, ushort GamePort, us
// Set up warning hook callback
//
SteamAPIWarningMessageHook ptr = InternalOnWarning;
// native.client.SetWarningMessageHook( Marshal.GetFunctionPointerForDelegate( ptr ) );
var d = Marshal.GetFunctionPointerForDelegate( ptr );
var rr = GCHandle.Alloc( d );
native.utils.SetWarningMessageHook( d );
//
// Cache common, unchanging info
@ -129,6 +132,10 @@ public Server( uint appId, uint IpAddress, ushort SteamPort, ushort GamePort, us
//
// Initial settings
//
native.gameServer.SetModDir( "rust" );
native.gameServer.SetProduct( "rust" );
native.gameServer.SetGameDescription( "rust" );
native.gameServer.LogOnAnonymous();
native.gameServer.EnableHeartbeats( true );
MaxPlayers = 32;
BotCount = 0;
@ -147,17 +154,25 @@ public void Dispose()
native.Dispose();
native = null;
}
foreach ( var d in Disposables )
{
d.Dispose();
}
Disposables.Clear();
}
[UnmanagedFunctionPointer( CallingConvention.Cdecl )]
public delegate void SteamAPIWarningMessageHook( int nSeverity, System.Text.StringBuilder pchDebugText );
public delegate void SteamAPIWarningMessageHook( int nSeverity, string pchDebugText );
private void InternalOnWarning( int nSeverity, System.Text.StringBuilder text )
private void InternalOnWarning( int nSeverity, string text )
{
if ( OnMessage != null )
{
OnMessage( ( MessageType)nSeverity, text.ToString() );
}
Console.WriteLine( "STEAM: {0}", text );
}
internal event Action OnUpdate;
@ -171,6 +186,7 @@ public void Update()
return;
Valve.Interop.NativeEntrypoints.Extended.SteamGameServer_RunCallbacks();
Valve.Steamworks.SteamAPI.RunCallbacks();
// Voice.Update();
// Inventory.Update();
// Networking.Update();
@ -184,9 +200,9 @@ public bool Valid
get { return native != null; }
}
internal Action InstallCallback( int type, Delegate action )
internal Action InstallCallback<T>( Action<T> action )
{
var del = Marshal.GetFunctionPointerForDelegate( action );
// var del = Marshal.GetFunctionPointerForDelegate( action );
// var ptr = Marshal.GetFunctionPointerForDelegate( action );
// Valve.Steamworks.SteamAPI.RegisterCallback( del, type );
@ -197,6 +213,13 @@ internal Action InstallCallback( int type, Delegate action )
return null;
}
List<IDisposable> Disposables = new List<IDisposable>();
internal void CallResult<T>( Action<T> Callback, int id )
{
var callback = new Facepunch.Steamworks.Interop.Callback<T>( true, id, Callback );
Disposables.Add( callback );
}
/// <summary>

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Facepunch.Steamworks.Callbacks.User;
namespace Facepunch.Steamworks
{
@ -14,7 +15,7 @@ public ServerAuth Auth
get
{
if ( _auth == null )
_auth = new ServerAuth { server = this };
_auth = new ServerAuth( this );
return _auth;
}
@ -26,7 +27,45 @@ public class ServerAuth
internal Server server;
/// <summary>
/// Start authorizing a ticket
/// Steamid, Ownerid, Status
/// </summary>
public Action<ulong, ulong, Status> OnAuthChange;
/// <summary>
/// Steam authetication statuses
/// </summary>
public enum Status : int
{
/// <summary>
/// Steam has verified the user is online, the ticket is valid and ticket has not been reused.
/// </summary>
OK = 0,
UserNotConnectedToSteam = 1, // The user in question is not connected to steam
NoLicenseOrExpired = 2, // The license has expired.
VACBanned = 3, // The user is VAC banned for this game.
LoggedInElseWhere = 4, // The user account has logged in elsewhere and the session containing the game instance has been disconnected.
VACCheckTimedOut = 5, // VAC has been unable to perform anti-cheat checks on this user
AuthTicketCanceled = 6, // The ticket has been canceled by the issuer
AuthTicketInvalidAlreadyUsed = 7, // This ticket has already been used, it is not valid.
AuthTicketInvalid = 8, // This ticket is not from a user instance currently connected to steam.
PublisherIssuedBan = 9, // The user is banned for this game. The ban came via the web api and not VAC
}
internal ServerAuth( Server s )
{
server = s;
server.CallResult<ValidateAuthTicketResponse>( OnAuthTicketValidate, ValidateAuthTicketResponse.CallbackId );
}
void OnAuthTicketValidate( ValidateAuthTicketResponse data )
{
if ( OnAuthChange != null )
OnAuthChange( data.SteamID, data.OwnerSteamID, (Status) data.AuthResponse );
}
/// <summary>
/// Start authorizing a ticket. This user isn't authorized yet. Wait for a call to OnAuthChange.
/// </summary>
public unsafe bool StartSession( byte[] data, ulong steamid )
{
@ -41,6 +80,9 @@ public unsafe bool StartSession( byte[] data, ulong steamid )
}
}
/// <summary>
/// Forget this guy. They're no longer in the game.
/// </summary>
public void EndSession( ulong steamid )
{
server.native.gameServer.EndAuthSession( steamid );