static void Convert() { LongEventHandler.QueueLongEvent(() => { try { const string suffix = "-preconvert"; var saveName = $"{GenFile.SanitizedFileName(Multiplayer.session.gameName)}{suffix}"; new FileInfo(Path.Combine(Multiplayer.ReplaysDir, saveName + ".zip")).Delete(); Replay.ForSaving(saveName).WriteCurrentData(); } catch (Exception e) { Log.Warning($"Convert to singleplayer failed to write pre-convert file: {e}"); } Find.GameInfo.permadeathMode = false; HostUtil.SetAllUniqueIds(Multiplayer.GlobalIdBlock.Current); Multiplayer.StopMultiplayer(); var doc = SaveLoad.SaveGameToDoc(); MemoryUtility.ClearAllMapsAndWorld(); Current.Game = new Game(); Current.Game.InitData = new GameInitData(); Current.Game.InitData.gameToLoad = "play"; LoadPatch.gameToLoad = new TempGameData(doc, new byte[0]); }, "Play", "MpConvertingToSp", true, null); }
public override bool Accept() { if (curName.Length == 0) { return(false); } try { if (Multiplayer.LocalServer != null && fullSave) { Multiplayer.LocalServer.DoAutosave(curName); } else { new FileInfo(Path.Combine(Multiplayer.ReplaysDir, $"{curName}.zip")).Delete(); Replay.ForSaving(curName).WriteCurrentData(); } Close(); Messages.Message("MpReplaySaved".Translate(), MessageTypeDefOf.SilentInput, false); } catch (Exception e) { Log.Error($"Exception saving replay {e}"); } return(true); }
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)); }
private bool TrySave() { if (curName.Length == 0) { return(false); } try { new FileInfo(Path.Combine(Multiplayer.ReplaysDir, $"{curName}.zip")).Delete(); Replay.ForSaving(curName).WriteCurrentData(); Close(); Messages.Message("MpReplaySaved".Translate(), MessageTypeDefOf.SilentInput, false); } catch (Exception e) { Log.Error($"Exception saving replay {e}"); } return(true); }
/// <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)); }
/// <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)); }