public void StartGame(Arguments args) { Launch = new LaunchArguments(args); Ui.ResetAll(); Game.Settings.Save(); if (Launch.Benchmark) { Log.AddChannel("cpu", "cpu.csv"); Log.Write("cpu", "tick;time [ms]"); Log.AddChannel("render", "render.csv"); Log.Write("render", "frame;time [ms]"); Console.WriteLine("Saving benchmark data into {0}".F(Path.Combine(Platform.SupportDir, "Logs"))); Game.BenchmarkMode = true; } // Join a server directly var connect = Launch.GetConnectAddress(); if (!string.IsNullOrEmpty(connect)) { var parts = connect.Split(':'); if (parts.Length == 2) { var host = parts[0]; var port = Exts.ParseIntegerInvariant(parts[1]); Game.LoadShellMap(); Game.RemoteDirectConnect(host, port); return; } } // Load a replay directly if (!string.IsNullOrEmpty(Launch.Replay)) { var replayMeta = ReplayMetadata.Read(Launch.Replay); if (ReplayUtils.PromptConfirmReplayCompatibility(replayMeta, Game.LoadShellMap)) { Game.JoinReplay(Launch.Replay); } if (replayMeta != null) { var mod = replayMeta.GameInfo.Mod; if (mod != null && mod != Game.ModData.Manifest.Mod.Id && ModMetadata.AllMods.ContainsKey(mod)) { Game.InitializeMod(mod, args); } } return; } Game.LoadShellMap(); Game.Settings.Save(); }
void IUtilityCommand.Run(Utility utility, string[] args) { var replay = ReplayMetadata.Read(args[1]); if (replay == null) { throw new InvalidDataException("Failed to read replay meta data"); } var info = replay.GameInfo; var lines = FieldSaver.Save(info).ToLines(replay.FilePath); foreach (var line in lines) { Console.WriteLine(line); } Console.WriteLine("\tPlayers:"); var playerCount = 0; foreach (var p in info.Players) { var playerLines = FieldSaver.Save(p).ToLines("{0}".F(playerCount++)); foreach (var line in playerLines) { Console.WriteLine("\t\t" + line); } } }
public virtual void StartGame(Arguments args) { Launch = new LaunchArguments(args); Ui.ResetAll(); Game.Settings.Save(); if (!string.IsNullOrEmpty(Launch.Benchmark)) { Console.WriteLine("Saving benchmark data into {0}".F(Path.Combine(Platform.SupportDir, "Logs"))); Game.BenchmarkMode(Launch.Benchmark); } // Join a server directly var connect = Launch.GetConnectEndPoint(); if (connect != null) { Game.LoadShellMap(); Game.RemoteDirectConnect(connect); return; } // Start a map directly if (!string.IsNullOrEmpty(Launch.Map)) { Game.LoadMap(Launch.Map); return; } // Load a replay directly if (!string.IsNullOrEmpty(Launch.Replay)) { ReplayMetadata replayMeta = null; try { replayMeta = ReplayMetadata.Read(Launch.Replay); } catch { } if (ReplayUtils.PromptConfirmReplayCompatibility(replayMeta, Game.LoadShellMap)) { Game.JoinReplay(Launch.Replay); } if (replayMeta != null) { var mod = replayMeta.GameInfo.Mod; if (mod != null && mod != Game.ModData.Manifest.Id && Game.Mods.ContainsKey(mod)) { Game.InitializeMod(mod, args); } } return; } Game.LoadShellMap(); Game.Settings.Save(); }
void RenameReplay(ReplayMetadata replay, string newFilenameWithoutExtension) { try { replay.RenameFile(newFilenameWithoutExtension); replayState[replay].Item.Text = newFilenameWithoutExtension; } catch (Exception ex) { Log.Write("debug", ex.ToString()); return; } }
public void StartGame(Arguments args) { Launch = new LaunchArguments(args); Ui.ResetAll(); Game.Settings.Save(); // Join a server directly var connect = Launch.GetConnectAddress(); if (!string.IsNullOrEmpty(connect)) { var parts = connect.Split(':'); if (parts.Length == 2) { var host = parts[0]; var port = Exts.ParseIntegerInvariant(parts[1]); Game.LoadShellMap(); Game.RemoteDirectConnect(host, port); return; } } // Load a replay directly if (!string.IsNullOrEmpty(Launch.Replay)) { var replayMeta = ReplayMetadata.Read(Launch.Replay); if (ReplayUtils.PromptConfirmReplayCompatibility(replayMeta, Game.LoadShellMap)) { Game.JoinReplay(Launch.Replay); } if (replayMeta != null) { var mod = replayMeta.GameInfo.Mod; if (mod != null && mod != Game.ModData.Manifest.Mod.Id && ModMetadata.AllMods.ContainsKey(mod)) { Game.InitializeMod(mod, args); } } return; } Game.LoadShellMap(); Game.Settings.Save(); }
void RenameReplay(ReplayMetadata replay, string newFilenameWithoutExtension) { try { var item = replayState[replay].Item; replay.RenameFile(newFilenameWithoutExtension); item.Text = newFilenameWithoutExtension; var label = item.Get <LabelWithTooltipWidget>("TITLE"); WidgetUtils.TruncateLabelToTooltip(label, item.Text); } catch (Exception ex) { Log.Write("debug", ex.ToString()); return; } }
void AddReplay(ReplayMetadata replay, ScrollItemWidget template) { var item = ScrollItemWidget.Setup(template, () => selectedReplay == replay, () => SelectReplay(replay), () => WatchReplay()); replayState[replay] = new ReplayState { Item = item, Visible = true }; item.Text = Path.GetFileNameWithoutExtension(replay.FilePath); item.Get <LabelWidget>("TITLE").GetText = () => item.Text; item.IsVisible = () => replayState[replay].Visible; replayList.AddChild(item); }
public static bool PromptConfirmReplayCompatibility(ReplayMetadata replayMeta, Action onCancel = null) { if (onCancel == null) { onCancel = DoNothing; } if (replayMeta == null) { return(IncompatibleReplayDialog("outdated engine", null, onCancel)); } var version = replayMeta.GameInfo.Version; if (version == null) { return(IncompatibleReplayDialog("unknown version", version, onCancel)); } var mod = replayMeta.GameInfo.Mod; if (mod == null) { return(IncompatibleReplayDialog("unknown mod", mod, onCancel)); } var allMods = ModMetadata.AllMods; if (!allMods.ContainsKey(mod)) { return(IncompatibleReplayDialog("unavailable mod", mod, onCancel)); } else if (allMods[mod].Version != version) { return(IncompatibleReplayDialog("incompatible version", version, onCancel)); } if (replayMeta.GameInfo.MapPreview.Status != MapStatus.Available) { return(IncompatibleReplayDialog("unavailable map", replayMeta.GameInfo.MapUid, onCancel)); } return(true); }
public static bool PromptConfirmReplayCompatibility(ReplayMetadata replayMeta, Action onCancel = null) { if (onCancel == null) { onCancel = DoNothing; } if (replayMeta == null) { ConfirmationDialogs.ButtonPrompt("Incompatible Replay", "Replay metadata could not be read.", onCancel: onCancel); return(false); } var version = replayMeta.GameInfo.Version; if (version == null) { return(IncompatibleReplayDialog("unknown version", version, onCancel)); } var mod = replayMeta.GameInfo.Mod; if (mod == null) { return(IncompatibleReplayDialog("unknown mod", mod, onCancel)); } if (!Game.Mods.ContainsKey(mod)) { return(IncompatibleReplayDialog("unavailable mod", mod, onCancel)); } if (Game.Mods[mod].Metadata.Version != version) { return(IncompatibleReplayDialog("incompatible version", version, onCancel)); } if (replayMeta.GameInfo.MapPreview.Status != MapStatus.Available) { return(IncompatibleReplayDialog("unavailable map", replayMeta.GameInfo.MapUid, onCancel)); } return(true); }
void DeleteReplay(ReplayMetadata replay) { try { File.Delete(replay.FilePath); } catch (Exception ex) { Game.Debug("Failed to delete replay file '{0}'. See the logs for details.", replay.FilePath); Log.Write("debug", ex.ToString()); return; } if (replay == selectedReplay) { SelectReplay(null); } replayList.RemoveChild(replayState[replay].Item); replays.Remove(replay); replayState.Remove(replay); }
void LoadReplays(string dir, ScrollItemWidget template) { using (new Support.PerfTimer("Load replays")) { var loadedReplays = new ConcurrentBag <ReplayMetadata>(); Parallel.ForEach(Directory.GetFiles(dir, "*.orarep", SearchOption.AllDirectories), (fileName, pls) => { if (cancelLoadingReplays) { pls.Stop(); return; } var replay = ReplayMetadata.Read(fileName); if (replay != null) { loadedReplays.Add(replay); } }); if (cancelLoadingReplays) { return; } var sortedReplays = loadedReplays.OrderByDescending(replay => replay.GameInfo.StartTimeUtc).ToList(); Game.RunAfterTick(() => { replayList.RemoveChildren(); foreach (var replay in sortedReplays) { AddReplay(replay, template); } SetupReplayDependentFilters(); ApplyFilter(); }); } }
void AddReplay(ReplayMetadata replay, ScrollItemWidget template) { replays.Add(replay); var item = ScrollItemWidget.Setup(template, () => selectedReplay == replay, () => SelectReplay(replay), () => WatchReplay()); replayState[replay] = new ReplayState { Item = item, Visible = true }; item.Text = Path.GetFileNameWithoutExtension(replay.FilePath); var label = item.Get <LabelWithTooltipWidget>("TITLE"); WidgetUtils.TruncateLabelToTooltip(label, item.Text); item.IsVisible = () => replayState[replay].Visible; replayList.AddChild(item); }
public void Run(ModData modData, string[] args) { var replay = ReplayMetadata.Read(args[1]); var info = replay.GameInfo; var lines = FieldSaver.Save(info).ToLines(replay.FilePath); foreach (var line in lines) { Console.WriteLine(line); } Console.WriteLine("\tPlayers:"); var playerCount = 0; foreach (var p in info.Players) { var playerLines = FieldSaver.Save(p).ToLines("{0}".F(playerCount++)); foreach (var line in playerLines) { Console.WriteLine("\t\t" + line); } } }
public void StartGame(Arguments args) { Ui.ResetAll(); Game.Settings.Save(); // Check whether the mod content is installed // TODO: The installation code has finally been beaten into shape, so we can // finally move it all into the planned "Manage Content" panel in the modchooser mod. var installData = Game.ModData.Manifest.Get <ContentInstaller>(); var installModContent = !installData.TestFiles.All(f => GlobalFileSystem.Exists(f)); var installModMusic = args != null && args.Contains("Install.Music"); if (installModContent || installModMusic) { var widgetArgs = new WidgetArgs() { { "continueLoading", () => Game.InitializeMod(Game.Settings.Game.Mod, args) }, }; if (installData.BackgroundWidget != null) { Ui.LoadWidget(installData.BackgroundWidget, Ui.Root, widgetArgs); } var menu = installModContent ? installData.MenuWidget : installData.MusicMenuWidget; Ui.OpenWindow(menu, widgetArgs); return; } // Join a server directly var connect = string.Empty; if (args != null) { if (args.Contains("Launch.Connect")) { connect = args.GetValue("Launch.Connect", null); } if (args.Contains("Launch.URI")) { connect = args.GetValue("Launch.URI", null); if (connect != null) { connect = connect.Replace("openra://", ""); connect = connect.TrimEnd('/'); } } } if (!string.IsNullOrEmpty(connect)) { var parts = connect.Split(':'); if (parts.Length == 2) { var host = parts[0]; var port = Exts.ParseIntegerInvariant(parts[1]); Game.LoadShellMap(); Game.RemoteDirectConnect(host, port); return; } } // Load a replay directly var replayFilename = args != null?args.GetValue("Launch.Replay", null) : null; if (!string.IsNullOrEmpty(replayFilename)) { var replayMeta = ReplayMetadata.Read(replayFilename); if (ReplayUtils.PromptConfirmReplayCompatibility(replayMeta, Game.LoadShellMap)) { Game.JoinReplay(replayFilename); } if (replayMeta != null) { var mod = replayMeta.GameInfo.Mod; if (mod != null && mod != Game.ModData.Manifest.Mod.Id && ModMetadata.AllMods.ContainsKey(mod)) { Game.InitializeMod(mod, args); } } return; } Game.LoadShellMap(); Game.Settings.Save(); }
void SelectReplay(ReplayMetadata replay) { selectedReplay = replay; map = selectedReplay != null ? selectedReplay.GameInfo.MapPreview : MapCache.UnknownMap; if (replay == null) { return; } try { if (map.Status != MapStatus.Available) { if (map.Status == MapStatus.DownloadAvailable) { LoadMapPreviewRules(map); } else if (Game.Settings.Game.AllowDownloading) { modData.MapCache.QueryRemoteMapDetails(services.MapRepository, new[] { map.Uid }, LoadMapPreviewRules); } } var players = replay.GameInfo.Players .GroupBy(p => p.Team) .OrderBy(g => g.Key); var teams = new Dictionary <string, IEnumerable <GameInformation.Player> >(); var noTeams = players.Count() == 1; foreach (var p in players) { var label = noTeams ? "Players" : p.Key == 0 ? "No Team" : "Team {0}".F(p.Key); teams.Add(label, p); } playerList.RemoveChildren(); foreach (var kv in teams) { var group = kv.Key; if (group.Length > 0) { var header = ScrollItemWidget.Setup(playerHeader, () => true, () => { }); header.Get <LabelWidget>("LABEL").GetText = () => group; playerList.AddChild(header); } foreach (var option in kv.Value) { var o = option; var color = o.Color; var item = ScrollItemWidget.Setup(playerTemplate, () => false, () => { }); var label = item.Get <LabelWidget>("LABEL"); var font = Game.Renderer.Fonts[label.Font]; var name = WidgetUtils.TruncateText(o.Name, label.Bounds.Width, font); label.GetText = () => name; label.GetColor = () => color; var flag = item.Get <ImageWidget>("FLAG"); flag.GetImageCollection = () => "flags"; var factionInfo = modData.DefaultRules.Actors["world"].TraitInfos <FactionInfo>(); flag.GetImageName = () => (factionInfo != null && factionInfo.Any(f => f.InternalName == o.FactionId)) ? o.FactionId : "Random"; playerList.AddChild(item); } } } catch (Exception e) { Log.Write("debug", "Exception while parsing replay: {0}", e); SelectReplay(null); } }
bool EvaluateReplayVisibility(ReplayMetadata replay) { // Game type if ((filter.Type == GameType.Multiplayer && replay.GameInfo.IsSinglePlayer) || (filter.Type == GameType.Singleplayer && !replay.GameInfo.IsSinglePlayer)) { return(false); } // Date type if (filter.Date != DateType.Any) { TimeSpan t; switch (filter.Date) { case DateType.Today: t = TimeSpan.FromDays(1d); break; case DateType.LastWeek: t = TimeSpan.FromDays(7d); break; case DateType.LastFortnight: t = TimeSpan.FromDays(14d); break; case DateType.LastMonth: default: t = TimeSpan.FromDays(30d); break; } if (replay.GameInfo.StartTimeUtc < DateTime.UtcNow - t) { return(false); } } // Duration if (filter.Duration != DurationType.Any) { var minutes = replay.GameInfo.Duration.TotalMinutes; switch (filter.Duration) { case DurationType.VeryShort: if (minutes >= 5) { return(false); } break; case DurationType.Short: if (minutes < 5 || minutes >= 20) { return(false); } break; case DurationType.Medium: if (minutes < 20 || minutes >= 60) { return(false); } break; case DurationType.Long: if (minutes < 60) { return(false); } break; } } // Map if (!string.IsNullOrEmpty(filter.MapName) && string.Compare(filter.MapName, replay.GameInfo.MapTitle, true) != 0) { return(false); } // Player if (!string.IsNullOrEmpty(filter.PlayerName)) { var player = replay.GameInfo.Players.FirstOrDefault(p => string.Compare(filter.PlayerName, p.Name, true) == 0); if (player == null) { return(false); } // Outcome if (filter.Outcome != WinState.Undefined && filter.Outcome != player.Outcome) { return(false); } // Faction if (!string.IsNullOrEmpty(filter.Faction) && string.Compare(filter.Faction, player.FactionName, true) != 0) { return(false); } } return(true); }
public ReplayBrowserLogic(Widget widget, Action onExit, Action onStart) { panel = widget; playerList = panel.Get <ScrollPanelWidget>("PLAYER_LIST"); playerHeader = playerList.Get <ScrollItemWidget>("HEADER"); playerTemplate = playerList.Get <ScrollItemWidget>("TEMPLATE"); playerList.RemoveChildren(); panel.Get <ButtonWidget>("CANCEL_BUTTON").OnClick = () => { Ui.CloseWindow(); onExit(); }; replayList = panel.Get <ScrollPanelWidget>("REPLAY_LIST"); var template = panel.Get <ScrollItemWidget>("REPLAY_TEMPLATE"); var mod = Game.modData.Manifest.Mod; var dir = new[] { Platform.SupportDir, "Replays", mod.Id, mod.Version }.Aggregate(Path.Combine); replayList.RemoveChildren(); if (Directory.Exists(dir)) { using (new Support.PerfTimer("Load replays")) { replays = Directory .GetFiles(dir, "*.rep") .Select((filename) => ReplayMetadata.Read(filename)) .Where((r) => r != null) .OrderByDescending(r => r.GameInfo.StartTimeUtc) .ToList(); } foreach (var replay in replays) { AddReplay(replay, template); } ApplyFilter(); } else { replays = new List <ReplayMetadata>(); } var watch = panel.Get <ButtonWidget>("WATCH_BUTTON"); watch.IsDisabled = () => selectedReplay == null || selectedReplay.GameInfo.MapPreview.Status != MapStatus.Available; watch.OnClick = () => { WatchReplay(); onStart(); }; panel.Get("REPLAY_INFO").IsVisible = () => selectedReplay != null; var preview = panel.Get <MapPreviewWidget>("MAP_PREVIEW"); preview.SpawnOccupants = () => selectedSpawns; preview.Preview = () => selectedReplay != null ? selectedReplay.GameInfo.MapPreview : null; var title = panel.GetOrNull <LabelWidget>("MAP_TITLE"); if (title != null) { title.GetText = () => selectedReplay != null ? selectedReplay.GameInfo.MapPreview.Title : null; } var type = panel.GetOrNull <LabelWidget>("MAP_TYPE"); if (type != null) { type.GetText = () => selectedReplay.GameInfo.MapPreview.Type; } panel.Get <LabelWidget>("DURATION").GetText = () => WidgetUtils.FormatTimeSeconds((int)selectedReplay.GameInfo.Duration.TotalSeconds); SetupFilters(); SetupManagement(); }
void SelectReplay(ReplayMetadata replay) { selectedReplay = replay; selectedSpawns = (selectedReplay != null) ? LobbyUtils.GetSpawnOccupants(selectedReplay.GameInfo.Players, selectedReplay.GameInfo.MapPreview) : new Dictionary <CPos, SpawnOccupant>(); if (replay == null) { return; } try { var players = replay.GameInfo.Players .GroupBy(p => p.Team) .OrderBy(g => g.Key); var teams = new Dictionary <string, IEnumerable <GameInformation.Player> >(); var noTeams = players.Count() == 1; foreach (var p in players) { var label = noTeams ? "Players" : p.Key == 0 ? "No Team" : "Team {0}".F(p.Key); teams.Add(label, p); } playerList.RemoveChildren(); foreach (var kv in teams) { var group = kv.Key; if (group.Length > 0) { var header = ScrollItemWidget.Setup(playerHeader, () => true, () => { }); header.Get <LabelWidget>("LABEL").GetText = () => group; playerList.AddChild(header); } foreach (var option in kv.Value) { var o = option; var color = o.Color.RGB; var item = ScrollItemWidget.Setup(playerTemplate, () => false, () => { }); var label = item.Get <LabelWidget>("LABEL"); label.GetText = () => o.Name; label.GetColor = () => color; var flag = item.Get <ImageWidget>("FLAG"); flag.GetImageCollection = () => "flags"; flag.GetImageName = () => o.FactionId; playerList.AddChild(item); } } } catch (Exception e) { Log.Write("debug", "Exception while parsing replay: {0}", e); SelectReplay(null); } }
internal Embed CreateEmbed(ReplayMetadata replayMetadata, string replayLink = null) { var mapEmbed = _mapToEmbedTransformer.CreateEmbed(replayMetadata.MapUid); var startTime = DateTime.ParseExact(replayMetadata.StartTimeUtc, "yyyy-MM-dd HH-mm-ss", new NumberFormatInfo()); var endTime = DateTime.ParseExact(replayMetadata.EndTimeUtc, "yyyy-MM-dd HH-mm-ss", new NumberFormatInfo()); var fields = new List <EmbedFieldBuilder> { new EmbedFieldBuilder { IsInline = true, Name = "Mod:", Value = replayMetadata.Mod }, new EmbedFieldBuilder { IsInline = true, Name = "Version:", Value = replayMetadata.Version }, new EmbedFieldBuilder { IsInline = true, Name = "Map:", Value = mapEmbed == null ? $"{replayMetadata.MapTitle}" : $"[{replayMetadata.MapTitle}]({mapEmbed.Url})" }, new EmbedFieldBuilder { IsInline = false, Name = "Duration:", Value = $"||{endTime - startTime}||" } }; var playersByTeam = replayMetadata.Players.Values.GroupBy(x => x.Team).OrderBy(x => x.Key); var teamsCountStr = new List <string>(); foreach (var kvp in playersByTeam) { if (kvp.Key == 0) { teamsCountStr.AddRange(kvp.Select(_ => "1")); fields.Add(new EmbedFieldBuilder { IsInline = true, Name = "No team:", Value = string.Join("\n", kvp.Select(x => $"{x.Name} [{x.FactionName}]")) }); } else { teamsCountStr.Add(kvp.Count().ToString()); fields.Add(new EmbedFieldBuilder { IsInline = true, Name = $"Team {kvp.Key}", Value = string.Join("\n", kvp.Select(x => $"{x.Name} [{x.FactionName}]")) }); } } var winners = replayMetadata.Players.Values.Where(x => x.Outcome == "Won").ToArray(); string winnerString; if (winners.Length == 0) { winnerString = "Winner is unknown."; } else if (winners.Length == 1) { winnerString = $"Winner is {winners[0].Name}"; } else { winnerString = $"Winners are {string.Join(", ", winners.Select(x => x.Name))}"; } var embed = new EmbedBuilder { Title = replayMetadata.FileName, ThumbnailUrl = mapEmbed?.Thumbnail?.Url, Url = replayLink, Description = $"This is a {string.Join("vs", teamsCountStr)} game. || {winnerString} ||", Footer = new EmbedFooterBuilder { Text = $"Played {replayMetadata.StartTimeUtc}" }, Color = mapEmbed?.Color ?? Color.Default, Fields = fields }; return(embed.Build()); }
public ReplayConnection(string replayFilename) { Filename = replayFilename; FinalGameTick = ReplayMetadata.Read(replayFilename).GameInfo.FinalGameTick; // Parse replay data into a struct that can be fed to the game in chunks // to avoid issues with all immediate orders being resolved on the first tick. using (var rs = File.OpenRead(replayFilename)) { var packets = new List <(int ClientId, byte[] Packet)>(); var chunk = new Chunk(); while (rs.Position < rs.Length) { var client = rs.ReadInt32(); if (client == ReplayMetadata.MetaStartMarker) { break; } var packetLen = rs.ReadInt32(); var packet = rs.ReadBytes(packetLen); var frame = BitConverter.ToInt32(packet, 0); packets.Add((client, packet)); if (frame != int.MaxValue && (!lastClientsFrame.ContainsKey(client) || frame > lastClientsFrame[client])) { lastClientsFrame[client] = frame; } if (packet.Length > 4 && (packet[4] == (byte)OrderType.Disconnect || packet[4] == (byte)OrderType.SyncHash)) { continue; } if (frame == 0) { // Parse replay metadata from orders stream var orders = packet.ToOrderList(null); foreach (var o in orders) { if (o.OrderString == "StartGame") { IsValid = true; } else if (o.OrderString == "SyncInfo" && !IsValid) { LobbyInfo = Session.Deserialize(o.TargetString); } } } else { // Regular order - finalize the chunk chunk.Frame = frame; chunk.Packets = packets.ToArray(); packets.Clear(); chunks.Enqueue(chunk); chunk = new Chunk(); TickCount = Math.Max(TickCount, frame); } } var lastClientToDisconnect = lastClientsFrame.MaxBy(kvp => kvp.Value).Key; // 2nd parse : replace all disconnect packets without frame with real // disconnect frame // NOTE: to modify/remove if a reconnect feature is set foreach (var tmpChunk in chunks) { foreach (var tmpPacketPair in tmpChunk.Packets) { var client = tmpPacketPair.ClientId; // Don't replace the final disconnection packet - we still want this to end the replay. if (client == lastClientToDisconnect) { continue; } var packet = tmpPacketPair.Packet; if (packet.Length == 5 && packet[4] == (byte)OrderType.Disconnect) { var lastClientFrame = lastClientsFrame[client]; var lastFramePacket = BitConverter.GetBytes(lastClientFrame); Array.Copy(lastFramePacket, packet, lastFramePacket.Length); } } } } ordersFrame = LobbyInfo.GlobalSettings.OrderLatency; }