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, bool isPublic) : 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 // //kind of a hack: //use an outdated version number to hide private servers from the server list. //couldn't find a way to do it otherwise - using 1 as the eServerMode doesn't //seem to work, the server info is still returned by the API calls string versionString = isPublic ? init.VersionString : "0.0.0.0"; if ( !native.InitServer( this, ipaddress, init.SteamPort, init.GamePort, init.QueryPort, init.Secure ? 3 : 2, isPublic ? init.VersionString : "0.0.0.0" ) ) { 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(); } } }