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

@ -25,39 +25,87 @@ public void AuthCallback()
{ {
using ( var client = new Facepunch.Steamworks.Client( 252490 ) ) using ( var client = new Facepunch.Steamworks.Client( 252490 ) )
{ {
Assert.IsTrue( client.Valid ); using ( var server = new Facepunch.Steamworks.Server( 252490, 0, 30001, 30002, 30003, true, "VersionString" ) )
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( client.Valid );
var ticket = client.Auth.GetAuthSessionTicket();
var ticketBinary = ticket.Data;
Assert.IsTrue( server.Valid ); 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++ ) for ( int i = 0; i < 16; i++ )
{ {
System.Threading.Thread.Sleep( 10 ); System.Threading.Thread.Sleep( 10 );
GC.Collect();
server.Update(); server.Update();
GC.Collect();
client.Update(); client.Update();
GC.Collect();
} }
GC.Collect();
if ( !server.Auth.StartSession( ticketBinary, client.SteamId ) ) if ( !server.Auth.StartSession( ticketBinary, client.SteamId ) )
{ {
Assert.Fail( "Start Session returned false" ); 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(); server.Update();
client.Update(); client.Update();
if ( Authed )
break;
} }
Assert.IsTrue( Authed );
// //
// Client cancels ticket // Client cancels ticket
// //
ticket.Cancel(); 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. /// for releasing his shit open source under the MIT license so we can all learn and iterate.
/// ///
/// </summary> /// </summary>
public static bool UseThisCall { get; set; } public static bool UseThisCall { get; set; } = true;
} }
} }

View File

@ -113,6 +113,7 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Callbacks\User.cs" />
<Compile Include="Client\Networking.cs" /> <Compile Include="Client\Networking.cs" />
<Compile Include="Client\Overlay.cs" /> <Compile Include="Client\Overlay.cs" />
<Compile Include="Client\ServerList.cs" /> <Compile Include="Client\ServerList.cs" />
@ -128,13 +129,16 @@
<Compile Include="Client\Stats.cs" /> <Compile Include="Client\Stats.cs" />
<Compile Include="Client\Voice.cs" /> <Compile Include="Client\Voice.cs" />
<Compile Include="Config.cs" /> <Compile Include="Config.cs" />
<Compile Include="Interop\Callback.cs" />
<Compile Include="Interop\ServerRules.cs" /> <Compile Include="Interop\ServerRules.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Interop\steam_api_interop.cs" /> <Compile Include="Interop\steam_api_interop.cs" />
<Compile Include="Server.cs" /> <Compile Include="Server.cs" />
<Compile Include="Server\Auth.cs" /> <Compile Include="Server\Auth.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup>
<Compile Include="Interop\Callback.This.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup> <PropertyGroup>
<PostBuildEvent> <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;
using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Facepunch.Steamworks namespace Facepunch.Steamworks
@ -119,7 +120,9 @@ public Server( uint appId, uint IpAddress, ushort SteamPort, ushort GamePort, us
// Set up warning hook callback // Set up warning hook callback
// //
SteamAPIWarningMessageHook ptr = InternalOnWarning; 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 // Cache common, unchanging info
@ -129,6 +132,10 @@ public Server( uint appId, uint IpAddress, ushort SteamPort, ushort GamePort, us
// //
// Initial settings // Initial settings
// //
native.gameServer.SetModDir( "rust" );
native.gameServer.SetProduct( "rust" );
native.gameServer.SetGameDescription( "rust" );
native.gameServer.LogOnAnonymous();
native.gameServer.EnableHeartbeats( true ); native.gameServer.EnableHeartbeats( true );
MaxPlayers = 32; MaxPlayers = 32;
BotCount = 0; BotCount = 0;
@ -147,17 +154,25 @@ public void Dispose()
native.Dispose(); native.Dispose();
native = null; native = null;
} }
foreach ( var d in Disposables )
{
d.Dispose();
}
Disposables.Clear();
} }
[UnmanagedFunctionPointer( CallingConvention.Cdecl )] [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 ) if ( OnMessage != null )
{ {
OnMessage( ( MessageType)nSeverity, text.ToString() ); OnMessage( ( MessageType)nSeverity, text.ToString() );
} }
Console.WriteLine( "STEAM: {0}", text );
} }
internal event Action OnUpdate; internal event Action OnUpdate;
@ -171,9 +186,10 @@ public void Update()
return; return;
Valve.Interop.NativeEntrypoints.Extended.SteamGameServer_RunCallbacks(); Valve.Interop.NativeEntrypoints.Extended.SteamGameServer_RunCallbacks();
Valve.Steamworks.SteamAPI.RunCallbacks();
// Voice.Update(); // Voice.Update();
// Inventory.Update(); // Inventory.Update();
// Networking.Update(); // Networking.Update();
if ( OnUpdate != null ) if ( OnUpdate != null )
OnUpdate(); OnUpdate();
@ -184,20 +200,27 @@ public bool Valid
get { return native != null; } 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 ); // var ptr = Marshal.GetFunctionPointerForDelegate( action );
// Valve.Steamworks.SteamAPI.RegisterCallback( del, type ); // Valve.Steamworks.SteamAPI.RegisterCallback( del, type );
// Valve.Steamworks.SteamAPI.UnregisterCallback( del ); // Valve.Steamworks.SteamAPI.UnregisterCallback( del );
//return () => Valve.Steamworks.SteamAPI.UnregisterCallback( ptr ); //return () => Valve.Steamworks.SteamAPI.UnregisterCallback( ptr );
return null; 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> /// <summary>
/// Gets or sets the current MaxPlayers. /// Gets or sets the current MaxPlayers.

View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Facepunch.Steamworks.Callbacks.User;
namespace Facepunch.Steamworks namespace Facepunch.Steamworks
{ {
@ -14,7 +15,7 @@ public ServerAuth Auth
get get
{ {
if ( _auth == null ) if ( _auth == null )
_auth = new ServerAuth { server = this }; _auth = new ServerAuth( this );
return _auth; return _auth;
} }
@ -26,7 +27,45 @@ public class ServerAuth
internal Server server; internal Server server;
/// <summary> /// <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> /// </summary>
public unsafe bool StartSession( byte[] data, ulong steamid ) 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 ) public void EndSession( ulong steamid )
{ {
server.native.gameServer.EndAuthSession( steamid ); server.native.gameServer.EndAuthSession( steamid );