diff --git a/OpenRA.Game/Network/Order.cs b/OpenRA.Game/Network/Order.cs index bf12bba32f..bd4e205248 100644 --- a/OpenRA.Game/Network/Order.cs +++ b/OpenRA.Game/Network/Order.cs @@ -52,6 +52,9 @@ namespace OpenRA // Length of orders with type OrderType.SyncHash public const int SyncHashOrderLength = 13; + // Length of orders with type OrderType.Disconnect + public const int DisconnectOrderLength = 5; + public readonly string OrderString; public readonly Actor Subject; public readonly bool Queued; diff --git a/OpenRA.Game/Network/OrderManager.cs b/OpenRA.Game/Network/OrderManager.cs index 48265c2062..08b82f1efd 100644 --- a/OpenRA.Game/Network/OrderManager.cs +++ b/OpenRA.Game/Network/OrderManager.cs @@ -131,8 +131,10 @@ namespace OpenRA.Network return; var frame = BitConverter.ToInt32(packet, 0); - if (packet.Length == 5 && packet[4] == (byte)OrderType.Disconnect) - pendingPackets.Remove(clientId); + if (packet.Length == Order.DisconnectOrderLength + 4 && packet[4] == (byte)OrderType.Disconnect) + { + pendingPackets.Remove(BitConverter.ToInt32(packet, 5)); + } else if (packet.Length > 4 && packet[4] == (byte)OrderType.SyncHash) { if (packet.Length != 4 + Order.SyncHashOrderLength) diff --git a/OpenRA.Game/Network/ReplayConnection.cs b/OpenRA.Game/Network/ReplayConnection.cs index 86325602ff..b5f6ea4ca4 100644 --- a/OpenRA.Game/Network/ReplayConnection.cs +++ b/OpenRA.Game/Network/ReplayConnection.cs @@ -115,7 +115,7 @@ namespace OpenRA.Network continue; var packet = tmpPacketPair.Packet; - if (packet.Length == 5 && packet[4] == (byte)OrderType.Disconnect) + if (packet.Length == Order.DisconnectOrderLength + 4 && packet[4] == (byte)OrderType.Disconnect) { var lastClientFrame = lastClientsFrame[client]; var lastFramePacket = BitConverter.GetBytes(lastClientFrame); diff --git a/OpenRA.Game/Server/ProtocolVersion.cs b/OpenRA.Game/Server/ProtocolVersion.cs index cd85bde5ba..7c890d2a81 100644 --- a/OpenRA.Game/Server/ProtocolVersion.cs +++ b/OpenRA.Game/Server/ProtocolVersion.cs @@ -68,6 +68,6 @@ namespace OpenRA.Server // The protocol for server and world orders // This applies after the handshake has completed, and is provided to support // alternative server implementations that wish to support multiple versions in parallel - public const int Orders = 12; + public const int Orders = 13; } } diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index bed966c325..cb0c42c3f9 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -482,7 +482,7 @@ namespace OpenRA.Server Log.Write("server", "{0} ({1}) has joined the game.", client.Name, newConn.EndPoint); // Report to all other players - SendMessage($"{client.Name} has joined the game.", newConn); + SendMessage($"{client.Name} has joined the game."); // Send initial ping SendOrderTo(newConn, "Ping", Game.RunTime.ToString(CultureInfo.InvariantCulture)); @@ -737,12 +737,17 @@ namespace OpenRA.Server public void DispatchOrdersToClients(Connection conn, int frame, byte[] data) { - var from = conn?.PlayerIndex ?? 0; + var from = conn.PlayerIndex; var frameData = CreateFrame(from, frame, data); foreach (var c in Conns.ToList()) if (c != conn && c.Validated) DispatchFrameToClient(c, from, frameData); + RecordOrder(frame, data, from); + } + + void RecordOrder(int frame, byte[] data, int from) + { if (recorder != null) { recorder.ReceiveFrame(from, frame, data); @@ -757,14 +762,31 @@ namespace OpenRA.Server } } + public void DispatchServerOrdersToClients(Order order) + { + DispatchServerOrdersToClients(order.Serialize()); + } + + public void DispatchServerOrdersToClients(byte[] data) + { + var from = 0; + var frame = 0; + var frameData = CreateFrame(from, frame, data); + foreach (var c in Conns.ToList()) + if (c.Validated) + DispatchFrameToClient(c, from, frameData); + + RecordOrder(frame, data, from); + } + public void DispatchOrders(Connection conn, int frame, byte[] data) { - if (frame == 0 && conn != null) + if (frame == 0) InterpretServerOrders(conn, data); else DispatchOrdersToClients(conn, frame, data); - if (GameSave != null && conn != null) + if (GameSave != null) GameSave.DispatchOrders(conn, frame, data); } @@ -791,9 +813,9 @@ namespace OpenRA.Server DispatchOrdersToClient(conn, 0, 0, Order.FromTargetString(order, data, true).Serialize()); } - public void SendMessage(string text, Connection conn = null) + public void SendMessage(string text) { - DispatchOrdersToClients(conn, 0, Order.FromTargetString("Message", text, true).Serialize()); + DispatchServerOrdersToClients(Order.FromTargetString("Message", text, true)); if (Type == ServerType.Dedicated) Console.WriteLine($"[{DateTime.Now.ToString(Settings.TimestampFormat)}] {text}"); @@ -903,7 +925,7 @@ namespace OpenRA.Server Directory.CreateDirectory(baseSavePath); GameSave.Save(Path.Combine(baseSavePath, filename)); - DispatchOrdersToClients(null, 0, Order.FromTargetString("GameSaved", filename, true).Serialize()); + DispatchServerOrdersToClients(Order.FromTargetString("GameSaved", filename, true)); } break; @@ -1039,7 +1061,10 @@ namespace OpenRA.Server } } - DispatchOrders(toDrop, toDrop.MostRecentFrame, new[] { (byte)OrderType.Disconnect }); + var disconnectPacket = new MemoryStream(5); + disconnectPacket.WriteByte((byte)OrderType.Disconnect); + disconnectPacket.Write(toDrop.PlayerIndex); + DispatchServerOrdersToClients(disconnectPacket.ToArray()); // All clients have left: clean up if (!Conns.Any(c => c.Validated)) @@ -1061,7 +1086,7 @@ namespace OpenRA.Server lock (LobbyInfo) { if (State == ServerState.WaitingPlayers) // Don't do this while the game is running, it breaks things! - DispatchOrders(null, 0, Order.FromTargetString("SyncInfo", LobbyInfo.Serialize(), true).Serialize()); + DispatchServerOrdersToClients(Order.FromTargetString("SyncInfo", LobbyInfo.Serialize(), true)); foreach (var t in serverTraits.WithInterface()) t.LobbyInfoSynced(this); @@ -1078,7 +1103,7 @@ namespace OpenRA.Server // TODO: Only need to sync the specific client that has changed to avoid conflicts! var clientData = LobbyInfo.Clients.Select(client => client.Serialize()).ToList(); - DispatchOrders(null, 0, Order.FromTargetString("SyncLobbyClients", clientData.WriteToString(), true).Serialize()); + DispatchServerOrdersToClients(Order.FromTargetString("SyncLobbyClients", clientData.WriteToString(), true)); foreach (var t in serverTraits.WithInterface()) t.LobbyInfoSynced(this); @@ -1095,7 +1120,7 @@ namespace OpenRA.Server // TODO: Don't sync all the slots if just one changed! var slotData = LobbyInfo.Slots.Select(slot => slot.Value.Serialize()).ToList(); - DispatchOrders(null, 0, Order.FromTargetString("SyncLobbySlots", slotData.WriteToString(), true).Serialize()); + DispatchServerOrdersToClients(Order.FromTargetString("SyncLobbySlots", slotData.WriteToString(), true)); foreach (var t in serverTraits.WithInterface()) t.LobbyInfoSynced(this); @@ -1111,7 +1136,7 @@ namespace OpenRA.Server { var sessionData = new List { LobbyInfo.GlobalSettings.Serialize() }; - DispatchOrders(null, 0, Order.FromTargetString("SyncLobbyGlobalSettings", sessionData.WriteToString(), true).Serialize()); + DispatchServerOrdersToClients(Order.FromTargetString("SyncLobbyGlobalSettings", sessionData.WriteToString(), true)); foreach (var t in serverTraits.WithInterface()) t.LobbyInfoSynced(this); @@ -1126,7 +1151,7 @@ namespace OpenRA.Server var clientPings = LobbyInfo.ClientPings.Select(ping => ping.Serialize()).ToList(); // Note that syncing pings doesn't trigger INotifySyncLobbyInfo - DispatchOrders(null, 0, Order.FromTargetString("SyncClientPings", clientPings.WriteToString(), true).Serialize()); + DispatchServerOrdersToClients(Order.FromTargetString("SyncClientPings", clientPings.WriteToString(), true)); } } @@ -1194,8 +1219,7 @@ namespace OpenRA.Server } } - DispatchOrders(null, 0, - Order.FromTargetString("StartGame", startGameData, true).Serialize()); + DispatchServerOrdersToClients(Order.FromTargetString("StartGame", startGameData, true)); foreach (var t in serverTraits.WithInterface()) t.GameStarted(this);