Skip to content

Commit 3a6558b

Browse files
AutoUpdate (#74)
1 parent 1f9cd49 commit 3a6558b

File tree

6 files changed

+257
-22
lines changed

6 files changed

+257
-22
lines changed

installer/main.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
"win32": "win",
1616
"darwin": "macos",
1717
}[sys.platform]
18-
BEPINEX = f"733/BepInEx-Unity.IL2CPP-{OS}-x64-6.0.0-be.733%2B995f049"
19-
POLYMOD = "https://github.com/PolyModdingTeam/PolyMod/releases/latest/download/PolyMod.dll"
18+
BEPINEX = "https://polymod.dev/data/bepinex.txt"
19+
POLYMOD = "https://api.github.com/repos/PolyModdingTeam/PolyMod/releases"
2020

2121

2222
def resource_path(path):
@@ -54,6 +54,7 @@ def prepare(target):
5454
return
5555
path_entry.configure(state=customtkinter.DISABLED)
5656
browse_button.configure(state=customtkinter.DISABLED)
57+
prerelease_checkbox.destroy()
5758
install_button.destroy()
5859
uninstall_button.destroy()
5960
progress_bar = customtkinter.CTkProgressBar(app, determinate_speed=50 / 2)
@@ -65,13 +66,16 @@ def prepare(target):
6566
def install(path):
6667
to_zip(
6768
requests.get(
68-
f"https://builds.bepinex.dev/projects/bepinex_be/{BEPINEX}.zip"
69+
requests.get(BEPINEX).text.strip().replace("{os}", OS)
6970
)
7071
).extractall(path)
7172
progress_bar.step()
7273

74+
for release in requests.get(POLYMOD).json():
75+
if release["prerelease"] and not prerelease_checkbox.get(): continue
76+
latest = release
7377
open(path + "/BepInEx/plugins/PolyMod.dll", "wb").write(
74-
requests.get(POLYMOD).content
78+
requests.get(latest["assets"][0]["browser_download_url"]).content
7579
)
7680
progress_bar.step()
7781

@@ -133,14 +137,17 @@ def quit():
133137
app, placeholder_text="Game path", width=228)
134138
browse_button = customtkinter.CTkButton(
135139
app, text="Browse", command=browse, width=1)
140+
prerelease_checkbox = customtkinter.CTkCheckBox(
141+
app, text="Prerelease", width=1)
136142
install_button = customtkinter.CTkButton(
137143
app, text="Install", command=lambda: prepare(install))
138144
uninstall_button = customtkinter.CTkButton(
139145
app, text="Uninstall", command=lambda: prepare(uninstall))
140146

141147
path_entry.grid(column=0, row=0, padx=5, pady=5)
142148
browse_button.grid(column=1, row=0, padx=(0, 5), pady=5)
143-
install_button.grid(column=0, row=1, columnspan=2, padx=5, pady=5)
144-
uninstall_button.grid(column=0, row=2, columnspan=2, padx=5, pady=5)
149+
prerelease_checkbox.grid(column=0, row=1, columnspan=2, padx=5, pady=5)
150+
install_button.grid(column=0, row=2, columnspan=2, padx=5, pady=5)
151+
uninstall_button.grid(column=0, row=3, columnspan=2, padx=5, pady=5)
145152

146153
app.mainloop()

