Exemple #1
0
        public string Compare(SyncInfo other)
        {
            if (!maps.Select(m => m.mapId).SequenceEqual(other.maps.Select(m => m.mapId)))
            {
                return($"Map instances don't match");
            }

            for (int i = 0; i < maps.Count; i++)
            {
                if (!maps[i].map.SequenceEqual(other.maps[i].map))
                {
                    return($"Wrong random state on map {maps[i].mapId}");
                }
            }

            if (!world.SequenceEqual(other.world))
            {
                return("Wrong random state for the world");
            }

            if (!cmds.SequenceEqual(other.cmds))
            {
                return("Random state from commands doesn't match");
            }

            if (!simulating && !other.simulating && traceHashes.Any() && other.traceHashes.Any() && !traceHashes.SequenceEqual(other.traceHashes))
            {
                return("Trace hashes don't match");
            }

            return(null);
        }
Exemple #2
0
        private void OnDesynced(SyncInfo one, SyncInfo two, string error)
        {
            if (TickPatch.Skipping)
            {
                return;
            }

            Multiplayer.Client.Send(Packets.Client_Desynced);

            var local  = one.local ? one : two;
            var remote = !one.local ? one : two;

            if (local.traces.Any())
            {
                PrintTrace(local, remote);
            }

            try
            {
                var desyncFile = PrepareNextDesyncFile();

                var replay = Replay.ForSaving(Replay.ReplayFile(desyncFile, Multiplayer.DesyncsDir));
                replay.WriteCurrentData();

                var savedGame = ScribeUtil.WriteExposable(Verse.Current.Game, "game", true, ScribeMetaHeaderUtility.WriteMetaHeader);

                using (var zip = replay.ZipFile)
                {
                    zip.AddEntry("sync_local", local.Serialize());
                    zip.AddEntry("sync_remote", remote.Serialize());
                    zip.AddEntry("game_snapshot", savedGame);

                    zip.AddEntry("static_fields", MpDebugTools.StaticFieldsToString());

                    var desyncInfo = new ByteWriter();
                    desyncInfo.WriteBool(Multiplayer.session.ArbiterPlaying);
                    desyncInfo.WriteInt32(lastValidTick);
                    desyncInfo.WriteBool(lastValidArbiter);
                    desyncInfo.WriteString(MpVersion.Version);
                    desyncInfo.WriteBool(MpVersion.IsDebug);
                    desyncInfo.WriteBool(Prefs.DevMode);
                    desyncInfo.WriteInt32(Multiplayer.session.players.Count);
                    desyncInfo.WriteBool(Multiplayer.WorldComp.debugMode);

                    zip.AddEntry("desync_info", desyncInfo.ToArray());
                    zip.Save();
                }
            }
            catch (Exception e)
            {
                Log.Error($"Exception writing desync info: {e}");
            }

            Find.WindowStack.windows.Clear();
            Find.WindowStack.Add(new DesyncedWindow(error));
        }
Exemple #3
0
        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;
                    }
                }
            }
        }
Exemple #4
0
        private void PrintTrace(SyncInfo local, SyncInfo remote)
        {
            Log.Message($"Printing {local.traces.Count} traces");

            int diffAt = -1;
            int count  = Math.Min(local.traceHashes.Count, remote.traceHashes.Count);

            for (int i = 0; i < count; i++)
            {
                if (local.traceHashes[i] != remote.traceHashes[i])
                {
                    diffAt = i;
                    break;
                }
            }

            if (diffAt == -1)
            {
                diffAt = count;
            }

            File.WriteAllText("local_traces.txt", local.TracesToString(diffAt - 40, diffAt + 40));
            Multiplayer.Client.Send(Packets.Client_Debug, local.startTick, diffAt - 40, diffAt + 40);
        }
