Add Tick scale plumbing

This commit is contained in:
teinarss
2021-10-09 19:02:10 +02:00
committed by abcdefg30
parent 7f3130c7a6
commit 1a56cee9a1
8 changed files with 62 additions and 11 deletions

View File

@@ -779,9 +779,7 @@ namespace OpenRA
// ReplayTimestep = 0 means the replay is paused: we need to keep logicInterval as UI.Timestep to avoid breakage // 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)) if (logicWorld != null && !(logicWorld.IsReplay && logicWorld.ReplayTimestep == 0))
logicInterval = logicWorld.IsLoadingGameSave ? 1 : logicInterval = logicWorld == OrderManager.World ? OrderManager.SuggestedTimestep : logicWorld.Timestep;
logicWorld.IsReplay ? logicWorld.ReplayTimestep :
logicWorld.Timestep;
// Ideal time between screen updates // Ideal time between screen updates
var maxFramerate = Settings.Graphics.CapFramerate ? Settings.Graphics.MaxFramerate.Clamp(1, 1000) : 1000; var maxFramerate = Settings.Graphics.CapFramerate ? Settings.Graphics.MaxFramerate.Clamp(1, 1000) : 1000;

View File

@@ -299,17 +299,23 @@ namespace OpenRA.Network
// Orders from other players // Orders from other players
while (receivedPackets.TryDequeue(out var p)) while (receivedPackets.TryDequeue(out var p))
{ {
var record = true;
if (OrderIO.TryParseDisconnect(p, out var disconnect)) if (OrderIO.TryParseDisconnect(p, out var disconnect))
{
orderManager.ReceiveDisconnect(disconnect.ClientId, disconnect.Frame); orderManager.ReceiveDisconnect(disconnect.ClientId, disconnect.Frame);
Recorder?.Receive(p.FromClient, p.Data);
}
else if (OrderIO.TryParseSync(p.Data, out var sync)) else if (OrderIO.TryParseSync(p.Data, out var sync))
{
orderManager.ReceiveSync(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)) else if (OrderIO.TryParsePingRequest(p, out var timestamp))
{ {
// Note that processing this here, rather than in NetworkConnectionReceive, // Note that processing this here, rather than in NetworkConnectionReceive,
// so that poor world tick performance can be reflected in the latency measurement // so that poor world tick performance can be reflected in the latency measurement
Send(OrderIO.SerializePingResponse(timestamp, (byte)orderManager.OrderQueueLength)); Send(OrderIO.SerializePingResponse(timestamp, (byte)orderManager.OrderQueueLength));
record = false;
} }
else if (OrderIO.TryParseAck(p, out var ackFrame, out var ackCount)) else if (OrderIO.TryParseAck(p, out var ackFrame, out var ackCount))
{ {
@@ -331,7 +337,6 @@ namespace OpenRA.Network
orderManager.ReceiveOrders(clientId, (ackFrame, packet)); orderManager.ReceiveOrders(clientId, (ackFrame, packet));
Recorder?.Receive(clientId, packet.Serialize(ackFrame)); Recorder?.Receive(clientId, packet.Serialize(ackFrame));
record = false;
} }
else if (OrderIO.TryParseOrderPacket(p.Data, out var orders)) else if (OrderIO.TryParseOrderPacket(p.Data, out var orders))
{ {
@@ -339,13 +344,12 @@ namespace OpenRA.Network
orderManager.ReceiveImmediateOrders(p.FromClient, orders.Orders); orderManager.ReceiveImmediateOrders(p.FromClient, orders.Orders);
else else
orderManager.ReceiveOrders(p.FromClient, orders); orderManager.ReceiveOrders(p.FromClient, orders);
Recorder?.Receive(p.FromClient, p.Data);
} }
else else
throw new InvalidDataException($"Received unknown packet from client {p.FromClient} with length {p.Data.Length}"); 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. // 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. // Bail out to avoid potential problems from acting on disposed objects.
if (disposed) if (disposed)

View File

@@ -20,6 +20,7 @@ namespace OpenRA
Ack = 0x10, Ack = 0x10,
Ping = 0x20, Ping = 0x20,
SyncHash = 0x65, SyncHash = 0x65,
TickScale = 0x76,
Disconnect = 0xBF, Disconnect = 0xBF,
Handshake = 0xFE, Handshake = 0xFE,
Fields = 0xFF Fields = 0xFF

View File

@@ -133,6 +133,27 @@ namespace OpenRA.Network
return true; 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) public static bool TryParsePingRequest((int FromClient, byte[] Data) packet, out long timestamp)
{ {
// Valid Ping requests are only ever generated by the server // Valid Ping requests are only ever generated by the server

View File

@@ -58,6 +58,7 @@ namespace OpenRA.Network
bool disposed; bool disposed;
bool generateSyncReport = false; bool generateSyncReport = false;
int sentOrdersFrame = 0; int sentOrdersFrame = 0;
float tickScale = 1f;
public struct ClientOrder public struct ClientOrder
{ {
@@ -157,6 +158,11 @@ namespace OpenRA.Network
syncForFrame.Add(sync.Frame, (sync.SyncHash, sync.DefeatState)); syncForFrame.Add(sync.Frame, (sync.SyncHash, sync.DefeatState));
} }
public void ReceiveTickScale(float scale)
{
tickScale = scale;
}
public void ReceiveImmediateOrders(int clientId, OrderPacket orders) public void ReceiveImmediateOrders(int clientId, OrderPacket orders)
{ {
foreach (var o in orders.GetOrders(World)) foreach (var o in orders.GetOrders(World))
@@ -184,7 +190,7 @@ namespace OpenRA.Network
bool IsReadyForNextFrame => GameStarted && pendingOrders.All(p => p.Value.Count > 0); bool IsReadyForNextFrame => GameStarted && pendingOrders.All(p => p.Value.Count > 0);
int SuggestedTimestep public int SuggestedTimestep
{ {
get get
{ {
@@ -197,6 +203,9 @@ namespace OpenRA.Network
if (World.IsReplay) if (World.IsReplay)
return World.ReplayTimestep; return World.ReplayTimestep;
if (tickScale != 1f)
return Math.Max((int)(tickScale * World.Timestep), 1);
return World.Timestep; return World.Timestep;
} }
} }

View File

@@ -42,6 +42,8 @@ namespace OpenRA.Server
// - 0x20: Ping // - 0x20: Ping
// - Int64 containing the server timestamp when the ping was generated // - Int64 containing the server timestamp when the ping was generated
// - [client -> server only] byte containing the number of frames ready to simulate // - [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: // A connection handshake begins when a client opens a connection to the server:
// - Server sends: // - Server sends:
@@ -75,6 +77,6 @@ namespace OpenRA.Server
// The protocol for server and world orders // The protocol for server and world orders
// This applies after the handshake has completed, and is provided to support // This applies after the handshake has completed, and is provided to support
// alternative server implementations that wish to support multiple versions in parallel // alternative server implementations that wish to support multiple versions in parallel
public const int Orders = 17; public const int Orders = 18;
} }
} }

View File

@@ -638,6 +638,17 @@ namespace OpenRA.Server
return ms.GetBuffer(); 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) void DispatchOrdersToClient(Connection c, int client, int frame, byte[] data)
{ {
DispatchFrameToClient(c, client, CreateFrame(client, frame, data)); DispatchFrameToClient(c, client, CreateFrame(client, frame, data));

View File

@@ -84,6 +84,11 @@ namespace OpenRA
s.WriteArray(BitConverter.GetBytes(value)); 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) public static float ReadFloat(this Stream s)
{ {
return BitConverter.ToSingle(s.ReadBytes(4), 0); return BitConverter.ToSingle(s.ReadBytes(4), 0);