From 0c104cfc3a9764fdcc8cc7048f554f2ff1725825 Mon Sep 17 00:00:00 2001 From: Sam Hegarty Date: Thu, 22 Nov 2012 20:04:27 +1300 Subject: [PATCH] Add a 'Shutting down' state to the gameserver. Tell the masterserver about it, so it can quickly remove games from the list when they are finished, rather than waiting for the 5 minute TTL to expire. --- OpenRA.Game/Game.cs | 3 +- OpenRA.Game/Server/Server.cs | 49 +++++++++++++------ OpenRA.Game/Server/TraitInterfaces.cs | 8 ++- OpenRA.Mods.RA/ServerTraits/LobbyCommands.cs | 2 +- .../ServerTraits/MasterServerPinger.cs | 6 ++- 5 files changed, 48 insertions(+), 20 deletions(-) diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index 0e837b1718..0157261181 100755 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -313,7 +313,8 @@ namespace OpenRA { System.Threading.Thread.Sleep(100); - if((server.GameStarted)&&(server.conns.Count<=1)) + if((server.State == Server.ServerState.GameStarted) + && (server.conns.Count<=1)) { Console.WriteLine("No one is playing, shutting down..."); server.Shutdown(); diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index 91f3670ba6..54eef011f2 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -26,6 +26,13 @@ using XTimer = System.Timers.Timer; namespace OpenRA.Server { + public enum ServerState : int + { + WaitingPlayers = 1, + GameStarted = 2, + ShuttingDown = 3 + } + public class Server { // Valid player connections @@ -40,7 +47,7 @@ namespace OpenRA.Server TypeDictionary ServerTraits = new TypeDictionary(); public Session lobbyInfo; - public bool GameStarted = false; + public readonly IPAddress Ip; public readonly int Port; int randomSeed; @@ -51,16 +58,31 @@ namespace OpenRA.Server public Map Map; XTimer gameTimeout; - volatile bool shutdown = false; + protected volatile ServerState pState = new ServerState(); + public ServerState State + { + get { return pState; } + protected set { pState = value; } + } + public void Shutdown() { - shutdown = true; + State = ServerState.ShuttingDown; + } + + public void EndGame() + { + foreach (var t in ServerTraits.WithInterface()) + t.GameEnded(this); + if (Settings.AllowUPnP) + RemovePortforward(); } public Server(IPEndPoint endpoint, string[] mods, ServerSettings settings, ModData modData) { Log.AddChannel("server", "server.log"); + pState = ServerState.WaitingPlayers; listener = new TcpListener(endpoint); listener.Start(); var localEndpoint = (IPEndPoint)listener.LocalEndpoint; @@ -140,10 +162,9 @@ namespace OpenRA.Server foreach( var c in preConns ) checkRead.Add( c.socket ); Socket.Select( checkRead, null, null, timeout ); - if (shutdown) + if (State == ServerState.ShuttingDown) { - if (Settings.AllowUPnP) - RemovePortforward(); + EndGame(); break; } @@ -159,15 +180,13 @@ namespace OpenRA.Server foreach (var t in ServerTraits.WithInterface()) t.Tick(this); - if (shutdown) + if (State == ServerState.ShuttingDown) { - if (Settings.AllowUPnP) - RemovePortforward(); + EndGame(); break; } } - GameStarted = false; foreach (var t in ServerTraits.WithInterface()) t.ServerShutdown(this); @@ -245,7 +264,7 @@ namespace OpenRA.Server { try { - if (GameStarted) + if (State == ServerState.GameStarted) { Log.Write("server", "Rejected connection from {0}; game is already started.", newConn.socket.RemoteEndPoint); @@ -498,13 +517,13 @@ namespace OpenRA.Server OpenRA.Network.Session.Client dropClient = lobbyInfo.Clients.Where(c1 => c1.Index == toDrop.PlayerIndex).Single(); - if (GameStarted) + if (State == ServerState.GameStarted) SendDisconnected(toDrop); /* Report disconnection */ lobbyInfo.Clients.RemoveAll(c => c.Index == toDrop.PlayerIndex); // reassign admin if necessary - if ( lobbyInfo.GlobalSettings.Dedicated && dropClient.IsAdmin && !GameStarted) + if ( lobbyInfo.GlobalSettings.Dedicated && dropClient.IsAdmin && State == ServerState.WaitingPlayers) { if (lobbyInfo.Clients.Where(c1 => c1.Bot == null).Count() > 0) { @@ -530,7 +549,7 @@ namespace OpenRA.Server public void SyncLobbyInfo() { - if (!GameStarted) /* don't do this while the game is running, it breaks things. */ + if (State != ServerState.GameStarted) /* don't do this while the game is running, it breaks things. */ DispatchOrders(null, 0, new ServerOrder("SyncInfo", lobbyInfo.Serialize()).Serialize()); @@ -540,7 +559,7 @@ namespace OpenRA.Server public void StartGame() { - GameStarted = true; + State = ServerState.GameStarted; listener.Stop(); Console.WriteLine("Game started"); diff --git a/OpenRA.Game/Server/TraitInterfaces.cs b/OpenRA.Game/Server/TraitInterfaces.cs index 03477e0955..e23ba207aa 100644 --- a/OpenRA.Game/Server/TraitInterfaces.cs +++ b/OpenRA.Game/Server/TraitInterfaces.cs @@ -20,6 +20,7 @@ namespace OpenRA.Server public interface INotifyServerShutdown { void ServerShutdown(Server server); } public interface IStartGame { void GameStarted(Server server); } public interface IClientJoined { void ClientJoined(Server server, Connection conn); } + public interface IEndGame { void GameEnded(Server server); } public interface ITick { void Tick(Server server); @@ -28,7 +29,7 @@ namespace OpenRA.Server public abstract class ServerTrait {} - public class DebugServerTrait : ServerTrait, IInterpretCommand, IStartGame, INotifySyncLobbyInfo, INotifyServerStart, INotifyServerShutdown + public class DebugServerTrait : ServerTrait, IInterpretCommand, IStartGame, INotifySyncLobbyInfo, INotifyServerStart, INotifyServerShutdown, IEndGame { public bool InterpretCommand(Server server, Connection conn, Session.Client client, string cmd) { @@ -55,5 +56,10 @@ namespace OpenRA.Server { Console.WriteLine("ServerShutdown()"); } + + public void GameEnded(Server server) + { + Console.WriteLine("GameEnded()"); + } } } diff --git a/OpenRA.Mods.RA/ServerTraits/LobbyCommands.cs b/OpenRA.Mods.RA/ServerTraits/LobbyCommands.cs index cc2dfa061f..3d645c162e 100644 --- a/OpenRA.Mods.RA/ServerTraits/LobbyCommands.cs +++ b/OpenRA.Mods.RA/ServerTraits/LobbyCommands.cs @@ -39,7 +39,7 @@ namespace OpenRA.Mods.RA.Server public static bool ValidateCommand(S server, Connection conn, Session.Client client, string cmd) { - if (server.GameStarted) + if (server.State == ServerState.GameStarted) { server.SendChatTo(conn, "Cannot change state when game started. ({0})".F(cmd)); return false; diff --git a/OpenRA.Mods.RA/ServerTraits/MasterServerPinger.cs b/OpenRA.Mods.RA/ServerTraits/MasterServerPinger.cs index 0ef5a2d9f5..d6115e574f 100644 --- a/OpenRA.Mods.RA/ServerTraits/MasterServerPinger.cs +++ b/OpenRA.Mods.RA/ServerTraits/MasterServerPinger.cs @@ -17,7 +17,7 @@ using S = OpenRA.Server.Server; namespace OpenRA.Mods.RA.Server { - public class MasterServerPinger : ServerTrait, ITick, INotifySyncLobbyInfo, IStartGame + public class MasterServerPinger : ServerTrait, ITick, INotifySyncLobbyInfo, IStartGame, IEndGame { const int MasterPingInterval = 60 * 3; // 3 minutes. server has a 5 minute TTL for games, so give ourselves a bit // of leeway. @@ -36,6 +36,7 @@ namespace OpenRA.Mods.RA.Server public void LobbyInfoSynced(S server) { PingMasterServer(server); } public void GameStarted(S server) { PingMasterServer(server); } + public void GameEnded(S server) { PingMasterServer(server); } int lastPing = 0; bool isInitialPing = true; @@ -60,10 +61,11 @@ namespace OpenRA.Mods.RA.Server using (var wc = new WebClient()) { wc.Proxy = null; + wc.DownloadData( server.Settings.MasterServer + url.F( server.Settings.ExternalPort, Uri.EscapeUriString(server.Settings.Name), - server.GameStarted ? 2 : 1, // todo: post-game states, etc. + (int) server.State, server.lobbyInfo.Clients.Count, Game.CurrentMods.Select(f => "{0}@{1}".F(f.Key, f.Value.Version)).JoinWith(","), server.lobbyInfo.GlobalSettings.Map,