Releases: ikostan/SkyLockAssault
Milestone 8: Audio crackling and delays in non threaded web exports main thread blocking warning in threaded exports, Part #13
Summary @sourcery-ai
Refine web export security and analysis workflows while extending audio configuration to support a dedicated rotors channel.
New Features:
- Add configurable rotors audio bus and volume setting integrated into global settings and the options volume slider.
- Enable progressive web app support for the web export preset.
Bug Fixes:
- Improve robustness of the index.js patching script by switching to fixed-string detection and adding an idempotent pre-check to avoid double patching.
- Audio crackling and delays in non threaded web exports main thread blocking warning in threaded exports (#266) @ikostan
- Rotors Volume Level Not Persisted Across Sessions #264
Enhancements:
- Centralize audio bus names into global constants to avoid hard-coded strings and reduce configuration drift.
- Reorder the options menu input handler without changing behavior to keep node initialization grouped logically.
Build:
- Enable advanced export options and configure PWA settings for the Web export preset.
- Add a flatten-export step to the CodeQL web export job to normalize the export directory layout before scanning.
CI:
- Re-enable the CodeQL workflow in the main lint/test/deploy pipeline and make deploy depend on its completion.
- Update CodeQL GitHub Action versions across CodeQL and Snyk workflows and refresh the pinned SHA for the Trivy SARIF upload action.
- Invoke the index.js patch script in both CodeQL and itch.io deploy workflows to keep browser exports consistently patched.
Deployment:
- Ensure web export archives are unzipped, patched, flattened, and re-zipped before deployment to itch.io.
🧰 Maintenance
- Audio crackling and delays in non threaded web exports main thread blocking warning in threaded exports (#266) @ikostan
- Bump github/codeql-action from 4.31.8 to 4.31.9 (#265) @ikostan @dependabot
Reviewer's Guide
Refines audio bus handling with a new rotor SFX volume path, reorders the options menu input callback, and hardens the web export/index.js patching plus CI/security workflows (CodeQL, Snyk, Trivy, itch deploy) to work on flattened, patched web exports with PWA enabled.
File-Level Changes
| Change | Details | Files |
|---|---|---|
| Introduce dedicated rotor SFX volume support and centralize audio bus naming via Globals constants. |
|
scripts/globals.gdscripts/volume_slider.gd |
| Reorder options menu input handling without changing its behavior. |
|
scripts/options_menu.gd |
| Make the index.js web export patch script idempotent and more robust via fixed-string matching. |
|
.github/scripts/patch_index_js.sh |
| Tighten CI/security workflows and ensure web exports are flattened and patched before CodeQL/Snyk/Trivy scanning and itch.io deploy. |
|
.github/workflows/lint_test_deploy.yml.github/workflows/codeql.yml.github/workflows/snyk.yml.github/workflows/trivy.yml.github/workflows/deploy_to_itch.yml |
| Adjust web export preset to enable advanced options and PWA support for the Web build. |
|
export_presets.cfg |
Assessment against linked issues
| Issue | Objective | Addressed | Explanation |
|---|---|---|---|
| #262 | Resolve audio crackling and delays in non-threaded web exports (e.g., Web_thread_off) so that audio playback is smooth. |
❌ | The PR modifies audio volume handling (new rotor SFX bus, use of bus name constants, persistence) but does not change any Godot or Web export settings that are specific to the non-threaded Web_thread_off preset, nor does it adjust audio buffering or driver settings. No changes are made to that export preset or to non-threaded audio behavior, so the root cause of crackling in non-threaded web exports is not addressed. |
| #262 | Eliminate or mitigate the Emscripten warning about blocking on the main thread in threaded web exports (the Web preset), reducing the risk of main-thread stalls. |
❌ | The PR enables PWA and advanced options for the Web export and hardens a custom index.js patch script, but the patch only wraps the Module[handler] function for print/printErr and is described as a security/reliability improvement. There is no explicit change to remove or alter the synchronous behavior Emscripten warns about, nor any mention that this patch addresses the main-thread blocking warning. The warning condition itself is not clearly targeted in the diff. |
| #262 | Adjust web export configuration so that both threaded and non-threaded web builds have stable audio without regressions or new warnings. | ❌ | Export configuration changes are limited to the threaded Web preset (enabling advanced options and PWA). The non-threaded preset mentioned in the issue (Web_thread_off) is not modified at all. While PWA and SharedArrayBuffer support may improve threaded behavior, they do not address non-threaded audio quality, and there is no clear link in the PR between these configuration changes and resolving the reported audio issues or warnings across both export modes. |
| #264 | Add a dedicated rotors_volume setting in globals.gd and ensure it is loaded, saved, and applied to the appropriate audio bus across sessions. | ✅ | |
| #264 | Wire the rotors volume slider to the rotors audio bus so that changing the slider updates Globals.rotors_volume and participates in the existing debounced settings save logic. | ✅ |
Milestone 8: Ignore Godot web export directories, Part #12
Summary
Adjust web export workflows to rely on Godot-generated export directories while stopping tracking of generated web build artifacts.
To remove the contents of the export/web and export/web_thread_off directories from the GitHub repository (while keeping the directories themselves if needed, or deleting them entirely), we'll use Git commands. This is common in Godot 4.5 game dev workflows because export folders contain generated build files (like index.js, .html, .wasm, etc.) that shouldn't be versioned—they bloat the repo and can cause issues with CI/CD or sharing the SkyLockAssault project. Instead, regenerate them via Godot's export menu when needed.
Update the CodeQL workflow to export the Godot project to Web before analysis and apply a security patch to the generated index.js.
Generate a Godot Web export as part of the CodeQL workflow so the scan runs on actual built web artifacts and centralize shared export post-processing logic into reusable scripts.
Bug Fixes:
- Prevent unintended exposure and misuse of the Godot web worker message handler by restricting patched index.js behavior to print and printErr handlers.
Enhancements:
- Ensure CI creates and flattens Godot web export directories in both deployment and browser test workflows to match preset export paths.
- Update the Web_thread_off export preset to be non-runnable to better reflect its intended use as a test/export target.
Build:
- Stop versioning generated Godot web export files by removing tracked artifacts from export/web and export/web_thread_off.
- Extend the CodeQL GitHub Actions workflow to perform a Godot 4.5 Web export with caching and directory flattening before scanning.
- Update the CodeQL workflow to run a Godot 4.5 Web export with caching and directory flattening before initializing analysis.
- Add Godot Web export, directory flattening, and index.js security patching steps to the CodeQL analysis workflow so it no longer assumes pre-existing exports.
- Refactor browser_test and deploy_to_itch workflows to use shared scripts for flattening export directories and applying the index.js security patch.
CI:
- Add Web export, directory normalization, and a post-processing security patch of index.js to the CodeQL analysis workflow.
Chores:
- Introduce shared flatten_export.sh and patch_index_js.sh scripts to deduplicate and harden export directory handling and index.js patching across workflows.
Reviewer's Guide
Configure Godot web exports to use preset export paths in CI, post-process the generated index.js for security, disable running the non-threaded web preset directly, and stop tracking generated web export artifacts in version control.
Updates the CodeQL workflow to perform a Godot 4.5 web export (including directory flattening and index.js security patching) before analysis, and centralizes shared export/post-processing logic into reusable bash scripts used by CodeQL, browser test, and deployment workflows.
File-Level Changes
| Change | Details | Files |
|---|---|---|
| Update CI deploy workflow to prepare web export directory, honor Godot preset export path, normalize layout, and patch exported index.js for safer message handling. |
|
.github/workflows/deploy_to_itch.yml |
| Update browser test workflow to export to a non-threaded web preset directory using preset paths, normalize layout, and apply the same index.js security patch. |
|
.github/workflows/browser_test.yml |
| Make the Web_thread_off Godot export preset non-runnable to prevent accidental use in development or deployment. |
|
export_presets.cfg |
| Stop tracking generated Godot web export artifacts and rely on Godot to regenerate them. |
|
.gitignoreexport/web/index.apple-touch-icon.png.importexport/web/index.audio.position.worklet.jsexport/web/index.audio.worklet.jsexport/web/index.htmlexport/web/index.icon.png.importexport/web/index.jsexport/web/index.png.importexport/web_thread_off/index.audio.position.worklet.jsexport/web_thread_off/index.audio.worklet.jsexport/web_thread_off/index.htmlexport/web_thread_off/index.js |
| Have CodeQL analyze a built Godot web export instead of an empty export/web directory. |
|
.github/workflows/codeql.yml |
| Deduplicate and harden export directory flattening logic across workflows. |
|
.github/scripts/flatten_export.sh.github/workflows/browser_test.yml.github/workflows/deploy_to_itch.yml |
| Centralize and strengthen the index.js security patch for web exports. |
|
.github/scripts/patch_index_js.sh.github/workflows/browser_test.yml.github/workflows/deploy_to_itch.yml.github/workflows/codeql.yml |
Assessment against linked issues
| Issue | Objective | Addressed | Explanation |
|---|---|---|---|
| #259 | Update the CodeQL GitHub Actions workflow to perform a Godot Web export (mirroring the deploy_to_itch.yml steps) so that export/web is populated before listing files and running CodeQL analysis, preventing failures due to an empty export/web directory. | ✅ |
Possibly linked issues
- #N/A: PR adds Godot web export steps to codeql.yml so export/web is populated, resolving the CodeQL failure.
Milestone 8: Multi-Thread for Production; Disabled for Playwright Testing., Part #11
Description:
Enable multi-thread in HTML5 export for production to fix music lags; disable for Playwright tests to avoid SAB/COI issues.
Why is this useful? (e.g., for learning Godot web exports)
Improves audio perf (e.g., rotors/music loop smooth) in browser prod; keeps tests stable without headers. Reason: Single-thread causes audio lags (e.g., music stutters after ~6s); threads require COOP/COEP headers for SAB, which local/CI servers lack, failing tests with "DataCloneError" – disable for test reliability, enable for prod perf.
Proposed Implementation (optional):
- Workflow (browser_test.yml): Use special preset "Web_thread_off" with threads off for Playwright testing (avoids SAB/COI errors).
* Production: use preset "Web" with threads on for production.
Additional Context:
Lags from single-thread; tests fail with threads (SAB error). Win10/Linux CI tested.
Changes:
The approach of using two separate presets is clean and robust for Godot 4.5 – "Web" with threads on for production (smooth audio like rotors/music without lags), and "Web_thread_off" with threads off for Playwright testing (avoids SAB/COI errors).
- In browser_test.yml (CI workflow): Use "Web_thread_off" to disable threads in "Export Godot to Web" step for testing. @ikostan
- Updated browser_test.yml snippet (no other changes): @ikostan
- name: "Export Godot to Web" id: "export" uses: "firebelley/godot-export@930577654862a320eef793f399ee911b4479efb9" with: # yamllint disable rule:line-length godot_executable_download_url: "https://github.com/godotengine/godot/releases/download/4.5-stable/Godot_v4.5-stable_linux.x86_64.zip" godot_export_templates_download_url: "https://github.com/godotengine/godot/releases/download/4.5-stable/Godot_v4.5-stable_export_templates.tpz" relative_project_path: "./" relative_export_path: "./export/web" archive_output: false cache: false presets_to_export: "Web_thread_off" # yamllint enable rule:line-length
- changed archive_output: true to archive_output: false in the browser_test.yml workflow to avoid unnecessary zipping of the web export folder during CI tests. This speeds up the export step (no archive creation) while keeping the files ready for the local server – essential for learning efficient CI setups without extra overhead for Playwright testing. @ikostan
- the line use_preset: "Web (Runnable)" in the browser_test.yml workflow (using the firebelley/godot-export action) refers to selecting a specific export preset from your project's Export dialog. In Godot, export presets are customizable configurations for building your game for different platforms. "Web (Runnable)" is a standard preset for HTML5/web exports that sets up the game to be runnable in browsers – it includes essential options like JavaScript evaluation, file compression, and potentially threads (which we disabled for testing to avoid SAB issues). This preset ensures the exported files (index.html, .js, .wasm, .pck) are optimized for web deployment, allowing quick CI testing without manual tweaks. @ikostan
- cache: false -> To ensure fresh HTML5 exports for each CI test run, preventing stale builds from cached artifacts that could cause inconsistent Playwright results (e.g., audio loops like rotors). @ikostan
Milestone 8: Player Upgrade: New P-38 Sprite, Dual Rotors with Animation & Stereo Sound, Part #9
New P-38 Sprite for player, Dual Rotors with Animation & Stereo Sound + Disable Thread Support
This release combines two PRs:
- [FEATURE] Player Upgrade: New P-38 Sprite, Dual Rotors with Animation. #252:
Enhances P-38 realism in SkyLockAssault: Visual rotor spin + immersive stereo audio (left/right engines). Teaches 2D animation, audio buses/panning, scene instancing, and options UI/sliders. Scalable for throttle/fuel sync later.
- Disable Thread Support in the Godot 4.5 export preset #253
Disabling Thread Support avoids the SharedArrayBuffer (SAB) issues in browsers, letting the web build load smoothly without needing COOP/COEP headers on the local server. It's a solid workaround for stable testing while learning game dev on Win10 64-bit, especially since your rotors animation and sound loop fine in single-thread mode for now (though threads could optimize perf later).
Why is this useful? (e.g., for learning Godot web exports)
Enhances P-38 realism in SkyLockAssault: Visual rotor spin + immersive stereo audio (left/right engines). Teaches 2D animation, audio buses/panning, scene instancing, and options UI/sliders. Scalable for throttle/fuel sync later.
Summary
Add animated dual-rotor visuals and stereo engine audio to the P-38 player, and adjust player bounds and projectile scaling to match the new art assets.
New Features:
- Introduce left and right rotor scenes with animated sprites and stereo AudioStreamPlayer2D nodes for the player aircraft. @ikostan
- Add a rotor SFX volume control scene and hook it into the options menu for in-game audio adjustments. @ikostan
Enhancements:
- Update the Player scene to use a new P-38 sprite and polygon-based collision, recalculating movement bounds from the sprite size instead of the collision shape. @ikostan
- Reduce projectile sprite scale to better fit the updated visual style. @ikostan
- Adjust the default audio bus layout to support the new rotor SFX routing. @ikostan
- Rotor Sound Stops After ~6s – Switch to Stream Playback (Godot 4.5, Win10 64-bit):
Cause (Game Dev Learning): Godot's HTML5 exports default to Sample playback mode for low-latency audio in browsers (WebAudio API). This mode prioritizes quick SFX (e.g., guns) but has bugs with looping (your ~6s stop = file end without repeat). OGG loops fail despite import ☑ or script .loop=true – web ignores them in Sample. PC uses full Stream mode (no issue). Weaker hardware/browsers worsen it (freezes/crackles). @ikostan - In _ready() you only null-check rotor_left_sfx but then unconditionally access rotor_right_sfx; consider independently validating both nodes (or failing fast with a clear error) to avoid a potential null dereference if only one rotor SFX is missing. @sourcery-ai
- issue (bug_risk): Avoid shadowing the member screen_size with a local variable.
This line redeclares screen_size as a local var, so the existing member var screen_size: Vector2 at the top of the script is never updated in _ready(). If other methods read the member, it will keep its default value. Assign to the member directly (remove var here) or rename the local variable. @sourcery-ai - The movement bounds now depend on player_sprite.texture.get_size(), but there’s no guard for a missing texture; adding a check with a sensible fallback (or asserting the texture’s presence) will prevent hard-to-debug crashes if the sprite setup changes. @sourcery-ai
- The use of / 4 scale factors when computing player_x_min/max and player_y_min/max is a bit opaque; it might be clearer to derive these from explicit constants (e.g., margin or hitbox scale) or document why a quarter of the texture size is used so future sprite swaps remain straightforward. @sourcery-ai
Build:
- Update the web export index.html configuration to reference the new, larger .pck size generated by the updated assets.
1. New Player Sprite
- Replaced old
Sprite2Dtexture inplayer.tscn(CharacterBody2D/Sprite2D). - New: Colorful top-down P-38 Lightning (e.g., cyan/yellow/green pixel art from free asset packs like OpenGameArt).
- Inspector: Texture imported as PNG/JPG, Centered ☑, Offset (0,0).
- Position: Centered under
CharacterBody2Dfor collision/pivot. - Learning: Improves visual fidelity; adjust scale (e.g., 0.25) for game view.
2. Dual Rotors Setup (Left/Right Scenes)
- Created
rotor_left.tscn&rotor_right.tscn: Node2D root ("RotorLeft/Right") >AnimatedSprite2Dchild +AudioStreamPlayer2Dchild. - Instanced in
player.tscnunderCharacterBody2D:RotorLeftat Position (-12.8, -25.0),RotorRightat (+12.8, -25.0) – wing-aligned for P-38 symmetry. - Script (
player.gd):@onready var rotor_left: Node2D = $CharacterBody2D/RotorLeft& right. Gets children for play(). - Learning: Scene instancing = reusable (e.g., add particles later). Positions via gizmo for precision.
3. Rotor Animation
AnimatedSprite2Din each rotor.tscn: SpriteFrames resource with 11 JPEG frames (propeller rotations).- Settings: FPS=24.0, Loop ☑, All frame durations=0.04 (select all > set).
- Auto-start: In
_ready()(player.gd):rotor_left.get_node("AnimatedSprite2D").play("default")& right. - Matches ~2600 RPM P-38 visual at game speed.
- Learning: Frame animation > rotation for detailed blur/realism. Throttle bonus:
speed_scalelinkable to fuel/speed.
4. Rotor Sound (Stereo SFX)
AudioStreamPlayer2Din each rotor.tscn: Stream =res://sounds/rotor.ogg(free twin-prop loop from Freesound/OpenGameArt, imported with Loop ☑).- Settings: Autoplay ☑ (backup), Volume dB=-5 (balance), Max Distance=2000, Attenuation=1/2000, Panning Strength=1.0.
- Script (
player.gd):@onready var rotor_left_sfx: AudioStreamPlayer2D = rotor_left.get_node("AudioStreamPlayer2D")& right. - In
_ready():rotor_left_sfx.play()& right (after buses set). - OGG loop:
ogg_stream.loop = true; ogg_stream.loop_offset = 0.0(prep in Audacity for seamless). - Learning: 2D nodes = positional audio (slight pan on plane turns).
5. Audio Buses & Panning (L/R Split)
- New buses: SFX Rotors (child of Master, for group volume).
- Sub-buses: SFX Rotor Left (Panner Pan=-1.0, hard LEFT speaker) & SFX Rotor Right (Pan=+1.0, hard RIGHT).
- Script:
rotor_left_sfx.bus = "SFX Rotor Left"& right. - Detune realism: In throttle setter –
rotor_left_sfx.pitch_scale = engine_throttle * 0.98(low) & right *1.02 (high). - Learning: Buses inherit volume (SFX slider controls all). Panner = cheap stereo immersion (headphones test!).
6. Options Menu: Rotors Volume Slider
- In
options_menu.tscn: AddedSFXRotorsVolumeControl(HSlider or similar, child of VBoxContainer). - Script (e.g.,
sfx_rotors_volume_control.gd): Links to "SFX Rotors" bus volume (e.g.,AudioServer.set_bus_volume_db(AudioServer.get_bus_index("SFX Rotors"), value)). - UI: Label "ROTORS VOLUME", slider 0-100 (map to dB -72 to 0).
- Learning: Teaches AudioServer API + UI signals for dynamic settings.
7. Overall Integration & Testing
- All in
player.gd_ready(): Auto-starts visuals/sounds on main_scene load. - Throttle/Fuel link: Export
engine_throttle, setter updates sprite speed_scale + audio pitch_scale (ties to fuel drain). - Test: F5 main_scene.tscn – spin + whir instant, L/R split, options slider fades rotors (inherits SFX).
- No errors: Fixed OGG loop (bool, not enum). Positions/collisions unaffected.
Additional Context:
- Assets: Rotor frames (JPEG), sound (OGG) – import/reimport for changes.
- Future: Sync throttle to input/fuel, add engine start/stop.
- Win10 64-bit: VSync ☑ (Project Settings) for smooth animation/audio.
Reviewer's Guide
Implements animated dual-rotor visuals and stereo audio for the P-38 player ship, updates collision and boundary logic to match the new sprite/scene setup, tweaks projectile visuals, and adds scene/assets for rotor instances and audio volume control (reflected in the web export size).
File-Level Changes
| Change | Details | Files |
|---|---|---|
| Hook up animated dual rotors and stereo rotor audio to the player ship. |
|
scripts/player.gdscenes/Player.tscnscenes/rotor_left.tscnscenes/rotor_right.tscnscenes/sfx_rotors_volume_control.tscndefault_bus_layout.tresscenes/options_menu.tscn |
| Align player collision and screen-boundary logic with the new P-38 sprite/scene configuration. |
|
scripts/player.gdscenes/Player.tscn |
| Adjust projectile visual scale to better match th... |
Milestone 8: Game Assets Pack, Part #8
Changes
I compiled this list based on searches across OpenGameArt. Chabull has about 15-20 submissions, mostly from 2013, focusing on 2D sprites suitable for top-down views. Not all are WW2-themed (some are fantasy icons), but since we like them all, I've included them. For each, download the zip/file from the page.
| Asset Title | Description | Link |
|---|---|---|
| Explosions | Pixel art explosion animations in various sizes, useful for combat effects in your airplane game. | https://opengameart.org/content/explosions-0 |
| Train | Top-down train sprites including locomotive and wagons, great for railroad backgrounds or targets. | https://opengameart.org/content/train |
| Bridges | Assorted bridge sprites (wooden, stone) in top-down view, with segments for building longer ones—perfect for engineering structures or destructible elements. | https://opengameart.org/content/bridges |
| Boss Aircraft | Large boss-level airplane sprites in WW2 style, ideal for end-level enemies in your multi-level game. | https://opengameart.org/content/boss-aircraft |
| Buildings / Bunkers / Weapon Platforms | Top-down structures like buildings, bunkers, and turrets, with normal and destroyed states—excellent for city views, ruins, or targets. | https://opengameart.org/content/buildings-bunkers-weapon-platforms |
| Tanks and Trucks | Ground vehicles like tanks and trucks in top-down WW2 style, for adding variety to terrain or enemies. | https://opengameart.org/content/tanks-and-trucks |
| Aircrafts | Multiple airplane sprites (fighters, bombers) in top-down view, core for your combat and player planes. | https://opengameart.org/content/aircrafts |
| Trees and Bushes | Natural vegetation sprites for grass/forest backgrounds, top-down style to layer in levels. | https://opengameart.org/content/trees-and-bushes |
| Ships with Ripple Effect | Naval ships with water ripple animations, top-down—great for water-based levels or backgrounds. | https://opengameart.org/content/ships-with-ripple-effect |
| Game HUD // Top Down Shoot Em Up | UI elements like health bars, ammo counters for shoot-em-up games, fits your weapons and fuel management. | https://opengameart.org/content/game-hud-top-down-shoot-em-up |
| Book Icon | Simple book icon sprite, possibly for menus or collectibles (fantasy style). | https://opengameart.org/content/book-icon |
| Shield and Swords | Weapon icons (shields, swords), could repurpose for upgrades or menus. | https://opengameart.org/content/shield-and-swords |
| Game Icons | Assorted icons for items, useful for UI in difficulty adjustments or levels. | https://opengameart.org/content/game-icons |
| Potions | Potion sprites, fantasy but could be fuel or power-ups in your game. | https://opengameart.org/content/potions |
| Boxes and Barrels | Crates and barrels for destructible props or factory backgrounds. | https://opengameart.org/content/boxes-and-barrels |
| Upgrade Screen / Shoot Em Up Game | Upgrade menu assets for shoot-em-ups, ties into multiple weapons and difficulty. | https://opengameart.org/content/upgrade-screen-shoot-em-up-game |
Milestone 8: Implement Controller Support, Part #7
Implement Gamepad/Controller Input Support with Remapping for Enhanced Accessibility and Gameplay.
Add joypad support for Gamepad PC Controller and arrow keys for movement, inspired by Godot tutorial on controller input management.
Summary
Add robust controller and keyboard remapping support, extend input settings persistence to handle multiple input types and formats, and refine player fuel UI/logic and projectile lifecycle, with comprehensive tests for all changes.
New Features:
- Support remapping of keyboard, joypad buttons, and joypad axes via an in‑game InputRemapButton, including readable labels and multi-event bindings. @ikostan
- Persist input mappings for keys and controllers across sessions, including multiple events per action and new actions such as pause, with migration from the old config format. @ikostan
Bug Fixes:
- Prevent potential errors when freeing projectiles by validating instances before queue_free and using a dedicated lifetime timer node.
- Ensure player movement and fuel systems respect new input actions and clamped fuel values, avoiding negative fuel and inconsistent UI state.
Enhancements:
- Refactor settings loading/saving to serialize all input events per action, add robust deserialization with validation and logging, and preserve project defaults when no overrides exist. @ikostan
- Improve the fuel bar to initialize and update via a dedicated helper that smoothly interpolates colors across fuel thresholds. @ikostan
- Simplify and harden player tests to use direct scene instantiation and add coverage for movement, clamping, and fuel behavior. @ikostan
- Expand and modernize tests for settings and input remap behavior, covering controller inputs, backward compatibility, malformed configs, and migration flags. @ikostan
Tests:
- Greatly expand GdUnit tests for settings, input remapping, and player behavior, including keyboard/controller persistence, migration paths, label rendering, remap flows, movement, clamping, and fuel UI. @ikostan
Reviewer's Guide
Adds configurable keyboard and controller input support (including axes) with persistent remapping, updates player movement and fuel UI to use the new actions, and significantly expands tests and settings handling to cover the new input model, migration, and edge cases. @sourcery-ai
File-Level Changes
| Change | Details | Files |
|---|---|---|
| Implement multi-device, multi-event input mapping persistence with backward-compatible serialization and migration logic. |
|
scripts/settings.gdtest/test_settings.gd |
| Enhance InputRemapButton to support remapping and displaying keyboard keys, joypad buttons, and axes, and add comprehensive tests. |
|
scripts/input_remap_button.gdtest/test_input_remap_button.gd |
| Align player movement and fuel UI behavior with the new input actions and improve fuel bar feedback logic and tests. |
|
scripts/player.gdscripts/bullet.gdtest/test_player.gd |
Assessment against linked issues
| Issue | Objective | Addressed | Explanation |
|---|---|---|---|
| #238 | Add controller/gamepad input support (buttons and axes) for core gameplay actions (movement, fire, next_weapon) and integrate it into the existing input system and gameplay code. | ✅ | |
| #238 | Extend the input remapping system (including the options menu wiring) to support remapping keyboard keys, controller buttons, and controller axes, and persist these mappings via the Settings singleton with backward compatibility. | ✅ | |
| #238 | Provide arrow keys as an alternative movement scheme alongside existing controls (e.g., WASD) within the new action setup. | ✅ | |
| #240 | Guard against out-of-range access when logging the newly bound input event in InputRemapButton by checking the events array size before indexing with action_event_index. | ✅ | |
| #240 | Refactor the logging code to use a local events array variable and safely skip or simplify the log when the index is out of range, avoiding runtime errors. | ✅ | |
| #241 | Guard deserialization of joybtn: mappings in settings.gd so that malformed strings (e.g. missing or invalid indices/devices) do not index beyond parts.size(), are skipped safely, and optionally log a warning. |
✅ | |
| #241 | Guard deserialization of joyaxis: mappings in settings.gd so that malformed strings (e.g. missing or invalid axis/values/devices) do not index beyond parts.size(), are skipped safely, and optionally log a warning. |
✅ | |
| #242 | Clamp current_fuel before any UI or percentage calculations, and compute fuel_percent / lerp_factor and fuel bar color based on this clamped value so movement and UI stay consistent. |
✅ | |
| #243 | Eliminate the HTML5 runtime error caused by the projectile lifetime lambda in bullet.gd ("Lambda capture at index 0 was freed" and subsequent null queue_free), ensuring bullets are safely freed on hit or timeout without console errors. | ✅ | |
| #242 | Clamp current_fuel before any UI or percentage calculations, and compute fuel_percent / lerp_factor and fuel bar color based on this clamped value so movement and UI stay consistent. |
✅ | |
| #244 | Using event.key_label may be invalid in Godot 4 and could break key display. | ✅ | |
| #245 | lerp_factor and progress_bar_bg_color are set but no longer used in the new fuel bar logic. | ✅ | |
| #247 | Treating all ConfigFile load errors as “no settings file” hides parse/IO problems.. | ✅ |
Possibly linked issues
- #ISSUE_NUMBER: PR delivers the requested controller support, remapping (keys, buttons, axes), and updates gameplay and settings accordingly.
- #FEATURE: PR implements the requested persistent keyboard remapping (via Settings and InputRemapButton), plus extra controller and gameplay changes.
Milestone 8: Bug Fixes & Maintenance, Part #6
Summary
- Prevent the options menu from briefly allowing pause toggling when opened and refresh CI and build-related configuration.
- Ensure the global options menu state is managed centrally and robustly, while refreshing CI workflows and web export configuration.
🚀 Features
- 224 feature options open is only set in options menu ready which leaves a small window where esc can still toggle pause (#236) @ikostan
🧰 Maintenance/CI
- 224 feature options open is only set in options menu ready which leaves a small window where esc can still toggle pause (#236) @sourcery-ai
- Bump github/codeql-action to v4.31.8 across CodeQL and Snyk workflows and refresh the pinned upload-sarif SHA in the Trivy workflow. @dependabot
- Dependabot/GitHub actions/GitHub/codeql action 4.31.8 (#235) @dependabot
- Dependabot/GitHub actions/actions/cache 5 (#234) @dependabot
- Upgrade all uses of actions/cache in browser tests, deployment, linting, and yamllint workflows to v5. @dependabot
- Merge from master (#233) @ikostan
- Merge from master (#232) @ikostan
Issue Addressed:
- Now that Globals.options_open is set in load_options, the commented-out assignment in options_menu.gd is dead code—either remove it or replace it with a brief comment explaining that the flag must be controlled centrally from load_options to avoid future confusion or accidental reintroduction.
- Since Globals.options_open is now flipped before instantiating the options menu, consider what should happen if instantiation fails or the node is freed unexpectedly (e.g., wrap instantiation in a match/if and reset the flag or connect to a destruction signal) to avoid leaving the global flag stuck in the open state.
- In globals.gd, the comments ## globals.gd (add after load_options func) and ## globals.gd (update handler) read like review notes rather than in‑code documentation—consider removing or replacing them with a concise description of the handler’s behavior to avoid confusion.
- In test_unexpected_exit_resets_flag, the spy_globals variable is created but never used; consider removing it to keep the test focused and avoid confusion about intended spying behavior.
Bug Fixes:
- Set the global options_open flag before instantiating the options menu to eliminate the short window where the pause state could still be toggled with ESC.
- Set the global options_open flag before instantiating the options menu and handle failed instantiation or unexpected exits to avoid stuck pause state and brief ESC toggling window.
Enhancements:
- Centralize options menu lifecycle handling in Globals, including an unexpected-exit handler that cleans up global references.
Build:
- Update the exported web index.html configuration to align with the current web build output.
Tests:
- Expand options teardown tests to cover load_options-based flag management, hidden menu restoration, and unexpected options exit handling. @sourcery-ai
Reviewer's Guide
Refactors options menu loading/teardown so the global options_open flag is set and cleared centrally and robustly, adds tests for unexpected teardown behavior, removes now-redundant flag setting in options_menu.gd, and refreshes several CI/build workflows (CodeQL, actions/cache, SARIF upload) to newer versions.
File-Level Changes
| Change | Details | Files |
|---|---|---|
| Centralize and harden options menu lifecycle handling so options_open is set before instantiation and reliably cleared, including on failed or unexpected exits. |
|
scripts/globals.gdscripts/options_menu.gd |
| Expand options teardown tests to reflect the new lifecycle behavior and cover unexpected exit handling. |
|
test/test_options_teardown.gd |
| Refresh CI and security scan workflows to newer action versions and correct pinned SHAs. |
|
.github/workflows/codeql.yml.github/workflows/browser_test.yml.github/workflows/snyk.yml.github/workflows/deploy_to_itch.yml.github/workflows/gdlint.yml.github/workflows/trivy.yml.github/workflows/yamllint.yml |
| Update exported web configuration to match current build output. |
|
export/web/index.html |
Assessment against linked issues
| Issue | Objective | Addressed | Explanation |
|---|---|---|---|
| #224 | Set Globals.options_open to true in Globals.load_options before the options menu is instantiated/added, and ensure it is only reset via the options menu teardown/close path (including safe handling of instantiation failure or unexpected exit). | ✅ | |
| #224 | Remove responsibility for managing Globals.options_open from options_menu._ready so that it no longer sets (or resets) the flag during menu creation. | ✅ | |
| #224 | Ensure all code paths that open the options menu go through Globals.load_options so the options_open flag is set before any add_child(options_instance) call. | ✅ |
Possibly linked issues
- #223: Yes. The PR moves options_open into load_options, centralizes its reset, adds tests, and fixes the ESC pause window.
- #unknown: PR adds central load_options handling and unexpected-exit reset for options_open, fixing the stuck-true options teardown bug
Milestone 8: Maintenance, Part #5
Summary
Update documentation and CI configuration for maintenance and formatting improvements.
CI:
- Bump the pinned SHA for the markdownlint-cli2 GitHub Action in the README lint workflow.
Documentation:
- Align SECURITY policy Godot version references with current supported release and clarify example environment. @sourcery-ai
- Reflow documentation text in Docker local test server and contributing guide for better readability. @sourcery-ai
- Annotate README music credits section with a markdownlint directive to allow longer lines. @sourcery-ai
🧰 Maintenance/Bump actions
- Maintenance (#226) @ikostan
- Dependabot/GitHub actions/actions/upload artifact 6 (#230) @dependabot
- Merge from master (#229) @ikostan
- Dependabot/GitHub actions/codecov/codecov action 5.5.2 (#228) @dependabot
- Merge from master (#227) @ikostan
- Dependabot/GitHub actions/david anson/markdownlint cli2 action 22.0.0 (#225) @dependabot
- Merge from master (#223) @ikostan
Reviewer's Guide
Maintenance PR that aligns documentation with the current Godot version, tidies markdown formatting for readability and linting, and updates the markdownlint GitHub Action pin to a newer commit for security/maintenance.
File-Level Changes
| Change | Details | Files |
|---|---|---|
| Align security policy and examples with the current supported Godot version. |
|
.github/SECURITY.md |
| Improve markdown readability and satisfy markdownlint by wrapping long lines and adding explicit lint directives where needed. |
|
files/docs/Docker_Local_Test_Server.mdCONTRIBUTING.mdREADME.md |
| Update the markdownlint GitHub Action pin to a newer, vetted commit for maintenance/security. |
|
.github/workflows/lint_readme.yml |
Milestone 8: Ensure menu items do not overlap eg hide main menu when options opens, Part #4
Summary
Ensure only one menu is visible at a time when the options menu is opened, and centralize menu visibility handling through Globals.
New Features:
- Track the currently hidden menu and options menu state globally to coordinate UI visibility across scenes. @ikostan
Bug Fixes:
- Prevent main and pause menus from overlapping with the options menu by hiding the invoking menu while options are open and restoring it when options close. @ikostan
- Guard options menu loading against re-entrancy to avoid multiple overlapping options instances and restore the caller menu if loading fails. @ikostan
- Ignore pause toggle input when the options menu is open to avoid unintended state changes. @ikostan
Enhancements and addressed issues: @sourcery-ai
- Improve pause and options menu scripts with clearer logging, documentation comments, and safer signal connections.
- The new Globals.load_options(menu_to_hide: Node) assumes a non-null, still-in-tree node; consider guarding against null or freed nodes and logging a warning instead of blindly dereferencing menu_to_hide.visible to avoid runtime errors from unexpected callers.
- The get_tree().paused = false line in options_menu._on_back_pressed() is now fully commented out; if only options opened from the pause menu should remain paused, consider restoring the prior pause state instead of hardcoding or removing it so behavior is consistent across entry points.
- When restoring Globals.hidden_menu in options_menu.gd, consider guarding with is_instance_valid before accessing .visible and .name to avoid errors if the menu was freed while options were open.
- get_tree().paused = false was removed from in _on_back_pressed; double-check that the game is still unpaused appropriately when exiting options (especially when opened from the pause menu), or move that responsibility explicitly to the caller to keep the behavior clear.
- The test_menu_visibility.gd mock for Globals relies on dynamically adding properties to a generic Node; using a small dedicated script/class or a Dictionary for the mock would make the test setup more robust and easier to maintain.
- In options_menu.gd._on_back_pressed you set Globals.options_open = false twice (before and after clearing hidden_menu); you can remove the duplicate assignment to keep the teardown logic simpler and clearer.
- The options menu teardown logic (restoring hidden_menu, clearing options_open and options_instance) is duplicated between _exit_tree and _on_back_pressed; consider centralizing this into a single helper on Globals or the options menu to avoid divergence and make future changes safer.
Fixing GDUnit Test State Leaks
Problem description
n the GDUnit tests you directly mutate global singletons (Globals.options_scene, Globals.options_instance) without always resetting them in before_test, which can leak state across tests; it would be more robust to fully restore any changed Globals fields after each test or to isolate them behind a dedicated test helper.
We'll fix by:
- Adding before_test() and after_test(): Save originals in before (e.g., var orig_scene = Globals.options_scene), reset to defaults. Restore in after (e.g., Globals.options_scene = orig_scene).
- For test_load_options_reentrancy.gd: Add these hooks—guards mutations (null scene, mock instance) without affecting other tests.
- For test_options_teardown.gd: Enhance existing before_test() to include options_scene (though not mutated here, consistency wins).
- Why? Godot singletons persist across tests; this "snapshot/restore" pattern (from GDUnit docs) isolates them. Educational bonus: Use auto_free() for mocks to avoid Win10 memory spikes in long test runs.
Tests:
- Add GDUnit tests to verify options menu state flags are set and cleared correctly, hidden menus are restored on exit, menu visibility toggling behaves as expected, and load_options correctly guards against re-entrancy.
Reviewer's Guide
Adds global tracking and coordination so only one options menu can exist at a time, hides the invoking menu while options is open, blocks pause toggling during options, and introduces GDUnit tests to verify options teardown, menu visibility, and re-entrancy guards.
File-Level Changes
| Change | Details | Files |
|---|---|---|
| Coordinate options menu visibility with the menus that invoke it via new global state and a guarded load_options API. |
|
scripts/globals.gdscripts/main_menu.gd |
| Ensure pause menu respects the global options state and participates in the hide/show flow when opening options. |
|
scripts/pause_menu.gd |
| Make the options menu responsible for setting and clearing global options state and restoring any hidden menu on close. |
|
scripts/options_menu.gd |
| Add automated tests to validate menu visibility behavior and load_options re-entrancy/teardown logic. |
|
test/test_options_teardown.gdtest/test_menu_visibility.gdtest/test_load_options_reentrancy.gd |
Assessment against linked issues
| Issue | Objective | Addressed | Explanation |
|---|---|---|---|
| #166 | Implement menu visibility management so that when the Options submenu opens, the invoking parent menu (e.g., Main Menu, Pause Menu) is hidden, and that parent menu is restored when Options closes, preventing overlapping menus. | ✅ | |
| #166 | Extend the visibility handling to all relevant menus (at least main and pause menus) and coordinate state so that menu interactions (e.g., pause toggling) do not conflict while the Options menu is open. | ✅ | |
| #166 | Add automated tests to verify menu visibility toggling and options load behavior (including open/close and re-entrancy guards). | ✅ | |
| #212 | Guard all accesses to Globals.hidden_menu.visible and Globals.hidden_menu.name in scripts/options_menu.gd with a validity check (Globals.hidden_menu and is_instance_valid(Globals.hidden_menu)), and clear Globals.hidden_menu afterward. | ✅ | |
| #213 | Ensure the global options_open flag accurately reflects the lifetime of the options menu, including being cleared when the options menu node is freed via scene change or queue_free (e.g., by clearing it in _exit_tree in addition to back button handling). | ✅ | |
| #214 | Update Globals.load_options so that when options_scene is null or fails to load, any previously hidden caller menu is made visible again and the global hidden_menu reference is cleared, preventing the UI from getting stuck hidden. | ✅ | |
| #220 | Add guards in Globals.load_options to prevent multiple concurrent options menus / re-entrancy and avoid overwriting the stored hidden_menu while an options menu is already open. | ✅ | |
| #220 | Track the active options menu and its invoking menu so that opening options hides the invoking menu and closing options reliably restores that menu’s visibility and clears global state. | ✅ | |
| #221 | Ensure that when the options menu node exits the scene tree (e.g., via scene change) without _on_back_pressed, the previously hidden menu is correctly restored or Globals.hidden_menu is cleared so that no menu remains permanently invisible. |
✅ | |
| #221 | Ensure teardown consistency of global state in options_menu.gd by resetting Globals.options_open and Globals.options_instance when the options menu is removed from the scene tree, not only when... |
Milestone 8: Fixing issue with the black screen when the game starts, Part #3
About
In our custom HTML shell, the default Godot loading splash and progress indicators aren't automatically included because we're overriding the built-in template. This leads to the black screen while the browser downloads and initializes the .wasm and .pck files (which can take a few seconds depending on file sizes and connection speed). To fix this, we'll modify custom_shell.html to manually add a loading screen that displays the boot splash image (the Godot logo by default, unless you've changed it in Project Settings > Application > Boot Splash > Image). We'll also hook into the engine's startup promise to hide the loading screen once everything is ready.
Summary
Add a custom loading screen to the web HTML shell to replace the initial black screen while the Godot WebAssembly game loads and initializes. For full PR info see here.
New Features:
- Display a full-screen loading overlay with the Godot boot splash image, progress bar, and status text while the web game loads. @ikostan
- Provide a retry button on the loading screen to allow users to reload the page if the engine fails to start. @sourcery-ai
Enhancements:
- Improve accessibility of the loading UI with ARIA attributes and status updates tied to load progress. @sourcery-ai
- Hide the loading overlay once the Godot engine has successfully initialized to reveal the game canvas. @ikostan
Reviewer's Guide
Adds an explicit HTML loading overlay to the custom web shell, wires it into the Godot engine startup progress, and improves accessibility and error handling for web initialization failures.
File-Level Changes
| Change | Details | Files |
|---|---|---|
| Add a full-screen loading overlay with splash image, progress bar, status text, and retry button to the custom HTML shell. |
|
custom_shell.html |
| Hook the custom loading UI into the Godot engine startup lifecycle with progress updates, completion handling, and error recovery. |
|
custom_shell.html |
Assessment against linked issues
| Issue | Objective | Addressed | Explanation |
|---|---|---|---|
| #205 | Display a loading UI (boot splash image and progress indication) during web export startup when using the custom HTML shell, instead of a black screen. | ✅ | |
| #205 | Automatically hide the loading UI once the Godot engine has finished initializing and the main scene is ready to show. | ✅ | |
| #205 | Integrate the loading behavior with the engine startup flow (including progress updates and error handling), similar to the default web template. | ✅ | |
| #207 | When engine.startGame rejects, update or hide the loading UI so the user is not left with a stuck loading screen showing misleading loading progress. |
✅ | |
| #208 | Keep an error state visible instead of auto-hiding the overlay when the Godot engine fails to initialize, avoiding a blank canvas while window.godotInitialized remains false. | ✅ | |
| #208 | Provide a clear user-facing recovery path (e.g., a Retry action) from the error state instead of leaving users with no way to recover. | ✅ | |
| #208 | Ensure that a true error state is distinguishable from a normal loading state (for both users and automated flows), by persisting visible error UI while window.godotInitialized stays false. | ✅ |
Possibly linked issues
- #(not specified): PR adds a splash/progress loading overlay in the custom shell, fixing the reported black loading screen issue.
- #bug_risk: They both address engine.startGame failure leaving a stuck loading overlay; PR adds explicit error messaging and retry.
- #(unnumbered in prompt): PR replaces auto-hiding error with persistent error view and retry button, preventing blank unrecoverable screens during startup.