19 Commits
Author SHA1 Message Date
Sansan 74fc28614f New app icon, packaged with a menu entry for Linux
Build / test (pull_request) Successful in 23s
Build / build (pull_request) Skipped
The icon is a plate wearing headphones with a red Nordic house cookie
on it. It replaces the default Godot icon, so the game window, Windows
and macOS builds use it.

Linux programs can't carry their own icon, so the build now also makes
MusicPlayer_<version>_linux.tar.gz with the game, the icon as a PNG, a
.desktop menu entry and install.sh, which installs them for the current
user. The bare Linux binary is still published.
2026-10-05 22:48:25 +02:00
Sansan 9a5806c56f Show where each item starts in the inspiration and sync lists
Items in the inspiration list and the sync result columns now each have
their own background box, alternating between two shades, with a small
gap between them. When a long name wraps over several lines it is
still clear which lines belong together. The sync columns keep their
alternating white and orange text.
2026-10-05 22:33:58 +02:00
Sansan 196a7abc35 Make the sync result look right with any number of columns
- Visible columns share the width equally, whether one or five are shown
- Long soundtrack names and errors wrap inside their column instead of
  widening the window, which pushed the title and Restart button out of
  view and cut off text
- Separators only go between columns; the extra one before the first
  column is gone
- Wider, centred panel with inner margins, and spacing above the
  Restart button
- The Re-added header no longer reads ReAdded
2026-10-05 22:26:58 +02:00
Sansan 212eda96e2 Tidy up popup layouts
- Settings: labels in one column and switches and number buttons lined
  up in another, with margins inside the panel
- Log: title and margins; text no longer touches the panel edge
- Sync result: error list centred under its header, which now reads
  Errors instead of CatchedErrors
- About: text wraps inside the panel instead of running under the
  scrollbar
- Character select: wider panel, so the right column of characters is
  no longer right next to the scrollbar
2026-10-05 21:42:53 +02:00
Sansan 65b73e3570 Rework popups and fix blurry text and player list layout
Popups:
- All nine popups now extend a shared PopupWindow class with open(),
  close() and toggle(), instead of each copying its own close-on-click
  code. Popups that need cleanup override close().
- A click outside a popup closed it and also pressed whatever was
  underneath, e.g. Reset playlist. The closing click is now consumed.
- Popups used Godot's see-through default panel, so the song list and
  titles showed through. They now use an opaque popup theme, and a
  backdrop dims the window behind them.
- The main window root now fills the screen, so the backdrop covers it.

Text and images:
- Many labels and buttons forced nearest filtering, which made text and
  icons jagged when the window is not 1920x1080. They now use the
  default smooth filtering. Only the pixel-art "no character" picture
  keeps nearest filtering when the winner window enlarges it.

Player list:
- A long player name pushed that row's character and buttons to the
  right. Names now take the free space and shorten with an ellipsis, so
  all rows line up and use the full width of the panel.
2026-10-05 21:40:40 +02:00
Sansan 7beaa7ee5c Use the Compatibility renderer
The game is a 2D interface without 3D, shaders or lights, so it does
not need Forward+. With Compatibility it uses about 25% less CPU and
30 MB less memory while running, with no visible difference.
2026-10-05 21:40:31 +02:00
Sansan f8bc4c4b71 Remove unused code
- The old character select scene, which was never shown but loaded
  every character image at startup
- Unused SyncResponse and ProgressResponse classes
- Playlist.set_last_played_answered and select/local-loading code in
  the new character select, none of which were called
- Settings.delete_player_children, identical to delete_children
- An unused variable holding a local file path
2026-10-05 21:13:30 +02:00
Sansan 59fcb3cbe3 Add one log row at a time and pad log times
The log rebuilt every row each time a point was given or taken. It now
adds just the new row, and shows times as 09:05:03 instead of 9:5:3.
2026-10-05 21:12:38 +02:00
Sansan 056ead47be Turn statistics back on for a new game and fix low winning scores
Winning turned statistics off and nothing turned them back on, while
the Settings checkbox still showed them as on. Resetting points now
turns them back on if the win turned them off, and the checkbox shows
the current state when Settings is opened.

