Remove FrameData and update OrderManager
This commit is contained in:
@@ -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 }));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user