From 2d08f2bbfdeeb7e726b830f302f361860611bb92 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 25 Sep 2021 15:09:23 +0100 Subject: [PATCH] Allow the server to ack no or multiple packets in the same frame. --- OpenRA.Game/Network/Connection.cs | 20 +++++++++++++++----- OpenRA.Game/Network/OrderIO.cs | 21 ++++++++++++++++++--- OpenRA.Game/Server/ProtocolVersion.cs | 3 ++- OpenRA.Game/Server/Server.cs | 9 +++++---- 4 files changed, 40 insertions(+), 13 deletions(-) diff --git a/OpenRA.Game/Network/Connection.cs b/OpenRA.Game/Network/Connection.cs index 1de67d2edb..43cc521baf 100644 --- a/OpenRA.Game/Network/Connection.cs +++ b/OpenRA.Game/Network/Connection.cs @@ -312,16 +312,26 @@ namespace OpenRA.Network Send(ping); record = false; } - else if (OrderIO.TryParseAck(p, out var ackFrame)) + else if (OrderIO.TryParseAck(p, out var ackFrame, out var ackCount)) { - if (!sentOrders.TryDequeue(out var q)) - throw new InvalidOperationException("Received Ack with empty send queue"); + if (ackCount > sentOrders.Count) + throw new InvalidOperationException($"Received Ack for {ackCount} > {sentOrders.Count} frames."); // The Acknowledgement packet is a placeholder that tells us to process the first packet in our // local sent buffer and the frame at which it should be applied. This is an optimization to avoid having // to send the (much larger than 5 byte) packet back to us over the network. - orderManager.ReceiveOrders(clientId, (ackFrame, q.Orders)); - Recorder?.Receive(clientId, q.Orders.Serialize(ackFrame)); + OrderPacket packet; + if (ackCount != 1) + { + var orders = Enumerable.Range(0, ackCount) + .Select(i => sentOrders.Dequeue().Orders); + packet = OrderPacket.Combine(orders); + } + else + packet = sentOrders.Dequeue().Orders; + + orderManager.ReceiveOrders(clientId, (ackFrame, packet)); + Recorder?.Receive(clientId, packet.Serialize(ackFrame)); record = false; } else if (OrderIO.TryParseOrderPacket(p.Data, out var orders)) diff --git a/OpenRA.Game/Network/OrderIO.cs b/OpenRA.Game/Network/OrderIO.cs index 483d26dd47..a6c3ad7816 100644 --- a/OpenRA.Game/Network/OrderIO.cs +++ b/OpenRA.Game/Network/OrderIO.cs @@ -63,6 +63,20 @@ namespace OpenRA.Network ms.WriteArray(o.Serialize()); return ms.ToArray(); } + + public static OrderPacket Combine(IEnumerable packets) + { + var orders = new List(); + foreach (var packet in packets) + { + if (packet.orders == null) + throw new InvalidOperationException("OrderPacket.Combine can only be used with locally generated OrderPackets."); + + orders.AddRange(packet.orders); + } + + return new OrderPacket(orders.ToArray()); + } } public static class OrderIO @@ -130,16 +144,17 @@ namespace OpenRA.Network return true; } - public static bool TryParseAck((int FromClient, byte[] Data) packet, out int frame) + public static bool TryParseAck((int FromClient, byte[] Data) packet, out int frame, out byte count) { // Ack packets are only accepted from the server - if (packet.FromClient != 0 || packet.Data.Length != 5 || packet.Data[4] != (byte)OrderType.Ack) + if (packet.FromClient != 0 || packet.Data.Length != 6 || packet.Data[4] != (byte)OrderType.Ack) { - frame = 0; + frame = count = 0; return false; } frame = BitConverter.ToInt32(packet.Data, 0); + count = packet.Data[5]; return true; } diff --git a/OpenRA.Game/Server/ProtocolVersion.cs b/OpenRA.Game/Server/ProtocolVersion.cs index 3e75a2a115..3256859bca 100644 --- a/OpenRA.Game/Server/ProtocolVersion.cs +++ b/OpenRA.Game/Server/ProtocolVersion.cs @@ -38,6 +38,7 @@ namespace OpenRA.Server // - Order-specific data - see OpenRA.Game/Server/Order.cs for details // - 0x10: Order acknowledgement (sent from the server to a client in response to a packet with world orders) // - Int32 containing the frame number that the client should apply the orders it sent + // - byte containing the number of sent order packets to apply // - 0x20: Ping // - Int64 containing the server timestamp when the ping was generated // @@ -73,6 +74,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 = 16; + public const int Orders = 17; } } diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index d972a5f08a..d11b7c5484 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -627,13 +627,14 @@ namespace OpenRA.Server return ms.GetBuffer(); } - byte[] CreateAckFrame(int frame) + byte[] CreateAckFrame(int frame, byte count) { - var ms = new MemoryStream(13); - ms.WriteArray(BitConverter.GetBytes(5)); + var ms = new MemoryStream(14); + ms.WriteArray(BitConverter.GetBytes(6)); ms.WriteArray(BitConverter.GetBytes(0)); ms.WriteArray(BitConverter.GetBytes(frame)); ms.WriteByte((byte)OrderType.Ack); + ms.WriteByte(count); return ms.GetBuffer(); } @@ -813,7 +814,7 @@ namespace OpenRA.Server if (data.Length == 0 || data[0] != (byte)OrderType.SyncHash) { frame += OrderLatency; - DispatchFrameToClient(conn, conn.PlayerIndex, CreateAckFrame(frame)); + DispatchFrameToClient(conn, conn.PlayerIndex, CreateAckFrame(frame, 1)); // Track the last frame for each client so the disconnect handling can write // an EndOfOrders marker with the correct frame number.