Remove FrameData and update OrderManager

This commit is contained in:
teinarss
2021-05-08 16:27:30 +02:00
committed by reaperrr
parent eda79d8626
commit 96b8273916
4 changed files with 55 additions and 119 deletions

View File

@@ -1,81 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Linq;
namespace OpenRA.Network
{
class FrameData
{
public struct ClientOrder
{
public int Client;
public Order Order;
public override string ToString()
{
return $"ClientId: {Client} {Order}";
}
}
readonly Dictionary<int, int> clientQuitTimes = new Dictionary<int, int>();
readonly Dictionary<int, Dictionary<int, byte[]>> framePackets = new Dictionary<int, Dictionary<int, byte[]>>();
public IEnumerable<int> ClientsPlayingInFrame(int frame)
{
return clientQuitTimes
.Where(x => frame <= x.Value)
.Select(x => x.Key)
.OrderBy(x => x);
}
public void ClientQuit(int clientId, int lastClientFrame)
{
if (lastClientFrame == -1)
lastClientFrame = framePackets
.Where(x => x.Value.ContainsKey(clientId))
.Select(x => x.Key).MaxByOrDefault(x => x);
clientQuitTimes[clientId] = lastClientFrame;
}
public void AddFrameOrders(int clientId, int frame, byte[] orders)
{
var frameData = framePackets.GetOrAdd(frame);
frameData.Add(clientId, orders);
}
public bool IsReadyForFrame(int frame)
{
return !ClientsNotReadyForFrame(frame).Any();
}
public IEnumerable<int> ClientsNotReadyForFrame(int frame)
{
var frameData = framePackets.GetOrAdd(frame);
return ClientsPlayingInFrame(frame)
.Where(client => !frameData.ContainsKey(client));
}
public IEnumerable<ClientOrder> OrdersForFrame(World world, int frame)
{
var frameData = framePackets[frame];
var clientData = ClientsPlayingInFrame(frame)
.ToDictionary(k => k, v => frameData[v]);
return clientData
.SelectMany(x => x.Value
.ToOrderList(world)
.Select(o => new ClientOrder { Client = x.Key, Order = o }));
}
}
}

View File

@@ -11,6 +11,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OpenRA.Primitives;
using OpenRA.Support;
@@ -19,10 +20,12 @@ namespace OpenRA.Network
{
public sealed class OrderManager : IDisposable
{
static readonly IEnumerable<Session.Client> NoClients = new Session.Client[] { };
readonly SyncReport syncReport;
readonly FrameData frameData = new FrameData();
// These are the clients who we expect to receive orders / sync from before we can simulate the next tick
readonly HashSet<int> activeClients = new HashSet<int>();
readonly Dictionary<int, Queue<byte[]>> pendingPackets = new Dictionary<int, Queue<byte[]>>();
public Session LobbyInfo = new Session();
public Session.Client LocalClient => LobbyInfo.ClientWithIndex(Connection.LocalClientId);
@@ -49,6 +52,7 @@ namespace OpenRA.Network
readonly List<Order> localOrders = new List<Order>();
readonly List<Order> localImmediateOrders = new List<Order>();
readonly List<(int ClientId, byte[] Packet)> immediatePackets = new List<(int ClientId, byte[] Packet)>();
readonly List<ChatLine> chatCache = new List<ChatLine>();
@@ -57,9 +61,20 @@ namespace OpenRA.Network
bool disposed;
bool generateSyncReport = false;
public struct ClientOrder
{
public int Client;
public Order Order;
public override string ToString()
{
return "ClientId: {0} {1}".F(Client, Order);
}
}
void OutOfSync(int frame)
{
syncReport.DumpSyncReport(frame, frameData.OrdersForFrame(World, frame));
syncReport.DumpSyncReport(frame);
throw new InvalidOperationException($"Out of sync in frame {frame}.\n Compare syncreport.log with other players.");
}
@@ -114,14 +129,12 @@ namespace OpenRA.Network
Connection.SendImmediate(localImmediateOrders.Select(o => o.Serialize()));
localImmediateOrders.Clear();
var immediatePackets = new List<(int ClientId, byte[] Packet)>();
Connection.Receive(
(clientId, packet) =>
{
var frame = BitConverter.ToInt32(packet, 0);
if (packet.Length == 5 && packet[4] == (byte)OrderType.Disconnect)
frameData.ClientQuit(clientId, frame);
activeClients.Remove(clientId);
else if (packet.Length > 4 && packet[4] == (byte)OrderType.SyncHash)
{
if (packet.Length != 4 + Order.SyncHashOrderLength)
@@ -135,7 +148,10 @@ namespace OpenRA.Network
else if (frame == 0)
immediatePackets.Add((clientId, packet));
else
frameData.AddFrameOrders(clientId, frame, packet);
{
activeClients.Add(clientId);
pendingPackets.GetOrAdd(clientId).Enqueue(packet);
}
});
foreach (var p in immediatePackets)
@@ -149,6 +165,8 @@ namespace OpenRA.Network
return;
}
}
immediatePackets.Clear();
}
Dictionary<int, byte[]> syncForFrame = new Dictionary<int, byte[]>();
@@ -169,18 +187,7 @@ namespace OpenRA.Network
syncForFrame.Add(frame, packet);
}
public bool IsReadyForNextFrame => GameStarted && frameData.IsReadyForFrame(NetFrameNumber);
public IEnumerable<Session.Client> GetClientsNotReadyForNextFrame
{
get
{
return GameStarted
? frameData.ClientsNotReadyForFrame(NetFrameNumber)
.Select(a => LobbyInfo.ClientWithIndex(a))
: NoClients;
}
}
public bool IsReadyForNextFrame => GameStarted && activeClients.All(client => pendingPackets[client].Count > 0);
public void Tick()
{
@@ -192,8 +199,26 @@ namespace OpenRA.Network
localOrders.Clear();
foreach (var order in frameData.OrdersForFrame(World, NetFrameNumber))
UnitOrders.ProcessOrder(this, World, order.Client, order.Order);
var clientOrders = new List<ClientOrder>();
foreach (var clientId in activeClients)
{
// The IsReadyForNextFrame check above guarantees that all clients have sent a packet
var frameData = pendingPackets[clientId].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))
{
UnitOrders.ProcessOrder(this, World, clientId, order);
clientOrders.Add(new ClientOrder { Client = clientId, Order = order });
}
}
if (NetFrameNumber + FramesAhead >= GameSaveLastSyncFrame)
{
@@ -209,7 +234,7 @@ namespace OpenRA.Network
if (generateSyncReport)
using (new PerfSample("sync_report"))
syncReport.UpdateSyncReport();
syncReport.UpdateSyncReport(clientOrders);
++NetFrameNumber;
}

