From 3255135df15ab703a015fd7103780798fbefadf9 Mon Sep 17 00:00:00 2001 From: Garry Newman Date: Mon, 15 Apr 2019 20:54:50 +0100 Subject: [PATCH] GameServer baseline --- .../Client/InventoryTest.cs | 3 +- .../Client/Server/ServerTest.cs | 3 +- .../Client/Server/StatsTest.cs | 3 +- .../Facepunch.Steamworks.csproj | 6 - .../Generated/Interfaces/ISteamApps.cs | 4 + .../Generated/Interfaces/ISteamFriends.cs | 4 + .../Generated/Interfaces/ISteamGameServer.cs | 563 ++++++++++++++++++ .../Interfaces/ISteamMatchmakingServers.cs | 4 + .../Generated/Interfaces/ISteamMusic.cs | 4 + .../Interfaces/ISteamParentalSettings.cs | 4 + .../Generated/Interfaces/ISteamUser.cs | 4 + .../Generated/Interfaces/ISteamUtils.cs | 4 + .../Generated/Interfaces/ISteamVideo.cs | 4 + Facepunch.Steamworks/Generated/SteamApi.cs | 3 + .../Generated/SteamInternal.cs | 4 + Facepunch.Steamworks/Interop/Native.cs | 3 - Facepunch.Steamworks/Redux/GameServer.cs | 334 +++++++++++ .../Redux/Structs/OutgoingPacket.cs | 30 + .../{Server => Redux/Structs}/ServerInit.cs | 36 +- Facepunch.Steamworks/Server.cs | 338 ----------- Facepunch.Steamworks/Server/Auth.cs | 73 --- Facepunch.Steamworks/Server/Query.cs | 88 --- Facepunch.Steamworks/Server/Stats.cs | 2 + .../SteamNative/SteamNative.Callback.cs | 3 - .../Utility/BaseSteamInterface.cs | 19 +- Generator/CodeWriter/ClassVTable.cs | 6 + Generator/CodeWriter/CodeWriter.cs | 1 + Generator/CodeWriter/Types/BaseType.cs | 25 +- 28 files changed, 1039 insertions(+), 536 deletions(-) create mode 100644 Facepunch.Steamworks/Generated/Interfaces/ISteamGameServer.cs create mode 100644 Facepunch.Steamworks/Redux/GameServer.cs create mode 100644 Facepunch.Steamworks/Redux/Structs/OutgoingPacket.cs rename Facepunch.Steamworks/{Server => Redux/Structs}/ServerInit.cs (72%) delete mode 100644 Facepunch.Steamworks/Server.cs delete mode 100644 Facepunch.Steamworks/Server/Auth.cs delete mode 100644 Facepunch.Steamworks/Server/Query.cs diff --git a/Facepunch.Steamworks.Test/Client/InventoryTest.cs b/Facepunch.Steamworks.Test/Client/InventoryTest.cs index f3bdd01..ebb3c63 100644 --- a/Facepunch.Steamworks.Test/Client/InventoryTest.cs +++ b/Facepunch.Steamworks.Test/Client/InventoryTest.cs @@ -196,7 +196,7 @@ namespace Facepunch.Steamworks.Test Assert.IsNotNull( client.Inventory.SerializedItems ); Assert.IsTrue( client.Inventory.SerializedItems.Length > 4 ); - + /* using ( var server = new Facepunch.Steamworks.Server( 252490, new ServerInit( "rust", "Rust" ) ) ) { server.LogOnAnonymous(); @@ -223,6 +223,7 @@ namespace Facepunch.Steamworks.Test Console.WriteLine( "Item: {0} ({1})", item.Id, item.DefinitionId ); } } + */ } } diff --git a/Facepunch.Steamworks.Test/Client/Server/ServerTest.cs b/Facepunch.Steamworks.Test/Client/Server/ServerTest.cs index e189c91..2bddda0 100644 --- a/Facepunch.Steamworks.Test/Client/Server/ServerTest.cs +++ b/Facepunch.Steamworks.Test/Client/Server/ServerTest.cs @@ -1,6 +1,6 @@ using System; using Microsoft.VisualStudio.TestTools.UnitTesting; - +/* namespace Facepunch.Steamworks.Test { [DeploymentItem( "steam_api64.dll" )] @@ -148,3 +148,4 @@ namespace Facepunch.Steamworks.Test } } } +*/ \ No newline at end of file diff --git a/Facepunch.Steamworks.Test/Client/Server/StatsTest.cs b/Facepunch.Steamworks.Test/Client/Server/StatsTest.cs index e870ff1..030b278 100644 --- a/Facepunch.Steamworks.Test/Client/Server/StatsTest.cs +++ b/Facepunch.Steamworks.Test/Client/Server/StatsTest.cs @@ -2,7 +2,7 @@ using System.Text; using System.Threading; using Microsoft.VisualStudio.TestTools.UnitTesting; - +/* namespace Facepunch.Steamworks.Test { public partial class Server @@ -40,3 +40,4 @@ namespace Facepunch.Steamworks.Test } } +*/ \ No newline at end of file diff --git a/Facepunch.Steamworks/Facepunch.Steamworks.csproj b/Facepunch.Steamworks/Facepunch.Steamworks.csproj index 702c88c..a229446 100644 --- a/Facepunch.Steamworks/Facepunch.Steamworks.csproj +++ b/Facepunch.Steamworks/Facepunch.Steamworks.csproj @@ -30,12 +30,6 @@ - - - Never - - - Always diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamApps.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamApps.cs index 43cd912..deef74c 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamApps.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamApps.cs @@ -9,6 +9,10 @@ namespace Steamworks.Internal { public class ISteamApps : BaseSteamInterface { + public ISteamApps( bool server = false ) : base( server ) + { + } + public override string InterfaceName => "STEAMAPPS_INTERFACE_VERSION008"; public override void InitInternals() diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamFriends.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamFriends.cs index 1450c54..cf5c815 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamFriends.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamFriends.cs @@ -9,6 +9,10 @@ namespace Steamworks.Internal { public class ISteamFriends : BaseSteamInterface { + public ISteamFriends( bool server = false ) : base( server ) + { + } + public override string InterfaceName => "SteamFriends017"; public override void InitInternals() diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamGameServer.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamGameServer.cs new file mode 100644 index 0000000..2fde8ac --- /dev/null +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamGameServer.cs @@ -0,0 +1,563 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using SteamNative; + + +namespace Steamworks.Internal +{ + public class ISteamGameServer : BaseSteamInterface + { + public ISteamGameServer( bool server = false ) : base( server ) + { + } + + public override string InterfaceName => "SteamGameServer012"; + + public override void InitInternals() + { + InitGameServerDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 0) ); + SetProductDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 8) ); + SetGameDescriptionDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 16) ); + SetModDirDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 24) ); + SetDedicatedServerDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 32) ); + LogOnDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 40) ); + LogOnAnonymousDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 48) ); + LogOffDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 56) ); + BLoggedOnDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 64) ); + BSecureDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 72) ); + GetSteamIDDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 80) ); + WasRestartRequestedDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 88) ); + SetMaxPlayerCountDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 96) ); + SetBotPlayerCountDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 104) ); + SetServerNameDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 112) ); + SetMapNameDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 120) ); + SetPasswordProtectedDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 128) ); + SetSpectatorPortDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 136) ); + SetSpectatorServerNameDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 144) ); + ClearAllKeyValuesDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 152) ); + SetKeyValueDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 160) ); + SetGameTagsDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 168) ); + SetGameDataDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 176) ); + SetRegionDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 184) ); + SendUserConnectAndAuthenticateDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 192) ); + CreateUnauthenticatedUserConnectionDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 200) ); + SendUserDisconnectDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 208) ); + BUpdateUserDataDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 216) ); + GetAuthSessionTicketDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 224) ); + BeginAuthSessionDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 232) ); + EndAuthSessionDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 240) ); + CancelAuthTicketDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 248) ); + UserHasLicenseForAppDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 256) ); + RequestUserGroupStatusDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 264) ); + GetGameplayStatsDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 272) ); + GetServerReputationDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 280) ); + GetPublicIPDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 288) ); + HandleIncomingPacketDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 296) ); + GetNextOutgoingPacketDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 304) ); + EnableHeartbeatsDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 312) ); + SetHeartbeatIntervalDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 320) ); + ForceHeartbeatDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 328) ); + AssociateWithClanDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 336) ); + ComputeNewPlayerCompatibilityDelegatePointer = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 344) ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + [return: MarshalAs( UnmanagedType.I1 )] + public delegate bool InitGameServerDelegate( IntPtr self, uint unIP, ushort usGamePort, ushort usQueryPort, uint unFlags, AppId_t nGameAppId, string pchVersionString ); + private InitGameServerDelegate InitGameServerDelegatePointer; + + #endregion + public bool InitGameServer( uint unIP, ushort usGamePort, ushort usQueryPort, uint unFlags, AppId_t nGameAppId, string pchVersionString ) + { + return InitGameServerDelegatePointer( Self, unIP, usGamePort, usQueryPort, unFlags, nGameAppId, pchVersionString ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void SetProductDelegate( IntPtr self, string pszProduct ); + private SetProductDelegate SetProductDelegatePointer; + + #endregion + public void SetProduct( string pszProduct ) + { + SetProductDelegatePointer( Self, pszProduct ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void SetGameDescriptionDelegate( IntPtr self, string pszGameDescription ); + private SetGameDescriptionDelegate SetGameDescriptionDelegatePointer; + + #endregion + public void SetGameDescription( string pszGameDescription ) + { + SetGameDescriptionDelegatePointer( Self, pszGameDescription ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void SetModDirDelegate( IntPtr self, string pszModDir ); + private SetModDirDelegate SetModDirDelegatePointer; + + #endregion + public void SetModDir( string pszModDir ) + { + SetModDirDelegatePointer( Self, pszModDir ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void SetDedicatedServerDelegate( IntPtr self, [MarshalAs( UnmanagedType.U1 )] bool bDedicated ); + private SetDedicatedServerDelegate SetDedicatedServerDelegatePointer; + + #endregion + public void SetDedicatedServer( [MarshalAs( UnmanagedType.U1 )] bool bDedicated ) + { + SetDedicatedServerDelegatePointer( Self, bDedicated ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void LogOnDelegate( IntPtr self, string pszToken ); + private LogOnDelegate LogOnDelegatePointer; + + #endregion + public void LogOn( string pszToken ) + { + LogOnDelegatePointer( Self, pszToken ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void LogOnAnonymousDelegate( IntPtr self ); + private LogOnAnonymousDelegate LogOnAnonymousDelegatePointer; + + #endregion + public void LogOnAnonymous() + { + LogOnAnonymousDelegatePointer( Self ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void LogOffDelegate( IntPtr self ); + private LogOffDelegate LogOffDelegatePointer; + + #endregion + public void LogOff() + { + LogOffDelegatePointer( Self ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + [return: MarshalAs( UnmanagedType.I1 )] + public delegate bool BLoggedOnDelegate( IntPtr self ); + private BLoggedOnDelegate BLoggedOnDelegatePointer; + + #endregion + public bool BLoggedOn() + { + return BLoggedOnDelegatePointer( Self ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + [return: MarshalAs( UnmanagedType.I1 )] + public delegate bool BSecureDelegate( IntPtr self ); + private BSecureDelegate BSecureDelegatePointer; + + #endregion + public bool BSecure() + { + return BSecureDelegatePointer( Self ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void GetSteamIDDelegate( IntPtr self, ref CSteamID retVal ); + private GetSteamIDDelegate GetSteamIDDelegatePointer; + + #endregion + public CSteamID GetSteamID() + { + var retVal = default( CSteamID ); + GetSteamIDDelegatePointer( Self, ref retVal ); + return retVal; + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + [return: MarshalAs( UnmanagedType.I1 )] + public delegate bool WasRestartRequestedDelegate( IntPtr self ); + private WasRestartRequestedDelegate WasRestartRequestedDelegatePointer; + + #endregion + public bool WasRestartRequested() + { + return WasRestartRequestedDelegatePointer( Self ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void SetMaxPlayerCountDelegate( IntPtr self, int cPlayersMax ); + private SetMaxPlayerCountDelegate SetMaxPlayerCountDelegatePointer; + + #endregion + public void SetMaxPlayerCount( int cPlayersMax ) + { + SetMaxPlayerCountDelegatePointer( Self, cPlayersMax ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void SetBotPlayerCountDelegate( IntPtr self, int cBotplayers ); + private SetBotPlayerCountDelegate SetBotPlayerCountDelegatePointer; + + #endregion + public void SetBotPlayerCount( int cBotplayers ) + { + SetBotPlayerCountDelegatePointer( Self, cBotplayers ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void SetServerNameDelegate( IntPtr self, string pszServerName ); + private SetServerNameDelegate SetServerNameDelegatePointer; + + #endregion + public void SetServerName( string pszServerName ) + { + SetServerNameDelegatePointer( Self, pszServerName ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void SetMapNameDelegate( IntPtr self, string pszMapName ); + private SetMapNameDelegate SetMapNameDelegatePointer; + + #endregion + public void SetMapName( string pszMapName ) + { + SetMapNameDelegatePointer( Self, pszMapName ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void SetPasswordProtectedDelegate( IntPtr self, [MarshalAs( UnmanagedType.U1 )] bool bPasswordProtected ); + private SetPasswordProtectedDelegate SetPasswordProtectedDelegatePointer; + + #endregion + public void SetPasswordProtected( [MarshalAs( UnmanagedType.U1 )] bool bPasswordProtected ) + { + SetPasswordProtectedDelegatePointer( Self, bPasswordProtected ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void SetSpectatorPortDelegate( IntPtr self, ushort unSpectatorPort ); + private SetSpectatorPortDelegate SetSpectatorPortDelegatePointer; + + #endregion + public void SetSpectatorPort( ushort unSpectatorPort ) + { + SetSpectatorPortDelegatePointer( Self, unSpectatorPort ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void SetSpectatorServerNameDelegate( IntPtr self, string pszSpectatorServerName ); + private SetSpectatorServerNameDelegate SetSpectatorServerNameDelegatePointer; + + #endregion + public void SetSpectatorServerName( string pszSpectatorServerName ) + { + SetSpectatorServerNameDelegatePointer( Self, pszSpectatorServerName ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void ClearAllKeyValuesDelegate( IntPtr self ); + private ClearAllKeyValuesDelegate ClearAllKeyValuesDelegatePointer; + + #endregion + public void ClearAllKeyValues() + { + ClearAllKeyValuesDelegatePointer( Self ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void SetKeyValueDelegate( IntPtr self, string pKey, string pValue ); + private SetKeyValueDelegate SetKeyValueDelegatePointer; + + #endregion + public void SetKeyValue( string pKey, string pValue ) + { + SetKeyValueDelegatePointer( Self, pKey, pValue ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void SetGameTagsDelegate( IntPtr self, string pchGameTags ); + private SetGameTagsDelegate SetGameTagsDelegatePointer; + + #endregion + public void SetGameTags( string pchGameTags ) + { + SetGameTagsDelegatePointer( Self, pchGameTags ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void SetGameDataDelegate( IntPtr self, string pchGameData ); + private SetGameDataDelegate SetGameDataDelegatePointer; + + #endregion + public void SetGameData( string pchGameData ) + { + SetGameDataDelegatePointer( Self, pchGameData ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void SetRegionDelegate( IntPtr self, string pszRegion ); + private SetRegionDelegate SetRegionDelegatePointer; + + #endregion + public void SetRegion( string pszRegion ) + { + SetRegionDelegatePointer( Self, pszRegion ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + [return: MarshalAs( UnmanagedType.I1 )] + public delegate bool SendUserConnectAndAuthenticateDelegate( IntPtr self, uint unIPClient, IntPtr pvAuthBlob, uint cubAuthBlobSize, ref CSteamID pSteamIDUser ); + private SendUserConnectAndAuthenticateDelegate SendUserConnectAndAuthenticateDelegatePointer; + + #endregion + public bool SendUserConnectAndAuthenticate( uint unIPClient, IntPtr pvAuthBlob, uint cubAuthBlobSize, ref CSteamID pSteamIDUser ) + { + return SendUserConnectAndAuthenticateDelegatePointer( Self, unIPClient, pvAuthBlob, cubAuthBlobSize, ref pSteamIDUser ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void CreateUnauthenticatedUserConnectionDelegate( IntPtr self, ref CSteamID retVal ); + private CreateUnauthenticatedUserConnectionDelegate CreateUnauthenticatedUserConnectionDelegatePointer; + + #endregion + public CSteamID CreateUnauthenticatedUserConnection() + { + var retVal = default( CSteamID ); + CreateUnauthenticatedUserConnectionDelegatePointer( Self, ref retVal ); + return retVal; + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void SendUserDisconnectDelegate( IntPtr self, CSteamID steamIDUser ); + private SendUserDisconnectDelegate SendUserDisconnectDelegatePointer; + + #endregion + public void SendUserDisconnect( CSteamID steamIDUser ) + { + SendUserDisconnectDelegatePointer( Self, steamIDUser ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + [return: MarshalAs( UnmanagedType.I1 )] + public delegate bool BUpdateUserDataDelegate( IntPtr self, CSteamID steamIDUser, string pchPlayerName, uint uScore ); + private BUpdateUserDataDelegate BUpdateUserDataDelegatePointer; + + #endregion + public bool BUpdateUserData( CSteamID steamIDUser, string pchPlayerName, uint uScore ) + { + return BUpdateUserDataDelegatePointer( Self, steamIDUser, pchPlayerName, uScore ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate HAuthTicket GetAuthSessionTicketDelegate( IntPtr self, IntPtr pTicket, int cbMaxTicket, ref uint pcbTicket ); + private GetAuthSessionTicketDelegate GetAuthSessionTicketDelegatePointer; + + #endregion + public HAuthTicket GetAuthSessionTicket( IntPtr pTicket, int cbMaxTicket, ref uint pcbTicket ) + { + return GetAuthSessionTicketDelegatePointer( Self, pTicket, cbMaxTicket, ref pcbTicket ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate BeginAuthSessionResult BeginAuthSessionDelegate( IntPtr self, IntPtr pAuthTicket, int cbAuthTicket, CSteamID steamID ); + private BeginAuthSessionDelegate BeginAuthSessionDelegatePointer; + + #endregion + public BeginAuthSessionResult BeginAuthSession( IntPtr pAuthTicket, int cbAuthTicket, CSteamID steamID ) + { + return BeginAuthSessionDelegatePointer( Self, pAuthTicket, cbAuthTicket, steamID ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void EndAuthSessionDelegate( IntPtr self, CSteamID steamID ); + private EndAuthSessionDelegate EndAuthSessionDelegatePointer; + + #endregion + public void EndAuthSession( CSteamID steamID ) + { + EndAuthSessionDelegatePointer( Self, steamID ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void CancelAuthTicketDelegate( IntPtr self, HAuthTicket hAuthTicket ); + private CancelAuthTicketDelegate CancelAuthTicketDelegatePointer; + + #endregion + public void CancelAuthTicket( HAuthTicket hAuthTicket ) + { + CancelAuthTicketDelegatePointer( Self, hAuthTicket ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate UserHasLicenseForAppResult UserHasLicenseForAppDelegate( IntPtr self, CSteamID steamID, AppId_t appID ); + private UserHasLicenseForAppDelegate UserHasLicenseForAppDelegatePointer; + + #endregion + public UserHasLicenseForAppResult UserHasLicenseForApp( CSteamID steamID, AppId_t appID ) + { + return UserHasLicenseForAppDelegatePointer( Self, steamID, appID ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + [return: MarshalAs( UnmanagedType.I1 )] + public delegate bool RequestUserGroupStatusDelegate( IntPtr self, CSteamID steamIDUser, CSteamID steamIDGroup ); + private RequestUserGroupStatusDelegate RequestUserGroupStatusDelegatePointer; + + #endregion + public bool RequestUserGroupStatus( CSteamID steamIDUser, CSteamID steamIDGroup ) + { + return RequestUserGroupStatusDelegatePointer( Self, steamIDUser, steamIDGroup ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void GetGameplayStatsDelegate( IntPtr self ); + private GetGameplayStatsDelegate GetGameplayStatsDelegatePointer; + + #endregion + public void GetGameplayStats() + { + GetGameplayStatsDelegatePointer( Self ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate SteamAPICall_t GetServerReputationDelegate( IntPtr self ); + private GetServerReputationDelegate GetServerReputationDelegatePointer; + + #endregion + public async Task GetServerReputation() + { + return await (new Result( GetServerReputationDelegatePointer( Self ) )).GetResult(); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate uint GetPublicIPDelegate( IntPtr self ); + private GetPublicIPDelegate GetPublicIPDelegatePointer; + + #endregion + public uint GetPublicIP() + { + return GetPublicIPDelegatePointer( Self ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + [return: MarshalAs( UnmanagedType.I1 )] + public delegate bool HandleIncomingPacketDelegate( IntPtr self, IntPtr pData, int cbData, uint srcIP, ushort srcPort ); + private HandleIncomingPacketDelegate HandleIncomingPacketDelegatePointer; + + #endregion + public bool HandleIncomingPacket( IntPtr pData, int cbData, uint srcIP, ushort srcPort ) + { + return HandleIncomingPacketDelegatePointer( Self, pData, cbData, srcIP, srcPort ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate int GetNextOutgoingPacketDelegate( IntPtr self, IntPtr pOut, int cbMaxOut, ref uint pNetAdr, ref ushort pPort ); + private GetNextOutgoingPacketDelegate GetNextOutgoingPacketDelegatePointer; + + #endregion + public int GetNextOutgoingPacket( IntPtr pOut, int cbMaxOut, ref uint pNetAdr, ref ushort pPort ) + { + return GetNextOutgoingPacketDelegatePointer( Self, pOut, cbMaxOut, ref pNetAdr, ref pPort ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void EnableHeartbeatsDelegate( IntPtr self, [MarshalAs( UnmanagedType.U1 )] bool bActive ); + private EnableHeartbeatsDelegate EnableHeartbeatsDelegatePointer; + + #endregion + public void EnableHeartbeats( [MarshalAs( UnmanagedType.U1 )] bool bActive ) + { + EnableHeartbeatsDelegatePointer( Self, bActive ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void SetHeartbeatIntervalDelegate( IntPtr self, int iHeartbeatInterval ); + private SetHeartbeatIntervalDelegate SetHeartbeatIntervalDelegatePointer; + + #endregion + public void SetHeartbeatInterval( int iHeartbeatInterval ) + { + SetHeartbeatIntervalDelegatePointer( Self, iHeartbeatInterval ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate void ForceHeartbeatDelegate( IntPtr self ); + private ForceHeartbeatDelegate ForceHeartbeatDelegatePointer; + + #endregion + public void ForceHeartbeat() + { + ForceHeartbeatDelegatePointer( Self ); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate SteamAPICall_t AssociateWithClanDelegate( IntPtr self, CSteamID steamIDClan ); + private AssociateWithClanDelegate AssociateWithClanDelegatePointer; + + #endregion + public async Task AssociateWithClan( CSteamID steamIDClan ) + { + return await (new Result( AssociateWithClanDelegatePointer( Self, steamIDClan ) )).GetResult(); + } + + #region FunctionMeta + [UnmanagedFunctionPointer( CallingConvention.ThisCall )] + public delegate SteamAPICall_t ComputeNewPlayerCompatibilityDelegate( IntPtr self, CSteamID steamIDNewPlayer ); + private ComputeNewPlayerCompatibilityDelegate ComputeNewPlayerCompatibilityDelegatePointer; + + #endregion + public async Task ComputeNewPlayerCompatibility( CSteamID steamIDNewPlayer ) + { + return await (new Result( ComputeNewPlayerCompatibilityDelegatePointer( Self, steamIDNewPlayer ) )).GetResult(); + } + + } +} diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServers.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServers.cs index 65d3d35..7d1813f 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServers.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServers.cs @@ -9,6 +9,10 @@ namespace Steamworks.Internal { public class ISteamMatchmakingServers : BaseSteamInterface { + public ISteamMatchmakingServers( bool server = false ) : base( server ) + { + } + public override string InterfaceName => "SteamMatchMakingServers002"; public override void InitInternals() diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamMusic.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamMusic.cs index 6fe961f..6e0d62c 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamMusic.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamMusic.cs @@ -9,6 +9,10 @@ namespace Steamworks.Internal { public class ISteamMusic : BaseSteamInterface { + public ISteamMusic( bool server = false ) : base( server ) + { + } + public override string InterfaceName => "STEAMMUSIC_INTERFACE_VERSION001"; public override void InitInternals() diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamParentalSettings.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamParentalSettings.cs index 691dfff..ffa8de8 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamParentalSettings.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamParentalSettings.cs @@ -9,6 +9,10 @@ namespace Steamworks.Internal { public class ISteamParentalSettings : BaseSteamInterface { + public ISteamParentalSettings( bool server = false ) : base( server ) + { + } + public override string InterfaceName => "STEAMPARENTALSETTINGS_INTERFACE_VERSION001"; public override void InitInternals() diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamUser.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamUser.cs index b91bf2d..caef035 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamUser.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamUser.cs @@ -9,6 +9,10 @@ namespace Steamworks.Internal { public class ISteamUser : BaseSteamInterface { + public ISteamUser( bool server = false ) : base( server ) + { + } + public override string InterfaceName => "SteamUser020"; public override void InitInternals() diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamUtils.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamUtils.cs index 271bfe7..7f01ea5 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamUtils.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamUtils.cs @@ -9,6 +9,10 @@ namespace Steamworks.Internal { public class ISteamUtils : BaseSteamInterface { + public ISteamUtils( bool server = false ) : base( server ) + { + } + public override string InterfaceName => "SteamUtils009"; public override void InitInternals() diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamVideo.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamVideo.cs index 00a59f9..3c92f33 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamVideo.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamVideo.cs @@ -9,6 +9,10 @@ namespace Steamworks.Internal { public class ISteamVideo : BaseSteamInterface { + public ISteamVideo( bool server = false ) : base( server ) + { + } + public override string InterfaceName => "STEAMVIDEO_INTERFACE_V002"; public override void InitInternals() diff --git a/Facepunch.Steamworks/Generated/SteamApi.cs b/Facepunch.Steamworks/Generated/SteamApi.cs index 444d9f0..c544a2f 100644 --- a/Facepunch.Steamworks/Generated/SteamApi.cs +++ b/Facepunch.Steamworks/Generated/SteamApi.cs @@ -13,6 +13,9 @@ public static class SteamApi [DllImport( "Steam_api64", EntryPoint = "SteamAPI_GetHSteamUser", CallingConvention = CallingConvention.Cdecl )] public static extern int GetHSteamUser(); + [DllImport( "Steam_api64", EntryPoint = "SteamGameServer_GetHSteamUser", CallingConvention = CallingConvention.Cdecl )] + public static extern int SteamGameServer_GetHSteamUser(); + [DllImport( "Steam_api64", EntryPoint = "SteamAPI_RunCallbacks", CallingConvention = CallingConvention.Cdecl )] public static extern int RunCallbacks(); diff --git a/Facepunch.Steamworks/Generated/SteamInternal.cs b/Facepunch.Steamworks/Generated/SteamInternal.cs index fc37bdd..e2024aa 100644 --- a/Facepunch.Steamworks/Generated/SteamInternal.cs +++ b/Facepunch.Steamworks/Generated/SteamInternal.cs @@ -8,4 +8,8 @@ public static class SteamInternal { [DllImport( "Steam_api64", EntryPoint = "SteamInternal_FindOrCreateUserInterface", CallingConvention = CallingConvention.Cdecl )] public static extern IntPtr FindOrCreateUserInterface( int hSteamUser, [MarshalAs( UnmanagedType.LPStr )] string pszVersion ); + + + [DllImport( "Steam_api64", EntryPoint = "SteamInternal_FindOrCreateGameServerInterface", CallingConvention = CallingConvention.Cdecl )] + public static extern IntPtr FindOrCreateGameServerInterface( int hSteamUser, [MarshalAs( UnmanagedType.LPStr )] string pszVersion ); } diff --git a/Facepunch.Steamworks/Interop/Native.cs b/Facepunch.Steamworks/Interop/Native.cs index d633a8c..5a3b2af 100644 --- a/Facepunch.Steamworks/Interop/Native.cs +++ b/Facepunch.Steamworks/Interop/Native.cs @@ -31,9 +31,6 @@ namespace Facepunch.Steamworks.Interop internal bool InitClient( BaseSteamworks steamworks ) { - if ( Steamworks.Server.Instance != null ) - throw new System.Exception("Steam client should be initialized before steam server - or there's big trouble."); - isServer = false; api = new SteamNative.SteamApi(); diff --git a/Facepunch.Steamworks/Redux/GameServer.cs b/Facepunch.Steamworks/Redux/GameServer.cs new file mode 100644 index 0000000..e6b4070 --- /dev/null +++ b/Facepunch.Steamworks/Redux/GameServer.cs @@ -0,0 +1,334 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Facepunch.Steamworks; +using SteamNative; + +namespace Steamworks +{ + /// + /// Provides the core of the Steam Game Servers API + /// + public static partial class GameServer + { + static Internal.ISteamGameServer _internal; + internal static Internal.ISteamGameServer Internal + { + get + { + if ( _internal == null ) + _internal = new Internal.ISteamGameServer( true ); + + return _internal; + } + } + + internal static void InstallEvents() + { + new Event( x => OnValidateAuthTicketResponse?.Invoke( x.SteamID, x.OwnerSteamID, x.AuthSessionResponse ), true ); + } + + /// + /// User has been authed or rejected + /// + public static event Action OnValidateAuthTicketResponse; + + public static bool Init( AppId appid, ServerInit init ) + { + uint ipaddress = 0; // Any Port + + if ( init.SteamPort == 0 ) + init = init.WithRandomSteamPort(); + + if ( init.IpAddress != null ) + ipaddress = Utility.IpToInt32( init.IpAddress ); + + // + // Get other interfaces + // + if ( !Internal.InitGameServer( ipaddress, init.GamePort, init.QueryPort, (uint)( init.Secure ? 3 : 2 ), appid.Value, init.VersionString ) ) + { + return false; + } + + // + // Initial settings + // + Internal.EnableHeartbeats( true ); + MaxPlayers = 32; + BotCount = 0; + Product = $"{appid.Value}"; + ModDir = init.ModDir; + GameDescription = init.GameDescription; + Passworded = false; + DedicatedServer = true; + + + InstallEvents(); + + return true; + } + + /// + /// Sets whether this should be marked as a dedicated server. + /// If not, it is assumed to be a listen server. + /// + public static bool DedicatedServer + { + get => _dedicatedServer; + set { if ( _dedicatedServer == value ) return; Internal.SetDedicatedServer( value ); _dedicatedServer = value; } + } + private static bool _dedicatedServer; + + /// + /// Gets or sets the current MaxPlayers. + /// This doesn't enforce any kind of limit, it just updates the master server. + /// + public static int MaxPlayers + { + get => _maxplayers; + set { if ( _maxplayers == value ) return; Internal.SetMaxPlayerCount( value ); _maxplayers = value; } + } + private static int _maxplayers = 0; + + /// + /// Gets or sets the current BotCount. + /// This doesn't enforce any kind of limit, it just updates the master server. + /// + public static int BotCount + { + get => _botcount; + set { if ( _botcount == value ) return; Internal.SetBotPlayerCount( value ); _botcount = value; } + } + private static int _botcount = 0; + + /// + /// Gets or sets the current Map Name. + /// + public static string MapName + { + get => _mapname; + set { if ( _mapname == value ) return; Internal.SetMapName( value ); _mapname = value; } + } + private static string _mapname; + + /// + /// Gets or sets the current ModDir + /// + public static string ModDir + { + get => _modDir; + internal set { if ( _modDir == value ) return; Internal.SetModDir( value ); _modDir = value; } + } + private static string _modDir = ""; + + /// + /// Gets the current product + /// + public static string Product + { + get => _product; + internal set { if ( _product == value ) return; Internal.SetProduct( value ); _product = value; } + } + private static string _product = ""; + + /// + /// Gets or sets the current Product + /// + public static string GameDescription + { + get => _gameDescription; + internal set { if ( _gameDescription == value ) return; Internal.SetGameDescription( value ); _gameDescription = value; } + } + private static string _gameDescription = ""; + + /// + /// Gets or sets the current ServerName + /// + public static string ServerName + { + get => _serverName; + set { if ( _serverName == value ) return; Internal.SetServerName( value ); _serverName = value; } + } + private static string _serverName = ""; + + /// + /// Set whether the server should report itself as passworded + /// + public static bool Passworded + { + get => _passworded; + set { if ( _passworded == value ) return; Internal.SetPasswordProtected( value ); _passworded = value; } + } + private static bool _passworded; + + /// + /// Gets or sets the current GameTags. This is a comma seperated list of tags for this server. + /// When querying the server list you can filter by these tags. + /// + public static string GameTags + { + get => _gametags; + set { if ( _gametags == value ) return; Internal.SetGameTags( value ); _gametags = value; } + } + private static string _gametags = ""; + + /// + /// Log onto Steam anonymously. + /// + public static void LogOnAnonymous() + { + Internal.LogOnAnonymous(); + ForceHeartbeat(); + } + + /// + /// Returns true if the server is connected and registered with the Steam master server + /// You should have called LogOnAnonymous etc on startup. + /// + public static bool LoggedOn => Internal.BLoggedOn(); + + /// + /// To the best of its ability this tries to get the server's + /// current public ip address. Be aware that this is likely to return + /// null for the first few seconds after initialization. + /// + public static System.Net.IPAddress PublicIp + { + get + { + var ip = Internal.GetPublicIP(); + if ( ip == 0 ) return null; + + return Utility.Int32ToIp( ip ); + } + } + + /// + /// Enable or disable heartbeats, which are sent regularly to the master server. + /// Enabled by default. + /// + public static bool AutomaticHeartbeats + { + set { Internal.EnableHeartbeats( value ); } + } + + /// + /// Set heartbeat interval, if automatic heartbeats are enabled. + /// You can leave this at the default. + /// + public static int AutomaticHeartbeatRate + { + set { Internal.SetHeartbeatInterval( value ); } + } + + /// + /// Force send a heartbeat to the master server instead of waiting + /// for the next automatic update (if you've left them enabled) + /// + public static void ForceHeartbeat() + { + Internal.ForceHeartbeat(); + } + + /// + /// Update this connected player's information. You should really call this + /// any time a player's name or score changes. This keeps the information shown + /// to server queries up to date. + /// + public static void UpdatePlayer( ulong steamid, string name, int score ) + { + Internal.BUpdateUserData( steamid, name, (uint)score ); + } + + static Dictionary KeyValue = new Dictionary(); + + /// + /// Sets a Key Value. These can be anything you like, and are accessible + /// when querying servers from the server list. + /// + /// Information describing gamemodes are common here. + /// + public static void SetKey( string Key, string Value ) + { + if ( KeyValue.ContainsKey( Key ) ) + { + if ( KeyValue[Key] == Value ) + return; + + KeyValue[Key] = Value; + } + else + { + KeyValue.Add( Key, Value ); + } + + Internal.SetKeyValue( Key, Value ); + } + + /// + /// Start authorizing a ticket. This user isn't authorized yet. Wait for a call to OnAuthChange. + /// + public static unsafe bool BeginAuthSession( byte[] data, ulong steamid ) + { + fixed ( byte* p = data ) + { + var result = Internal.BeginAuthSession( (IntPtr)p, data.Length, steamid ); + + if ( result == SteamNative.BeginAuthSessionResult.OK ) + return true; + + return false; + } + } + + /// + /// Forget this guy. They're no longer in the game. + /// + public static void EndSession( ulong steamid ) + { + Internal.EndAuthSession( steamid ); + } + + /// + /// If true, Steam wants to send a packet. You should respond by sending + /// this packet in an unconnected way to the returned Address and Port. + /// + /// Packet to send. The Data passed is pooled - so use it immediately. + /// True if we want to send a packet + public static unsafe bool GetOutgoingPacket( out OutgoingPacket packet ) + { + var buffer = Helpers.TakeBuffer( 1024 * 32 ); + packet = new OutgoingPacket(); + + fixed ( byte* ptr = buffer ) + { + uint addr = 0; + ushort port = 0; + + var size = Internal.GetNextOutgoingPacket( (IntPtr)ptr, buffer.Length, ref addr, ref port ); + if ( size == 0 ) + return false; + + packet.Size = size; + packet.Data = buffer; + packet.Address = addr; + packet.Port = port; + return true; + } + } + + /// + /// We have received a server query on our game port. Pass it to Steam to handle. + /// + public static unsafe void HandleIncomingPacket( byte[] data, int size, uint address, ushort port ) + { + fixed ( byte* ptr = data ) + { + Internal.HandleIncomingPacket( (IntPtr)ptr, size, address, port ); + } + } + } +} \ No newline at end of file diff --git a/Facepunch.Steamworks/Redux/Structs/OutgoingPacket.cs b/Facepunch.Steamworks/Redux/Structs/OutgoingPacket.cs new file mode 100644 index 0000000..1495dff --- /dev/null +++ b/Facepunch.Steamworks/Redux/Structs/OutgoingPacket.cs @@ -0,0 +1,30 @@ +namespace Steamworks +{ + /// + /// A server query packet. + /// + public struct OutgoingPacket + { + /// + /// Target IP address + /// + public uint Address { get; internal set; } + + /// + /// Target port + /// + public ushort Port { get; internal set; } + + /// + /// This data is pooled. Make a copy if you don't use it immediately. + /// This buffer is also quite large - so pay attention to Size. + /// + public byte[] Data { get; internal set; } + + /// + /// Size of the data + /// + public int Size { get; internal set; } + } + +} \ No newline at end of file diff --git a/Facepunch.Steamworks/Server/ServerInit.cs b/Facepunch.Steamworks/Redux/Structs/ServerInit.cs similarity index 72% rename from Facepunch.Steamworks/Server/ServerInit.cs rename to Facepunch.Steamworks/Redux/Structs/ServerInit.cs index d4117ec..60153d5 100644 --- a/Facepunch.Steamworks/Server/ServerInit.cs +++ b/Facepunch.Steamworks/Redux/Structs/ServerInit.cs @@ -5,48 +5,56 @@ using System.Net; using System.Runtime.InteropServices; using System.Text; -namespace Facepunch.Steamworks +namespace Steamworks { /// /// Used to set up the server. /// The variables in here are all required to be set, and can't be changed once the server is created. /// - public class ServerInit + public struct ServerInit { public IPAddress IpAddress; public ushort SteamPort; - public ushort GamePort = 27015; - public ushort QueryPort = 27016; - public bool Secure = true; + public ushort GamePort; + public ushort QueryPort; + public bool Secure; /// /// The version string is usually in the form x.x.x.x, and is used by the master server to detect when the server is out of date. /// If you go into the dedicated server tab on steamworks you'll be able to server the latest version. If this version number is /// less than that latest version then your server won't show. /// - public string VersionString = "2.0.0.0"; + public string VersionString; - /// - /// This should be the same directory game where gets installed into. Just the folder name, not the whole path. I.e. "Rust", "Garrysmod". - /// - public string ModDir = "unset"; + /// + /// This should be the same directory game where gets installed into. Just the folder name, not the whole path. I.e. "Rust", "Garrysmod". + /// + public string ModDir; /// /// The game description. Setting this to the full name of your game is recommended. /// - public string GameDescription = "unset"; + public string GameDescription; public ServerInit( string modDir, string gameDesc ) { ModDir = modDir; GameDescription = gameDesc; - } + GamePort = 27015; + QueryPort = 27016; + Secure = true; + VersionString = "1.0.0.0"; + ModDir = "unset"; + GameDescription = "unset"; + IpAddress = null; + SteamPort = 0; + } /// /// Set the Steam quert port /// - public ServerInit RandomSteamPort() + public ServerInit WithRandomSteamPort() { SteamPort = (ushort)new Random().Next( 10000, 60000 ); return this; @@ -59,7 +67,7 @@ namespace Facepunch.Steamworks /// /// More info about this here: https://partner.steamgames.com/doc/api/ISteamGameServer#HandleIncomingPacket /// - public ServerInit QueryShareGamePort() + public ServerInit WithQueryShareGamePort() { QueryPort = 0xFFFF; return this; diff --git a/Facepunch.Steamworks/Server.cs b/Facepunch.Steamworks/Server.cs deleted file mode 100644 index e2091c0..0000000 --- a/Facepunch.Steamworks/Server.cs +++ /dev/null @@ -1,338 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Facepunch.Steamworks -{ - /// - /// Initialize this class for Game Servers. - /// - /// Game servers offer a limited amount of Steam functionality - and don't require the Steam client. - /// - public partial class Server : BaseSteamworks - { - /// - /// A singleton accessor to get the current client instance. - /// - public static Server Instance { get; private set; } - - internal override bool IsGameServer { get { return true; } } - - public ServerQuery Query { get; internal set; } - public ServerStats Stats { get; internal set; } - public ServerAuth Auth { get; internal set; } - - /// - /// Initialize a Steam Server instance - /// - public Server( uint appId, ServerInit init) : base( appId ) - { - if ( Instance != null ) - { - throw new System.Exception( "Only one Facepunch.Steamworks.Server can exist - dispose the old one before trying to create a new one." ); - } - - Instance = this; - native = new Interop.NativeInterface(); - uint ipaddress = 0; // Any Port - - if ( init.SteamPort == 0 ) init.RandomSteamPort(); - if ( init.IpAddress != null ) ipaddress = Utility.IpToInt32( init.IpAddress ); - - // - // Get other interfaces - // - if ( !native.InitServer( this, ipaddress, init.SteamPort, init.GamePort, init.QueryPort, init.Secure ? 3 : 2, init.VersionString ) ) - { - native.Dispose(); - native = null; - Instance = null; - return; - } - - // - // Register Callbacks - // - - SteamNative.Callbacks.RegisterCallbacks( this ); - - // - // Setup interfaces that client and server both have - // - SetupCommonInterfaces(); - - - - // - // Initial settings - // - native.gameServer.EnableHeartbeats( true ); - MaxPlayers = 32; - BotCount = 0; - Product = $"{AppId}"; - ModDir = init.ModDir; - GameDescription = init.GameDescription; - Passworded = false; - DedicatedServer = true; - - // - // Child classes - // - Query = new ServerQuery( this ); - Stats = new ServerStats( this ); - Auth = new ServerAuth( this ); - - // - // Run update, first call does some initialization - // - Update(); - } - - ~Server() - { - Dispose(); - } - - /// - /// Should be called at least once every frame - /// - public override void Update() - { - if ( !IsValid ) - return; - - native.api.SteamGameServer_RunCallbacks(); - - base.Update(); - } - - /// - /// Sets whether this should be marked as a dedicated server. - /// If not, it is assumed to be a listen server. - /// - public bool DedicatedServer - { - get { return _dedicatedServer; } - set { if ( _dedicatedServer == value ) return; native.gameServer.SetDedicatedServer( value ); _dedicatedServer = value; } - } - private bool _dedicatedServer; - - /// - /// Gets or sets the current MaxPlayers. - /// This doesn't enforce any kind of limit, it just updates the master server. - /// - public int MaxPlayers - { - get { return _maxplayers; } - set { if ( _maxplayers == value ) return; native.gameServer.SetMaxPlayerCount( value ); _maxplayers = value; } - } - private int _maxplayers = 0; - - /// - /// Gets or sets the current BotCount. - /// This doesn't enforce any kind of limit, it just updates the master server. - /// - public int BotCount - { - get { return _botcount; } - set { if ( _botcount == value ) return; native.gameServer.SetBotPlayerCount( value ); _botcount = value; } - } - private int _botcount = 0; - - /// - /// Gets or sets the current Map Name. - /// - public string MapName - { - get { return _mapname; } - set { if ( _mapname == value ) return; native.gameServer.SetMapName( value ); _mapname = value; } - } - private string _mapname; - - /// - /// Gets or sets the current ModDir - /// - public string ModDir - { - get { return _modDir; } - internal set { if ( _modDir == value ) return; native.gameServer.SetModDir( value ); _modDir = value; } - } - private string _modDir = ""; - - /// - /// Gets the current product - /// - public string Product - { - get { return _product; } - internal set { if ( _product == value ) return; native.gameServer.SetProduct( value ); _product = value; } - } - private string _product = ""; - - /// - /// Gets or sets the current Product - /// - public string GameDescription - { - get { return _gameDescription; } - internal set { if ( _gameDescription == value ) return; native.gameServer.SetGameDescription( value ); _gameDescription = value; } - } - private string _gameDescription = ""; - - /// - /// Gets or sets the current ServerName - /// - public string ServerName - { - get { return _serverName; } - set { if ( _serverName == value ) return; native.gameServer.SetServerName( value ); _serverName = value; } - } - private string _serverName = ""; - - /// - /// Set whether the server should report itself as passworded - /// - public bool Passworded - { - get { return _passworded; } - set { if ( _passworded == value ) return; native.gameServer.SetPasswordProtected( value ); _passworded = value; } - } - private bool _passworded; - - /// - /// Gets or sets the current GameTags. This is a comma seperated list of tags for this server. - /// When querying the server list you can filter by these tags. - /// - public string GameTags - { - get { return _gametags; } - set { if ( _gametags == value ) return; native.gameServer.SetGameTags( value ); _gametags = value; } - } - private string _gametags = ""; - - /// - /// Log onto Steam anonymously. - /// - public void LogOnAnonymous() - { - native.gameServer.LogOnAnonymous(); - ForceHeartbeat(); - } - - /// - /// Returns true if the server is connected and registered with the Steam master server - /// You should have called LogOnAnonymous etc on startup. - /// - public bool LoggedOn => native.gameServer.BLoggedOn(); - - Dictionary KeyValue = new Dictionary(); - - /// - /// Sets a Key Value. These can be anything you like, and are accessible - /// when querying servers from the server list. - /// - /// Information describing gamemodes are common here. - /// - public void SetKey( string Key, string Value ) - { - if ( KeyValue.ContainsKey( Key ) ) - { - if ( KeyValue[Key] == Value ) - return; - - KeyValue[Key] = Value; - } - else - { - KeyValue.Add( Key, Value ); - } - - native.gameServer.SetKeyValue( Key, Value ); - } - - /// - /// Update this connected player's information. You should really call this - /// any time a player's name or score changes. This keeps the information shown - /// to server queries up to date. - /// - public void UpdatePlayer( ulong steamid, string name, int score ) - { - native.gameServer.BUpdateUserData( steamid, name, (uint) score ); - } - - - /// - /// Shutdown interface, disconnect from Steam - /// - public override void Dispose() - { - if ( disposed ) return; - - if ( Query != null ) - { - Query = null; - } - - if ( Stats != null ) - { - Stats = null; - } - - if ( Auth != null ) - { - Auth = null; - } - - if ( Instance == this ) - { - Instance = null; - } - - base.Dispose(); - } - - /// - /// To the best of its ability this tries to get the server's - /// current public ip address. Be aware that this is likely to return - /// null for the first few seconds after initialization. - /// - public System.Net.IPAddress PublicIp - { - get - { - var ip = native.gameServer.GetPublicIP(); - if ( ip == 0 ) return null; - - return Utility.Int32ToIp( ip ); - } - } - - /// - /// Enable or disable heartbeats, which are sent regularly to the master server. - /// Enabled by default. - /// - public bool AutomaticHeartbeats - { - set { native.gameServer.EnableHeartbeats( value ); } - } - - /// - /// Set heartbeat interval, if automatic heartbeats are enabled. - /// You can leave this at the default. - /// - public int AutomaticHeartbeatRate - { - set { native.gameServer.SetHeartbeatInterval( value ); } - } - - /// - /// Force send a heartbeat to the master server instead of waiting - /// for the next automatic update (if you've left them enabled) - /// - public void ForceHeartbeat() - { - native.gameServer.ForceHeartbeat(); - } - - - } -} diff --git a/Facepunch.Steamworks/Server/Auth.cs b/Facepunch.Steamworks/Server/Auth.cs deleted file mode 100644 index 306a8ed..0000000 --- a/Facepunch.Steamworks/Server/Auth.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Facepunch.Steamworks -{ - public class ServerAuth - { - internal Server server; - - /// - /// Steamid, Ownerid, Status - /// - public Action OnAuthChange; - - /// - /// Steam authetication statuses - /// - public enum Status : int - { - OK = 0, - UserNotConnectedToSteam = 1, - NoLicenseOrExpired = 2, - VACBanned = 3, - LoggedInElseWhere = 4, - VACCheckTimedOut = 5, - AuthTicketCanceled = 6, - AuthTicketInvalidAlreadyUsed = 7, - AuthTicketInvalid = 8, - PublisherIssuedBan = 9, - } - - internal ServerAuth( Server s ) - { - server = s; - - server.RegisterCallback( OnAuthTicketValidate ); - } - - void OnAuthTicketValidate( SteamNative.ValidateAuthTicketResponse_t data ) - { - if ( OnAuthChange != null ) - OnAuthChange( data.SteamID, data.OwnerSteamID, (Status) data.AuthSessionResponse ); - } - - /// - /// Start authorizing a ticket. This user isn't authorized yet. Wait for a call to OnAuthChange. - /// - public unsafe bool StartSession( byte[] data, ulong steamid ) - { - fixed ( byte* p = data ) - { - var result = server.native.gameServer.BeginAuthSession( (IntPtr)p, data.Length, steamid ); - - if ( result == SteamNative.BeginAuthSessionResult.OK ) - return true; - - return false; - } - } - - /// - /// Forget this guy. They're no longer in the game. - /// - public void EndSession( ulong steamid ) - { - server.native.gameServer.EndAuthSession( steamid ); - } - - - } -} diff --git a/Facepunch.Steamworks/Server/Query.cs b/Facepunch.Steamworks/Server/Query.cs deleted file mode 100644 index 7ae8321..0000000 --- a/Facepunch.Steamworks/Server/Query.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; - -namespace Facepunch.Steamworks -{ - /// - /// If you're manually processing the server queries, you should use this class. - /// - public class ServerQuery - { - internal Server server; - internal static byte[] buffer = new byte[64*1024]; - - internal ServerQuery( Server s ) - { - server = s; - } - - /// - /// A server query packet. - /// - public struct Packet - { - /// - /// Target IP address - /// - public uint Address { get; internal set; } - - /// - /// Target port - /// - public ushort Port { get; internal set; } - - /// - /// This data is pooled. Make a copy if you don't use it immediately. - /// This buffer is also quite large - so pay attention to Size. - /// - public byte[] Data { get; internal set; } - - /// - /// Size of the data - /// - public int Size { get; internal set; } - } - - /// - /// If true, Steam wants to send a packet. You should respond by sending - /// this packet in an unconnected way to the returned Address and Port. - /// - /// Packet to send. The Data passed is pooled - so use it immediately. - /// True if we want to send a packet - public unsafe bool GetOutgoingPacket( out Packet packet ) - { - packet = new Packet(); - - fixed ( byte* ptr = buffer ) - { - uint addr = 0; - ushort port = 0; - - var size = server.native.gameServer.GetNextOutgoingPacket( (IntPtr)ptr, buffer.Length, out addr, out port ); - if ( size == 0 ) - return false; - - packet.Size = size; - packet.Data = buffer; - packet.Address = addr; - packet.Port = port; - return true; - } - } - - /// - /// We have received a server query on our game port. Pass it to Steam to handle. - /// - public unsafe void Handle( byte[] data, int size, uint address, ushort port ) - { - fixed ( byte* ptr = data ) - { - server.native.gameServer.HandleIncomingPacket( (IntPtr)ptr, size, address, port ); - } - } - - } -} diff --git a/Facepunch.Steamworks/Server/Stats.cs b/Facepunch.Steamworks/Server/Stats.cs index e30a22d..a2a3213 100644 --- a/Facepunch.Steamworks/Server/Stats.cs +++ b/Facepunch.Steamworks/Server/Stats.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Runtime.InteropServices; using System.Text; +#if false namespace Facepunch.Steamworks { /// @@ -144,3 +145,4 @@ namespace Facepunch.Steamworks } } } +#endif \ No newline at end of file diff --git a/Facepunch.Steamworks/SteamNative/SteamNative.Callback.cs b/Facepunch.Steamworks/SteamNative/SteamNative.Callback.cs index c2fcb86..2feed87 100644 --- a/Facepunch.Steamworks/SteamNative/SteamNative.Callback.cs +++ b/Facepunch.Steamworks/SteamNative/SteamNative.Callback.cs @@ -326,9 +326,6 @@ namespace SteamNative if ( Facepunch.Steamworks.Client.Instance != null ) Facepunch.Steamworks.Client.Instance.OnCallback( value ); - - if ( Facepunch.Steamworks.Server.Instance != null ) - Facepunch.Steamworks.Server.Instance.OnCallback( value ); } } diff --git a/Facepunch.Steamworks/Utility/BaseSteamInterface.cs b/Facepunch.Steamworks/Utility/BaseSteamInterface.cs index da2bd9c..970148d 100644 --- a/Facepunch.Steamworks/Utility/BaseSteamInterface.cs +++ b/Facepunch.Steamworks/Utility/BaseSteamInterface.cs @@ -16,14 +16,25 @@ namespace Steamworks.Internal public virtual string InterfaceName => null; - public BaseSteamInterface() + public BaseSteamInterface( bool server = false ) { - if ( Steamworks.Steam.HUser == 0 ) + var hUser = server ? SteamApi.SteamGameServer_GetHSteamUser() : SteamApi.GetHSteamUser(); + + if ( hUser == 0 ) throw new System.Exception( "Steamworks is uninitialized" ); - Self = SteamInternal.FindOrCreateUserInterface( Steamworks.Steam.HUser, InterfaceName ); + if ( server ) + { + Self = SteamInternal.FindOrCreateUserInterface( hUser, InterfaceName ); + } + else + { + Self = SteamInternal.FindOrCreateUserInterface( hUser, InterfaceName ); + } + if ( Self == IntPtr.Zero ) - throw new System.Exception( $"Couldn't find interface {InterfaceName}" ); + throw new System.Exception( $"Couldn't find interface {InterfaceName} (server:{server})" ); + VTable = Marshal.ReadIntPtr( Self, 0 ); if ( Self == IntPtr.Zero ) diff --git a/Generator/CodeWriter/ClassVTable.cs b/Generator/CodeWriter/ClassVTable.cs index 2a91919..93065bf 100644 --- a/Generator/CodeWriter/ClassVTable.cs +++ b/Generator/CodeWriter/ClassVTable.cs @@ -27,6 +27,12 @@ namespace Generator { StartBlock( $"public class {clss.Name} : BaseSteamInterface" ); { + StartBlock( $"public {clss.Name}( bool server = false ) : base( server )" ); + { + + } + EndBlock(); + WriteLine(); WriteLine( $"public override string InterfaceName => \"{clss.InterfaceString}\";" ); WriteLine(); diff --git a/Generator/CodeWriter/CodeWriter.cs b/Generator/CodeWriter/CodeWriter.cs index e3f1456..50a4b55 100644 --- a/Generator/CodeWriter/CodeWriter.cs +++ b/Generator/CodeWriter/CodeWriter.cs @@ -100,6 +100,7 @@ namespace Generator GenerateVTableClass( "ISteamUser", $"{folder}../Generated/Interfaces/ISteamUser.cs" ); GenerateVTableClass( "ISteamMatchmakingServers", $"{folder}../Generated/Interfaces/ISteamMatchmakingServers.cs" ); GenerateVTableClass( "ISteamFriends", $"{folder}../Generated/Interfaces/ISteamFriends.cs" ); + GenerateVTableClass( "ISteamGameServer", $"{folder}../Generated/Interfaces/ISteamGameServer.cs" ); } } diff --git a/Generator/CodeWriter/Types/BaseType.cs b/Generator/CodeWriter/Types/BaseType.cs index fe5f768..fb042d8 100644 --- a/Generator/CodeWriter/Types/BaseType.cs +++ b/Generator/CodeWriter/Types/BaseType.cs @@ -40,7 +40,7 @@ internal class BaseType return new BaseType { NativeType = type, VarName = varname }; } - public virtual string AsArgument() => IsVector? $"[In,Out] {Ref}{TypeName.Trim( '*', ' ' )}[] {VarName}" : $"{Ref}{TypeName.Trim( '*', ' ' )} {VarName}"; + public virtual string AsArgument() => IsVector ? $"[In,Out] {Ref}{TypeName.Trim( '*', ' ' )}[] {VarName}" : $"{Ref}{TypeName.Trim( '*', ' ' )} {VarName}"; public virtual string AsCallArgument() => $"{Ref}{VarName}"; public virtual string Return( string varname ) => $"return {varname};"; @@ -49,9 +49,26 @@ internal class BaseType public virtual string ReturnType => TypeName; public virtual string Ref => !IsVector && NativeType.EndsWith( "*" ) || NativeType.EndsWith( "**" ) ? "ref " : ""; - public virtual bool IsVector => (NativeType.EndsWith( "*" ) && (VarName.StartsWith( "pvec" ) || VarName.StartsWith( "pub" ) || VarName.StartsWith( "pOut" ))) - || NativeType.EndsWith( "**" ) - || VarName == "psteamIDClans"; + public virtual bool IsVector + { + get + { + if ( VarName == "pOut" ) return false; + + if ( VarName == "psteamIDClans" ) return true; + if ( NativeType.EndsWith( "**" ) ) return true; + + if ( NativeType.EndsWith( "*" ) ) + { + if ( VarName.StartsWith( "pvec" ) ) return true; + if ( VarName.StartsWith( "pub" ) ) return true; + if ( VarName.StartsWith( "pOut" ) ) return true; + } + + return false; + } + } + public virtual bool IsVoid => false; public virtual bool IsReturnedWeird => false;