Skip to content

Commit 882838c

Browse files
committed
add update button and self updating
1 parent 492dd09 commit 882838c

File tree

5 files changed

+116
-15
lines changed

5 files changed

+116
-15
lines changed

ThirdPartyLicenses.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Icons by css.gg: https://github.com/astrit/css.gg/blob/main/LICENSE.md

gui/index.html

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@
8585

8686
.btn-secondary {
8787
background-color: rgb(97, 97, 97);
88-
color: white;
8988
}
9089

9190
.btn-secondary:hover:not(:disabled) {
@@ -141,6 +140,16 @@
141140
<div class="game-menu">
142141
<span class="info-label" style="display: none">Offline mode</span>
143142
<button id="launch-btn" class="btn btn-primary btn-lg" disabled>Updating...</button>
143+
<button id="update-btn" class="btn btn-secondary btn-lg" title="Check for updates" disabled>
144+
<svg width="24" height="20" viewBox="0 0 24 20" fill="none" xmlns="http://www.w3.org/2000/svg">
145+
<path
146+
d="M4.56079 10.6418L6.35394 3.94971L8.25402 5.84979C11.7312 3.6588 16.3814 4.07764 19.41 7.1063L17.9958 8.52052C15.7536 6.27827 12.3686 5.87519 9.71551 7.31128L11.2529 8.84869L4.56079 10.6418Z"
147+
fill="currentColor" />
148+
<path
149+
d="M19.4392 13.3581L17.646 20.0502L15.7459 18.1501C12.2688 20.3411 7.61857 19.9223 4.58991 16.8936L6.00413 15.4794C8.24638 17.7217 11.6313 18.1247 14.2844 16.6887L12.747 15.1512L19.4392 13.3581Z"
150+
fill="currentColor" />
151+
</svg>
152+
</button>
144153
<button id="settings-btn" class="btn btn-secondary btn-lg" title="Settings">
145154
<svg width="24" height="20" viewBox="0 0 24 20" fill="none" xmlns="http://www.w3.org/2000/svg">
146155
<path fill-rule="evenodd" clip-rule="evenodd"
@@ -179,17 +188,35 @@ <h2>Install Folder</h2>
179188
document.getElementById("folder-btn").addEventListener("click", () => {
180189
window.pywebview.api.open_install_folder();
181190
});
191+
document.getElementById("update-btn").addEventListener("click", () => {
192+
if (document.getElementById("update-btn").disabled) {
193+
return;
194+
}
195+
document.getElementById("update-btn").disabled = true;
196+
document.getElementById("launch-btn").innerText = "Updating...";
197+
document.getElementById("launch-btn").disabled = true;
198+
document.getElementById("folder-btn").disabled = true;
199+
window.pywebview.api.check_for_updates();
200+
});
182201
function archiveReady(offline) {
183-
document.getElementById("launch-btn").disabled = false;
184-
document.getElementById("folder-btn").disabled = false;
185-
document.getElementById("launch-btn").innerText = "Launch";
202+
const available = offline <= 1;
203+
document.getElementById("update-btn").disabled = false;
204+
if (available) {
205+
document.getElementById("launch-btn").disabled = false;
206+
document.getElementById("folder-btn").disabled = false;
207+
document.getElementById("launch-btn").innerText = "Launch";
208+
} else {
209+
document.getElementById("launch-btn").disabled = true;
210+
document.getElementById("folder-btn").disabled = true;
211+
}
186212
const online = offline === 0;
187-
if (!online) {
188-
document.querySelector(".info-label").innerText = "Offline mode";
189-
if (offline !== 1) {
213+
if (online) {
214+
document.querySelector(".info-label").style.display = "none";
215+
} else {
216+
document.querySelector(".info-label").style.display = "inline";
217+
if (offline > 1) {
190218
document.getElementById("launch-btn").innerText = "Install failed";
191219
}
192-
document.querySelector(".info-label").style.display = "inline";
193220
}
194221
}
195222
</script>

tc2_launcher/__main__.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import argparse
22
import multiprocessing
3+
import time
34
from pathlib import Path
45
import sys
56

@@ -8,11 +9,36 @@
89
from tc2_launcher.run import launch_game
910
from tc2_launcher.run import set_launch_options
1011
from tc2_launcher.run import default_dest_dir
12+
from tc2_launcher.run import update_self
1113

1214
version = "0.1.0"
1315

1416

1517
def main():
18+
launch_gui = False
19+
if len(sys.argv) >= 3 and sys.argv[1] == "--replace":
20+
# Replacement mode after self-update
21+
original_path = Path(sys.argv[2]).resolve()
22+
current_path = Path(sys.argv[0]).resolve()
23+
try:
24+
# Wait a moment to ensure the original process has exited
25+
time.sleep(2)
26+
# Replace the original file with the current file
27+
original_path.unlink()
28+
current_path.replace(original_path)
29+
print("Self-update applied successfully.")
30+
except Exception as e:
31+
print(f"ERROR: Failed to apply self-update: {e}")
32+
33+
launch_gui = len(sys.argv) == 3
34+
else:
35+
if update_self(version):
36+
sys.exit(0)
37+
return
38+
39+
launch_gui = len(sys.argv) == 1
40+
41+
1642
parser = argparse.ArgumentParser(description=f"TC2 Launcher v{version}")
1743
parser.add_argument(
1844
"--dest",
@@ -44,8 +70,8 @@ def main():
4470
nargs=argparse.REMAINDER,
4571
help="User launch options",
4672
)
47-
48-
if len(sys.argv) == 1:
73+
74+
if launch_gui:
4975
start_gui()
5076
return
5177

tc2_launcher/gui.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ def set_launch_options(self, options):
2424
def open_install_folder(self):
2525
open_install_folder()
2626

27+
def check_for_updates(self):
28+
res = update_archive()
29+
webview.windows[0].evaluate_js(f"archiveReady({res});")
30+
2731

2832
def get_entrypoint():
2933
def exists(path):

tc2_launcher/run.py

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import os
22
import sys
33
import json
4+
import tempfile
45
from pathlib import Path
56
from typing import Optional, Tuple
67
from shutil import rmtree
@@ -57,8 +58,51 @@ def _find_asset(release: dict, asset_filter: str) -> Optional[Tuple[str, str]]:
5758
return None
5859

5960

60-
def update_self():
61-
pass
61+
def update_self(current_version: str) -> bool:
62+
try:
63+
release = _get_latest_release(LAUNCHER_REPO)
64+
tag = release.get("tag_name", "").lstrip("v")
65+
except Exception as e:
66+
print(f"ERROR: Failed to get self-update info: {e}")
67+
return False
68+
69+
if tag == current_version:
70+
return False
71+
72+
print(f"Self-update available: {current_version} -> {tag}")
73+
74+
if os.name == "nt":
75+
asset_filter = ".exe"
76+
else:
77+
asset_filter = "-linux"
78+
asset = _find_asset(release, asset_filter)
79+
if not asset:
80+
print(f"ERROR: No asset matching '{asset_filter}' found in self-update release {tag}.")
81+
return False
82+
83+
asset_name, download_url = asset
84+
85+
# Step 4: Download asset to temp file
86+
temp_dir = Path(tempfile.gettempdir())
87+
temp_path = temp_dir / f"TC2LauncherUpdate_{tag}" / asset_name
88+
print(f"Downloading self-update to {temp_path}...")
89+
try:
90+
_download(download_url, temp_path)
91+
except Exception as e:
92+
print(f"ERROR: Failed to download self-update: {e}")
93+
return False
94+
print("Self-update download complete.")
95+
96+
current_path = Path(sys.argv[0]).resolve()
97+
print("Launching self-update...")
98+
try:
99+
filtered_args = [arg for arg in sys.argv[1:] if arg != "--replace"]
100+
run_non_blocking([temp_path, "--replace", current_path] + filtered_args)
101+
sys.exit(0)
102+
return True
103+
except Exception as e:
104+
print(f"Failed to launch self-update: {e}")
105+
return False
62106

63107

64108
def _read_data(path: Path) -> dict:
@@ -144,7 +188,7 @@ def update_archive(
144188

145189
state = read_state(dest)
146190
current_tag = state.get("tag")
147-
if not force and current_tag == tag:
191+
if not force and current_tag == tag and fail_code == 1:
148192
print("Latest asset already downloaded.")
149193
return 0
150194

@@ -153,7 +197,6 @@ def update_archive(
153197
else:
154198
asset_filter = "game-linux.zip"
155199
asset = _find_asset(release, asset_filter)
156-
157200
if not asset:
158201
print(f"ERROR: No asset matching '{asset_filter}' found in release {tag}.")
159202
return fail_code
@@ -168,7 +211,7 @@ def update_archive(
168211
asset_path = dest / asset_name
169212
game_dir = dest / "game"
170213

171-
print("Downloading latest asset...")
214+
print(f"Downloading latest asset to {asset_path}...")
172215
try:
173216
_download(download_url, asset_path)
174217
except Exception as e:

0 commit comments

Comments
 (0)