Repository navigation
v0.7.1
Six days since v0.7.0: 770 commits that touch the engine, the editor or the demos, out of 2,259 in total.
This release has two themes. The first is identity that outlives its owner. koota reuses entity ids, so a respawned entity could pick up a destroyed entity's particles, audio voice, UI state, screen bounds or 3D pick target. Every per-entity cache was moved onto a single shape that checks the entity is still alive. The second is agent tooling that fails loudly. The MCP tools stopped turning an unknown input into a plausible action, and stopped reporting a refusal as a success.
Dependencies
- three.js stays at 0.184.0. r185 black-screens iOS on the first launch after a clean install. The package peer range is now
>=0.183.0 <0.185.0, and a test enforces it. - PixiJS and Rapier are unchanged for this release.
- Security: all 10 open Dependabot alerts are cleared (sharp, js-yaml, vitest, and hono in both MCP servers).
⚠️ Upgrading from 0.7.0
GameDefinition.UIComponentis removed. The default game UI layer always mounts.- The
capacitor-litert-lmplugin is removed, together with@mediapipe/tasks-genai. The packaged editor's CSP no longer allows remote origins inscript-src/worker-src. - OTA publishing now deletes old bundle versions beyond
ota.retainVersions(default 5). It always keeps the live version and anything under an hour old. Publishing also needs a git checkout and refuses a dirty tree. - UI actions: a param that arrives as
''is now treated as absent. Built-in engine actions report a refusal asok:falseinstead ofdispatched:true. - Scene load waits for Rapier and for sprite-animation, rig, animset and particle definitions, so the first frame is posed and has physics. The cost: loading takes longer in scenes that use them (about 130–175 ms more for physics on a Galaxy A23).
- Build failures: a failed icon or splash generation now fails the build instead of shipping stale art.
- Listener errors: an exception thrown by an engine listener is caught and reported instead of propagating.
- Crashlytics:
journalErrornow reports to Crashlytics in every build. findEntityis typed (Entity | null), so code that leaned on it beinganymay need type fixes.- Packaged editor profile: the profile moved to a per-install, per-project folder. Editor settings such as the CDP opt-out, ports and tokens are carried over.
- Updates: "Check for Updates" now asks before downloading.
✨ New
- Reserved edge bands: mark a strip such as an ad banner with
UIAnchor.reservesEdge. A dialog root markedclearsReservedEdgesthen stays clear of it and of the safe area. - Adaptive design height on
Canvas2D(maxReferenceHeight, opt-in): tall phones use the extra vertical space instead of letterboxing. UIElement.minTapSizeenlarges a control's tap zone without moving its artwork. Real interactive content underneath still wins.- GTAO cost controls:
AmbientOcclusionPostFX.resolutionScaleandsamplesare authorable. On a Galaxy S22, scale 0.5 took postfx-demo from 28 to 60 fps. - UI overflow warning: in editor and debug builds, text that paints outside its box is reported. It appears as a
[UIOverflow]console warning, in the journal and indiagnose. refuseAction(): a UIAction handler can refuse, anddispatch_actionreports it.- New
engine.directoraction: play, pause, seek and change the speed of a cutscene from UI bindings. - OTA:
- publish a shell's sub-games from the editor (Project Settings → OTA → Sub-games) or from MCP;
- each manifest records the commit that built it;
- a corrupt embedded manifest is reported instead of being treated as missing.
- Playable ads:
asset-keep.jsontakes per-targetplayablekeep/drop rules;- the end card is the call to action;
- the time cap starts when the ad is viewable.
- New engine APIs:
measureText2D- the
ScreenBandlayout solver resolveCanvas2DHostensurePhysicsReadyentryIndexOf- testing helpers:
tapTargetFloor,inOrder,scratchDir
- Early monetisation modules, promoted from a shipping game and still evolving:
- the store shelf (
ShelfSession) - a daily calendar
- a login bonus
- the
capacitor-applovin-maxplugin
- the store shelf (
- Demos: all six demos have an app icon.
🐛 Fixes
- 2D text was invisible on every iOS 16 device. The MTSDF atlas was decoded premultiplied. Verified on an iPhone 8.
- Recycled entity ids: a respawned entity no longer inherits a destroyed entity's state. This covered particles, audio voices, skinned clones, text animation, UI DOM and hooks, prefab override marks, physics controls, video decoders, timeline registries, screen bounds and 3D picking.
- Physics from the first tick: Play and step used to run their first ticks with no 2D physics while Rapier's WASM was still loading.
- No pre-pose flash: the first frame after a scene load is posed.
- 2D textures:
- the "destroyed while still bound" WebGPU errors are gone;
- textures survive a scene swap instead of being destroyed and decoded again;
- a wedged 2D canvas recovers;
- a re-imported sprite, font, model or audio file shows its new bytes in the editor.
- Scene swaps survive a throwing listener, instead of aborting halfway.
- Packaged editor:
- "Check for Updates" always answers, including when an update exists;
- "Restart Now" no longer races the macOS updater;
- a startup failure exits (and leaves a log) instead of hanging;
- main-process dialogs no longer freeze the app.
- Preview and undo safety:
- saving inside a preview no longer loses one of its writes;
- undo, Play and scene edits are refused or scoped correctly during scrubbing and preview restores;
- a scene or prefab reload that arrives during Play is deferred to Stop instead of being dropped;
- editing a
.prefab.jsonoutside the editor now reloads it.
- Scene view:
- the UI preview picks the 2D entity under the cursor;
- 2D gizmo, group and vertex drags released outside the canvas commit;
- a lost graphics context recovers instead of leaving the panel black;
- collider-only view mode announces itself, and the Type filter badge names the active types.
- Editor panels:
- Sprite and Nine-Slice editors: resize follows the pointer and no longer collapses a slice.
- Physics Layers: the matrix stays symmetric after adding a layer.
- Undo history: undo labels name the entity.
- Assets and Hierarchy: re-selecting reveals the row again.
- Running game: it no longer steals presses on editor panels.
- Skin weight painting and 2D edits reach the Game view while stopped.
.meta.json: write failures are shown instead of swallowed.- Asset move/rename: reference repair no longer skips when a runtime tab is open.
- UI runtime:
- a single-axis scroll view no longer traps swipes on the other axis;
- a quick double page step moves two pages;
- pooled rows read nested prefab roots correctly, and refuse a vw/vh-sized root instead of guessing.
- Timeline:
- a seek while paused re-poses the scene;
- seeking onto the end fires the end event once;
- a slaved sub-director refuses transport commands instead of pretending;
- an un-slaved sub-director restarts correctly.
- Data robustness:
- keys such as
constructoror__proto__in scene or config data no longer crash anything or pollute objects; audio.setBusVolumechecks the bus name before writing.
- keys such as
- Builds:
- the CLI native build generates icons from
project.config.json; - an empty
iconSourcefalls back to the bundled icon; - the iOS heal strips an unused Facebook SDK from Firebase Auth;
- opening a project waits for a build that holds it.
- the CLI native build generates icons from
- Crash reporting: on iOS, JavaScript errors reach Crashlytics with their message, and each report gets its own grouping.
- Toolchain: removing tools refuses a junctioned folder instead of reporting it removed, and works after a toolchain folder rename.
- Demos: forest-camp's D-pad buttons no longer overlap on notched phones, and its progress bars are the right size.
⚡ Performance
- Boot: the shader prewarm skips work that does nothing under a post-FX stack, cutting its main-thread stall by a third on a Galaxy A23 (289 → 193 ms).
- GTAO: see the cost controls under New.
- Level reloads: a scene swap no longer destroys and re-decodes 2D textures.
🤖 Claude Code / MCP
- New
modoki_wait_for: waits until an editor, entity, console or chrome condition holds, instead of a fixed sleep. It also works as amodoki_batchstep. This is the only new tool; none were renamed or removed. - Editor chrome is readable and aimable by label:
modoki_handles {editor:'chrome'}reads live inputs, checkboxes, tabs and mixed multi-select state;- tap, drag, pointer, hover, scroll and focus accept a
labelaim.
- iOS over USB:
device_connect {useUsb:true}, through go-ios. - Errors say what happened:
- a refusal carries its real error code, not a generic
REFUSED_BY_OPor a timeout that looks like a dead editor; - an unknown value is refused with the valid options. This covers mouse buttons, pointer actions, modifiers, key names and journal levels;
modoki_evalreports op refusals correctly.
- a refusal carries its real error code, not a generic
- Quieter correctness:
modoki_type_textcan type a newline;modoki_dndreports asset-editor drops that did land;modoki.importloads the app's own module instance;get_scene_statereportsguid:nullfor guid-less entities, and a new scene's starter entities get real GUIDs;scene-mutateandasset-writerefuse when they would destroy unsaved editor changes.
- Device leases: ending a lease always stops WebDriverAgent, and one holder can no longer release another's phone.
Full Changelog: v0.7.0...v0.7.1