public static void SendCurrentGameData(bool async) { var mapsData = new Dictionary <int, byte[]>(OnMainThread.cachedMapData); var gameData = OnMainThread.cachedGameData; void Send() { var writer = new ByteWriter(); writer.WriteInt32(mapsData.Count); foreach (var mapData in mapsData) { writer.WriteInt32(mapData.Key); writer.WritePrefixedBytes(GZipStream.CompressBuffer(mapData.Value)); } writer.WritePrefixedBytes(GZipStream.CompressBuffer(gameData)); byte[] data = writer.ToArray(); OnMainThread.Enqueue(() => Multiplayer.Client.SendFragmented(Packets.Client_AutosavedData, data)); }; if (async) { ThreadPool.QueueUserWorkItem(c => Send()); } else { Send(); } }
public void Add(SyncInfo info) { if (Multiplayer.session.desynced) { return; } if (TickPatch.Skipping) { return; } if (buffer.Count == 0) { buffer.Add(info); return; } if (buffer[0].local == info.local) { buffer.Add(info); if (buffer.Count > 30) { buffer.RemoveAt(0); } } else { while (buffer.Count > 0 && buffer[0].startTick < info.startTick) { buffer.RemoveAt(0); } if (buffer.Count == 0) { buffer.Add(info); } else if (buffer.First().startTick == info.startTick) { var first = buffer.RemoveFirst(); var error = first.Compare(info); if (error != null) { MpLog.Log($"Desynced {lastValidTick}: {error}"); Multiplayer.session.desynced = true; OnMainThread.Enqueue(() => OnDesynced(first, info, error)); } else { lastValidTick = first.startTick; lastValidArbiter = Multiplayer.session.ArbiterPlaying; } } } }
/// <summary> /// Adds a client opinion to the <see cref="knownClientOpinions"/> list and checks that it matches the most recent currently in there. If not, a desync event is fired. /// </summary> /// <param name="newOpinion">The <see cref="ClientSyncOpinion"/> to add and check.</param> public void AddClientOpinionAndCheckDesync(ClientSyncOpinion newOpinion) { //If we've already desynced, don't even bother if (Multiplayer.session.desynced) { return; } //If we're skipping ticks, again, don't bother if (TickPatch.Skipping) { return; } //If this is the first client opinion we have nothing to compare it with, so just add it if (knownClientOpinions.Count == 0) { knownClientOpinions.Add(newOpinion); return; } if (knownClientOpinions[0].isLocalClientsOpinion == newOpinion.isLocalClientsOpinion) { knownClientOpinions.Add(newOpinion); if (knownClientOpinions.Count > 30) { knownClientOpinions.RemoveAt(0); } } else { //Remove all opinions that started before this one, as it's the most up to date one while (knownClientOpinions.Count > 0 && knownClientOpinions[0].startTick < newOpinion.startTick) { knownClientOpinions.RemoveAt(0); } //If there are none left, we don't need to compare this new one if (knownClientOpinions.Count == 0) { knownClientOpinions.Add(newOpinion); } else if (knownClientOpinions.First().startTick == newOpinion.startTick) { //If these two contain the same tick range - i.e. they start at the same time, cause they should continue to the current tick, then do a comparison. var oldOpinion = knownClientOpinions.RemoveFirst(); //Actually do the comparison to find any desync var desyncMessage = oldOpinion.CheckForDesync(newOpinion); if (desyncMessage != null) { MpLog.Log($"Desynced after tick {lastValidTick}: {desyncMessage}"); Multiplayer.session.desynced = true; OnMainThread.Enqueue(() => HandleDesync(oldOpinion, newOpinion, desyncMessage)); } else { //Update fields lastValidTick = oldOpinion.startTick; arbiterWasPlayingOnLastValidTick = Multiplayer.session.ArbiterPlaying; } } } }