From d44d839478d3edc15cbef47d4ab0724ceb03a35c Mon Sep 17 00:00:00 2001 From: IExploitableMan Date: Wed, 23 Apr 2025 18:15:10 +0300 Subject: [PATCH 01/10] Implement auto-update feature and add UnwrapAsync extension method --- src/Managers/AutoUpdate.cs | 76 ++++++++++++++++++++++++++++++++++++++ src/Plugin.cs | 1 + src/Util.cs | 5 +++ 3 files changed, 82 insertions(+) create mode 100644 src/Managers/AutoUpdate.cs diff --git a/src/Managers/AutoUpdate.cs b/src/Managers/AutoUpdate.cs new file mode 100644 index 0000000..c981b65 --- /dev/null +++ b/src/Managers/AutoUpdate.cs @@ -0,0 +1,76 @@ +using System.Diagnostics; +using System.Text.Json; +using HarmonyLib; +using UnityEngine; + +namespace PolyMod.Managers; +internal static class AutoUpdate +{ + [HarmonyPostfix] + [HarmonyPatch(typeof(StartScreen), nameof(StartScreen.Start))] + private static void StartScreen_Start() + { + HttpClient client = new(); + client.DefaultRequestHeaders.Add("User-Agent", "PolyMod"); + try + { + var json = JsonDocument.Parse( + client.GetAsync("https://api.github.com/repos/PolyModdingTeam/PolyMod/releases/latest").UnwrapAsync() + .Content.ReadAsStringAsync().UnwrapAsync() + ); + if ( + new Version(json.RootElement.GetProperty("tag_name").GetString()!.TrimStart('v')) + <= + new Version(Plugin.VERSION) + ) return; + void Update() + { + File.WriteAllBytes( + Path.Combine(Plugin.BASE_PATH, "BepInEx", "plugins", "PolyMod.new.dll"), + client.GetAsync(json.RootElement.GetProperty("assets")[0].GetProperty("browser_download_url").GetString()!).UnwrapAsync() + .Content.ReadAsByteArrayAsync().UnwrapAsync() + ); + ProcessStartInfo info = new() + { + WorkingDirectory = Path.Combine(Plugin.BASE_PATH, "BepInEx", "plugins"), + CreateNoWindow = true, + }; + if (Application.platform == RuntimePlatform.WindowsPlayer) + { + info.FileName = "cmd.exe"; + info.Arguments + = "/C timeout 3 && del /F /Q PolyMod.dll && move /Y PolyMod.new.dll PolyMod.dll && start steam://rungameid/874390"; + } + else + { + info.FileName = "/bin/bash"; + info.Arguments + = "-c 'sleep 3 && rm -f PolyMod.dll && mv PolyMod.new.dll PolyMod.dll && xdg-open steam://rungameid/874390'"; + } + Process.Start(info); + Application.Quit(); + } + PopupManager.GetBasicPopup(new( + Localization.Get("polymod.version.autoupdate"), + Localization.Get("polymod.version.autoupdate.description"), + new(new PopupBase.PopupButtonData[] { + new( + "buttons.ok", + PopupBase.PopupButtonData.States.None, + (Il2CppSystem.Action)Update, + closesPopup: false + ) + })) + ).Show(); + } + catch (Exception e) + { + Plugin.logger.LogError($"Failed to check updates: {e.Message}"); + } + } + + internal static void Init() + { + Harmony.CreateAndPatchAll(typeof(AutoUpdate)); + } +} \ No newline at end of file diff --git a/src/Plugin.cs b/src/Plugin.cs index 86a49ea..dfcf132 100644 --- a/src/Plugin.cs +++ b/src/Plugin.cs @@ -54,6 +54,7 @@ public override void Load() Compatibility.Init(); Audio.Init(); + AutoUpdate.Init(); Loc.Init(); Visual.Init(); Hub.Init(); diff --git a/src/Util.cs b/src/Util.cs index b88eb43..65c6a72 100644 --- a/src/Util.cs +++ b/src/Util.cs @@ -30,6 +30,11 @@ internal static Version Cast(this Il2CppSystem.Version self) return new(self.ToString()); } + internal static T UnwrapAsync(this Task self) + { + return self.GetAwaiter().GetResult(); + } + internal static Version CutRevision(this Version self) { return new(self.Major, self.Minor, self.Build); From df8b063595c97afaad4076b2fcb50c619c5d7dbc Mon Sep 17 00:00:00 2001 From: IExploitableMan Date: Sun, 27 Apr 2025 14:51:10 +0300 Subject: [PATCH 02/10] Add localization and prerelease option --- installer/main.py | 15 +++++++++++---- resources/localization.json | 12 ++++++++++++ src/Managers/AutoUpdate.cs | 29 +++++++++++++++++++---------- src/Plugin.cs | 4 +++- 4 files changed, 45 insertions(+), 15 deletions(-) diff --git a/installer/main.py b/installer/main.py index 5474049..f4ed007 100644 --- a/installer/main.py +++ b/installer/main.py @@ -15,7 +15,7 @@ "darwin": "macos", }[sys.platform] BEPINEX = f"733/BepInEx-Unity.IL2CPP-{OS}-x64-6.0.0-be.733%2B995f049" -POLYMOD = "https://github.com/PolyModdingTeam/PolyMod/releases/latest/download/PolyMod.dll" +POLYMOD = "https://api.github.com/repos/PolyModdingTeam/PolyMod/releases" def resource_path(path): @@ -53,6 +53,7 @@ def prepare(target): return path_entry.configure(state=customtkinter.DISABLED) browse_button.configure(state=customtkinter.DISABLED) + prerelease_checkbox.destroy() install_button.destroy() uninstall_button.destroy() progress_bar = customtkinter.CTkProgressBar(app, determinate_speed=50 / 2) @@ -69,8 +70,11 @@ def install(path): ).extractall(path) progress_bar.step() + for release in requests.get(POLYMOD).json(): + if release["prerelease"] and not prerelease_checkbox.get(): continue + latest = release open(path + "/BepInEx/plugins/PolyMod.dll", "wb").write( - requests.get(POLYMOD).content + requests.get(latest["assets"][0]["browser_download_url"]).content ) progress_bar.step() @@ -127,6 +131,8 @@ def quit(): app, placeholder_text="Game path", width=228) browse_button = customtkinter.CTkButton( app, text="Browse", command=browse, width=1) +prerelease_checkbox = customtkinter.CTkCheckBox( + app, text="Prerelease", width=1) install_button = customtkinter.CTkButton( app, text="Install", command=lambda: prepare(install)) uninstall_button = customtkinter.CTkButton( @@ -134,7 +140,8 @@ def quit(): path_entry.grid(column=0, row=0, padx=5, pady=5) browse_button.grid(column=1, row=0, padx=(0, 5), pady=5) -install_button.grid(column=0, row=1, columnspan=2, padx=5, pady=5) -uninstall_button.grid(column=0, row=2, columnspan=2, padx=5, pady=5) +prerelease_checkbox.grid(column=0, row=1, columnspan=2, padx=5, pady=5) +install_button.grid(column=0, row=2, columnspan=2, padx=5, pady=5) +uninstall_button.grid(column=0, row=3, columnspan=2, padx=5, pady=5) app.mainloop() diff --git a/resources/localization.json b/resources/localization.json index 8a39199..94e6072 100644 --- a/resources/localization.json +++ b/resources/localization.json @@ -160,5 +160,17 @@ "Portuguese (Brazil)": "Essa versão do PolyMod não foi desenvolvida para a versão atual do aplicativo e pode não funcionar corretamente!", "Elyrion": "πȱ∫ỹmȱδ ƒƒƒƒƒƒƒ ŋȱŧ ȱrrȱ #₺rr∑ŋŧ ƒƒƒƒƒƒƒ ỹ maỹ ŋȱŧ ~ȱr§ #ȱrr∑#ŧ∫ỹ!", "German (Germany)": "Diese Version von PolyMod ist nicht für die aktuelle Version der Anwendung ausgelegt und könnte nicht funktionieren!" + }, + "polymod_autoupdate": { + "English": "Auto-update", + "Russian": "Автообновление" + }, + "polymod_autoupdate_description": { + "English": "New update available!", + "Russian": "Доступно новое обновление!" + }, + "polymod_autoupdate_update": { + "English": "Update", + "Russian": "Обновить" } } diff --git a/src/Managers/AutoUpdate.cs b/src/Managers/AutoUpdate.cs index c981b65..fe48f73 100644 --- a/src/Managers/AutoUpdate.cs +++ b/src/Managers/AutoUpdate.cs @@ -10,24 +10,33 @@ internal static class AutoUpdate [HarmonyPatch(typeof(StartScreen), nameof(StartScreen.Start))] private static void StartScreen_Start() { + if (!Plugin.config.autoUpdate) return; HttpClient client = new(); client.DefaultRequestHeaders.Add("User-Agent", "PolyMod"); try { var json = JsonDocument.Parse( - client.GetAsync("https://api.github.com/repos/PolyModdingTeam/PolyMod/releases/latest").UnwrapAsync() + client.GetAsync("https://api.github.com/repos/PolyModdingTeam/PolyMod/releases").UnwrapAsync() .Content.ReadAsStringAsync().UnwrapAsync() ); + JsonElement? latest = null; + for (int i = 0; i < json.RootElement.GetArrayLength(); i++) + { + var release = json.RootElement[i]; + if (release.GetProperty("prerelease").GetBoolean() && !Plugin.config.updatePrerelease) continue; + latest = release; + break; + } if ( - new Version(json.RootElement.GetProperty("tag_name").GetString()!.TrimStart('v')) - <= + new Version(latest?.GetProperty("tag_name").GetString()!.TrimStart('v')!) + <= new Version(Plugin.VERSION) ) return; void Update() { File.WriteAllBytes( Path.Combine(Plugin.BASE_PATH, "BepInEx", "plugins", "PolyMod.new.dll"), - client.GetAsync(json.RootElement.GetProperty("assets")[0].GetProperty("browser_download_url").GetString()!).UnwrapAsync() + client.GetAsync(latest?.GetProperty("assets")[0].GetProperty("browser_download_url").GetString()!).UnwrapAsync() .Content.ReadAsByteArrayAsync().UnwrapAsync() ); ProcessStartInfo info = new() @@ -38,24 +47,24 @@ void Update() if (Application.platform == RuntimePlatform.WindowsPlayer) { info.FileName = "cmd.exe"; - info.Arguments + info.Arguments = "/C timeout 3 && del /F /Q PolyMod.dll && move /Y PolyMod.new.dll PolyMod.dll && start steam://rungameid/874390"; } else { info.FileName = "/bin/bash"; - info.Arguments - = "-c 'sleep 3 && rm -f PolyMod.dll && mv PolyMod.new.dll PolyMod.dll && xdg-open steam://rungameid/874390'"; + info.Arguments + = "-c 'sleep 3 && rm -f PolyMod.dll && mv PolyMod.new.dll PolyMod.dll && xdg-open steam://rungameid/874390'"; } Process.Start(info); Application.Quit(); } PopupManager.GetBasicPopup(new( - Localization.Get("polymod.version.autoupdate"), - Localization.Get("polymod.version.autoupdate.description"), + Localization.Get("polymod.autoupdate"), + Localization.Get("polymod.autoupdate.description"), new(new PopupBase.PopupButtonData[] { new( - "buttons.ok", + "polymod.autoupdate.update", PopupBase.PopupButtonData.States.None, (Il2CppSystem.Action)Update, closesPopup: false diff --git a/src/Plugin.cs b/src/Plugin.cs index dfcf132..a0b906e 100644 --- a/src/Plugin.cs +++ b/src/Plugin.cs @@ -10,7 +10,9 @@ namespace PolyMod; public partial class Plugin : BepInEx.Unity.IL2CPP.BasePlugin { internal record PolyConfig( - bool debug = false + bool debug = false, + bool autoUpdate = true, + bool updatePrerelease = false ); internal const int AUTOIDX_STARTS_FROM = 1000; From 5bd58c50d311f62d03c811188171e8282f61df32 Mon Sep 17 00:00:00 2001 From: IExploitableMan Date: Sun, 27 Apr 2025 15:16:35 +0300 Subject: [PATCH 03/10] Use dynamic BepInEx version --- installer/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/installer/main.py b/installer/main.py index f4ed007..10a7a65 100644 --- a/installer/main.py +++ b/installer/main.py @@ -14,7 +14,7 @@ "win32": "win", "darwin": "macos", }[sys.platform] -BEPINEX = f"733/BepInEx-Unity.IL2CPP-{OS}-x64-6.0.0-be.733%2B995f049" +BEPINEX = "https://polymod.dev/data/bepinex.txt" POLYMOD = "https://api.github.com/repos/PolyModdingTeam/PolyMod/releases" @@ -65,7 +65,7 @@ def prepare(target): def install(path): to_zip( requests.get( - f"https://builds.bepinex.dev/projects/bepinex_be/{BEPINEX}.zip" + requests.get(BEPINEX).text.strip().replace("{os}", OS) ) ).extractall(path) progress_bar.step() From a2b430059e1522d44cfcde3c81cccb441d01c3c5 Mon Sep 17 00:00:00 2001 From: IExploitableMan Date: Wed, 11 Jun 2025 23:44:56 +0300 Subject: [PATCH 04/10] Implement BepInEx autoupdate --- src/Managers/AutoUpdate.cs | 46 +++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/src/Managers/AutoUpdate.cs b/src/Managers/AutoUpdate.cs index fe48f73..d28e5ed 100644 --- a/src/Managers/AutoUpdate.cs +++ b/src/Managers/AutoUpdate.cs @@ -1,9 +1,11 @@ using System.Diagnostics; +using System.IO.Compression; using System.Text.Json; using HarmonyLib; using UnityEngine; namespace PolyMod.Managers; + internal static class AutoUpdate { [HarmonyPostfix] @@ -11,6 +13,11 @@ internal static class AutoUpdate private static void StartScreen_Start() { if (!Plugin.config.autoUpdate) return; + if (Environment.GetEnvironmentVariable("WINEPREFIX") != null) + { + Plugin.logger.LogWarning("Wine/Proton is not supported!"); + return; + } HttpClient client = new(); client.DefaultRequestHeaders.Add("User-Agent", "PolyMod"); try @@ -32,29 +39,53 @@ private static void StartScreen_Start() <= new Version(Plugin.VERSION) ) return; + Console.WriteLine(latest?.GetProperty("assets")[0].GetProperty("browser_download_url").GetString()!); + string bepinex_version = client.GetAsync("https://polymod.dev/data/bepinex.txt").UnwrapAsync().Content.ReadAsStringAsync().UnwrapAsync(); + string os = Application.platform switch + { + RuntimePlatform.WindowsPlayer => "win", + RuntimePlatform.LinuxPlayer => "linux", + RuntimePlatform.OSXPlayer => "macos", + _ => "unknown", + }; + if (os == "unknown") return; + bepinex_version = bepinex_version.Replace("{os}", os); + Console.WriteLine(bepinex_version); void Update() { + Time.timeScale = 0; File.WriteAllBytes( - Path.Combine(Plugin.BASE_PATH, "BepInEx", "plugins", "PolyMod.new.dll"), + Path.Combine(Plugin.BASE_PATH, "PolyMod.new.dll"), client.GetAsync(latest?.GetProperty("assets")[0].GetProperty("browser_download_url").GetString()!).UnwrapAsync() .Content.ReadAsByteArrayAsync().UnwrapAsync() ); + using ZipArchive bepinex = new(client.GetAsync(bepinex_version).UnwrapAsync().Content.ReadAsStream()); + bepinex.ExtractToDirectory(Path.Combine(Plugin.BASE_PATH, "New")); ProcessStartInfo info = new() { - WorkingDirectory = Path.Combine(Plugin.BASE_PATH, "BepInEx", "plugins"), + WorkingDirectory = Path.Combine(Plugin.BASE_PATH), CreateNoWindow = true, }; if (Application.platform == RuntimePlatform.WindowsPlayer) { info.FileName = "cmd.exe"; - info.Arguments - = "/C timeout 3 && del /F /Q PolyMod.dll && move /Y PolyMod.new.dll PolyMod.dll && start steam://rungameid/874390"; + info.Arguments = + "/C timeout 3" + + " && robocopy \"New\" . /E /MOVE /NFL /NDL /NJH /NJS /NP" + + " && rmdir /S /Q \"New\"" + + " && del /F /Q \"BepInEx\\plugins\\PolyMod.dll\"" + + " && move /Y \"PolyMod.new.dll\" \"BepInEx\\plugins\\PolyMod.dll\"" + + " && start steam://rungameid/874390"; } else { info.FileName = "/bin/bash"; - info.Arguments - = "-c 'sleep 3 && rm -f PolyMod.dll && mv PolyMod.new.dll PolyMod.dll && xdg-open steam://rungameid/874390'"; + info.Arguments = + "-c 'sleep 3" + + " && mv -f New/* . && mv -f New/.* . 2>/dev/null || true && rm -rf New" + + " && rm -f BepInEx/plugins/PolyMod.dll" + + " && mv -f PolyMod.new.dll BepInEx/plugins/PolyMod.dll" + + " && xdg-open steam://rungameid/874390'"; } Process.Start(info); Application.Quit(); @@ -66,8 +97,7 @@ void Update() new( "polymod.autoupdate.update", PopupBase.PopupButtonData.States.None, - (Il2CppSystem.Action)Update, - closesPopup: false + (Il2CppSystem.Action)Update ) })) ).Show(); From 79cf37285cd4fd3622ba826e26b08f4069bc7f66 Mon Sep 17 00:00:00 2001 From: IExploitableMan Date: Fri, 13 Jun 2025 23:19:51 +0300 Subject: [PATCH 05/10] Refine version comparison logic --- src/Managers/AutoUpdate.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Managers/AutoUpdate.cs b/src/Managers/AutoUpdate.cs index d28e5ed..9151f4a 100644 --- a/src/Managers/AutoUpdate.cs +++ b/src/Managers/AutoUpdate.cs @@ -35,11 +35,10 @@ private static void StartScreen_Start() break; } if ( - new Version(latest?.GetProperty("tag_name").GetString()!.TrimStart('v')!) + new Version(latest?.GetProperty("tag_name").GetString()!.TrimStart('v')!.Split('-')[0]!) <= - new Version(Plugin.VERSION) + new Version(Plugin.VERSION.Split('-')[0]) ) return; - Console.WriteLine(latest?.GetProperty("assets")[0].GetProperty("browser_download_url").GetString()!); string bepinex_version = client.GetAsync("https://polymod.dev/data/bepinex.txt").UnwrapAsync().Content.ReadAsStringAsync().UnwrapAsync(); string os = Application.platform switch { @@ -49,8 +48,6 @@ private static void StartScreen_Start() _ => "unknown", }; if (os == "unknown") return; - bepinex_version = bepinex_version.Replace("{os}", os); - Console.WriteLine(bepinex_version); void Update() { Time.timeScale = 0; From a13e6b693c9524c64e00eeca3587d69879c681d5 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 30 Jun 2025 16:44:54 +0200 Subject: [PATCH 06/10] Made working buttons for enabling autoupdate for releases and prereleases --- resources/localization.json | 8 +++++++ src/Managers/Hub.cs | 48 +++++++++++++++++++++++++++---------- src/Plugin.cs | 2 +- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/resources/localization.json b/resources/localization.json index c9dd484..efc9a56 100644 --- a/resources/localization.json +++ b/resources/localization.json @@ -161,10 +161,18 @@ "Elyrion": "πȱ∫ỹmȱδ ƒƒƒƒƒƒƒ ŋȱŧ ȱrrȱ #₺rr∑ŋŧ ƒƒƒƒƒƒƒ ỹ maỹ ŋȱŧ ~ȱr§ #ȱrr∑#ŧ∫ỹ!", "German (Germany)": "Diese Version von PolyMod ist nicht für die aktuelle Version der Anwendung ausgelegt und könnte nicht funktionieren!" }, + "polymod_debug": { + "English": "Debug", + "Russian": "Дебаг" + }, "polymod_autoupdate": { "English": "Auto-update", "Russian": "Автообновление" }, + "polymod_autoupdate_alpha": { + "English": "Include alphas", + "Russian": "Include alphas" + }, "polymod_autoupdate_description": { "English": "New update available!", "Russian": "Доступно новое обновление!" diff --git a/src/Managers/Hub.cs b/src/Managers/Hub.cs index ff8abc1..43b186f 100644 --- a/src/Managers/Hub.cs +++ b/src/Managers/Hub.cs @@ -298,6 +298,7 @@ internal static void ShowConfigPopup() polymodPopup.Description = ""; polymodPopup.buttonData = CreateConfigPopupButtonData(); + polymodPopup.ShowSetWidth(POPUP_WIDTH); polymodPopup.Show(); } @@ -315,41 +316,62 @@ internal static PopupButtonData[] CreateConfigPopupButtonData() else { string debugButtonName = Localization.Get( - "polymod.hub.config.enable", - new Il2CppSystem.Object[] { "DEBUG" } + Plugin.config.debug ? "polymod.hub.config.disable" : "polymod.hub.config.enable", + new Il2CppSystem.Object[] { Localization.Get("polymod.debug", + new Il2CppSystem.Object[]{}).ToUpperInvariant() } + ); + string autoUpdateButtonName = Localization.Get( + Plugin.config.autoUpdate ? "polymod.hub.config.disable" : "polymod.hub.config.enable", + new Il2CppSystem.Object[] { Localization.Get("polymod.autoupdate", + new Il2CppSystem.Object[]{}).ToUpperInvariant() } + ); + string includeAlphasButtonName = Localization.Get( + Plugin.config.updatePrerelease ? "polymod.hub.config.disable" : "polymod.hub.config.enable", + new Il2CppSystem.Object[] { Localization.Get("polymod.autoupdate.alpha", + new Il2CppSystem.Object[]{}).ToUpperInvariant() } ); - if (Plugin.config.debug) - { - debugButtonName = Localization.Get( - "polymod.hub.config.disable", - new Il2CppSystem.Object[] { "DEBUG" } - ); - } popupButtons.Add(new PopupButtonData(debugButtonName, PopupButtonData.States.None, (UIButtonBase.ButtonAction)OnDebugButtonClicked, -1, true, null)); - //popupButtons.Add(new PopupButtonData("", PopupButtonData.States.None, (UIButtonBase.ButtonAction)OnAutoUpdateButtonClicked, -1, true, null)); - //popupButtons.Add(new PopupButtonData("", PopupButtonData.States.Disabled, (UIButtonBase.ButtonAction)OnIncludeAlphasButtonClicked, -1, true, null)); + popupButtons.Add(new PopupButtonData(autoUpdateButtonName, PopupButtonData.States.None, (UIButtonBase.ButtonAction)OnAutoUpdateButtonClicked, -1, true, null)); + popupButtons.Add(new PopupButtonData(includeAlphasButtonName, Plugin.config.autoUpdate ? PopupButtonData.States.None : PopupButtonData.States.Disabled, (UIButtonBase.ButtonAction)OnIncludeAlphasButtonClicked, -1, true, null)); } return popupButtons.ToArray(); void OnDebugButtonClicked(int buttonId, BaseEventData eventData) { - Plugin.config = new(debug: !Plugin.config.debug); + Plugin.config = new(debug: !Plugin.config.debug, autoUpdate: Plugin.config.autoUpdate, updatePrerelease: Plugin.config.updatePrerelease); Plugin.WriteConfig(); Plugin.UpdateConsole(); NotificationManager.Notify(Localization.Get( "polymod.config.setto", - new Il2CppSystem.Object[] { "Debug", Plugin.config.debug } + new Il2CppSystem.Object[] { Localization.Get("polymod.debug", + new Il2CppSystem.Object[]{}), Plugin.config.debug } )); isConfigPopupActive = false; } void OnAutoUpdateButtonClicked(int buttonId, BaseEventData eventData) { + Plugin.config = new(debug: Plugin.config.debug, autoUpdate: !Plugin.config.autoUpdate, updatePrerelease: Plugin.config.updatePrerelease); + Plugin.WriteConfig(); + Plugin.UpdateConsole(); + NotificationManager.Notify(Localization.Get( + "polymod.config.setto", + new Il2CppSystem.Object[] { Localization.Get("polymod.autoupdate", + new Il2CppSystem.Object[]{}), Plugin.config.autoUpdate } + )); isConfigPopupActive = false; } void OnIncludeAlphasButtonClicked(int buttonId, BaseEventData eventData) { + Plugin.config = new(debug: Plugin.config.debug, autoUpdate: Plugin.config.autoUpdate, updatePrerelease: !Plugin.config.updatePrerelease); + Plugin.WriteConfig(); + Plugin.UpdateConsole(); + NotificationManager.Notify(Localization.Get( + "polymod.config.setto", + new Il2CppSystem.Object[] { Localization.Get("polymod.autoupdate.alpha", + new Il2CppSystem.Object[]{}), Plugin.config.updatePrerelease } + )); isConfigPopupActive = false; } diff --git a/src/Plugin.cs b/src/Plugin.cs index 5c70cd6..025258c 100644 --- a/src/Plugin.cs +++ b/src/Plugin.cs @@ -51,8 +51,8 @@ public override void Load() catch { config = new(); - WriteConfig(); } + WriteConfig(); UpdateConsole(); logger = Log; ConfigFile.CoreConfig[new("Logging.Disk", "WriteUnityLog")].BoxedValue = true; From c61a53feb1522fd12f323598551187636ac80855 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 30 Jun 2025 18:37:43 +0200 Subject: [PATCH 07/10] Fixed windows auto update --- src/Managers/AutoUpdate.cs | 40 ++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/Managers/AutoUpdate.cs b/src/Managers/AutoUpdate.cs index 9151f4a..3f2573e 100644 --- a/src/Managers/AutoUpdate.cs +++ b/src/Managers/AutoUpdate.cs @@ -39,7 +39,6 @@ private static void StartScreen_Start() <= new Version(Plugin.VERSION.Split('-')[0]) ) return; - string bepinex_version = client.GetAsync("https://polymod.dev/data/bepinex.txt").UnwrapAsync().Content.ReadAsStringAsync().UnwrapAsync(); string os = Application.platform switch { RuntimePlatform.WindowsPlayer => "win", @@ -48,6 +47,7 @@ private static void StartScreen_Start() _ => "unknown", }; if (os == "unknown") return; + string bepinex_url = client.GetAsync("https://polymod.dev/data/bepinex.txt").UnwrapAsync().Content.ReadAsStringAsync().UnwrapAsync().Replace("{os}", os); void Update() { Time.timeScale = 0; @@ -56,23 +56,43 @@ void Update() client.GetAsync(latest?.GetProperty("assets")[0].GetProperty("browser_download_url").GetString()!).UnwrapAsync() .Content.ReadAsByteArrayAsync().UnwrapAsync() ); - using ZipArchive bepinex = new(client.GetAsync(bepinex_version).UnwrapAsync().Content.ReadAsStream()); - bepinex.ExtractToDirectory(Path.Combine(Plugin.BASE_PATH, "New")); + using ZipArchive bepinex = new(client.GetAsync(bepinex_url).UnwrapAsync().Content.ReadAsStream()); + bepinex.ExtractToDirectory(Path.Combine(Plugin.BASE_PATH, "New"), overwriteFiles: true); ProcessStartInfo info = new() { WorkingDirectory = Path.Combine(Plugin.BASE_PATH), CreateNoWindow = true, }; + Console.Write(5); if (Application.platform == RuntimePlatform.WindowsPlayer) { + string batchPath = Path.Combine(Plugin.BASE_PATH, "update.bat"); + File.WriteAllText(batchPath, $@" + @echo off + echo Waiting for Polytopia.exe to exit... + :waitloop + tasklist | findstr /I ""Polytopia.exe"" >nul + if not errorlevel 1 ( + timeout /T 1 >nul + goto waitloop + ) + + echo Updating... + robocopy ""New"" . /E /MOVE /NFL /NDL /NJH /NJS /NP >nul + rmdir /S /Q ""New"" + del /F /Q ""BepInEx\plugins\PolyMod.dll"" + move /Y ""PolyMod.new.dll"" ""BepInEx\plugins\PolyMod.dll"" + + echo Launching game... + start steam://rungameid/874390 + timeout /T 3 /NOBREAK >nul + exit + "); info.FileName = "cmd.exe"; - info.Arguments = - "/C timeout 3" + - " && robocopy \"New\" . /E /MOVE /NFL /NDL /NJH /NJS /NP" + - " && rmdir /S /Q \"New\"" + - " && del /F /Q \"BepInEx\\plugins\\PolyMod.dll\"" + - " && move /Y \"PolyMod.new.dll\" \"BepInEx\\plugins\\PolyMod.dll\"" + - " && start steam://rungameid/874390"; + info.Arguments = $"/C start \"\" \"{batchPath}\""; + info.WorkingDirectory = Plugin.BASE_PATH; + info.CreateNoWindow = true; + info.UseShellExecute = false; } else { From 47e85da08369661cd5029b0c91ca7fe725902b18 Mon Sep 17 00:00:00 2001 From: IExploitableMan Date: Mon, 30 Jun 2025 21:50:59 +0300 Subject: [PATCH 08/10] Refactor autoupdate cmds, error handling and improve version comparison logic --- src/Managers/AutoUpdate.cs | 70 +++++++++++++++----------------------- src/Util.cs | 20 +++++++++-- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/Managers/AutoUpdate.cs b/src/Managers/AutoUpdate.cs index 3f2573e..1853d69 100644 --- a/src/Managers/AutoUpdate.cs +++ b/src/Managers/AutoUpdate.cs @@ -15,7 +15,7 @@ private static void StartScreen_Start() if (!Plugin.config.autoUpdate) return; if (Environment.GetEnvironmentVariable("WINEPREFIX") != null) { - Plugin.logger.LogWarning("Wine/Proton is not supported!"); + Plugin.logger.LogError("Autoupdate is not supported on Wine!"); return; } HttpClient client = new(); @@ -34,11 +34,8 @@ private static void StartScreen_Start() latest = release; break; } - if ( - new Version(latest?.GetProperty("tag_name").GetString()!.TrimStart('v')!.Split('-')[0]!) - <= - new Version(Plugin.VERSION.Split('-')[0]) - ) return; + string newVersion = latest?.GetProperty("tag_name").GetString()!.TrimStart('v')!; + if (newVersion.IsVersionOlderOrEqual(Plugin.VERSION)) return; string os = Application.platform switch { RuntimePlatform.WindowsPlayer => "win", @@ -46,8 +43,15 @@ private static void StartScreen_Start() RuntimePlatform.OSXPlayer => "macos", _ => "unknown", }; - if (os == "unknown") return; - string bepinex_url = client.GetAsync("https://polymod.dev/data/bepinex.txt").UnwrapAsync().Content.ReadAsStringAsync().UnwrapAsync().Replace("{os}", os); + if (os == "unknown") + { + Plugin.logger.LogError("Unsupported platform for autoupdate!"); + return; + } + string bepinex_url = client + .GetAsync("https://polymod.dev/data/bepinex.txt").UnwrapAsync() + .Content.ReadAsStringAsync().UnwrapAsync() + .Replace("{os}", os); void Update() { Time.timeScale = 0; @@ -63,46 +67,28 @@ void Update() WorkingDirectory = Path.Combine(Plugin.BASE_PATH), CreateNoWindow = true, }; - Console.Write(5); if (Application.platform == RuntimePlatform.WindowsPlayer) { - string batchPath = Path.Combine(Plugin.BASE_PATH, "update.bat"); - File.WriteAllText(batchPath, $@" - @echo off - echo Waiting for Polytopia.exe to exit... - :waitloop - tasklist | findstr /I ""Polytopia.exe"" >nul - if not errorlevel 1 ( - timeout /T 1 >nul - goto waitloop - ) - - echo Updating... - robocopy ""New"" . /E /MOVE /NFL /NDL /NJH /NJS /NP >nul - rmdir /S /Q ""New"" - del /F /Q ""BepInEx\plugins\PolyMod.dll"" - move /Y ""PolyMod.new.dll"" ""BepInEx\plugins\PolyMod.dll"" - - echo Launching game... - start steam://rungameid/874390 - timeout /T 3 /NOBREAK >nul - exit - "); info.FileName = "cmd.exe"; - info.Arguments = $"/C start \"\" \"{batchPath}\""; - info.WorkingDirectory = Plugin.BASE_PATH; - info.CreateNoWindow = true; - info.UseShellExecute = false; + info.Arguments = "/C " + + ":waitloop & " + + "tasklist | findstr /I \"Polytopia.exe\" >nul && (timeout /T 1 >nul & goto waitloop) & " + + "robocopy \"New\" . /E /MOVE /NFL /NDL /NJH /NJS /NP >nul & " + + "rmdir /S /Q \"New\" & " + + "del /F /Q \"BepInEx\\plugins\\PolyMod.dll\" & " + + "move /Y \"PolyMod.new.dll\" \"BepInEx\\plugins\\PolyMod.dll\" & " + + "start steam://rungameid/874390"; } - else + if (Application.platform == RuntimePlatform.LinuxPlayer || Application.platform == RuntimePlatform.OSXPlayer) { info.FileName = "/bin/bash"; - info.Arguments = - "-c 'sleep 3" + - " && mv -f New/* . && mv -f New/.* . 2>/dev/null || true && rm -rf New" + - " && rm -f BepInEx/plugins/PolyMod.dll" + - " && mv -f PolyMod.new.dll BepInEx/plugins/PolyMod.dll" + - " && xdg-open steam://rungameid/874390'"; + info.Arguments = "-c \"" + + "while pgrep -x Polytopia > /dev/null; do sleep 1; done && " + + "mv New/* . && rm -rf New && " + + "rm -f BepInEx/plugins/PolyMod.dll && " + + "mv PolyMod.new.dll BepInEx/plugins/PolyMod.dll && " + + "steam steam://rungameid/874390" + + "\""; } Process.Start(info); Application.Quit(); diff --git a/src/Util.cs b/src/Util.cs index f4ddc33..e527d85 100644 --- a/src/Util.cs +++ b/src/Util.cs @@ -6,7 +6,6 @@ using Polytopia.Data; namespace PolyMod; - internal static class Util { internal static Il2CppSystem.Type WrapType() where T : class @@ -41,6 +40,23 @@ internal static Version CutRevision(this Version self) return new(self.Major, self.Minor, self.Build); } + internal static bool IsVersionOlderOrEqual(this string version1, string version2) + { + Version version1_ = new(version1.Split('-')[0]); + Version version2_ = new(version2.Split('-')[0]); + + if (version1_ < version2_) return true; + if (version1_ > version2_) return false; + + string pre1 = version1.Contains('-') ? version1.Split('-')[1] : ""; + string pre2 = version2.Contains('-') ? version2.Split('-')[1] : ""; + + if (string.IsNullOrEmpty(pre1) && !string.IsNullOrEmpty(pre2)) return false; + if (!string.IsNullOrEmpty(pre1) && string.IsNullOrEmpty(pre2)) return true; + + return string.Compare(pre1, pre2, StringComparison.Ordinal) <= 0; + } + internal static string GetStyle(TribeData.Type tribe, SkinType skin) { return skin != SkinType.Default ? EnumCache.GetName(skin) : EnumCache.GetName(tribe); @@ -91,8 +107,6 @@ internal static string GetStyle(TribeData.Type tribe, SkinType skin) baseName = baseName.Replace(SpriteData.TILE_UNKNOWN, EnumCache.GetName(TerrainData.Type.Field)); baseName = baseName.Replace(SpriteData.TILE_WATER, EnumCache.GetName(TerrainData.Type.Water)); baseName = baseName.Replace(SpriteData.TILE_WETLAND, EnumCache.GetName(TerrainData.Type.Field) + "_flooded"); - - baseName = baseName.Replace("UI_", ""); return baseName; } } From 80c1f5c77c7c13a4b4d335e7b70afc3dacc306f5 Mon Sep 17 00:00:00 2001 From: IExploitableMan Date: Mon, 30 Jun 2025 21:52:56 +0300 Subject: [PATCH 09/10] Bring back `UI_` prefix removal --- src/Util.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Util.cs b/src/Util.cs index e527d85..4d2fce2 100644 --- a/src/Util.cs +++ b/src/Util.cs @@ -107,6 +107,8 @@ internal static string GetStyle(TribeData.Type tribe, SkinType skin) baseName = baseName.Replace(SpriteData.TILE_UNKNOWN, EnumCache.GetName(TerrainData.Type.Field)); baseName = baseName.Replace(SpriteData.TILE_WATER, EnumCache.GetName(TerrainData.Type.Water)); baseName = baseName.Replace(SpriteData.TILE_WETLAND, EnumCache.GetName(TerrainData.Type.Field) + "_flooded"); + + baseName = baseName.Replace("UI_", ""); return baseName; } } From 387780200f77a33d63e301369fdb172ff1d74e03 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 30 Jun 2025 21:02:07 +0200 Subject: [PATCH 10/10] Fixed again --- src/Managers/AutoUpdate.cs | 74 ++++++++++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 15 deletions(-) diff --git a/src/Managers/AutoUpdate.cs b/src/Managers/AutoUpdate.cs index 1853d69..ef7a043 100644 --- a/src/Managers/AutoUpdate.cs +++ b/src/Managers/AutoUpdate.cs @@ -69,26 +69,70 @@ void Update() }; if (Application.platform == RuntimePlatform.WindowsPlayer) { + string batchPath = Path.Combine(Plugin.BASE_PATH, "update.bat"); + File.WriteAllText(batchPath, $@" + @echo off + echo Waiting for Polytopia.exe to exit... + :waitloop + tasklist | findstr /I ""Polytopia.exe"" >nul + if not errorlevel 1 ( + timeout /T 1 >nul + goto waitloop + ) + + echo Updating... + robocopy ""New"" . /E /MOVE /NFL /NDL /NJH /NJS /NP >nul + rmdir /S /Q ""New"" + del /F /Q ""BepInEx\plugins\PolyMod.dll"" + move /Y ""PolyMod.new.dll"" ""BepInEx\plugins\PolyMod.dll"" + + echo Launching game... + start steam://rungameid/874390 + timeout /T 3 /NOBREAK >nul + exit + "); info.FileName = "cmd.exe"; - info.Arguments = "/C " + - ":waitloop & " + - "tasklist | findstr /I \"Polytopia.exe\" >nul && (timeout /T 1 >nul & goto waitloop) & " + - "robocopy \"New\" . /E /MOVE /NFL /NDL /NJH /NJS /NP >nul & " + - "rmdir /S /Q \"New\" & " + - "del /F /Q \"BepInEx\\plugins\\PolyMod.dll\" & " + - "move /Y \"PolyMod.new.dll\" \"BepInEx\\plugins\\PolyMod.dll\" & " + - "start steam://rungameid/874390"; + info.Arguments = $"/C start \"\" \"{batchPath}\""; + info.WorkingDirectory = Plugin.BASE_PATH; + info.CreateNoWindow = true; + info.UseShellExecute = false; } if (Application.platform == RuntimePlatform.LinuxPlayer || Application.platform == RuntimePlatform.OSXPlayer) { + string bashPath = Path.Combine(Plugin.BASE_PATH, "update.sh"); + File.WriteAllText(bashPath, $@" + #!/bin/bash + + echo ""Waiting for Polytopia to exit..."" + while pgrep -x ""Polytopia"" > /dev/null; do + sleep 1 + done + + echo ""Updating..."" + mv New/* . && rm -rf New + rm -f BepInEx/plugins/PolyMod.dll + mv -f PolyMod.new.dll BepInEx/plugins/PolyMod.dll + + echo ""Launching game..."" + xdg-open steam://rungameid/874390 & + + sleep 3 + exit 0 + "); + + System.Diagnostics.Process chmod = new System.Diagnostics.Process(); + chmod.StartInfo.FileName = "chmod"; + chmod.StartInfo.Arguments = $"+x \"{bashPath}\""; + chmod.StartInfo.UseShellExecute = false; + chmod.StartInfo.CreateNoWindow = true; + chmod.Start(); + chmod.WaitForExit(); + info.FileName = "/bin/bash"; - info.Arguments = "-c \"" + - "while pgrep -x Polytopia > /dev/null; do sleep 1; done && " + - "mv New/* . && rm -rf New && " + - "rm -f BepInEx/plugins/PolyMod.dll && " + - "mv PolyMod.new.dll BepInEx/plugins/PolyMod.dll && " + - "steam steam://rungameid/874390" + - "\""; + info.Arguments = $"\"{bashPath}\""; + info.WorkingDirectory = Plugin.BASE_PATH; + info.CreateNoWindow = true; + info.UseShellExecute = false; } Process.Start(info); Application.Quit();