diff --git a/OpenRA.Game/Network/Session.cs b/OpenRA.Game/Network/Session.cs index c032e45340..f2340b24d8 100644 --- a/OpenRA.Game/Network/Session.cs +++ b/OpenRA.Game/Network/Session.cs @@ -81,7 +81,7 @@ namespace OpenRA.Network get { return Clients.Count(c => c.Bot == null) == 1; } } - public enum ClientState { NotReady, Ready, Disconnected = 1000 } + public enum ClientState { NotReady, Invalid, Ready, Disconnected = 1000 } public class Client { @@ -93,13 +93,14 @@ namespace OpenRA.Network public int SpawnPoint; public string Name; public string IpAddress; - public ClientState State; + public ClientState State = ClientState.Invalid; public int Team; public string Slot; // slot ID, or null for observer public string Bot; // Bot type, null for real clients public int BotControllerClientIndex; // who added the bot to the slot public bool IsAdmin; public bool IsReady { get { return State == ClientState.Ready; } } + public bool IsInvalid { get { return State == ClientState.Invalid; } } public bool IsObserver { get { return Slot == null; } } public int Latency = -1; public int LatencyJitter = -1; diff --git a/OpenRA.Game/Network/UnitOrders.cs b/OpenRA.Game/Network/UnitOrders.cs index 8bdcede585..87b501bc90 100644 --- a/OpenRA.Game/Network/UnitOrders.cs +++ b/OpenRA.Game/Network/UnitOrders.cs @@ -130,7 +130,7 @@ namespace OpenRA.Network Country = "random", SpawnPoint = 0, Team = 0, - State = Session.ClientState.NotReady + State = Session.ClientState.Invalid }; var response = new HandshakeResponse() diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index 38ec7eaef2..cea2d9425b 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -272,7 +272,7 @@ namespace OpenRA.Server Country = "random", SpawnPoint = 0, Team = 0, - State = Session.ClientState.NotReady, + State = Session.ClientState.Invalid, IsAdmin = !LobbyInfo.Clients.Any(c1 => c1.IsAdmin) }; @@ -572,19 +572,31 @@ namespace OpenRA.Server public void StartGame() { - State = ServerState.GameStarted; listener.Stop(); Console.WriteLine("Game started"); - foreach (var c in Conns) - foreach (var d in Conns) - DispatchOrdersToClient(c, d.PlayerIndex, 0x7FFFFFFF, new byte[] { 0xBF }); - // Drop any unvalidated clients foreach (var c in PreConns.ToArray()) DropClient(c); + // Drop any players who are not ready + foreach (var c in Conns.ToArray()) + { + if (GetClient(c).IsInvalid) + { + SendOrderTo(c, "ServerError", "You have been kicked from the server"); + DropClient(c); + } + } + + SyncLobbyInfo(); + State = ServerState.GameStarted; + + foreach (var c in Conns) + foreach (var d in Conns) + DispatchOrdersToClient(c, d.PlayerIndex, 0x7FFFFFFF, new byte[] { 0xBF }); + DispatchOrders(null, 0, new ServerOrder("StartGame", "").Serialize()); diff --git a/OpenRA.Mods.RA/ServerTraits/LobbyCommands.cs b/OpenRA.Mods.RA/ServerTraits/LobbyCommands.cs index 6565b1b796..95a0683eb7 100644 --- a/OpenRA.Mods.RA/ServerTraits/LobbyCommands.cs +++ b/OpenRA.Mods.RA/ServerTraits/LobbyCommands.cs @@ -44,7 +44,7 @@ namespace OpenRA.Mods.RA.Server server.SendOrderTo(conn, "Message", "Cannot change state when game started. ({0})".F(cmd)); return false; } - else if (client.State == Session.ClientState.Ready && !(cmd == "ready" || cmd == "startgame")) + else if (client.State == Session.ClientState.Ready && !(cmd.StartsWith("state") || cmd == "startgame")) { server.SendOrderTo(conn, "Message", "Cannot change state when marked as ready."); return false; @@ -75,14 +75,17 @@ namespace OpenRA.Mods.RA.Server var dict = new Dictionary> { - { "ready", + { "state", s => { - // if we're downloading, we can't ready up. - if (client.State == Session.ClientState.NotReady) - client.State = Session.ClientState.Ready; - else if (client.State == Session.ClientState.Ready) - client.State = Session.ClientState.NotReady; + var state = Session.ClientState.Invalid; + if (!Enum.TryParse(s, false, out state)) + { + server.SendOrderTo(conn, "Message", "Malformed state command"); + return true; + } + + client.State = state; Log.Write("server", "Player @{0} is {1}", conn.socket.RemoteEndPoint, client.State); @@ -290,6 +293,10 @@ namespace OpenRA.Mods.RA.Server LoadMap(server); SetDefaultDifficulty(server); + // Reset client states + foreach (var c in server.LobbyInfo.Clients) + c.State = Session.ClientState.Invalid; + // Reassign players into new slots based on their old slots: // - Observers remain as observers // - Players who now lack a slot are made observers @@ -303,7 +310,6 @@ namespace OpenRA.Mods.RA.Server continue; c.SpawnPoint = 0; - c.State = Session.ClientState.NotReady; c.Slot = i < slots.Length ? slots[i++] : null; if (c.Slot != null) { diff --git a/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs index 4a4b998903..b4287d68c7 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs @@ -530,6 +530,9 @@ namespace OpenRA.Mods.RA.Widgets.Logic if (Map.Status == MapStatus.Available) { + // Tell the server that we have the map + orderManager.IssueOrder(Order.Command("state {0}".F(Session.ClientState.NotReady))); + // Restore default starting cash if the last map set it to something invalid var pri = Rules.Info["player"].Traits.Get(); if (!Map.Map.Options.StartingCash.HasValue && !pri.SelectableCash.Contains(orderManager.LobbyInfo.GlobalSettings.StartingCash)) diff --git a/OpenRA.Mods.RA/Widgets/Logic/LobbyUtils.cs b/OpenRA.Mods.RA/Widgets/Logic/LobbyUtils.cs index c6940cb513..25d32678e1 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/LobbyUtils.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/LobbyUtils.cs @@ -383,7 +383,8 @@ namespace OpenRA.Mods.RA.Widgets.Logic status.IsVisible = () => true; status.IsDisabled = () => c.Bot != null || map.Status != MapStatus.Available; - status.OnClick = () => orderManager.IssueOrder(Order.Command("ready")); + var state = orderManager.LocalClient.IsReady ? Session.ClientState.NotReady : Session.ClientState.Ready; + status.OnClick = () => orderManager.IssueOrder(Order.Command("state {0}".F(state))); } public static void SetupReadyWidget(Widget parent, Session.Slot s, Session.Client c)