diff --git a/Source/Client/MultiplayerStatic.cs b/Source/Client/MultiplayerStatic.cs index ffd5f290..75f5638b 100644 --- a/Source/Client/MultiplayerStatic.cs +++ b/Source/Client/MultiplayerStatic.cs @@ -42,6 +42,8 @@ public static class MultiplayerStatic public static readonly Texture2D GiftModeIcon = ContentFinder.Get("UI/Buttons/GiftMode"); public static readonly Texture2D TradeModeIcon = ContentFinder.Get("UI/Buttons/TradeMode"); + public const string MpHostReplayCmdLineArg = "mphostreplay"; + static MultiplayerStatic() { Native.InitLmfPtr( @@ -263,6 +265,11 @@ void TickBatch() DirectXmlSaver.SaveDataObject(new SyncContainer(), "SyncHandlers.xml"); ExtendDirectXmlSaver.extend = false; } + + if (GenCommandLine.TryGetCommandLineArg(MpHostReplayCmdLineArg, out var path)) + { + DoubleLongEvent(() => HostWindow.VerifyAndOpen(path), "Loading"); + } } public class SyncContainer diff --git a/Source/Client/Windows/HostWindow.cs b/Source/Client/Windows/HostWindow.cs index 34beae3e..ff79b7f8 100644 --- a/Source/Client/Windows/HostWindow.cs +++ b/Source/Client/Windows/HostWindow.cs @@ -2,6 +2,7 @@ using RimWorld; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using UnityEngine; using Verse; @@ -34,6 +35,16 @@ enum Tab private ServerSettings serverSettings; + public static void VerifyAndOpen(string path) + { + FileInfo fileInfo = new(path); + var saveFile = SaveFile.ReadMpSave(fileInfo); + ServerBrowser.CheckGameVersionAndMods( + saveFile, + () => { Find.WindowStack.Add(new HostWindow(saveFile) { returnToServerBrowser = false }); } + ); + } + public HostWindow(SaveFile file = null) { closeOnAccept = false; diff --git a/Source/Client/Windows/SaveFileReader.cs b/Source/Client/Windows/SaveFileReader.cs index fcda2dcc..e8ea9a19 100644 --- a/Source/Client/Windows/SaveFileReader.cs +++ b/Source/Client/Windows/SaveFileReader.cs @@ -1,3 +1,4 @@ +#nullable enable using Multiplayer.Common; using RimWorld; using System; @@ -14,17 +15,17 @@ namespace Multiplayer.Client { public class SaveFileReader { - public List SpSaves { get; private set; } - public List MpSaves { get; private set; } + public List SpSaves { get; private set; } = []; + public List MpSaves { get; private set; } = []; private ConcurrentDictionary data = new(); - private Task spTask, mpTask; + private Task? spTask, mpTask; public void StartReading() { SpSaves = GenFilePaths.AllSavedGameFiles.OrderByDescending(f => f.LastWriteTime).ToList(); - var replaysDir = new DirectoryInfo(GenFilePaths.FolderUnderSaveData("MpReplays")); + var replaysDir = new DirectoryInfo(Multiplayer.ReplaysDir); var toRead = replaysDir.GetFiles("*.zip", MpVersion.IsDebug ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly); MpSaves = toRead.OrderByDescending(f => f.LastWriteTime).ToList(); @@ -37,18 +38,15 @@ public void StartReading() public void WaitTasks() { - spTask.Wait(); - mpTask.Wait(); + spTask?.Wait(); + mpTask?.Wait(); } private void ReadSpSave(FileInfo file) { try { - var saveFile = new SaveFile(Path.GetFileNameWithoutExtension(file.Name), false, file); - using var stream = file.OpenRead(); - ReadSaveInfo(stream, saveFile); - data[file] = saveFile; + data[file] = SaveFile.ReadSpSave(file); } catch (Exception ex) { @@ -60,32 +58,8 @@ private void ReadMpSave(FileInfo file) { try { - var displayName = Path.ChangeExtension(Path.GetRelativePath(Multiplayer.ReplaysDir, file.FullName), null); - var saveFile = new SaveFile(displayName, true, file); - - var replay = Replay.ForLoading(file); - if (!replay.LoadInfo()) return; - - saveFile.gameName = replay.info.name; - saveFile.protocol = replay.info.protocol; - saveFile.replaySections = replay.info.sections.Count; - - if (!replay.info.rwVersion.NullOrEmpty()) - { - saveFile.rwVersion = replay.info.rwVersion; - saveFile.modIds = replay.info.modIds.ToArray(); - saveFile.modNames = replay.info.modNames.ToArray(); - saveFile.asyncTime = replay.info.asyncTime; - saveFile.multifaction = replay.info.multifaction; - } - else - { - using var zip = replay.OpenZipRead(); - using var stream = zip.GetEntry("world/000_save")!.Open(); - ReadSaveInfo(stream, saveFile); - } - - data[file] = saveFile; + var saveFile = SaveFile.ReadMpSave(file); + if (saveFile != null) data[file] = saveFile; } catch (Exception ex) { @@ -93,25 +67,7 @@ private void ReadMpSave(FileInfo file) } } - private void ReadSaveInfo(Stream stream, SaveFile save) - { - using var reader = new XmlTextReader(stream); - reader.ReadToNextElement(); // savedGame - reader.ReadToNextElement(); // meta - - if (reader.Name != "meta") return; - - reader.ReadToDescendant("gameVersion"); - save.rwVersion = VersionControl.VersionStringWithoutRev(reader.ReadString()); - - reader.ReadToNextSibling("modIds"); - save.modIds = reader.ReadStrings(); - - reader.ReadToNextSibling("modNames"); - save.modNames = reader.ReadStrings(); - } - - public SaveFile GetData(FileInfo file) + public SaveFile? GetData(FileInfo file) { data.TryGetValue(file, out var saveFile); return saveFile; @@ -124,18 +80,18 @@ public void RemoveFile(FileInfo info) } } - public class SaveFile + public class SaveFile(string displayName, bool replay, FileInfo file) { - public string displayName; - public bool replay; + public string displayName = displayName; + public bool replay = replay; public int replaySections; - public FileInfo file; + public FileInfo file = file; - public string gameName; + public string? gameName; - public string rwVersion; - public string[] modNames = Array.Empty(); - public string[] modIds = Array.Empty(); + public string? rwVersion; + public string[] modNames = []; + public string[] modIds = []; public int protocol; public bool asyncTime; @@ -143,7 +99,7 @@ public class SaveFile public bool HasRwVersion => rwVersion != null; - public bool MajorAndMinorVerEqualToCurrent => + public bool MajorAndMinorVerEqualToCurrent => rwVersion != null && VersionControl.MajorFromVersionString(rwVersion) == VersionControl.CurrentMajor && VersionControl.MinorFromVersionString(rwVersion) == VersionControl.CurrentMinor; @@ -164,11 +120,60 @@ public Color VersionColor } } - public SaveFile(string displayName, bool replay, FileInfo file) + public static SaveFile ReadSpSave(FileInfo file) + { + var saveFile = new SaveFile(Path.GetFileNameWithoutExtension(file.Name), false, file); + using var stream = file.OpenRead(); + ReadSaveInfo(stream, saveFile); + return saveFile; + } + + public static SaveFile? ReadMpSave(FileInfo file) { - this.displayName = displayName; - this.replay = replay; - this.file = file; + var displayName = Path.ChangeExtension(Path.GetRelativePath(Multiplayer.ReplaysDir, file.FullName), null); + var saveFile = new SaveFile(displayName, true, file); + + var replay = Replay.ForLoading(file); + if (!replay.LoadInfo()) return null; + + saveFile.gameName = replay.info.name; + saveFile.protocol = replay.info.protocol; + saveFile.replaySections = replay.info.sections.Count; + + if (!replay.info.rwVersion.NullOrEmpty()) + { + saveFile.rwVersion = replay.info.rwVersion; + saveFile.modIds = replay.info.modIds.ToArray(); + saveFile.modNames = replay.info.modNames.ToArray(); + saveFile.asyncTime = replay.info.asyncTime; + saveFile.multifaction = replay.info.multifaction; + } + else + { + using var zip = replay.OpenZipRead(); + using var stream = zip.GetEntry("world/000_save")!.Open(); + ReadSaveInfo(stream, saveFile); + } + + return saveFile; + } + + private static void ReadSaveInfo(Stream stream, SaveFile save) + { + using var reader = new XmlTextReader(stream); + reader.ReadToNextElement(); // savedGame + reader.ReadToNextElement(); // meta + + if (reader.Name != "meta") return; + + reader.ReadToDescendant("gameVersion"); + save.rwVersion = VersionControl.VersionStringWithoutRev(reader.ReadString()); + + reader.ReadToNextSibling("modIds"); + save.modIds = reader.ReadStrings(); + + reader.ReadToNextSibling("modNames"); + save.modNames = reader.ReadStrings(); } } } diff --git a/Source/Client/Windows/ServerBrowser.cs b/Source/Client/Windows/ServerBrowser.cs index 8ce3edbf..9e782de9 100644 --- a/Source/Client/Windows/ServerBrowser.cs +++ b/Source/Client/Windows/ServerBrowser.cs @@ -271,7 +271,7 @@ private void DrawFileButtons(SaveFile file, ref float width) width += 120; } - private static void CheckGameVersionAndMods(SaveFile file, Action action) + public static void CheckGameVersionAndMods(SaveFile file, Action action) { ScribeMetaHeaderUtility.lastMode = ScribeMetaHeaderUtility.ScribeHeaderMode.Map; ScribeMetaHeaderUtility.loadedGameVersion = file.rwVersion;