Add Tick scale plumbing
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user