Exemple #5
0
        public static string Get(Replay replay)
        {
            var text = new StringBuilder();

            using (var zip = replay.ZipFile)
            {
                try
                {
                    text.AppendLine($"[header]");

                    using (var reader = new XmlTextReader(new MemoryStream(zip["game_snapshot"].GetBytes())))
                    {
                        reader.ReadToNextElement();
                        reader.ReadToNextElement();

                        text.AppendLine(reader.ReadOuterXml());
                    }
                }
                catch (Exception e)
                {
                    text.AppendLine(e.Message);
                }

                text.AppendLine();

                try
                {
                    text.AppendLine("[info]");
                    text.AppendLine(zip["info"].GetString());
                }
                catch { }

                text.AppendLine();

                SyncInfo local = null;
                try
                {
                    local = PrintSyncInfo(text, zip, "sync_local");
                }
                catch { }

                text.AppendLine();

                SyncInfo remote = null;
                try
                {
                    remote = PrintSyncInfo(text, zip, "sync_remote");
                }
                catch { }

                text.AppendLine();

                try
                {
                    text.AppendLine("[desync_info]");
                    var desyncInfo = new ByteReader(zip["desync_info"].GetBytes());
                    text.AppendLine($"Arbiter online: {desyncInfo.ReadBool()}");
                    text.AppendLine($"Last valid tick: {desyncInfo.ReadInt32()}");
                    text.AppendLine($"Last valid arbiter online: {desyncInfo.ReadBool()}");
                    text.AppendLine($"Mod version: {desyncInfo.ReadString()}");
                    text.AppendLine($"Mod is debug: {desyncInfo.ReadBool()}");
                    text.AppendLine($"Dev mode: {desyncInfo.ReadBool()}");
                    text.AppendLine($"Player count: {desyncInfo.ReadInt32()}");
                    text.AppendLine($"Game debug mode: {desyncInfo.ReadBool()}");
                }
                catch { }

                text.AppendLine();

                if (local != null && remote != null)
                {
                    text.AppendLine("[compare]");

                    for (int i = 0; i < Math.Min(local.maps.Count, remote.maps.Count); i++)
                    {
                        var  localMap  = local.maps[i].map;
                        var  remoteMap = remote.maps[i].map;
                        bool equal     = localMap.SequenceEqual(remoteMap);
                        text.AppendLine($"Map {local.maps[i].mapId}: {equal}");

                        if (!equal)
                        {
                            for (int j = 0; j < Math.Min(localMap.Count, remoteMap.Count); j++)
                            {
                                text.AppendLine($"{localMap[j]} {remoteMap[j]} {(localMap[j] != remoteMap[j] ? "x" : "")}");
                            }
                        }
                    }

                    text.AppendLine($"World: {local.world.SequenceEqual(remote.world)}");
                    text.AppendLine($"Cmds: {local.cmds.SequenceEqual(remote.cmds)}");
                }

                text.AppendLine();

                try
                {
                    text.AppendLine("[map_cmds]");
                    foreach (var cmd in Replay.DeserializeCmds(zip["maps/000_0_cmds"].GetBytes()))
                    {
                        PrintCmdInfo(text, cmd);
                    }
                }
                catch { }

                text.AppendLine();

                try
                {
                    text.AppendLine("[world_cmds]");
                    foreach (var cmd in Replay.DeserializeCmds(zip["world/000_cmds"].GetBytes()))
                    {
                        PrintCmdInfo(text, cmd);
                    }
                }
                catch { }
            }

            return(text.ToString());

            void PrintCmdInfo(StringBuilder builder, ScheduledCommand cmd)
            {
                builder.Append($"{cmd.type} {cmd.ticks} {cmd.mapId} {cmd.factionId}");

                if (cmd.type == CommandType.Sync)
                {
                    builder.Append($" {Sync.handlers[BitConverter.ToInt32(cmd.data, 0)]}");
                }

                builder.AppendLine();
            }

            SyncInfo PrintSyncInfo(StringBuilder builder, ZipFile zip, string file)
            {
                builder.AppendLine($"[{file}]");

                var sync = SyncInfo.Deserialize(new ByteReader(zip[file].GetBytes()));

                builder.AppendLine($"Start: {sync.startTick}");
                builder.AppendLine($"Was simulating: {sync.simulating}");
                builder.AppendLine($"Map count: {sync.maps.Count}");
                builder.AppendLine($"Last map state: {sync.maps.Select(m => $"{m.mapId}/{m.map.LastOrDefault()}/{m.map.Count}").ToStringSafeEnumerable()}");
                builder.AppendLine($"Last world state: {sync.world.LastOrDefault()}/{sync.world.Count}");
                builder.AppendLine($"Last cmd state: {sync.cmds.LastOrDefault()}/{sync.cmds.Count}");
                builder.AppendLine($"Trace hashes: {sync.traceHashes.Count}");

                return(sync);
            }
        }
 public void HandleDesyncCheck(ByteReader data)
 {
     Multiplayer.game?.sync.Add(SyncInfo.Deserialize(data));
 }
