Skip to content

Releases: labelle-toolkit/labelle-engine

v1.49.0

11 Jun 00:11
4dc2468

Choose a tag to compare

gamepad event drain + enriched payloads, ControllerManager (player mapping, debounce, resume), and input-mixin gamepad forwarders. NOTE: mobile (android/ios/linux/wasm) controller code is compile/cross-compile-verified only — on-device validation tracked in labelle-engine#261.

v1.47.0 — flat-form unified loader (v2.0 prep)

26 May 20:36

Choose a tag to compare

First release of the v2.0 foundation. Engine loader now accepts both root-wrapped AND flat-form unified files (RFC #594).

Highlights

  • #595 — Loader dual-accepts both shapes. { "root": { "components": {...} } } and { "components": {...} } are equivalent. §B2 enforcement (RFC #560) still fires at the flat top-level. Surprise discovery: most of the loader's helpers (`rootObject`, `fileChildren`, etc.) were already shape-agnostic; only the file-root §B2 gate needed adjusting.

Compatibility

Both forms accepted, no warnings emitted. v2.0 (per engine#592) will remove the root-wrapped path along with the other legacy unified-format aliases.

Pairs with

  • labelle-cli v1.44.0 — `labelle audit unification` detects `legacy_root_wrapper`; `labelle migrate unified` auto-fixes all 4 legacy patterns
  • labelle-assembler v0.35.0 — pre-build scan also dual-accepts both forms + hybrid rejection

v1.46.0 — runtime helpers + io_helper Mutex fix

26 May 14:46

Choose a tag to compare

Consolidated release containing the work that was originally intended for v1.45.0 plus the fixes that surfaced after.

What changed

  • #588engine.ScreenshotRequest + engine.nowNs() env-var helpers for cli#227 --screenshot flow.
  • #589engine.runtime_env + engine.requestedScene() for cli#229 --scene runtime override.
  • #591fix(io_helper): std.Thread.Mutex was removed in Zig 0.16. Replaced with std.atomic.Mutex + tryLock spin. v1.45.0 shipped this bug; engine's own CI masked it via builtin.is_test short-circuit, but downstream game compilation forced full evaluation and failed.
  • #590 — bump zspec v0.9.1 → v0.9.2 to fix the Linux RegistryScanSpec hang (closes #583). Root cause was zspec v0.9.1 never initializing std.testing.io_instance.

Compatibility

  • Additive surface — no breaking changes vs v1.45.0's intended shape.
  • v1.45.0 should not be used (broken on non-test builds). Either bump straight to v1.46.0 or pin to commit 8f6e898 if you must use the v1.45.0 commit explicitly.

Pairs with

  • labelle-assembler v0.33.0 — screenshot template + .initial_prefab + main_zig.zig refactor
  • labelle-cli v1.42.0 — --screenshot, --scene runtime override, labelle audit unification

v1.45.0 — engine.ScreenshotRequest

26 May 13:40
8f6e898

Choose a tag to compare

Adds engine.requestedScreenshot() env-var reader + engine.nowNs() helper used by labelle-cli's --screenshot flag + assembler's screenshot template plumbing.

What changed

  • New src/screenshot_request.zig exposing:
    • pub const Request = struct { path: [:0]const u8, after_sec: f32 = 0.0 }
    • pub fn parse() ?Request — reads LABELLE_SCREENSHOT_PATH / LABELLE_SCREENSHOT_AFTER_SEC via libc getenv (Zig 0.16 dropped std.posix.getenv)
    • pub fn nowNs() i128 — wraps clock_gettime(CLOCK_MONOTONIC) (Zig 0.16 dropped std.time.nanoTimestamp)
  • Re-exported from src/root.zig as ScreenshotRequest, requestedScreenshot, nowNs.
  • Bugbot finding (uninitialized Timespec + discarded clock_gettime return) addressed inline (#588).

Pairs with

Compatibility

Additive — no changes to existing engine surface. Older projects that don't set the env vars get a no-op.

labelle-engine v1.44.0 — Unify Scenes and Prefabs (RFC #560)

25 May 23:45

Choose a tag to compare

labelle-engine v1.44.0 — Unify Scenes and Prefabs (RFC #560)

This release lands the unified scene/prefab format from RFC #560. Scenes and prefabs are now the same file format — both have a top-level "root" wrapper, prefab-reference child entries use "overrides" instead of "components", and §B2 enforces that reference-mode entries cannot declare children (authoring belongs in inline mode; instantiating belongs in reference mode).

Highlights

  • Unified loader accepts both legacy { "entities": [...] } and the new { "root": { "children": [...] } } shapes
  • Eager filesystem registry scan with collision detection — idempotent + transactional
  • Deep-merge override semantics: shallow struct-field overlay; null removal for components; whole-field replacement for nested struct/list fields
  • Shared entity-tree walker with cycle detection — used by asset inference, save/load, post-load hook dispatch, gizmo registration
  • Standalone Image component for entities via AssetCatalog

Compatibility

Legacy entities / components / assets keys are still accepted (with deprecation warnings). The legacy form will be removed in a future major release.

Cross-repo coordination

This release pairs with:

  • labelle-assembler v0.32.0 — pre-build scene scan accepts the unified format + the .initial_scene.initial_prefab rename
  • labelle-box2d v0.3.0 — adopts the new Game.emit(GameEvents) signature introduced by #578
  • labelle-cli v1.41.0--scene writes .initial_prefab

Projects using box2d must update to box2d v0.3.0 before bumping to engine v1.44.0.

Also in this release

  • Fix font_loader_test race (#583) — atomic decode_calls + last_pixel_height counters
  • Fix WASM {{hooks_init_block}} template expansion at module scope (RFC-PLUGIN-EVENTS #578 compat)

v1.42.0 — Android: hide system bars at launch

20 May 17:41

Choose a tag to compare

Highlights

  • Immersive mode now hides the system bars at launch (#567). v1.41.0 installed the immersive hook by chaining sokol's onWindowFocusChanged from the render-thread init callback — too late to catch the window's first focus event, so the status + navigation bars stayed visible until the player background+foregrounded the app. The hook is now installed into the onContentRectChanged callback slot, which sokol leaves unset and the framework fires on the UI thread once the content rect is established at launch. Verified on an Android 14 device: dumpsys reports both ITYPE_STATUS_BAR and ITYPE_NAVIGATION_BAR invisible ~0.3s after launch with no focus cycle; immersive-sticky re-apply on focus regain still holds.

Requires

  • Zig 0.16.0
  • labelle-assembler v0.30.0+ — the call must now be emitted into sokol_main() (UI thread), not the render-thread init. Older assembler versions emit it into init and the bars will still stay visible until the first focus cycle.

Refs

PR #567. Builds on the v1.41.0 WindowInsetsController immersive path.

v1.41.0 — Android: hide the navigation bar (WindowInsetsController)

20 May 17:05

Choose a tag to compare

Highlights

  • Android immersive mode now hides the navigation bar (#559). v1.40.0's runtime immersive used the legacy View.setSystemUiVisibility, which on Android 11+ (API 30+) hides the status bar but no longer reliably hides the navigation bar. engine.android now uses WindowInsetsController.hide(WindowInsets.Type.systemBars()) + setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) on API 30+ — verified on an Android 14 device: dumpsys reports both ITYPE_STATUS_BAR and ITYPE_NAVIGATION_BAR invisible, full-height window frame, immersive-sticky re-apply holds across focus cycles.
  • API 28–29 fallback preserved: when WindowInsetsController isn't available (Android 9–10), it falls back to the legacy setSystemUiVisibility immersive-sticky flags — so the project's min_sdk = 28 is fully supported, no regression for old devices.

Requires

  • Zig 0.16.0
  • labelle-assembler v0.29.0+ (emits the enableImmersiveMode() call).

Refs

PR #559. Supersedes the v1.40.0 immersive path for navigation-bar hiding.

v1.40.0 — Android runtime immersive mode

20 May 15:57

Choose a tag to compare

Highlights

  • Android runtime immersive mode (#558) — new engine.android.enableImmersiveMode(). When a project sets immersive_mode = true, the running Android game hides both system bars (status + navigation) in immersive-sticky mode via JNI (setSystemUiVisibility with the immersive-sticky flag set). The legacy manifest Theme.NoTitleBar.Fullscreen is a no-op on Android 11+ — this is the runtime call that actually works on current devices.
    • UI-thread requirement solved by chaining the ANativeActivityCallbacks.onWindowFocusChanged callback — the framework invokes it on the UI thread with a valid JNIEnv, and it doubles as the immersive-sticky re-apply hook on focus regain.
    • The callback globals are atomic (std.atomic.Value) — no data race between the render thread (install) and UI thread (hook).
    • Verified on-device (Android 14 Samsung tablet): game renders fullscreen, no status/navigation bar, survives focus cycles without crashing.

Requires

  • Zig 0.16.0
  • labelle-assembler v0.29.0+ (emits the enableImmersiveMode() call into the generated Android main.zig when immersive_mode is set).

Refs

PR #558.

v1.39.0 — Android: drop Bionic-incompatible libc symbols

20 May 13:07

Choose a tag to compare

Highlights

  • libgame.so now dlopens on Android. Removed two Bionic-incompatible libc symbol references that made the loader reject the shared object on NativeActivity.onCreate:
    • shm_open / shm_unlinkpreview_shm.zig used POSIX shared memory for the editor's Play-in-Editor pixel ring. Bionic doesn't ship those (Android uses ashmem). The PIE consumer never runs on-device, so createMapping / openMapping / unlinkName now return error.ShmOpenFailed (or no-op) under a comptime is_android_abi guard — Zig drops the dead branches and the extern refs vanish from the object file.
    • __errno_locationpreview_mode.zig declared the glibc errno thunk and called it for any non-macOS target. Bionic uses __errno; the file now branches on builtin.target.abi == .android.

Desktop / WASM / iOS code paths are untouched — the new branches only fire on aarch64-linux-android / arm-linux-androideabi.

Verification

flying-platform-labelle on Zig 0.16 with labelle-assembler v0.27.0 + labelle-imgui v0.5.0:

$ nm -D libgame.so | grep -iE 'shm_|errno'
                 U __errno@LIBC      # Bionic's symbol, resolves at runtime

$ labelle android run
labelle: app launched on device

The full game renders on a Samsung Tab — sprite scene, workers, imgui Build menu.

Refs

Unblocks Flying-Platform/flying-platform-labelle#450 (PR #557).

v1.38.0 — FrameCapture trait + InputEvent ring

19 May 16:07

Choose a tag to compare

Minor release shipping the preview pipeline's backend-agnostic frame-capture trait and the editor → game input channel (#140 + #143).

New public API

  • engine.preview_capture.FrameCapture — vtable + publishFrame(producer, capture, stamp_now) for backend-side frame producers. Replaces ~200 lines of inlined backend-specific code that previously lived in the labelle-assembler codegen templates. Backend authors implement a single trait shape; the engine handles the SHM / IOSurface plumbing.
  • Preview.InputEvent — tagged union (mouse_pos, mouse_button) parsed from editor JSON frames on the existing control socket.
  • Preview.popInputEvent() — game frame loops drain pending events from a 256-slot ring buffer. Overflow drops oldest. Codegen template wires this through to the imgui sokol bridge's simgui_add_*_event (labelle-imgui#10) so games receive editor mouse activity in headless preview.
  • PublishError.SizeMismatchpublishFrame now validates slot capacity (width * height * 4slot_size - sizeof(SlotTrailer)) and short-circuits with this error before invoking the backend's capture fn. Catches post-init producer.opts drift / header tampering.

Bug fixes

  • io_helper.io() on wasm32-emscripten — returns std.Io.failing instead of instantiating std.Io.Threaded. Avoids the upstream Zig 0.16 stdlib bug (ziglang/zig#31849, fixed by PR #31850). Asset / scene load paths on wasm surface a clean Failed error rather than a compile failure. Remove this gate when the toolkit moves off 0.16.

Pairs with

  • labelle-imgui#10 — sokol bridge with SOKOL_IMGUI_NO_SOKOL_APP + per-event-type simgui dispatch (mouse / scroll / keyboard / touch / focus / leave).
  • labelle-assembler#141 — headless preview mode + codegen drains the InputEvent ring + WASM panic-handler workaround.
  • labelle-gui#146 — Game View tab forwards mouse over the IOSurface texture to the new sendMousePos / sendMouseButton protocol messages.

Tests: 437/437 pass on macOS + Linux (incl. new publishFrame size-mismatch regression test).