Server: handle wins/losses using Sync hash
Signed-off-by: Paul Chote <pchote@users.noreply.github.com>
This commit is contained in:
@@ -679,6 +679,101 @@ namespace OpenRA.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AnyUndefinedWinStates()
|
||||||
|
{
|
||||||
|
var lastTeam = -1;
|
||||||
|
var remainingPlayers = gameInfo.Players.Where(p => p.Outcome == WinState.Undefined);
|
||||||
|
foreach (var player in remainingPlayers)
|
||||||
|
{
|
||||||
|
if (lastTeam >= 0 && (player.Team != lastTeam || player.Team == 0))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
lastTeam = player.Team;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetPlayerDefeat(int playerIndex)
|
||||||
|
{
|
||||||
|
var defeatedPlayer = worldPlayers[playerIndex];
|
||||||
|
if (defeatedPlayer == null || defeatedPlayer.Outcome != WinState.Undefined)
|
||||||
|
return;
|
||||||
|
|
||||||
|
defeatedPlayer.Outcome = WinState.Lost;
|
||||||
|
defeatedPlayer.OutcomeTimestampUtc = DateTime.UtcNow;
|
||||||
|
|
||||||
|
// Set remaining players as winners if only one side remains
|
||||||
|
if (!AnyUndefinedWinStates())
|
||||||
|
{
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
var remainingPlayers = gameInfo.Players.Where(p => p.Outcome == WinState.Undefined);
|
||||||
|
foreach (var winner in remainingPlayers)
|
||||||
|
{
|
||||||
|
winner.Outcome = WinState.Won;
|
||||||
|
winner.OutcomeTimestampUtc = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutOfSync(int frame)
|
||||||
|
{
|
||||||
|
Log.Write("server", "Out of sync detected at frame {0}, cancel replay recording", frame);
|
||||||
|
|
||||||
|
// Make sure the written file is not valid
|
||||||
|
// TODO: storing a serverside replay on desync would be extremely useful
|
||||||
|
recorder.Metadata = null;
|
||||||
|
|
||||||
|
recorder.Dispose();
|
||||||
|
|
||||||
|
// Stop the recording
|
||||||
|
recorder = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly Dictionary<int, byte[]> syncForFrame = new Dictionary<int, byte[]>();
|
||||||
|
int lastDefeatStateFrame;
|
||||||
|
ulong lastDefeatState;
|
||||||
|
|
||||||
|
void HandleSyncOrder(int frame, byte[] packet)
|
||||||
|
{
|
||||||
|
if (syncForFrame.TryGetValue(frame, out var existingSync))
|
||||||
|
{
|
||||||
|
if (packet.Length != existingSync.Length)
|
||||||
|
{
|
||||||
|
OutOfSync(frame);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < packet.Length; i++)
|
||||||
|
{
|
||||||
|
if (packet[i] != existingSync[i])
|
||||||
|
{
|
||||||
|
OutOfSync(frame);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Update player losses based on the new defeat state.
|
||||||
|
// Do this once for the first player, the check above
|
||||||
|
// guarantees a desync if any other player disagrees.
|
||||||
|
var playerDefeatState = BitConverter.ToUInt64(packet, 1 + 4);
|
||||||
|
if (frame > lastDefeatStateFrame && lastDefeatState != playerDefeatState)
|
||||||
|
{
|
||||||
|
var newDefeats = playerDefeatState & ~lastDefeatState;
|
||||||
|
for (var i = 0; i < worldPlayers.Count; i++)
|
||||||
|
if ((newDefeats & (1UL << i)) != 0)
|
||||||
|
SetPlayerDefeat(i);
|
||||||
|
|
||||||
|
lastDefeatState = playerDefeatState;
|
||||||
|
lastDefeatStateFrame = frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
syncForFrame.Add(frame, packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void DispatchOrdersToClients(Connection conn, int frame, byte[] data)
|
public void DispatchOrdersToClients(Connection conn, int frame, byte[] data)
|
||||||
{
|
{
|
||||||
var from = conn != null ? conn.PlayerIndex : 0;
|
var from = conn != null ? conn.PlayerIndex : 0;
|
||||||
@@ -686,7 +781,12 @@ namespace OpenRA.Server
|
|||||||
DispatchOrdersToClient(c, from, frame, data);
|
DispatchOrdersToClient(c, from, frame, data);
|
||||||
|
|
||||||
if (recorder != null)
|
if (recorder != null)
|
||||||
|
{
|
||||||
recorder.ReceiveFrame(from, frame, data);
|
recorder.ReceiveFrame(from, frame, data);
|
||||||
|
|
||||||
|
if (data.Length == 1 + 4 + 8 && data[0] == (byte)OrderType.SyncHash)
|
||||||
|
HandleSyncOrder(frame, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DispatchOrders(Connection conn, int frame, byte[] data)
|
public void DispatchOrders(Connection conn, int frame, byte[] data)
|
||||||
|
|||||||
Reference in New Issue
Block a user