Exemple #7
0
        public static string Get(Replay replay)
        {
            var text = new StringBuilder();

            using (var zip = replay.ZipFile)
            {
                try
                {
                    text.AppendLine("[info]");
                    text.AppendLine(zip["info"].GetString());
                    text.AppendLine();
                }
                catch { }

                SyncInfo local = null;
                try
                {
                    local = PrintSyncInfo(text, zip, "sync_local");
                }
                catch { }

                SyncInfo remote = null;
                try
                {
                    remote = PrintSyncInfo(text, zip, "sync_remote");
                }
                catch { }

                try
                {
                    text.AppendLine("[desync_info]");
                    var desyncInfo = new ByteReader(zip["desync_info"].GetBytes());
                    text.AppendLine($"Arbiter online: {desyncInfo.ReadBool()}");
                    text.AppendLine($"Last valid tick: {desyncInfo.ReadInt32()}");
                    text.AppendLine($"Last valid arbiter online: {desyncInfo.ReadBool()}");
                    text.AppendLine($"Mod version: {desyncInfo.ReadString()}");
                    text.AppendLine($"Mod is debug: {desyncInfo.ReadBool()}");
                    text.AppendLine($"Dev mode: {desyncInfo.ReadBool()}");
                    text.AppendLine();
                }
                catch { }

                if (local != null && remote != null)
                {
                    text.AppendLine("[compare]");

                    for (int i = 0; i < Math.Min(local.maps.Count, remote.maps.Count); i++)
                    {
                        var  localMap  = local.maps[i].map;
                        var  remoteMap = remote.maps[i].map;
                        bool equal     = localMap.SequenceEqual(remoteMap);
                        text.AppendLine($"Map {local.maps[i].mapId}: {equal}");

                        if (!equal)
                        {
                            for (int j = 0; j < Math.Min(localMap.Count, remoteMap.Count); j++)
                            {
                                text.AppendLine($"{localMap[j]} {remoteMap[j]} {(localMap[j] != remoteMap[j] ? "x" : "")}");
                            }
                        }
                    }

                    text.AppendLine($"World: {local.world.SequenceEqual(remote.world)}");
                    text.AppendLine($"Cmds: {local.cmds.SequenceEqual(remote.cmds)}");
                }
            }

            return(text.ToString());

            SyncInfo PrintSyncInfo(StringBuilder builder, ZipFile zip, string file)
            {
                builder.AppendLine($"[{file}]");

                var sync = SyncInfo.Deserialize(new ByteReader(zip[file].GetBytes()));

                builder.AppendLine($"Start: {sync.startTick}");
                builder.AppendLine($"Map count: {sync.maps.Count}");
                builder.AppendLine($"Last map state: {sync.maps.Select(m => $"{m.mapId}/{m.map.LastOrDefault()}/{m.map.Count}").ToStringSafeEnumerable()}");
                builder.AppendLine($"Last world state: {sync.world.LastOrDefault()}/{sync.world.Count}");
                builder.AppendLine($"Last cmd state: {sync.cmds.LastOrDefault()}/{sync.cmds.Count}");
                builder.AppendLine($"Trace hashes: {sync.traceHashes.Count}");

                builder.AppendLine();

                return(sync);
            }
        }
Exemple #8
0
 private void PrintTrace(SyncInfo local, SyncInfo remote)
 {
     File.WriteAllText("host_traces.txt", local.traces.Join(delimiter: "\n\n"));
     Multiplayer.Client.Send(Packets.Client_Debug, local.startTick);
 }
Exemple #9
0
 private void PrintTrace(SyncInfo local, SyncInfo remote)
 {
     File.WriteAllText("host_traces.txt", local.TracesToString());
     Multiplayer.Client.Send(Packets.Client_Debug, local.startTick);
 }