Remove packet byte wrangling from OrderManager.
This commit is contained in:
@@ -13,7 +13,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
@@ -22,8 +21,8 @@ namespace OpenRA.Network
|
||||
public sealed class OrderManager : IDisposable
|
||||
{
|
||||
readonly SyncReport syncReport;
|
||||
|
||||
readonly Dictionary<int, Queue<byte[]>> pendingPackets = new Dictionary<int, Queue<byte[]>>();
|
||||
readonly Dictionary<int, Queue<(int Frame, OrderPacket Orders)>> pendingOrders = new Dictionary<int, Queue<(int, OrderPacket)>>();
|
||||
readonly Dictionary<int, (int SyncHash, ulong DefeatState)> syncForFrame = new Dictionary<int, (int, ulong)>();
|
||||
|
||||
public Session LobbyInfo = new Session();
|
||||
public Session.Client LocalClient => LobbyInfo.ClientWithIndex(Connection.LocalClientId);
|
||||
@@ -78,7 +77,7 @@ namespace OpenRA.Network
|
||||
|
||||
foreach (var client in LobbyInfo.Clients)
|
||||
if (!client.IsBot)
|
||||
pendingPackets.Add(client.Index, new Queue<byte[]>());
|
||||
pendingOrders.Add(client.Index, new Queue<(int, OrderPacket)>());
|
||||
|
||||
// Generating sync reports is expensive, so only do it if we have
|
||||
// other players to compare against if a desync did occur
|
||||
@@ -90,7 +89,7 @@ namespace OpenRA.Network
|
||||
|
||||
if (GameSaveLastFrame < 0)
|
||||
for (var i = NetFrameNumber; i <= FramesAhead; i++)
|
||||
Connection.Send(i, new List<byte[]>());
|
||||
Connection.Send(i, Array.Empty<Order>());
|
||||
}
|
||||
|
||||
public OrderManager(IConnection conn)
|
||||
@@ -125,74 +124,68 @@ namespace OpenRA.Network
|
||||
void SendImmediateOrders()
|
||||
{
|
||||
if (localImmediateOrders.Count != 0 && GameSaveLastFrame < NetFrameNumber + FramesAhead)
|
||||
Connection.SendImmediate(localImmediateOrders.Select(o => o.Serialize()));
|
||||
Connection.SendImmediate(localImmediateOrders);
|
||||
localImmediateOrders.Clear();
|
||||
}
|
||||
|
||||
public void ReceiveDisconnect(int clientIndex)
|
||||
{
|
||||
// HACK: The shellmap relies on ticking a disposed OM
|
||||
if (disposed && World.Type != WorldType.Shellmap)
|
||||
return;
|
||||
|
||||
pendingOrders.Remove(clientIndex);
|
||||
}
|
||||
|
||||
public void ReceiveSync(int frame, int syncHash, ulong defeatState)
|
||||
{
|
||||
// HACK: The shellmap relies on ticking a disposed OM
|
||||
if (disposed && World.Type != WorldType.Shellmap)
|
||||
return;
|
||||
|
||||
if (syncForFrame.TryGetValue(frame, out var s))
|
||||
{
|
||||
if (s.SyncHash != syncHash || s.DefeatState != defeatState)
|
||||
OutOfSync(frame);
|
||||
}
|
||||
else
|
||||
syncForFrame.Add(frame, (syncHash, defeatState));
|
||||
}
|
||||
|
||||
public void ReceiveImmediateOrders(int clientId, OrderPacket orders)
|
||||
{
|
||||
// HACK: The shellmap relies on ticking a disposed OM
|
||||
if (disposed && World.Type != WorldType.Shellmap)
|
||||
return;
|
||||
|
||||
foreach (var o in orders.GetOrders(World))
|
||||
{
|
||||
UnitOrders.ProcessOrder(this, World, clientId, o);
|
||||
|
||||
// A mod switch or other event has pulled the ground from beneath us
|
||||
if (disposed)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void ReceiveOrders(int clientId, int frame, OrderPacket orders)
|
||||
{
|
||||
// HACK: The shellmap relies on ticking a disposed OM
|
||||
if (disposed && World.Type != WorldType.Shellmap)
|
||||
return;
|
||||
|
||||
if (pendingOrders.TryGetValue(clientId, out var queue))
|
||||
queue.Enqueue((frame, orders));
|
||||
else
|
||||
Log.Write("debug", $"Received packet from disconnected client '{clientId}'");
|
||||
}
|
||||
|
||||
void ReceiveAllOrdersAndCheckSync()
|
||||
{
|
||||
Connection.Receive(
|
||||
(clientId, packet) =>
|
||||
{
|
||||
// HACK: The shellmap relies on ticking a disposed OM
|
||||
if (disposed && World.Type != WorldType.Shellmap)
|
||||
return;
|
||||
|
||||
var frame = BitConverter.ToInt32(packet, 0);
|
||||
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)
|
||||
{
|
||||
Log.Write("debug", $"Dropped sync order with length {packet.Length}. Expected length {4 + Order.SyncHashOrderLength}.");
|
||||
return;
|
||||
}
|
||||
|
||||
CheckSync(packet);
|
||||
}
|
||||
else if (frame == 0)
|
||||
{
|
||||
foreach (var o in packet.ToOrderList(World))
|
||||
{
|
||||
UnitOrders.ProcessOrder(this, World, clientId, o);
|
||||
|
||||
// A mod switch or other event has pulled the ground from beneath us
|
||||
if (disposed)
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pendingPackets.TryGetValue(clientId, out var queue))
|
||||
queue.Enqueue(packet);
|
||||
else
|
||||
Log.Write("debug", $"Received packet from disconnected client '{clientId}'");
|
||||
}
|
||||
});
|
||||
Connection.Receive(this);
|
||||
}
|
||||
|
||||
Dictionary<int, byte[]> syncForFrame = new Dictionary<int, byte[]>();
|
||||
|
||||
void CheckSync(byte[] packet)
|
||||
{
|
||||
var frame = BitConverter.ToInt32(packet, 0);
|
||||
if (syncForFrame.TryGetValue(frame, out var existingSync))
|
||||
{
|
||||
if (packet.Length != existingSync.Length)
|
||||
OutOfSync(frame);
|
||||
else
|
||||
for (var i = 0; i < packet.Length; i++)
|
||||
if (packet[i] != existingSync[i])
|
||||
OutOfSync(frame);
|
||||
}
|
||||
else
|
||||
syncForFrame.Add(frame, packet);
|
||||
}
|
||||
|
||||
bool IsReadyForNextFrame => GameStarted && pendingPackets.All(p => p.Value.Count > 0);
|
||||
bool IsReadyForNextFrame => GameStarted && pendingOrders.All(p => p.Value.Count > 0);
|
||||
|
||||
int SuggestedTimestep
|
||||
{
|
||||
@@ -218,7 +211,7 @@ namespace OpenRA.Network
|
||||
|
||||
if (GameSaveLastFrame < NetFrameNumber + FramesAhead)
|
||||
{
|
||||
Connection.Send(NetFrameNumber + FramesAhead, localOrders.Select(o => o.Serialize()).ToList());
|
||||
Connection.Send(NetFrameNumber + FramesAhead, localOrders);
|
||||
localOrders.Clear();
|
||||
}
|
||||
}
|
||||
@@ -227,20 +220,19 @@ namespace OpenRA.Network
|
||||
{
|
||||
var clientOrders = new List<ClientOrder>();
|
||||
|
||||
foreach (var (clientId, clientPackets) in pendingPackets)
|
||||
foreach (var (clientId, frameOrders) in pendingOrders)
|
||||
{
|
||||
// The IsReadyForNextFrame check above guarantees that all clients have sent a packet
|
||||
var frameData = clientPackets.Dequeue();
|
||||
var (frameNumber, orders) = frameOrders.Dequeue();
|
||||
|
||||
// Orders are synchronised by sending an initial FramesAhead set of empty packets
|
||||
// and then making sure that we enqueue and process exactly one packet for each player each tick.
|
||||
// This may change in the future, so sanity check that the orders are for the frame we expect
|
||||
// and crash early instead of risking desyncs.
|
||||
var frameNumber = BitConverter.ToInt32(frameData, 0);
|
||||
if (frameNumber != NetFrameNumber)
|
||||
throw new InvalidDataException($"Attempted to process orders from client {clientId} for frame {frameNumber} on frame {NetFrameNumber}");
|
||||
|
||||
foreach (var order in frameData.ToOrderList(World))
|
||||
foreach (var order in orders.GetOrders(World))
|
||||
{
|
||||
UnitOrders.ProcessOrder(this, World, clientId, order);
|
||||
clientOrders.Add(new ClientOrder { Client = clientId, Order = order });
|
||||
@@ -254,10 +246,10 @@ namespace OpenRA.Network
|
||||
if (World.Players[i].WinState == WinState.Lost)
|
||||
defeatState |= 1UL << i;
|
||||
|
||||
Connection.SendSync(NetFrameNumber, OrderIO.SerializeSync(World.SyncHash(), defeatState));
|
||||
Connection.SendSync(NetFrameNumber, World.SyncHash(), defeatState);
|
||||
}
|
||||
else
|
||||
Connection.SendSync(NetFrameNumber, OrderIO.SerializeSync(0, 0));
|
||||
Connection.SendSync(NetFrameNumber, 0, 0);
|
||||
|
||||
if (generateSyncReport)
|
||||
using (new PerfSample("sync_report"))
|
||||
@@ -287,7 +279,7 @@ namespace OpenRA.Network
|
||||
{
|
||||
// Check whether or not we will be ready for a tick next frame
|
||||
// We don't need to include ourselves in the equation because we can always generate orders this frame
|
||||
shouldTick = pendingPackets.All(p => p.Key == Connection.LocalClientId || p.Value.Count > 0);
|
||||
shouldTick = pendingOrders.All(p => p.Key == Connection.LocalClientId || p.Value.Count > 0);
|
||||
|
||||
// Send orders only if we are currently ready, this prevents us sending orders too soon if we are
|
||||
// stalling
|
||||
|
||||
Reference in New Issue
Block a user