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.
This commit is contained in:
Sam Hegarty
2012-11-22 20:04:27 +13:00
committed by Chris Forbes
parent 3a77082c66
commit 0c104cfc3a
5 changed files with 48 additions and 20 deletions

View File

@@ -313,7 +313,8 @@ namespace OpenRA
{ {
System.Threading.Thread.Sleep(100); 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..."); Console.WriteLine("No one is playing, shutting down...");
server.Shutdown(); server.Shutdown();

View File

@@ -26,6 +26,13 @@ using XTimer = System.Timers.Timer;
namespace OpenRA.Server namespace OpenRA.Server
{ {
public enum ServerState : int
{
WaitingPlayers = 1,
GameStarted = 2,
ShuttingDown = 3
}
public class Server public class Server
{ {
// Valid player connections // Valid player connections
@@ -40,7 +47,7 @@ namespace OpenRA.Server
TypeDictionary ServerTraits = new TypeDictionary(); TypeDictionary ServerTraits = new TypeDictionary();
public Session lobbyInfo; public Session lobbyInfo;
public bool GameStarted = false;
public readonly IPAddress Ip; public readonly IPAddress Ip;
public readonly int Port; public readonly int Port;
int randomSeed; int randomSeed;
@@ -51,16 +58,31 @@ namespace OpenRA.Server
public Map Map; public Map Map;
XTimer gameTimeout; 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() public void Shutdown()
{ {
shutdown = true; State = ServerState.ShuttingDown;
}
public void EndGame()
{
foreach (var t in ServerTraits.WithInterface<IEndGame>())
t.GameEnded(this);
if (Settings.AllowUPnP)
RemovePortforward();
} }
public Server(IPEndPoint endpoint, string[] mods, ServerSettings settings, ModData modData) public Server(IPEndPoint endpoint, string[] mods, ServerSettings settings, ModData modData)
{ {
Log.AddChannel("server", "server.log"); Log.AddChannel("server", "server.log");
pState = ServerState.WaitingPlayers;
listener = new TcpListener(endpoint); listener = new TcpListener(endpoint);
listener.Start(); listener.Start();
var localEndpoint = (IPEndPoint)listener.LocalEndpoint; var localEndpoint = (IPEndPoint)listener.LocalEndpoint;
@@ -140,10 +162,9 @@ namespace OpenRA.Server
foreach( var c in preConns ) checkRead.Add( c.socket ); foreach( var c in preConns ) checkRead.Add( c.socket );
Socket.Select( checkRead, null, null, timeout ); Socket.Select( checkRead, null, null, timeout );
if (shutdown) if (State == ServerState.ShuttingDown)
{ {
if (Settings.AllowUPnP) EndGame();
RemovePortforward();
break; break;
} }
@@ -159,15 +180,13 @@ namespace OpenRA.Server
foreach (var t in ServerTraits.WithInterface<ITick>()) foreach (var t in ServerTraits.WithInterface<ITick>())
t.Tick(this); t.Tick(this);
if (shutdown) if (State == ServerState.ShuttingDown)
{ {
if (Settings.AllowUPnP) EndGame();
RemovePortforward();
break; break;
} }
} }
GameStarted = false;
foreach (var t in ServerTraits.WithInterface<INotifyServerShutdown>()) foreach (var t in ServerTraits.WithInterface<INotifyServerShutdown>())
t.ServerShutdown(this); t.ServerShutdown(this);
@@ -245,7 +264,7 @@ namespace OpenRA.Server
{ {
try try
{ {
if (GameStarted) if (State == ServerState.GameStarted)
{ {
Log.Write("server", "Rejected connection from {0}; game is already started.", Log.Write("server", "Rejected connection from {0}; game is already started.",
newConn.socket.RemoteEndPoint); newConn.socket.RemoteEndPoint);
@@ -498,13 +517,13 @@ namespace OpenRA.Server
OpenRA.Network.Session.Client dropClient = lobbyInfo.Clients.Where(c1 => c1.Index == toDrop.PlayerIndex).Single(); OpenRA.Network.Session.Client dropClient = lobbyInfo.Clients.Where(c1 => c1.Index == toDrop.PlayerIndex).Single();
if (GameStarted) if (State == ServerState.GameStarted)
SendDisconnected(toDrop); /* Report disconnection */ SendDisconnected(toDrop); /* Report disconnection */
lobbyInfo.Clients.RemoveAll(c => c.Index == toDrop.PlayerIndex); lobbyInfo.Clients.RemoveAll(c => c.Index == toDrop.PlayerIndex);
// reassign admin if necessary // 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) if (lobbyInfo.Clients.Where(c1 => c1.Bot == null).Count() > 0)
{ {
@@ -530,7 +549,7 @@ namespace OpenRA.Server
public void SyncLobbyInfo() 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, DispatchOrders(null, 0,
new ServerOrder("SyncInfo", lobbyInfo.Serialize()).Serialize()); new ServerOrder("SyncInfo", lobbyInfo.Serialize()).Serialize());
@@ -540,7 +559,7 @@ namespace OpenRA.Server
public void StartGame() public void StartGame()
{ {
GameStarted = true; State = ServerState.GameStarted;
listener.Stop(); listener.Stop();
Console.WriteLine("Game started"); Console.WriteLine("Game started");

View File

@@ -20,6 +20,7 @@ namespace OpenRA.Server
public interface INotifyServerShutdown { void ServerShutdown(Server server); } public interface INotifyServerShutdown { void ServerShutdown(Server server); }
public interface IStartGame { void GameStarted(Server server); } public interface IStartGame { void GameStarted(Server server); }
public interface IClientJoined { void ClientJoined(Server server, Connection conn); } public interface IClientJoined { void ClientJoined(Server server, Connection conn); }
public interface IEndGame { void GameEnded(Server server); }
public interface ITick public interface ITick
{ {
void Tick(Server server); void Tick(Server server);
@@ -28,7 +29,7 @@ namespace OpenRA.Server
public abstract class ServerTrait {} 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) public bool InterpretCommand(Server server, Connection conn, Session.Client client, string cmd)
{ {
@@ -55,5 +56,10 @@ namespace OpenRA.Server
{ {
Console.WriteLine("ServerShutdown()"); Console.WriteLine("ServerShutdown()");
} }
public void GameEnded(Server server)
{
Console.WriteLine("GameEnded()");
}
} }
} }

View File

@@ -39,7 +39,7 @@ namespace OpenRA.Mods.RA.Server
public static bool ValidateCommand(S server, Connection conn, Session.Client client, string cmd) 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)); server.SendChatTo(conn, "Cannot change state when game started. ({0})".F(cmd));
return false; return false;

View File

@@ -17,7 +17,7 @@ using S = OpenRA.Server.Server;
namespace OpenRA.Mods.RA.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 const int MasterPingInterval = 60 * 3; // 3 minutes. server has a 5 minute TTL for games, so give ourselves a bit
// of leeway. // of leeway.
@@ -36,6 +36,7 @@ namespace OpenRA.Mods.RA.Server
public void LobbyInfoSynced(S server) { PingMasterServer(server); } public void LobbyInfoSynced(S server) { PingMasterServer(server); }
public void GameStarted(S server) { PingMasterServer(server); } public void GameStarted(S server) { PingMasterServer(server); }
public void GameEnded(S server) { PingMasterServer(server); }
int lastPing = 0; int lastPing = 0;
bool isInitialPing = true; bool isInitialPing = true;
@@ -60,10 +61,11 @@ namespace OpenRA.Mods.RA.Server
using (var wc = new WebClient()) using (var wc = new WebClient())
{ {
wc.Proxy = null; wc.Proxy = null;
wc.DownloadData( wc.DownloadData(
server.Settings.MasterServer + url.F( server.Settings.MasterServer + url.F(
server.Settings.ExternalPort, Uri.EscapeUriString(server.Settings.Name), server.Settings.ExternalPort, Uri.EscapeUriString(server.Settings.Name),
server.GameStarted ? 2 : 1, // todo: post-game states, etc. (int) server.State,
server.lobbyInfo.Clients.Count, server.lobbyInfo.Clients.Count,
Game.CurrentMods.Select(f => "{0}@{1}".F(f.Key, f.Value.Version)).JoinWith(","), Game.CurrentMods.Select(f => "{0}@{1}".F(f.Key, f.Value.Version)).JoinWith(","),
server.lobbyInfo.GlobalSettings.Map, server.lobbyInfo.GlobalSettings.Map,