using System; using System.Collections.Generic; using System.Linq; using System.Text; using Facepunch.Steamworks.Interop; namespace Facepunch.Steamworks { /// /// Implements shared functionality between Steamworks.Client and Steamworks.Server /// public class BaseSteamworks : IDisposable { /// /// Current running program's AppId /// public uint AppId { get; internal set; } public Networking Networking { get; internal set; } public Inventory Inventory { get; internal set; } public Workshop Workshop { get; internal set; } internal event Action OnUpdate; internal Interop.NativeInterface native; private List CallbackHandles = new List(); private List CallResults = new List(); protected bool disposed = false; protected BaseSteamworks( uint appId ) { AppId = appId; // // No need for the "steam_appid.txt" file any more // System.Environment.SetEnvironmentVariable("SteamAppId", AppId.ToString()); System.Environment.SetEnvironmentVariable("SteamGameId", AppId.ToString()); } ~BaseSteamworks() { Dispose(); } public virtual void Dispose() { if ( disposed ) return; Callbacks.Clear(); foreach ( var h in CallbackHandles ) { h.Dispose(); } CallbackHandles.Clear(); foreach ( var h in CallResults ) { h.Dispose(); } CallResults.Clear(); if ( Workshop != null ) { Workshop.Dispose(); Workshop = null; } if ( Inventory != null ) { Inventory.Dispose(); Inventory = null; } if ( Networking != null ) { Networking.Dispose(); Networking = null; } if ( native != null ) { try { native.Dispose(); } catch (DllNotFoundException e) { System.Diagnostics.Debug.WriteLine("Disposing SteamWorks NativeInterface failed (" + e.Message + ")\n" + e.StackTrace); } native = null; } System.Environment.SetEnvironmentVariable("SteamAppId", null ); System.Environment.SetEnvironmentVariable("SteamGameId", null ); disposed = true; } protected void SetupCommonInterfaces() { Networking = new Steamworks.Networking( this, native.networking ); Inventory = new Steamworks.Inventory( this, native.inventory, IsGameServer ); Workshop = new Steamworks.Workshop( this, native.ugc, native.remoteStorage ); } /// /// Returns true if this instance has initialized properly. /// If this returns false you should Dispose and throw an error. /// public bool IsValid { get { return native != null; } } internal virtual bool IsGameServer { get { return false; } } internal void RegisterCallbackHandle( SteamNative.CallbackHandle handle ) { CallbackHandles.Add( handle ); } internal void RegisterCallResult( SteamNative.CallResult handle ) { CallResults.Add( handle ); } internal void UnregisterCallResult( SteamNative.CallResult handle ) { CallResults.Remove( handle ); } public virtual void Update() { Networking.Update(); RunUpdateCallbacks(); } /// /// This gets called automatically in Update. Only call it manually if you know why you're doing it. /// public void RunUpdateCallbacks() { if ( OnUpdate != null ) OnUpdate(); for( int i=0; i < CallResults.Count; i++ ) { CallResults[i].Try(); } // // The SourceServerQuery's happen in another thread, so we // query them to see if they're finished, and if so post a callback // in our main thread. This will all suck less once we have async. // Facepunch.Steamworks.SourceServerQuery.Cycle(); } /// /// Run Update until func returns false. /// This will cause your program to lock up until it finishes. /// This is useful for things like tests or command line utilities etc. /// public void UpdateWhile( Func func ) { const int sleepMs = 1; while ( func() ) { Update(); #if NET_CORE System.Threading.Tasks.Task.Delay( sleepMs ).Wait(); #else System.Threading.Thread.Sleep( sleepMs ); #endif } } /// /// Debug function, called for every callback. Only really used to confirm that callbacks are working properly. /// public Action OnAnyCallback; Dictionary>> Callbacks = new Dictionary>>(); internal List> CallbackList( Type T ) { List> list = null; if ( !Callbacks.TryGetValue( T, out list ) ) { list = new List>(); Callbacks[T] = list; } return list; } internal void OnCallback( T data ) { var list = CallbackList( typeof( T ) ); foreach ( var i in list ) { i( data ); } if ( OnAnyCallback != null ) { OnAnyCallback.Invoke( data ); } } internal void RegisterCallback( Action func ) { var list = CallbackList( typeof( T ) ); list.Add( ( o ) => func( (T) o ) ); } } }