diff --git a/OpenRA.Game/GameInformation.cs b/OpenRA.Game/GameInformation.cs index 0ec409877d..6bca091ef6 100644 --- a/OpenRA.Game/GameInformation.cs +++ b/OpenRA.Game/GameInformation.cs @@ -179,6 +179,9 @@ namespace OpenRA /// The time when this player won or lost the game. public DateTime OutcomeTimestampUtc; + /// The frame at which this player disconnected. + public int DisconnectFrame; + #endregion } } diff --git a/OpenRA.Game/Network/UnitOrders.cs b/OpenRA.Game/Network/UnitOrders.cs index 82b7fe8a65..08188f794a 100644 --- a/OpenRA.Game/Network/UnitOrders.cs +++ b/OpenRA.Game/Network/UnitOrders.cs @@ -39,7 +39,13 @@ namespace OpenRA.Network { 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; } diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index 80e70467ae..705bea821c 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -1044,6 +1044,12 @@ namespace OpenRA.Server // Send disconnected order, even if still in the lobby DispatchOrdersToClients(toDrop, 0, Order.FromTargetString("Disconnected", "", true).Serialize()); + if (gameInfo != null && !dropClient.IsObserver) + { + var disconnectedPlayer = gameInfo.Players.First(p => p.ClientIndex == toDrop.PlayerIndex); + disconnectedPlayer.DisconnectFrame = toDrop.MostRecentFrame; + } + LobbyInfo.Clients.RemoveAll(c => c.Index == toDrop.PlayerIndex); LobbyInfo.ClientPings.RemoveAll(p => p.Index == toDrop.PlayerIndex); diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index 3a26388609..8f8f68ad58 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -552,6 +552,15 @@ namespace OpenRA } } + public void OnPlayerDisconnected(Player player) + { + var pi = gameInfo.GetPlayer(player); + if (pi == null) + return; + + pi.DisconnectFrame = OrderManager.NetFrameNumber; + } + public void RequestGameSave(string filename) { // Allow traits to save arbitrary data that will be passed back via IGameSaveTraitData.ResolveTraitData