Esempio n. 1
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));
        }
Esempio n. 2
0
        private static void HandleCommandLine()
        {
            if (GenCommandLine.TryGetCommandLineArg("connect", out string addressPort) && Multiplayer.restartConnect == null)
            {
                int port = MultiplayerServer.DefaultPort;

                var split = addressPort.Split(':');
                if (split.Length == 0)
                {
                    addressPort = "127.0.0.1";
                }
                else if (split.Length >= 1)
                {
                    addressPort = split[0];
                }

                if (split.Length == 2)
                {
                    int.TryParse(split[1], out port);
                }

                DoubleLongEvent(() => ClientUtil.TryConnectWithWindow(addressPort, port, false), "Connecting");
            }

            if (GenCommandLine.CommandLineArgPassed("arbiter"))
            {
                Multiplayer.username = "******";
                Prefs.VolumeGame     = 0;
            }

            if (GenCommandLine.TryGetCommandLineArg("replay", out string replay))
            {
                DoubleLongEvent(() =>
                {
                    Replay.LoadReplay(Replay.ReplayFile(replay), true, () =>
                    {
                        var rand = Find.Maps.Select(m => m.AsyncTime().randState).Select(s => $"{s} {(uint)s} {s >> 32}");

                        Log.Message($"timer {TickPatch.Timer}");
                        Log.Message($"world rand {Multiplayer.WorldComp.randState} {(uint)Multiplayer.WorldComp.randState} {Multiplayer.WorldComp.randState >> 32}");
                        Log.Message($"map rand {rand.ToStringSafeEnumerable()} | {Find.Maps.Select(m => m.AsyncTime().mapTicks).ToStringSafeEnumerable()}");

                        Application.Quit();
                    });
                }, "Replay");
            }

            if (GenCommandLine.CommandLineArgPassed("printsync"))
            {
                ExtendDirectXmlSaver.extend = true;
                DirectXmlSaver.SaveDataObject(new SyncContainer(), "SyncHandlers.xml");
                ExtendDirectXmlSaver.extend = false;
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Called by <see cref="AddClientOpinionAndCheckDesync"/> if the newly added opinion doesn't match with what other ones.
        /// </summary>
        /// <param name="oldOpinion">The first up-to-date client opinion present in <see cref="knownClientOpinions"/>, that disagreed with the new one</param>
        /// <param name="newOpinion">The opinion passed to <see cref="AddClientOpinionAndCheckDesync"/> that disagreed with the currently known opinions.</param>
        /// <param name="desyncMessage">The error message that explains exactly what desynced.</param>
        private void HandleDesync(ClientSyncOpinion oldOpinion, ClientSyncOpinion newOpinion, string desyncMessage)
        {
            Multiplayer.Client.Send(Packets.Client_Desynced);

            //Identify which of the two sync infos is local, and which is the remote.
            var local  = oldOpinion.isLocalClientsOpinion ? oldOpinion : newOpinion;
            var remote = !oldOpinion.isLocalClientsOpinion ? oldOpinion : newOpinion;

            //Print arbiter desync stacktrace if it exists
            if (local.desyncStackTraces.Any())
            {
                SaveStackTracesToDisk(local, remote);
            }

            try
            {
                //Get the filename of the next desync file to create.
                var desyncFilePath = FindFileNameForNextDesyncFile();

                //Initialize the Replay object.
                var replay = Replay.ForSaving(Replay.ReplayFile(desyncFilePath, Multiplayer.DesyncsDir));

                //Write the universal replay data (i.e. world and map folders, and the info file) so this desync can be reviewed as a standard replay.
                replay.WriteCurrentData();

                //Dump our current game object.
                var savedGame = ScribeUtil.WriteExposable(Current.Game, "game", true, ScribeMetaHeaderUtility.WriteMetaHeader);

                using (var zip = replay.ZipFile)
                    using (var desyncReport = new ZipFile()) //Create a desync report for uploading to the reports server.
                    {
                        //Write the local sync data
                        var syncLocal = local.Serialize();
                        zip.AddEntry("sync_local", syncLocal);
                        desyncReport.AddEntry("sync_local", syncLocal);

                        //Write the remote sync data
                        var syncRemote = remote.Serialize();
                        zip.AddEntry("sync_remote", syncRemote);
                        desyncReport.AddEntry("sync_remote", syncRemote);

                        //Dump the entire save file to the zip.
                        zip.AddEntry("game_snapshot", savedGame);
//                    desyncReport.AddEntry("game_snapshot", savedGame); //This ends up being about 15MB, we really don't want that.

                        //Add local stack traces
                        zip.AddEntry("local_stacks", GetDesyncStackTraces(local, remote, out _));
                        desyncReport.AddEntry("local_stacks", GetDesyncStackTraces(local, remote, out _));

                        //Add remote's stack traces
                        zip.AddEntry("remote_stacks", GetDesyncStackTraces(remote, local, out _));
                        desyncReport.AddEntry("remote_stacks", GetDesyncStackTraces(remote, local, out _));

                        //Prepare the desync info
                        var desyncInfo = new StringBuilder();

                        desyncInfo.AppendLine("###Tick Data###")
                        .AppendLine($"Arbiter Connected And Playing|||{Multiplayer.session.ArbiterPlaying}")
                        .AppendLine($"Last Valid Tick - Local|||{lastValidTick}")
                        .AppendLine($"Arbiter Present on Last Tick|||{arbiterWasPlayingOnLastValidTick}")
                        .AppendLine("\n###Version Data###")
                        .AppendLine($"Multiplayer Mod Version|||{MpVersion.Version}")
                        .AppendLine($"Rimworld Version and Rev|||{VersionControl.CurrentVersionStringWithRev}")
                        .AppendLine("\n###Debug Options###")
                        .AppendLine($"Multiplayer Debug Build - Client|||{MpVersion.IsDebug}")
                        .AppendLine($"Multiplayer Debug Build - Host|||{Multiplayer.WorldComp.debugMode}")
                        .AppendLine($"Rimworld Developer Mode - Client|||{Prefs.DevMode}")
                        .AppendLine("\n###Server Info###")
                        .AppendLine($"Player Count|||{Multiplayer.session.players.Count}")
                        .AppendLine("\n###CPU Info###")
                        .AppendLine($"Processor Name|||{SystemInfo.processorType}")
                        .AppendLine($"Processor Speed (MHz)|||{SystemInfo.processorFrequency}")
                        .AppendLine($"Thread Count|||{SystemInfo.processorCount}")
                        .AppendLine("\n###GPU Info###")
                        .AppendLine($"GPU Family|||{SystemInfo.graphicsDeviceVendor}")
                        .AppendLine($"GPU Type|||{SystemInfo.graphicsDeviceType}")
                        .AppendLine($"GPU Name|||{SystemInfo.graphicsDeviceName}")
                        .AppendLine($"GPU VRAM|||{SystemInfo.graphicsMemorySize}")
                        .AppendLine("\n###RAM Info###")
                        .AppendLine($"Physical Memory Present|||{SystemInfo.systemMemorySize}")
                        .AppendLine("\n###OS Info###")
                        .AppendLine($"OS Type|||{SystemInfo.operatingSystemFamily}")
                        .AppendLine($"OS Name and Version|||{SystemInfo.operatingSystem}");

                        //Save debug info to the zip
                        zip.AddEntry("desync_info", desyncInfo.ToString());
                        desyncReport.AddEntry("desync_info", desyncInfo.ToString());

                        zip.Save();

                        //Add the basic info to the report
                        desyncReport.AddEntry("info", zip["info"].GetBytes());

                        desyncReport.AddEntry("ign", Multiplayer.session.GetPlayerInfo(Multiplayer.session.playerId).username);
                        desyncReport.AddEntry("steamName", SteamUtility.SteamPersonaName);

                        //Report desync to the server
                        var request = (HttpWebRequest)WebRequest.Create("https://multiplayer.samboycoding.me/api/desync/upload");
//                    var request = (HttpWebRequest) WebRequest.Create("http://localhost:4193/api/desync/upload");
                        request.Method      = "POST";
                        request.ContentType = "application/zip";

                        using (var stream = new MemoryStream())
                        {
                            desyncReport.Save(stream);

                            stream.Seek(0, SeekOrigin.Begin);
                            request.ContentLength = stream.Length;
                            var data = new byte[stream.Length];
                            stream.Read(data, 0, data.Length);

                            using (var outStream = request.GetRequestStream())
                                outStream.Write(data, 0, data.Length);
                        }

                        //TODO: Some user interaction here?
                        try
                        {
                            using (var response = (HttpWebResponse)request.GetResponse())
                            {
                                if (response.StatusCode != HttpStatusCode.OK)
                                {
                                    Log.Error("Failed to report desync; Status code " + response.StatusCode);
                                }
                                else
                                {
                                    using (var stream = response.GetResponseStream())
                                        using (var reader = new StreamReader(stream))
                                        {
                                            var desyncReportId = reader.ReadToEnd();
                                            Log.Message("Desync Reported with ID " + desyncReportId);
                                        }
                                }
                            }
                        }
                        catch (WebException e)
                        {
                            Log.Error("Failed to report desync; " + e.Message);
                        }
                    }
            }
            catch (Exception e)
            {
                Log.Error($"Exception writing desync info: {e}");
            }

            Find.WindowStack.windows.Clear();
            Find.WindowStack.Add(new DesyncedWindow(desyncMessage));
        }
Esempio n. 4
0
        /// <summary>
        /// Called by <see cref="AddClientOpinionAndCheckDesync"/> if the newly added opinion doesn't match with what other ones.
        /// </summary>
        /// <param name="oldOpinion">The first up-to-date client opinion present in <see cref="knownClientOpinions"/>, that disagreed with the new one</param>
        /// <param name="newOpinion">The opinion passed to <see cref="AddClientOpinionAndCheckDesync"/> that disagreed with the currently known opinions.</param>
        /// <param name="desyncMessage">The error message that explains exactly what desynced.</param>
        private void HandleDesync(ClientSyncOpinion oldOpinion, ClientSyncOpinion newOpinion, string desyncMessage)
        {
            Multiplayer.Client.Send(Packets.Client_Desynced);

            //Identify which of the two sync infos is local, and which is the remote.
            var local  = oldOpinion.isLocalClientsOpinion ? oldOpinion : newOpinion;
            var remote = !oldOpinion.isLocalClientsOpinion ? oldOpinion : newOpinion;

            //Print arbiter desync stacktrace if it exists
            if (local.desyncStackTraces.Any())
            {
                SaveStackTracesToDisk(local, remote);
            }

            try
            {
                //Get the filename of the next desync file to create.
                var desyncFilePath = FindFileNameForNextDesyncFile();

                //Initialize the Replay object.
                var replay = Replay.ForSaving(Replay.ReplayFile(desyncFilePath, Multiplayer.DesyncsDir));

                //Write the universal replay data (i.e. world and map folders, and the info file) so this desync can be reviewed as a standard replay.
                replay.WriteCurrentData();

                //Dump our current game object.
                var savedGame = ScribeUtil.WriteExposable(Current.Game, "game", true, ScribeMetaHeaderUtility.WriteMetaHeader);

                using (var zip = replay.ZipFile)
                {
                    //Write the local sync data
                    var syncLocal = local.Serialize();
                    zip.AddEntry("sync_local", syncLocal);

                    //Write the remote sync data
                    var syncRemote = remote.Serialize();
                    zip.AddEntry("sync_remote", syncRemote);

                    //Dump the entire save file to the zip.
                    zip.AddEntry("game_snapshot", savedGame);

                    //Prepare the desync info
                    var desyncInfo = new StringBuilder();

                    desyncInfo.AppendLine("###Tick Data###")
                    .AppendLine($"Arbiter Connected And Playing|||{Multiplayer.session.ArbiterPlaying}")
                    .AppendLine($"Last Valid Tick - Local|||{lastValidTick}")
                    .AppendLine($"Last Valid Tick - Arbiter|||{arbiterWasPlayingOnLastValidTick}")
                    .AppendLine("\n###Version Data###")
                    .AppendLine($"Multiplayer Mod Version|||{MpVersion.Version}")
                    .AppendLine($"Rimworld Version and Rev|||{VersionControl.CurrentVersionStringWithRev}")
                    .AppendLine("\n###Debug Options###")
                    .AppendLine($"Multiplayer Debug Build - Client|||{MpVersion.IsDebug}")
                    .AppendLine($"Multiplayer Debug Build - Host|||{Multiplayer.WorldComp.debugMode}")
                    .AppendLine($"Rimworld Developer Mode - Client|||{Prefs.DevMode}")
                    .AppendLine("\n###Server Info###")
                    .AppendLine($"Player Count|||{Multiplayer.session.players.Count}")
                    .AppendLine("\n###CPU Info###")
                    .AppendLine($"Processor Name|||{SystemInfo.processorType}")
                    .AppendLine($"Processor Speed (MHz)|||{SystemInfo.processorFrequency}")
                    .AppendLine($"Thread Count|||{SystemInfo.processorCount}")
                    .AppendLine("\n###GPU Info###")
                    .AppendLine($"GPU Family|||{SystemInfo.graphicsDeviceVendor}")
                    .AppendLine($"GPU Type|||{SystemInfo.graphicsDeviceType}")
                    .AppendLine($"GPU Name|||{SystemInfo.graphicsDeviceName}")
                    .AppendLine($"GPU VRAM|||{SystemInfo.graphicsMemorySize}")
                    .AppendLine("\n###RAM Info###")
                    .AppendLine($"Physical Memory Present|||{SystemInfo.systemMemorySize}")
                    .AppendLine("\n###OS Info###")
                    .AppendLine($"OS Type|||{SystemInfo.operatingSystemFamily}")
                    .AppendLine($"OS Name and Version|||{SystemInfo.operatingSystem}");

                    //Save debug info to the zip
                    zip.AddEntry("desync_info", desyncInfo.ToString());

                    zip.Save();
                }
            }
            catch (Exception e)
            {
                Log.Error($"Exception writing desync info: {e}");
            }

            Find.WindowStack.windows.Clear();
            Find.WindowStack.Add(new DesyncedWindow(desyncMessage));
        }