View File

@@ -51,19 +51,20 @@ namespace OpenRA.Network
syncReports[i] = new Report();
}
internal void UpdateSyncReport()
internal void UpdateSyncReport(List<OrderManager.ClientOrder> orders)
{
GenerateSyncReport(syncReports[curIndex]);
GenerateSyncReport(syncReports[curIndex], orders);
curIndex = ++curIndex % NumSyncReports;
}
void GenerateSyncReport(Report report)
void GenerateSyncReport(Report report, List<OrderManager.ClientOrder> orders)
{
report.Frame = orderManager.NetFrameNumber;
report.SyncedRandom = orderManager.World.SharedRandom.Last;
report.TotalCount = orderManager.World.SharedRandom.TotalCount;
report.Traits.Clear();
report.Effects.Clear();
report.Orders = orders;
foreach (var actor in orderManager.World.ActorsHavingTrait<ISync>())
{
@@ -100,7 +101,7 @@ namespace OpenRA.Network
}
}
internal void DumpSyncReport(int frame, IEnumerable<FrameData.ClientOrder> orders)
internal void DumpSyncReport(int frame)
{
var reportName = "syncreport-" + DateTime.UtcNow.ToString("yyyy-MM-ddTHHmmssZ", CultureInfo.InvariantCulture) + ".log";
Log.AddChannel("sync", reportName);
@@ -137,7 +138,7 @@ namespace OpenRA.Network
}
Log.Write("sync", "Orders Issued:");
foreach (var o in orders)
foreach (var o in r.Orders)
Log.Write("sync", "\t {0}", o.ToString());
return;
@@ -154,6 +155,7 @@ namespace OpenRA.Network
public int TotalCount;
public List<TraitReport> Traits = new List<TraitReport>();
public List<EffectReport> Effects = new List<EffectReport>();
public List<OrderManager.ClientOrder> Orders = new List<OrderManager.ClientOrder>();
}
struct TraitReport

View File

@@ -1252,16 +1252,6 @@ namespace OpenRA.Server
SyncLobbyInfo();
State = ServerState.GameStarted;
var disconnectData = new[] { (byte)OrderType.Disconnect };
foreach (var c in Conns)
{
foreach (var d in Conns)
DispatchOrdersToClient(c, d.PlayerIndex, int.MaxValue, disconnectData);
if (recorder != null)
recorder.ReceiveFrame(c.PlayerIndex, int.MaxValue, disconnectData);
}
if (GameSave == null && LobbyInfo.GlobalSettings.GameSavesEnabled)
GameSave = new GameSave();