Update the game from inside it (#13)
Build / test (push) Successful in 1m53s
Build / build (push) Successful in 1m15s

"Version X is available" in the top bar is now a button that opens an
Update popup:
- Update and restart gets the download link from the server (the redirect
  is not followed, as the Gitea link needs no token), downloads the new game
  next to the running one with a progress bar, checks it (complete, and an
  MZ or ELF file), swaps it in and restarts. The quiz is saved first and
  continues. On Windows the running .exe is renamed to .old, put back if
  the swap fails, and deleted at the next start.
- Download in browser opens the .exe, the Linux package, or the release
  page on other platforms.
- From the editor, an unwritable folder or another platform, only the
  browser download is offered, with the reason.

The message now fits between the tabs and the buttons in every theme; in
Nord and Christmas it ran into Sync. The macOS export preset is removed,
and the unused download_client_* functions are replaced by
fetch_client_download_url.

Closes #13

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
SansanandClaude Opus 5.5 committed 2026-10-15 21:23:26 +02:00
1 parent c03c0e4efe
commit f39865fffc
30 files changed
+2363 -354

No files matched your search

+38 -2
View File
@@ -81,6 +81,40 @@ func test_fetch_song_passes_raw_bytes() -> void:
assert_array(received).contains_exactly([mp3])
func test_download_url_is_where_the_server_redirects() -> void:
var link: String = "https://gitea.example/releases/download/2.0.0/MusicPlayer_2.0.0.exe"
_server.respond_redirect("/download/windows", link)
var urls: Array[String] = []
MusicServerClient.fetch_client_download_url("windows",
func(url: String) -> void: urls.append(url))
assert_array(urls).contains_exactly([link])
# The token is only for the server, so the redirect is not followed with it
assert_array(_server.requested_follow_redirects).contains_exactly([false])
func test_missing_download_calls_on_failed() -> void:
var failed: Array[bool] = []
MusicServerClient.fetch_client_download_url("linux-package",
func(_url: String) -> void: pass, func() -> void: failed.append(true))
assert_array(failed).has_size(1)
@warning_ignore("unused_parameter")
func test_header_value(name: String, value: String, test_parameters: Array = [
["Location", "https://x/y"],
["location", "https://x/y"],
["X-Queue-Id", "abc"],
["Missing", ""],
]) -> void:
var headers: PackedStringArray = ["Location: https://x/y", "x-queue-id: abc "]
assert_str(MusicServerClient.header_value(headers, name)).is_equal(value)
func test_failed_request_does_not_call_back() -> void:
_server.respond_json("/music/list", [], 500)
var called: Array[bool] = [false]
@@ -402,9 +436,9 @@ func test_endpoint_paths_and_methods() -> void:
"/download", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.fetch_client_assets(noop),
"/download/list", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.download_client_windows(noop),
[func() -> void: MusicServerClient.fetch_client_download_url("windows", noop),
"/download/windows", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.download_client_linux(noop),
[func() -> void: MusicServerClient.fetch_client_download_url("linux", noop),
"/download/linux", HTTPClient.METHOD_GET],
[func() -> void: MusicServerClient.cleanup_tokens(noop),
"/token/cleanup", HTTPClient.METHOD_POST],
@@ -640,6 +674,7 @@ class _HeaderTransport:
on_complete: Callable,
_request_headers: PackedStringArray = PackedStringArray(),
_body: PackedByteArray = PackedByteArray(),
_follow_redirects: bool = true,
) -> void:
on_complete.call(200, JSON.stringify({"token": "t"} if _url.ends_with("/token") else [])
.to_utf8_buffer(), _headers)
@@ -660,6 +695,7 @@ class _RecordingTransport:
on_complete: Callable,
_headers: PackedStringArray = PackedStringArray(),
_body: PackedByteArray = PackedByteArray(),
_follow_redirects: bool = true,
) -> void:
_urls.append(url)
if url.ends_with("/token"):