A winning score of 1 could never be won and with 2 the first point was
not announced as match point, because the first-point check ran first.
The winning score also could not be kept from going below 1.
2026-10-05 21:12:17 +02:00
Sansan d856e03c32 Preload songs only after the song list has loaded
At startup and after resetting the playlist, loading the song list and
preloading the first song started at the same time. When the list
answered last, it cleared the playlist and dropped the preloaded song.
Preloading now starts once the list has loaded, and loading the list is
retried if it fails.
2026-10-05 21:10:42 +02:00
Sansan 05655cf81c Ignore Show answer when no song is playing
Before the first song played, currently_playing_song is -1, so pressing
Show answer marked the last song in the list as answered. That is the
preloaded song, so its title was shown as soon as it started playing,
and played?song=-1 was sent to the server. With an empty playlist it
crashed.
2026-10-05 21:09:38 +02:00
Sansan 6fdd7a2b76 Time out requests the server never answers
HTTPRequest has no timeout by default, so a server that accepts the
connection but never responds left song downloads waiting forever.
Requests now fail after 120 seconds, which triggers the normal retry.
2026-10-05 21:09:06 +02:00
Sansan d13210e32b Fill the song cache when the settings menu is closed
Raising the cache size used to have no effect until the next song was
played. Downloads now start when the settings window closes, so clicking
the number up and down while it is open does not start any downloads.
2026-10-05 20:05:48 +02:00
Sansan 74caacc9b3 Test that the song cache fills up to the cache size setting
Covers a cache size above 1: songs are preloaded until the setting is
reached, playing a song tops the cache up again, and the cache keeps
filling after a failed download once the server answers again.
2026-10-05 19:54:36 +02:00
Sansan 249462f4a1 Release from the version in project.godot
The version now lives in project.godot (application/config/version)
and Settings.version reads it from there. The Build workflow tests and
exports on every push, and on main creates the tag, uploads the builds
and creates the Gitea release when that version has no tag yet. This
replaces gitea-release.yaml, which released when a branch named after
the version was pushed.

Godot setup moves into a local .gitea/actions/setup-godot action shared
by the test and build jobs, and Godot is updated to 4.7.2 to match the
editor the project is made with.
2026-10-05 19:47:10 +02:00
Sansan 40ed4da908 Retry failed song downloads instead of getting stuck
MusicServerClient functions now take an optional on_failed callback,
called when a request fails or returns invalid JSON. Before, a failed
request was silently dropped, so a failure while preloading a song left
Playlist.is_song_downloading set forever and no more songs were fetched.

Song fetching retries the step that failed (download, queue or info)
every song_fetch_retry_seconds, so the server and the game keep the same
playlist.
2026-10-05 19:47:10 +02:00
Sansan 91adf52cf4 Replace the broken test suite with working GdUnit4 tests
The previous tests never compiled: they used GUT's assert functions,
Godot 3 APIs and reassigned autoload functions, so no test ran.

- 90 tests: unit tests for the playlist, songs, player scoring, time
  formatting, search and the server client, plus integration tests
  that run MainWindow and SyncWindow against a fake server
- Keybind tests check that Alt+<number> and Ctrl+Alt+<number> keep
  following the player rows while players are added and removed
- run_tests.sh finds Godot and runs the suite headless (~10 seconds)
- CI runs the tests before the build job in gitea-build.yaml; the
  separate test.yml used a GitHub action that does not exist
- GdUnit4 is committed instead of ignored, since the project enables
  it, and .gitea/ is no longer ignored
- Exported builds leave out tests/ and addons/gdUnit4/
2026-10-05 19:33:41 +02:00
Sansan 87ff3d899b Move server calls to MusicServerClient and give players stable ids
Server requests move out of Settings into a new MusicServerClient
autoload with one function per endpoint. It sends requests through a
swappable HttpTransport so tests can fake the server, and it frees its
HTTPRequest nodes after use.

Players now have a UUID that never changes. Their position in
Settings.player_array is only used for the Alt+<number> keybinds.
Previously the position doubled as the id, so removing a player made
points and wins land on the wrong player, and could delete another
player's point from a song.

Also fixes:
- format_time showed 59.6 seconds as "00:60"
- searches containing regex characters such as "Q*bert" broke search
- SongObject and PlayerObject were Nodes that were never freed; they
  are now RefCounted
- search matching is split out of the UI code into find_matches()
2026-10-05 19:33:41 +02:00
Sansan 4a275e6a88 Add GdUnit4 testing framework with comprehensive test coverage
- Add GdUnit4 v6.2.1 framework in addons/gdUnit4/
- Create test directory structure (tests/unit/, tests/integration/, tests/mocks/)
- Add unit tests for MusicPlayer.gd (8 tests)
- Add unit tests for MainWindow.gd (15+ tests)
- Add unit tests for Settings.gd (5 tests)
- Add unit tests for Playlist.gd (11 tests)
- Add integration tests for full flow (10+ tests)
- Add MockHTTPRequest for HTTP request mocking
- Add TestHelpers with utility functions
- Add Gitea Actions CI workflow (.gitea/workflows/test.yml)
- Add .runsettings for GdUnit4 CLI configuration
- Add run_tests.sh for convenient test execution
- Add TESTING.md with comprehensive documentation
- Add TESTING_PLAN.md with implementation details
- Update .gitignore for test artifacts
- Enable GdUnit4 plugin in project.godot

Features:
- Test button functionality (play, pause, search, sync, etc.)
- Mock REST API requests to server
- Test complete application flows
- Run tests in CI (headless mode)
- Run tests in GUI for visual feedback
- JUnit XML and HTML test reports

Generated by OpenCode AI assistant.
2026-10-01 20:55:09 +02:00

Diff Content Not Available