diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index f17042c72f..649651d977 100755 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -195,6 +195,7 @@ + diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index daf20e9a46..95f552a733 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -34,22 +34,13 @@ namespace OpenRA.Server static TypeDictionary ServerTraits = new TypeDictionary(); public static Session lobbyInfo; public static bool GameStarted = false; - static string Name; - static int ExternalPort; + public static string Name; static int randomSeed; const int DownloadChunkInterval = 20000; const int DownloadChunkSize = 16384; - const int MasterPingInterval = 60 * 3; // 3 minutes. server has a 5 minute TTL for games, so give ourselves a bit - // of leeway. - public static int MaxSpectators = 4; // How many spectators to allow // @todo Expose this as an option - - static int lastPing = 0; - static bool isInternetServer; - static string masterServerUrl; - static bool isInitialPing; public static ModData ModData; public static Map Map; @@ -68,12 +59,10 @@ namespace OpenRA.Server ServerTraits.Add( new DebugServerTrait() ); ServerTraits.Add( new PlayerCommands() ); ServerTraits.Add( new LobbyCommands() ); - isInitialPing = true; - Server.masterServerUrl = settings.Server.MasterServer; - isInternetServer = settings.Server.AdvertiseOnline; + ServerTraits.Add( new MasterServerPinger() ); + listener = new TcpListener(IPAddress.Any, settings.Server.ListenPort); Name = settings.Server.Name; - ExternalPort = settings.Server.ExternalPort; randomSeed = (int)DateTime.Now.ToBinary(); ModData = modData; @@ -90,7 +79,7 @@ namespace OpenRA.Server foreach( var m in lobbyInfo.GlobalSettings.Mods ) Log.Write("server","- {0}", m); - Log.Write("server", "Initial map: {0}",lobbyInfo.GlobalSettings.Map); + //Log.Write("server", "Initial map: {0}",lobbyInfo.GlobalSettings.Map); try { @@ -103,24 +92,21 @@ namespace OpenRA.Server new Thread( _ => { + var timeout = ServerTraits.WithInterface().Min(t => t.TickTimeout); for( ; ; ) { var checkRead = new ArrayList(); checkRead.Add( listener.Server ); foreach( var c in conns ) checkRead.Add( c.socket ); - Socket.Select( checkRead, null, null, MasterPingInterval * 10000 ); + Socket.Select( checkRead, null, null, timeout ); foreach( Socket s in checkRead ) if( s == listener.Server ) AcceptConnection(); else if (conns.Count > 0) conns.Single( c => c.socket == s ).ReadData(); - if (Environment.TickCount - lastPing > MasterPingInterval * 1000) - PingMasterServer(); - else - lock (masterServerMessages) - while (masterServerMessages.Count > 0) - SendChat(null, masterServerMessages.Dequeue()); + foreach (var t in ServerTraits.WithInterface()) + t.Tick(); if (conns.Count() == 0) { @@ -130,8 +116,6 @@ namespace OpenRA.Server } } } ) { IsBackground = true }.Start(); - - } /* lobby rework todo: @@ -205,7 +189,7 @@ namespace OpenRA.Server } } - public static void DispatchOrdersToClient(Connection c, int client, int frame, byte[] data) + static void DispatchOrdersToClient(Connection c, int client, int frame, byte[] data) { try { @@ -326,54 +310,22 @@ namespace OpenRA.Server DispatchOrders(null, 0, new ServerOrder("SyncInfo", lobbyInfo.Serialize()).Serialize()); - PingMasterServer(); + foreach (var t in ServerTraits.WithInterface()) + t.LobbyInfoSynced(); } - - static volatile bool isBusy; - static Queue masterServerMessages = new Queue(); - public static void PingMasterServer() + + public static void StartGame() { - if (isBusy || !isInternetServer) return; + Server.GameStarted = true; + foreach( var c in Server.conns ) + foreach( var d in Server.conns ) + DispatchOrdersToClient( c, d.PlayerIndex, 0x7FFFFFFF, new byte[] { 0xBF } ); - lastPing = Environment.TickCount; - isBusy = true; + DispatchOrders(null, 0, + new ServerOrder("StartGame", "").Serialize()); - Action a = () => - { - try - { - var url = "ping.php?port={0}&name={1}&state={2}&players={3}&mods={4}&map={5}"; - if (isInitialPing) url += "&new=1"; - - using (var wc = new WebClient()) - { - wc.DownloadData( - masterServerUrl + url.F( - ExternalPort, Uri.EscapeUriString(Name), - GameStarted ? 2 : 1, // todo: post-game states, etc. - lobbyInfo.Clients.Count, - string.Join(",", lobbyInfo.GlobalSettings.Mods), - lobbyInfo.GlobalSettings.Map)); - - if (isInitialPing) - { - isInitialPing = false; - lock (masterServerMessages) - masterServerMessages.Enqueue("Master server communication established."); - } - } - } - catch(Exception ex) - { - Log.Write("server", ex.ToString()); - lock( masterServerMessages ) - masterServerMessages.Enqueue( "Master server communication failed." ); - } - - isBusy = false; - }; - - a.BeginInvoke(null, null); + foreach (var t in ServerTraits.WithInterface()) + t.GameStarted(); } } } diff --git a/OpenRA.Game/ServerTraits/LobbyCommands.cs b/OpenRA.Game/ServerTraits/LobbyCommands.cs index 4bfba3831d..2e35f64ab2 100644 --- a/OpenRA.Game/ServerTraits/LobbyCommands.cs +++ b/OpenRA.Game/ServerTraits/LobbyCommands.cs @@ -46,15 +46,7 @@ namespace OpenRA.Server.Traits { "startgame", s => { - Server.GameStarted = true; - foreach( var c in Server.conns ) - foreach( var d in Server.conns ) - Server.DispatchOrdersToClient( c, d.PlayerIndex, 0x7FFFFFFF, new byte[] { 0xBF } ); - - Server.DispatchOrders(null, 0, - new ServerOrder("StartGame", "").Serialize()); - - Server.PingMasterServer(); + Server.StartGame(); return true; }}, { "lag", diff --git a/OpenRA.Game/ServerTraits/MasterServerPinger.cs b/OpenRA.Game/ServerTraits/MasterServerPinger.cs new file mode 100644 index 0000000000..0f9b6b3822 --- /dev/null +++ b/OpenRA.Game/ServerTraits/MasterServerPinger.cs @@ -0,0 +1,91 @@ +#region Copyright & License Information +/* + * Copyright 2007-2010 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see LICENSE. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Net; + +namespace OpenRA.Server.Traits +{ + public class MasterServerPinger : ITick, INotifySyncLobbyInfo, IStartGame + { + const int MasterPingInterval = 60 * 3; // 3 minutes. server has a 5 minute TTL for games, so give ourselves a bit + // of leeway. + public int TickTimeout { get { return MasterPingInterval * 10000; } } + public void Tick() + { + if (Environment.TickCount - lastPing > MasterPingInterval * 1000) + PingMasterServer(); + else + lock (masterServerMessages) + while (masterServerMessages.Count > 0) + Server.SendChat(null, masterServerMessages.Dequeue()); + + } + + + public void LobbyInfoSynced() { PingMasterServer(); } + public void GameStarted() { PingMasterServer(); } + + static int lastPing = 0; + // Todo: use the settings passed to the server instead + static bool isInternetServer = Game.Settings.Server.AdvertiseOnline; + static string masterServerUrl = Game.Settings.Server.MasterServer; + static int externalPort = Game.Settings.Server.ExternalPort; + static bool isInitialPing = true; + + static volatile bool isBusy; + static Queue masterServerMessages = new Queue(); + public static void PingMasterServer() + { + if (isBusy || !isInternetServer) return; + + lastPing = Environment.TickCount; + isBusy = true; + + Action a = () => + { + try + { + var url = "ping.php?port={0}&name={1}&state={2}&players={3}&mods={4}&map={5}"; + if (isInitialPing) url += "&new=1"; + + using (var wc = new WebClient()) + { + wc.DownloadData( + masterServerUrl + url.F( + externalPort, Uri.EscapeUriString(Server.Name), + Server.GameStarted ? 2 : 1, // todo: post-game states, etc. + Server.lobbyInfo.Clients.Count, + string.Join(",", Server.lobbyInfo.GlobalSettings.Mods), + Server.lobbyInfo.GlobalSettings.Map)); + + if (isInitialPing) + { + isInitialPing = false; + lock (masterServerMessages) + masterServerMessages.Enqueue("Master server communication established."); + } + } + } + catch(Exception ex) + { + Log.Write("server", ex.ToString()); + lock( masterServerMessages ) + masterServerMessages.Enqueue( "Master server communication failed." ); + } + + isBusy = false; + }; + + a.BeginInvoke(null, null); + } + } +} diff --git a/OpenRA.Game/ServerTraits/TraitInterfaces.cs b/OpenRA.Game/ServerTraits/TraitInterfaces.cs index 6a03298d69..b90544ddf2 100644 --- a/OpenRA.Game/ServerTraits/TraitInterfaces.cs +++ b/OpenRA.Game/ServerTraits/TraitInterfaces.cs @@ -7,20 +7,39 @@ * see LICENSE. */ #endregion - +using System; namespace OpenRA.Server.Traits { // Returns true if order is handled public interface IInterpretCommand { bool InterpretCommand(Connection conn, string cmd); } + public interface ITick + { + void Tick(); + int TickTimeout { get; } + } + + public interface INotifySyncLobbyInfo { void LobbyInfoSynced(); } public interface IStartServer { void ServerStarted(); } + public interface IStartGame { void GameStarted(); } + public interface IClientJoined { void ClientJoined(Connection conn); } - public class DebugServerTrait : IInterpretCommand + public class DebugServerTrait : IInterpretCommand, IStartGame, INotifySyncLobbyInfo { public bool InterpretCommand(Connection conn, string cmd) { - Game.Debug("Server received command from player {1}: {0}".F(cmd, conn.PlayerIndex)); + Console.WriteLine("Server received command from player {1}: {0}",cmd, conn.PlayerIndex); return false; } + + public void GameStarted() + { + Console.WriteLine("GameStarted()"); + } + + public void LobbyInfoSynced() + { + Console.WriteLine("LobbyInfoSynced()"); + } } }