resources/localization.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,26 @@
161161
"Elyrion": "πȱ∫ỹmȱδ ƒƒƒƒƒƒƒ ŋȱŧ ȱrrȱ #₺rr∑ŋŧ ƒƒƒƒƒƒƒ ỹ maỹ ŋȱŧ ~ȱr§ #ȱrr∑#ŧ∫ỹ!",
162162
"German (Germany)": "Diese Version von PolyMod ist nicht für die aktuelle Version der Anwendung ausgelegt und könnte nicht funktionieren!"
163163
},
164+
"polymod_debug": {
165+
"English": "Debug",
166+
"Russian": "Дебаг"
167+
},
168+
"polymod_autoupdate": {
169+
"English": "Auto-update",
170+
"Russian": "Автообновление"
171+
},
172+
"polymod_autoupdate_alpha": {
173+
"English": "Include alphas",
174+
"Russian": "Include alphas"
175+
},
176+
"polymod_autoupdate_description": {
177+
"English": "New update available!",
178+
"Russian": "Доступно новое обновление!"
179+
},
180+
"polymod_autoupdate_update": {
181+
"English": "Update",
182+
"Russian": "Обновить"
183+
},
164184
"polymod_hub_config": {
165185
"English": "CONFIG",
166186
"Russian": "КОНФИГ"

src/Managers/AutoUpdate.cs

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
using System.Diagnostics;
2+
using System.IO.Compression;
3+
using System.Text.Json;
4+
using HarmonyLib;
5+
using UnityEngine;
6+
7+
namespace PolyMod.Managers;
8+
9+
internal static class AutoUpdate
10+
{
11+
[HarmonyPostfix]
12+
[HarmonyPatch(typeof(StartScreen), nameof(StartScreen.Start))]
13+
private static void StartScreen_Start()
14+
{
15+
if (!Plugin.config.autoUpdate) return;
16+
if (Environment.GetEnvironmentVariable("WINEPREFIX") != null)
17+
{
18+
Plugin.logger.LogError("Autoupdate is not supported on Wine!");
19+
return;
20+
}
21+
HttpClient client = new();
22+
client.DefaultRequestHeaders.Add("User-Agent", "PolyMod");
23+
try
24+
{
25+
var json = JsonDocument.Parse(
26+
client.GetAsync("https://api.github.com/repos/PolyModdingTeam/PolyMod/releases").UnwrapAsync()
27+
.Content.ReadAsStringAsync().UnwrapAsync()
28+
);
29+
JsonElement? latest = null;
30+
for (int i = 0; i < json.RootElement.GetArrayLength(); i++)
31+
{
32+
var release = json.RootElement[i];
33+
if (release.GetProperty("prerelease").GetBoolean() && !Plugin.config.updatePrerelease) continue;
34+
latest = release;
35+
break;
36+
}
37+
string newVersion = latest?.GetProperty("tag_name").GetString()!.TrimStart('v')!;
38+
if (newVersion.IsVersionOlderOrEqual(Plugin.VERSION)) return;
39+
string os = Application.platform switch
40+
{
41+
RuntimePlatform.WindowsPlayer => "win",
42+
RuntimePlatform.LinuxPlayer => "linux",
43+
RuntimePlatform.OSXPlayer => "macos",
44+
_ => "unknown",
45+
};
46+
if (os == "unknown")
47+
{
48+
Plugin.logger.LogError("Unsupported platform for autoupdate!");
49+
return;
50+
}
51+
string bepinex_url = client
52+
.GetAsync("https://polymod.dev/data/bepinex.txt").UnwrapAsync()
53+
.Content.ReadAsStringAsync().UnwrapAsync()
54+
.Replace("{os}", os);
55+
void Update()
56+
{
57+
Time.timeScale = 0;
58+
File.WriteAllBytes(
59+
Path.Combine(Plugin.BASE_PATH, "PolyMod.new.dll"),
60+
client.GetAsync(latest?.GetProperty("assets")[0].GetProperty("browser_download_url").GetString()!).UnwrapAsync()
61+
.Content.ReadAsByteArrayAsync().UnwrapAsync()
62+
);
63+
using ZipArchive bepinex = new(client.GetAsync(bepinex_url).UnwrapAsync().Content.ReadAsStream());
64+
bepinex.ExtractToDirectory(Path.Combine(Plugin.BASE_PATH, "New"), overwriteFiles: true);
65+
ProcessStartInfo info = new()
66+
{
67+
WorkingDirectory = Path.Combine(Plugin.BASE_PATH),
68+
CreateNoWindow = true,
69+
};
70+
if (Application.platform == RuntimePlatform.WindowsPlayer)
71+
{
72+
string batchPath = Path.Combine(Plugin.BASE_PATH, "update.bat");
73+
File.WriteAllText(batchPath, $@"
74+
@echo off
75+
echo Waiting for Polytopia.exe to exit...
76+
:waitloop
77+
tasklist | findstr /I ""Polytopia.exe"" >nul
78+
if not errorlevel 1 (
79+
timeout /T 1 >nul
80+
goto waitloop
81+
)
82+
83+
echo Updating...
84+
robocopy ""New"" . /E /MOVE /NFL /NDL /NJH /NJS /NP >nul
85+
rmdir /S /Q ""New""
86+
del /F /Q ""BepInEx\plugins\PolyMod.dll""
87+
move /Y ""PolyMod.new.dll"" ""BepInEx\plugins\PolyMod.dll""
88+
89+
echo Launching game...
90+
start steam://rungameid/874390
91+
timeout /T 3 /NOBREAK >nul
92+
exit
93+
");
94+
info.FileName = "cmd.exe";
95+
info.Arguments = $"/C start \"\" \"{batchPath}\"";
96+
info.WorkingDirectory = Plugin.BASE_PATH;
97+
info.CreateNoWindow = true;
98+
info.UseShellExecute = false;
99+
}
100+
if (Application.platform == RuntimePlatform.LinuxPlayer || Application.platform == RuntimePlatform.OSXPlayer)
101+
{
102+
string bashPath = Path.Combine(Plugin.BASE_PATH, "update.sh");
103+
File.WriteAllText(bashPath, $@"
104+
#!/bin/bash
105+
106+
echo ""Waiting for Polytopia to exit...""
107+
while pgrep -x ""Polytopia"" > /dev/null; do
108+
sleep 1
109+
done
110+
111+
echo ""Updating...""
112+
mv New/* . && rm -rf New
113+
rm -f BepInEx/plugins/PolyMod.dll
114+
mv -f PolyMod.new.dll BepInEx/plugins/PolyMod.dll
115+
116+
echo ""Launching game...""
117+
xdg-open steam://rungameid/874390 &
118+
119+
sleep 3
120+
exit 0
121+
");
122+
123+
System.Diagnostics.Process chmod = new System.Diagnostics.Process();
124+
chmod.StartInfo.FileName = "chmod";
125+
chmod.StartInfo.Arguments = $"+x \"{bashPath}\"";
126+
chmod.StartInfo.UseShellExecute = false;
127+
chmod.StartInfo.CreateNoWindow = true;
128+
chmod.Start();
129+
chmod.WaitForExit();
130+
131+
info.FileName = "/bin/bash";
132+
info.Arguments = $"\"{bashPath}\"";
133+
info.WorkingDirectory = Plugin.BASE_PATH;
134+
info.CreateNoWindow = true;
135+
info.UseShellExecute = false;
136+
}
137+
Process.Start(info);
138+
Application.Quit();
139+
}
140+
PopupManager.GetBasicPopup(new(
141+
Localization.Get("polymod.autoupdate"),
142+
Localization.Get("polymod.autoupdate.description"),
143+
new(new PopupBase.PopupButtonData[] {
144+
new(
145+
"polymod.autoupdate.update",
146+
PopupBase.PopupButtonData.States.None,
147+
(Il2CppSystem.Action)Update
148+
)
149+
}))
150+
).Show();
151+
}
152+
catch (Exception e)
153+
{
154+
Plugin.logger.LogError($"Failed to check updates: {e.Message}");
155+
}
156+
}
157+
158+
internal static void Init()
159+
{
160+
Harmony.CreateAndPatchAll(typeof(AutoUpdate));
161+
}
162+
}

src/Managers/Hub.cs

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ internal static void ShowConfigPopup()
298298
polymodPopup.Description = "";
299299

300300
polymodPopup.buttonData = CreateConfigPopupButtonData();
301+
polymodPopup.ShowSetWidth(POPUP_WIDTH);
301302
polymodPopup.Show();
302303
}
303304

@@ -315,41 +316,62 @@ internal static PopupButtonData[] CreateConfigPopupButtonData()
315316
else
316317
{
317318
string debugButtonName = Localization.Get(
318-
"polymod.hub.config.enable",
319-
new Il2CppSystem.Object[] { "DEBUG" }
319+
Plugin.config.debug ? "polymod.hub.config.disable" : "polymod.hub.config.enable",
320+
new Il2CppSystem.Object[] { Localization.Get("polymod.debug",
321+
new Il2CppSystem.Object[]{}).ToUpperInvariant() }
322+
);
323+
string autoUpdateButtonName = Localization.Get(
324+
Plugin.config.autoUpdate ? "polymod.hub.config.disable" : "polymod.hub.config.enable",
325+
new Il2CppSystem.Object[] { Localization.Get("polymod.autoupdate",
326+
new Il2CppSystem.Object[]{}).ToUpperInvariant() }
327+
);
328+
string includeAlphasButtonName = Localization.Get(
329+
Plugin.config.updatePrerelease ? "polymod.hub.config.disable" : "polymod.hub.config.enable",
330+
new Il2CppSystem.Object[] { Localization.Get("polymod.autoupdate.alpha",
331+
new Il2CppSystem.Object[]{}).ToUpperInvariant() }
320332
);
321-
if (Plugin.config.debug)
322-
{
323-
debugButtonName = Localization.Get(
324-
"polymod.hub.config.disable",
325-
new Il2CppSystem.Object[] { "DEBUG" }
326-
);
327-
}
328333
popupButtons.Add(new PopupButtonData(debugButtonName, PopupButtonData.States.None, (UIButtonBase.ButtonAction)OnDebugButtonClicked, -1, true, null));
329-
//popupButtons.Add(new PopupButtonData("", PopupButtonData.States.None, (UIButtonBase.ButtonAction)OnAutoUpdateButtonClicked, -1, true, null));
330-
//popupButtons.Add(new PopupButtonData("", PopupButtonData.States.Disabled, (UIButtonBase.ButtonAction)OnIncludeAlphasButtonClicked, -1, true, null));
334+
popupButtons.Add(new PopupButtonData(autoUpdateButtonName, PopupButtonData.States.None, (UIButtonBase.ButtonAction)OnAutoUpdateButtonClicked, -1, true, null));
335+
popupButtons.Add(new PopupButtonData(includeAlphasButtonName, Plugin.config.autoUpdate ? PopupButtonData.States.None : PopupButtonData.States.Disabled, (UIButtonBase.ButtonAction)OnIncludeAlphasButtonClicked, -1, true, null));
331336
}
332337
return popupButtons.ToArray();
333338

334339
void OnDebugButtonClicked(int buttonId, BaseEventData eventData)
335340
{
336-
Plugin.config = new(debug: !Plugin.config.debug);
341+
Plugin.config = new(debug: !Plugin.config.debug, autoUpdate: Plugin.config.autoUpdate, updatePrerelease: Plugin.config.updatePrerelease);
337342
Plugin.WriteConfig();
338343
Plugin.UpdateConsole();
339344
NotificationManager.Notify(Localization.Get(
340345
"polymod.config.setto",
341-
new Il2CppSystem.Object[] { "Debug", Plugin.config.debug }
346+
new Il2CppSystem.Object[] { Localization.Get("polymod.debug",
347+
new Il2CppSystem.Object[]{}), Plugin.config.debug }
342348
));
343349
isConfigPopupActive = false;
344350
}
345351

346352
void OnAutoUpdateButtonClicked(int buttonId, BaseEventData eventData)
347353
{
354+
Plugin.config = new(debug: Plugin.config.debug, autoUpdate: !Plugin.config.autoUpdate, updatePrerelease: Plugin.config.updatePrerelease);
355+
Plugin.WriteConfig();
356+
Plugin.UpdateConsole();
357+
NotificationManager.Notify(Localization.Get(
358+
"polymod.config.setto",
359+
new Il2CppSystem.Object[] { Localization.Get("polymod.autoupdate",
360+
new Il2CppSystem.Object[]{}), Plugin.config.autoUpdate }
361+
));
348362
isConfigPopupActive = false;
349363
}
350364

351365
void OnIncludeAlphasButtonClicked(int buttonId, BaseEventData eventData)
352366
{
367+
Plugin.config = new(debug: Plugin.config.debug, autoUpdate: Plugin.config.autoUpdate, updatePrerelease: !Plugin.config.updatePrerelease);
368+
Plugin.WriteConfig();
369+
Plugin.UpdateConsole();
370+
NotificationManager.Notify(Localization.Get(
371+
"polymod.config.setto",
372+
new Il2CppSystem.Object[] { Localization.Get("polymod.autoupdate.alpha",
373+
new Il2CppSystem.Object[]{}), Plugin.config.updatePrerelease }
374+
));
353375
isConfigPopupActive = false;
354376
}
355377

src/Plugin.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ namespace PolyMod;
1212
public partial class Plugin : BepInEx.Unity.IL2CPP.BasePlugin
1313
{
1414
internal record PolyConfig(
15-
bool debug = false
15+
bool debug = false,
16+
bool autoUpdate = true,
17+
bool updatePrerelease = false
1618
);
1719

1820
internal const int AUTOIDX_STARTS_FROM = 1000;
@@ -49,15 +51,16 @@ public override void Load()
4951
catch
5052
{
5153
config = new();
52-
WriteConfig();
5354
}
55+
WriteConfig();
5456
UpdateConsole();
5557
logger = Log;
5658
ConfigFile.CoreConfig[new("Logging.Disk", "WriteUnityLog")].BoxedValue = true;
5759

5860
Compatibility.Init();
5961

6062
Audio.Init();
63+
AutoUpdate.Init();
6164
Loc.Init();
6265
Visual.Init();
6366
Hub.Init();

0 commit comments

Comments
 (0)