diff --git a/Facepunch.Steamworks.Test/Client/Friends.cs b/Facepunch.Steamworks.Test/Client/Friends.cs
index 9604516..b580f9f 100644
--- a/Facepunch.Steamworks.Test/Client/Friends.cs
+++ b/Facepunch.Steamworks.Test/Client/Friends.cs
@@ -4,6 +4,8 @@
namespace Facepunch.Steamworks.Test
{
+ [DeploymentItem( "FacepunchSteamworksApi.dll" )]
+ [DeploymentItem( "steam_appid.txt" )]
[TestClass]
public class Friends
{
diff --git a/Facepunch.Steamworks.Test/Facepunch.Steamworks.Test.csproj b/Facepunch.Steamworks.Test/Facepunch.Steamworks.Test.csproj
index 25e9e2b..c87f1b0 100644
--- a/Facepunch.Steamworks.Test/Facepunch.Steamworks.Test.csproj
+++ b/Facepunch.Steamworks.Test/Facepunch.Steamworks.Test.csproj
@@ -92,6 +92,7 @@
+
diff --git a/Facepunch.Steamworks.Test/Server/Server.cs b/Facepunch.Steamworks.Test/Server/Server.cs
new file mode 100644
index 0000000..0d85f9c
--- /dev/null
+++ b/Facepunch.Steamworks.Test/Server/Server.cs
@@ -0,0 +1,23 @@
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Facepunch.Steamworks.Test
+{
+ [DeploymentItem( "FacepunchSteamworksApi.dll" )]
+ [DeploymentItem( "steam_appid.txt" )]
+ [DeploymentItem( "tier0_s.dll" )]
+ [DeploymentItem( "vstdlib_s.dll" )]
+ [DeploymentItem( "steamclient.dll" )]
+ [TestClass]
+ public class Server
+ {
+ [TestMethod]
+ public void Init()
+ {
+ using ( var server = new Facepunch.Steamworks.Server( 252490, 0, 20216, 20816, 20817, false, "VersionString" ) )
+ {
+ Assert.IsTrue( server.Valid );
+ }
+ }
+ }
+}
diff --git a/Facepunch.Steamworks.Test/bin/Debug/steam_api.dll b/Facepunch.Steamworks.Test/bin/Debug/steam_api.dll
new file mode 100644
index 0000000..535ba15
Binary files /dev/null and b/Facepunch.Steamworks.Test/bin/Debug/steam_api.dll differ
diff --git a/Facepunch.Steamworks.Test/bin/Debug/steamclient.dll b/Facepunch.Steamworks.Test/bin/Debug/steamclient.dll
new file mode 100644
index 0000000..7f00869
Binary files /dev/null and b/Facepunch.Steamworks.Test/bin/Debug/steamclient.dll differ
diff --git a/Facepunch.Steamworks.Test/bin/Debug/tier0_s.dll b/Facepunch.Steamworks.Test/bin/Debug/tier0_s.dll
new file mode 100644
index 0000000..8ef8a75
Binary files /dev/null and b/Facepunch.Steamworks.Test/bin/Debug/tier0_s.dll differ
diff --git a/Facepunch.Steamworks.Test/bin/Debug/vstdlib_s.dll b/Facepunch.Steamworks.Test/bin/Debug/vstdlib_s.dll
new file mode 100644
index 0000000..00901fa
Binary files /dev/null and b/Facepunch.Steamworks.Test/bin/Debug/vstdlib_s.dll differ
diff --git a/Facepunch.Steamworks/Client.cs b/Facepunch.Steamworks/Client.cs
index 9bc66e3..0b60b3e 100644
--- a/Facepunch.Steamworks/Client.cs
+++ b/Facepunch.Steamworks/Client.cs
@@ -7,8 +7,6 @@ namespace Facepunch.Steamworks
{
public partial class Client : IDisposable
{
-
-
internal class Internal : IDisposable
{
private uint _hpipe;
diff --git a/Facepunch.Steamworks/Facepunch.Steamworks.csproj b/Facepunch.Steamworks/Facepunch.Steamworks.csproj
index 3a9c050..d70f83a 100644
--- a/Facepunch.Steamworks/Facepunch.Steamworks.csproj
+++ b/Facepunch.Steamworks/Facepunch.Steamworks.csproj
@@ -131,8 +131,11 @@
+
+
+
+
-
diff --git a/Facepunch.Steamworks/Interop/steam_api_interop.cs b/Facepunch.Steamworks/Interop/steam_api_interop.cs
index 01c332a..cf49dbe 100644
--- a/Facepunch.Steamworks/Interop/steam_api_interop.cs
+++ b/Facepunch.Steamworks/Interop/steam_api_interop.cs
@@ -15,6 +15,28 @@ namespace Valve.Interop
internal class NativeEntrypoints
{
+ internal class Extended
+ {
+ [DllImportAttribute( "FacepunchSteamworksApi", CallingConvention = CallingConvention.Cdecl )]
+ public static extern bool SteamInternal_GameServer_Init( uint unIP, ushort usSteamPort, ushort usGamePort, ushort usQueryPort, int eServerMode, string pchVersionString );
+
+ [DllImportAttribute( "FacepunchSteamworksApi", CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr SteamInternal_CreateInterface( string ver );
+
+ [DllImportAttribute( "FacepunchSteamworksApi", CallingConvention = CallingConvention.Cdecl )]
+ internal static extern uint SteamGameServer_GetHSteamUser();
+
+ [DllImportAttribute( "FacepunchSteamworksApi", CallingConvention = CallingConvention.Cdecl )]
+ internal static extern uint SteamGameServer_GetHSteamPipe();
+
+ [DllImportAttribute( "FacepunchSteamworksApi", CallingConvention = CallingConvention.Cdecl )]
+ internal static extern void SteamGameServer_Shutdown();
+
+ [DllImportAttribute( "FacepunchSteamworksApi", CallingConvention = CallingConvention.Cdecl )]
+ internal static extern void SteamGameServer_RunCallbacks();
+
+ }
+
[DllImportAttribute( "FacepunchSteamworksApi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_CreateSteamPipe" )]
internal static extern uint SteamAPI_ISteamClient_CreateSteamPipe( IntPtr instancePtr );
[DllImportAttribute( "FacepunchSteamworksApi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_BReleaseSteamPipe" )]
@@ -2416,7 +2438,7 @@ internal override ISteamGameServer GetISteamGameServer( uint hSteamUser, uint hS
{
CheckIfUsable();
IntPtr result = NativeEntrypoints.SteamAPI_ISteamClient_GetISteamGameServer(m_pSteamClient,hSteamUser,hSteamPipe,pchVersion);
- return (ISteamGameServer)Marshal.PtrToStructure( result, typeof( ISteamGameServer ) );
+ return new CSteamGameServer( result );
}
internal override void SetLocalIPBinding( uint unIP, char usPort )
{
@@ -2463,7 +2485,7 @@ internal override ISteamGameServerStats GetISteamGameServerStats( uint hSteamuse
{
CheckIfUsable();
IntPtr result = NativeEntrypoints.SteamAPI_ISteamClient_GetISteamGameServerStats(m_pSteamClient,hSteamuser,hSteamPipe,pchVersion);
- return (ISteamGameServerStats)Marshal.PtrToStructure( result, typeof( ISteamGameServerStats ) );
+ return new CSteamGameServerStats( result );
}
internal override ISteamApps GetISteamApps( uint hSteamUser, uint hSteamPipe, string pchVersion )
{
@@ -2510,7 +2532,7 @@ internal override ISteamHTTP GetISteamHTTP( uint hSteamuser, uint hSteamPipe, st
{
CheckIfUsable();
IntPtr result = NativeEntrypoints.SteamAPI_ISteamClient_GetISteamHTTP(m_pSteamClient,hSteamuser,hSteamPipe,pchVersion);
- return (ISteamHTTP)Marshal.PtrToStructure( result, typeof( ISteamHTTP ) );
+ return new CSteamHTTP( result );
}
internal override ISteamUnifiedMessages GetISteamUnifiedMessages( uint hSteamuser, uint hSteamPipe, string pchVersion )
{
@@ -2528,7 +2550,7 @@ internal override ISteamUGC GetISteamUGC( uint hSteamUser, uint hSteamPipe, stri
{
CheckIfUsable();
IntPtr result = NativeEntrypoints.SteamAPI_ISteamClient_GetISteamUGC(m_pSteamClient,hSteamUser,hSteamPipe,pchVersion);
- return (ISteamUGC)Marshal.PtrToStructure( result, typeof( ISteamUGC ) );
+ return new CSteamUGC( result );
}
internal override ISteamAppList GetISteamAppList( uint hSteamUser, uint hSteamPipe, string pchVersion )
{
diff --git a/Facepunch.Steamworks/Server.cs b/Facepunch.Steamworks/Server.cs
new file mode 100644
index 0000000..b99976b
--- /dev/null
+++ b/Facepunch.Steamworks/Server.cs
@@ -0,0 +1,239 @@
+
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace Facepunch.Steamworks
+{
+ public partial class Server : IDisposable
+ {
+ internal class Internal : IDisposable
+ {
+ internal Valve.Steamworks.ISteamClient client;
+ internal Valve.Steamworks.ISteamGameServer gameServer;
+ internal Valve.Steamworks.ISteamUtils utils;
+ internal Valve.Steamworks.ISteamNetworking networking;
+ internal Valve.Steamworks.ISteamGameServerStats stats;
+ internal Valve.Steamworks.ISteamHTTP http;
+ internal Valve.Steamworks.ISteamInventory inventory;
+ internal Valve.Steamworks.ISteamUGC ugc;
+ internal Valve.Steamworks.ISteamApps apps;
+
+ internal bool Init()
+ {
+ var user = Valve.Interop.NativeEntrypoints.Extended.SteamGameServer_GetHSteamUser();
+ var pipe = Valve.Interop.NativeEntrypoints.Extended.SteamGameServer_GetHSteamPipe();
+ if ( pipe == 0 )
+ return false;
+
+ var clientPtr = Valve.Interop.NativeEntrypoints.Extended.SteamInternal_CreateInterface( "SteamClient017" );
+ if ( clientPtr == IntPtr.Zero )
+ {
+ throw new System.Exception( "Steam Server: Couldn't load SteamClient017" );
+ }
+
+ client = new Valve.Steamworks.CSteamClient( clientPtr );
+
+
+ gameServer = client.GetISteamGameServer( user, pipe, "SteamGameServer012" );
+
+ if ( gameServer.GetIntPtr() == IntPtr.Zero )
+ {
+ gameServer = null;
+ throw new System.Exception( "Steam Server: Couldn't load SteamGameServer012" );
+ }
+
+ utils = client.GetISteamUtils( pipe, "SteamUtils008" );
+ networking = client.GetISteamNetworking( user, pipe, "SteamNetworking005" );
+ stats = client.GetISteamGameServerStats( user, pipe, "SteamGameServerStats001" );
+ http = client.GetISteamHTTP( user, pipe, "STEAMHTTP_INTERFACE_VERSION002" );
+ inventory = client.GetISteamInventory( user, pipe, "STEAMINVENTORY_INTERFACE_V001" );
+ ugc = client.GetISteamUGC( user, pipe, "STEAMUGC_INTERFACE_VERSION008" );
+
+ if ( ugc.GetIntPtr() == IntPtr.Zero )
+ throw new System.Exception( "Steam Server: Couldn't load STEAMUGC_INTERFACE_VERSION008" );
+
+ apps = client.GetISteamApps( user, pipe, "STEAMAPPS_INTERFACE_VERSION008" );
+
+ if ( apps.GetIntPtr() == IntPtr.Zero )
+ throw new System.Exception( "Steam Server: Couldn't load STEAMAPPS_INTERFACE_VERSION008" );
+
+ return true;
+ }
+
+ public void Dispose()
+ {
+ if ( client != null )
+ {
+ client.BShutdownIfAllPipesClosed();
+ client = null;
+ }
+ }
+ }
+
+ internal Internal native;
+
+ ///
+ /// Current running program's AppId
+ ///
+ public uint AppId { get; private set; }
+
+ ///
+ /// Current user's Username
+ ///
+ public string Username { get; private set; }
+
+ ///
+ /// Current user's SteamId
+ ///
+ public ulong SteamId { get; private set; }
+
+ public enum MessageType : int
+ {
+ Message = 0,
+ Warning = 1
+ }
+
+ ///
+ /// Called with a message from Steam
+ ///
+ public Action OnMessage;
+
+ public Server( uint appId, uint IpAddress, ushort SteamPort, ushort GamePort, ushort QueryPort, bool Secure, string VersionString )
+ {
+ if ( !Valve.Interop.NativeEntrypoints.Extended.SteamInternal_GameServer_Init( IpAddress, SteamPort, GamePort, QueryPort, Secure ? 3 : 2 , VersionString ) )
+ {
+ return;
+ }
+
+ //Valve.Steamworks.SteamAPI.Init( appId );
+
+ native = new Internal();
+
+ //
+ // Get other interfaces
+ //
+ if ( !native.Init() )
+ {
+ native.Dispose();
+ native = null;
+ return;
+ }
+
+ //
+ // Set up warning hook callback
+ //
+ SteamAPIWarningMessageHook ptr = InternalOnWarning;
+ // native.client.SetWarningMessageHook( Marshal.GetFunctionPointerForDelegate( ptr ) );
+
+ //
+ // Cache common, unchanging info
+ //
+ AppId = appId;
+
+ //
+ // Initial settings
+ //
+ native.gameServer.EnableHeartbeats( true );
+ MaxPlayers = 32;
+ BotCount = 0;
+ MapName = "unset";
+
+ //
+ // Run update, first call does some initialization
+ //
+ Update();
+ }
+
+ public void Dispose()
+ {
+ if ( native != null)
+ {
+ native.Dispose();
+ native = null;
+ }
+ }
+
+ [UnmanagedFunctionPointer( CallingConvention.Cdecl )]
+ public delegate void SteamAPIWarningMessageHook( int nSeverity, System.Text.StringBuilder pchDebugText );
+
+ private void InternalOnWarning( int nSeverity, System.Text.StringBuilder text )
+ {
+ if ( OnMessage != null )
+ {
+ OnMessage( ( MessageType)nSeverity, text.ToString() );
+ }
+ }
+
+ internal event Action OnUpdate;
+
+ ///
+ /// Should be called at least once every frame
+ ///
+ public void Update()
+ {
+ if ( native == null )
+ return;
+
+ Valve.Interop.NativeEntrypoints.Extended.SteamGameServer_RunCallbacks();
+ // Voice.Update();
+ // Inventory.Update();
+ // Networking.Update();
+
+ if ( OnUpdate != null )
+ OnUpdate();
+ }
+
+ public bool Valid
+ {
+ get { return native != null; }
+ }
+
+ internal Action InstallCallback( int type, Delegate action )
+ {
+ var del = Marshal.GetFunctionPointerForDelegate( action );
+
+ // var ptr = Marshal.GetFunctionPointerForDelegate( action );
+ // Valve.Steamworks.SteamAPI.RegisterCallback( del, type );
+
+ // Valve.Steamworks.SteamAPI.UnregisterCallback( del );
+
+ //return () => Valve.Steamworks.SteamAPI.UnregisterCallback( ptr );
+ return null;
+ }
+
+
+
+ ///
+ /// 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;
+ }
+}