Overhaul player disconnect notifications.
This commit is contained in:
@@ -300,8 +300,8 @@ namespace OpenRA.Network
|
||||
while (receivedPackets.TryDequeue(out var p))
|
||||
{
|
||||
var record = true;
|
||||
if (OrderIO.TryParseDisconnect(p.Data, out var disconnectClient))
|
||||
orderManager.ReceiveDisconnect(disconnectClient);
|
||||
if (OrderIO.TryParseDisconnect(p, out var disconnect))
|
||||
orderManager.ReceiveDisconnect(disconnect.ClientId, disconnect.Frame);
|
||||
else if (OrderIO.TryParseSync(p.Data, out var sync))
|
||||
orderManager.ReceiveSync(sync);
|
||||
else if (OrderIO.TryParseAck(p, out var ackFrame))
|
||||
|
||||
@@ -79,16 +79,19 @@ namespace OpenRA.Network
|
||||
return ms.GetBuffer();
|
||||
}
|
||||
|
||||
public static bool TryParseDisconnect(byte[] packet, out int clientId)
|
||||
public static bool TryParseDisconnect((int FromClient, byte[] Data) packet, out (int Frame, int ClientId) disconnect)
|
||||
{
|
||||
if (packet.Length == Order.DisconnectOrderLength + 4 && packet[4] == (byte)OrderType.Disconnect)
|
||||
// Valid Disconnect packets are only ever generated by the server
|
||||
if (packet.FromClient != 0 || packet.Data.Length != Order.DisconnectOrderLength + 4 || packet.Data[4] != (byte)OrderType.Disconnect)
|
||||
{
|
||||
clientId = BitConverter.ToInt32(packet, 5);
|
||||
return true;
|
||||
disconnect = (0, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
clientId = 0;
|
||||
return false;
|
||||
var frame = BitConverter.ToInt32(packet.Data, 0);
|
||||
var clientId = BitConverter.ToInt32(packet.Data, 5);
|
||||
disconnect = (frame, clientId);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryParseSync(byte[] packet, out (int Frame, int SyncHash, ulong DefeatState) data)
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace OpenRA.Network
|
||||
{
|
||||
public sealed class OrderManager : IDisposable
|
||||
{
|
||||
const OrderPacket ClientDisconnected = null;
|
||||
|
||||
readonly SyncReport syncReport;
|
||||
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)>();
|
||||
@@ -45,6 +47,9 @@ namespace OpenRA.Network
|
||||
readonly List<Order> localOrders = new List<Order>();
|
||||
readonly List<Order> localImmediateOrders = new List<Order>();
|
||||
|
||||
readonly List<ClientOrder> processClientOrders = new List<ClientOrder>();
|
||||
readonly List<int> processClientsToRemove = new List<int>();
|
||||
|
||||
readonly List<TextNotification> notificationsCache = new List<TextNotification>();
|
||||
|
||||
public IReadOnlyList<TextNotification> NotificationsCache => notificationsCache;
|
||||
@@ -126,9 +131,18 @@ namespace OpenRA.Network
|
||||
localImmediateOrders.Clear();
|
||||
}
|
||||
|
||||
public void ReceiveDisconnect(int clientIndex)
|
||||
public void ReceiveDisconnect(int clientId, int frame)
|
||||
{
|
||||
pendingOrders.Remove(clientIndex);
|
||||
// All clients must process the disconnect on the same world tick to allow synced actions to run deterministically.
|
||||
// The server guarantees that we will not receive any more order packets from this client from this frame, so we
|
||||
// can insert a marker in the orders stream and process the synced disconnect behaviours on the first tick of that frame.
|
||||
if (GameStarted)
|
||||
ReceiveOrders(clientId, (frame, ClientDisconnected));
|
||||
|
||||
// The Client state field is not synced; update it immediately so it can be shown in the UI
|
||||
var client = LobbyInfo.ClientWithIndex(clientId);
|
||||
if (client != null)
|
||||
client.State = Session.ClientState.Disconnected;
|
||||
}
|
||||
|
||||
public void ReceiveSync((int Frame, int SyncHash, ulong DefeatState) sync)
|
||||
@@ -198,8 +212,6 @@ namespace OpenRA.Network
|
||||
|
||||
void ProcessOrders()
|
||||
{
|
||||
var clientOrders = new List<ClientOrder>();
|
||||
|
||||
foreach (var (clientId, frameOrders) in pendingOrders)
|
||||
{
|
||||
// The IsReadyForNextFrame check above guarantees that all clients have sent a packet
|
||||
@@ -211,13 +223,24 @@ namespace OpenRA.Network
|
||||
if (frameNumber != NetFrameNumber)
|
||||
throw new InvalidDataException($"Attempted to process orders from client {clientId} for frame {frameNumber} on frame {NetFrameNumber}");
|
||||
|
||||
if (orders == ClientDisconnected)
|
||||
{
|
||||
processClientsToRemove.Add(clientId);
|
||||
World.OnClientDisconnected(clientId);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var order in orders.GetOrders(World))
|
||||
{
|
||||
UnitOrders.ProcessOrder(this, World, clientId, order);
|
||||
clientOrders.Add(new ClientOrder { Client = clientId, Order = order });
|
||||
processClientOrders.Add(new ClientOrder { Client = clientId, Order = order });
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var clientId in processClientsToRemove)
|
||||
pendingOrders.Remove(clientId);
|
||||
|
||||
if (NetFrameNumber >= GameSaveLastSyncFrame)
|
||||
{
|
||||
var defeatState = 0UL;
|
||||
@@ -232,7 +255,10 @@ namespace OpenRA.Network
|
||||
|
||||
if (generateSyncReport)
|
||||
using (new PerfSample("sync_report"))
|
||||
syncReport.UpdateSyncReport(clientOrders);
|
||||
syncReport.UpdateSyncReport(processClientOrders);
|
||||
|
||||
processClientOrders.Clear();
|
||||
processClientsToRemove.Clear();
|
||||
|
||||
++NetFrameNumber;
|
||||
}
|
||||
|
||||
@@ -147,8 +147,8 @@ namespace OpenRA.Network
|
||||
{
|
||||
foreach (var o in chunks.Dequeue().Packets)
|
||||
{
|
||||
if (OrderIO.TryParseDisconnect(o.Packet, out var disconnectClient))
|
||||
orderManager.ReceiveDisconnect(disconnectClient);
|
||||
if (OrderIO.TryParseDisconnect(o, out var disconnect))
|
||||
orderManager.ReceiveDisconnect(disconnect.ClientId, disconnect.Frame);
|
||||
else if (OrderIO.TryParseSync(o.Packet, out var sync))
|
||||
orderManager.ReceiveSync(sync);
|
||||
else if (OrderIO.TryParseOrderPacket(o.Packet, out var orders))
|
||||
|
||||
@@ -51,20 +51,21 @@ namespace OpenRA.Network
|
||||
syncReports[i] = new Report();
|
||||
}
|
||||
|
||||
internal void UpdateSyncReport(List<OrderManager.ClientOrder> orders)
|
||||
internal void UpdateSyncReport(IEnumerable<OrderManager.ClientOrder> orders)
|
||||
{
|
||||
GenerateSyncReport(syncReports[curIndex], orders);
|
||||
curIndex = ++curIndex % NumSyncReports;
|
||||
}
|
||||
|
||||
void GenerateSyncReport(Report report, List<OrderManager.ClientOrder> orders)
|
||||
void GenerateSyncReport(Report report, IEnumerable<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;
|
||||
report.Orders.Clear();
|
||||
report.Orders.AddRange(orders);
|
||||
|
||||
foreach (var actor in orderManager.World.ActorsHavingTrait<ISync>())
|
||||
{
|
||||
|
||||
@@ -34,21 +34,6 @@ namespace OpenRA.Network
|
||||
TextNotificationsManager.AddSystemLine(order.TargetString);
|
||||
break;
|
||||
|
||||
// Reports that the target player disconnected
|
||||
case "Disconnected":
|
||||
{
|
||||
var client = orderManager.LobbyInfo.ClientWithIndex(clientId);
|
||||
if (client != null)
|
||||
{
|
||||
client.State = Session.ClientState.Disconnected;
|
||||
var player = world?.FindPlayerByClient(client);
|
||||
if (player != null)
|
||||
world.OnPlayerDisconnected(player);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "Chat":
|
||||
{
|
||||
var client = orderManager.LobbyInfo.ClientWithIndex(clientId);
|
||||
|
||||
Reference in New Issue
Block a user