From 1a56cee9a1e94c396b09ad2adbac73d431dac3dc Mon Sep 17 00:00:00 2001 From: teinarss Date: Sat, 9 Oct 2021 19:02:10 +0200 Subject: [PATCH] Add Tick scale plumbing --- OpenRA.Game/Game.cs | 4 +--- OpenRA.Game/Network/Connection.cs | 16 ++++++++++------ OpenRA.Game/Network/Order.cs | 1 + OpenRA.Game/Network/OrderIO.cs | 21 +++++++++++++++++++++ OpenRA.Game/Network/OrderManager.cs | 11 ++++++++++- OpenRA.Game/Server/ProtocolVersion.cs | 4 +++- OpenRA.Game/Server/Server.cs | 11 +++++++++++ OpenRA.Game/StreamExts.cs | 5 +++++ 8 files changed, 62 insertions(+), 11 deletions(-) diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index 1c50bb9461..3df307a941 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -779,9 +779,7 @@ namespace OpenRA // ReplayTimestep = 0 means the replay is paused: we need to keep logicInterval as UI.Timestep to avoid breakage if (logicWorld != null && !(logicWorld.IsReplay && logicWorld.ReplayTimestep == 0)) - logicInterval = logicWorld.IsLoadingGameSave ? 1 : - logicWorld.IsReplay ? logicWorld.ReplayTimestep : - logicWorld.Timestep; + logicInterval = logicWorld == OrderManager.World ? OrderManager.SuggestedTimestep : logicWorld.Timestep; // Ideal time between screen updates var maxFramerate = Settings.Graphics.CapFramerate ? Settings.Graphics.MaxFramerate.Clamp(1, 1000) : 1000; diff --git a/OpenRA.Game/Network/Connection.cs b/OpenRA.Game/Network/Connection.cs index 9e1e8c6e8b..eda7dedfe5 100644 --- a/OpenRA.Game/Network/Connection.cs +++ b/OpenRA.Game/Network/Connection.cs @@ -299,17 +299,23 @@ namespace OpenRA.Network // Orders from other players while (receivedPackets.TryDequeue(out var p)) { - var record = true; if (OrderIO.TryParseDisconnect(p, out var disconnect)) + { orderManager.ReceiveDisconnect(disconnect.ClientId, disconnect.Frame); + Recorder?.Receive(p.FromClient, p.Data); + } else if (OrderIO.TryParseSync(p.Data, out var sync)) + { orderManager.ReceiveSync(sync); + Recorder?.Receive(p.FromClient, p.Data); + } + else if (OrderIO.TryParseTickScale(p, out var scale)) + orderManager.ReceiveTickScale(scale); else if (OrderIO.TryParsePingRequest(p, out var timestamp)) { // Note that processing this here, rather than in NetworkConnectionReceive, // so that poor world tick performance can be reflected in the latency measurement Send(OrderIO.SerializePingResponse(timestamp, (byte)orderManager.OrderQueueLength)); - record = false; } else if (OrderIO.TryParseAck(p, out var ackFrame, out var ackCount)) { @@ -331,7 +337,6 @@ namespace OpenRA.Network orderManager.ReceiveOrders(clientId, (ackFrame, packet)); Recorder?.Receive(clientId, packet.Serialize(ackFrame)); - record = false; } else if (OrderIO.TryParseOrderPacket(p.Data, out var orders)) { @@ -339,13 +344,12 @@ namespace OpenRA.Network orderManager.ReceiveImmediateOrders(p.FromClient, orders.Orders); else orderManager.ReceiveOrders(p.FromClient, orders); + + Recorder?.Receive(p.FromClient, p.Data); } else throw new InvalidDataException($"Received unknown packet from client {p.FromClient} with length {p.Data.Length}"); - if (record) - Recorder?.Receive(p.FromClient, p.Data); - // An immediate order may trigger a chain of actions that disposes the OrderManager and connection. // Bail out to avoid potential problems from acting on disposed objects. if (disposed) diff --git a/OpenRA.Game/Network/Order.cs b/OpenRA.Game/Network/Order.cs index f16767c6be..ef39f463d3 100644 --- a/OpenRA.Game/Network/Order.cs +++ b/OpenRA.Game/Network/Order.cs @@ -20,6 +20,7 @@ namespace OpenRA Ack = 0x10, Ping = 0x20, SyncHash = 0x65, + TickScale = 0x76, Disconnect = 0xBF, Handshake = 0xFE, Fields = 0xFF diff --git a/OpenRA.Game/Network/OrderIO.cs b/OpenRA.Game/Network/OrderIO.cs index c3c840416b..99ec304902 100644 --- a/OpenRA.Game/Network/OrderIO.cs +++ b/OpenRA.Game/Network/OrderIO.cs @@ -133,6 +133,27 @@ namespace OpenRA.Network return true; } + public static bool TryParseTickScale((int FromClient, byte[] Data) packet, out float scale) + { + // Valid tick scale commands are only ever generated by the server + if (packet.FromClient != 0 || packet.Data.Length != 9 || packet.Data[4] != (byte)OrderType.TickScale) + { + scale = 1; + return false; + } + + // Valid tick scale packets always have frame 0 + var frame = BitConverter.ToInt32(packet.Data, 0); + if (frame != 0) + { + scale = 1; + return false; + } + + scale = BitConverter.ToSingle(packet.Data, 5); + return true; + } + public static bool TryParsePingRequest((int FromClient, byte[] Data) packet, out long timestamp) { // Valid Ping requests are only ever generated by the server diff --git a/OpenRA.Game/Network/OrderManager.cs b/OpenRA.Game/Network/OrderManager.cs index 6eb158b8cb..26ffa0f8ea 100644 --- a/OpenRA.Game/Network/OrderManager.cs +++ b/OpenRA.Game/Network/OrderManager.cs @@ -58,6 +58,7 @@ namespace OpenRA.Network bool disposed; bool generateSyncReport = false; int sentOrdersFrame = 0; + float tickScale = 1f; public struct ClientOrder { @@ -157,6 +158,11 @@ namespace OpenRA.Network syncForFrame.Add(sync.Frame, (sync.SyncHash, sync.DefeatState)); } + public void ReceiveTickScale(float scale) + { + tickScale = scale; + } + public void ReceiveImmediateOrders(int clientId, OrderPacket orders) { foreach (var o in orders.GetOrders(World)) @@ -184,7 +190,7 @@ namespace OpenRA.Network bool IsReadyForNextFrame => GameStarted && pendingOrders.All(p => p.Value.Count > 0); - int SuggestedTimestep + public int SuggestedTimestep { get { @@ -197,6 +203,9 @@ namespace OpenRA.Network if (World.IsReplay) return World.ReplayTimestep; + if (tickScale != 1f) + return Math.Max((int)(tickScale * World.Timestep), 1); + return World.Timestep; } } diff --git a/OpenRA.Game/Server/ProtocolVersion.cs b/OpenRA.Game/Server/ProtocolVersion.cs index b04ffd56ef..4a5ffd8a1d 100644 --- a/OpenRA.Game/Server/ProtocolVersion.cs +++ b/OpenRA.Game/Server/ProtocolVersion.cs @@ -42,6 +42,8 @@ namespace OpenRA.Server // - 0x20: Ping // - Int64 containing the server timestamp when the ping was generated // - [client -> server only] byte containing the number of frames ready to simulate + // - 0x76: TickScale + // - Float containing the scale. // // A connection handshake begins when a client opens a connection to the server: // - Server sends: @@ -75,6 +77,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 = 17; + public const int Orders = 18; } } diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index 211e5fa736..550b2e5ee9 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -638,6 +638,17 @@ namespace OpenRA.Server return ms.GetBuffer(); } + byte[] CreateTickScaleFrame(float scale) + { + var ms = new MemoryStream(17); + ms.WriteArray(BitConverter.GetBytes(9)); + ms.WriteArray(BitConverter.GetBytes(0)); + ms.WriteArray(BitConverter.GetBytes(0)); + ms.WriteByte((byte)OrderType.TickScale); + ms.Write(scale); + return ms.GetBuffer(); + } + void DispatchOrdersToClient(Connection c, int client, int frame, byte[] data) { DispatchFrameToClient(c, client, CreateFrame(client, frame, data)); diff --git a/OpenRA.Game/StreamExts.cs b/OpenRA.Game/StreamExts.cs index 007a54db46..a95ff86c91 100644 --- a/OpenRA.Game/StreamExts.cs +++ b/OpenRA.Game/StreamExts.cs @@ -84,6 +84,11 @@ namespace OpenRA s.WriteArray(BitConverter.GetBytes(value)); } + public static void Write(this Stream s, float value) + { + s.WriteArray(BitConverter.GetBytes(value)); + } + public static float ReadFloat(this Stream s) { return BitConverter.ToSingle(s.ReadBytes(4), 0);