Skip to content

v20.1.1

Latest

Choose a tag to compare

@github-actions github-actions released this 25 Aug 03:32
· 1 commit to master since this release
8e8a8d3

What's New

A bug-fix release, and mostly one bug wearing seven faces. Two crashes were reported from a game in production; chasing the first turned up five siblings of the same shape, all fixed here.

The shape: an engine loop hands control to your code, then carries on using state your code was free to tear down. "Remove me when this animation finishes" and "remove it on pickup" are the most ordinary things to write in a callback, and removeChildNow() destroys immediately. Every one of these was reproduced before being fixed, and every fix is pinned by a test that fails against the previous build.

No API changes. No new features. melonjs only.

Bug Fixes

The two reported crashes

  • An animation callback that removed its own sprite crashed the frame. FrameAnimation.update() fires onended and the completion callback in the middle of its frame loop, then read the animation map the callback had just emptied — so a death animation removing its own sprite threw Cannot read properties of undefined (reading 'frames'). The {next, onComplete} chain form needed a separate fix: that wrapper re-enters the engine while still inside your callback.
  • A font whose filename contained a space failed to preload. data/fnt/Super Bouncer.ttf was wrapped as an unquoted CSS url(), which may not contain whitespace, so the descriptor never parsed and no request was made. Only fontface was affected — every other asset type hands its path to the browser, which encodes it.

Found by looking for siblings of the first

  • A collision handler that removed an object crashed the physics step, at four distinct sites depending on how you spelled it — including return false, the documented opt-out from push-out and therefore the likeliest form. "Remove it on pickup or on hit" is the commonest thing a collision handler does; the deferred world.removeChild() was always safe, removeChildNow() was not.
  • A GLTFModel animation callback that removed its own model crashed the frame — the 3D counterpart of the first bug, needing its own fix because GLTFModel re-implements the animation-callback contract rather than sharing it.
  • timer.updateTimers() skipped a timer whenever another fired. It iterated the timer list while removal spliced that same list, so two setTimeouts due on the same frame ran only the first — the second arriving a frame late. Silent, no error.
  • Container.update() updated a child twice when another child's update() removed a sibling at a lower index, double-stepping that child's animation, physics and timers for a frame. Silent. Self-removal was always safe and still is.
  • Renderable.parentApp threw for a renderable in a container never added to a world, instead of returning undefined as its own documentation promises.

Compatibility

No breaking changes. Every fix is a guard on a path that previously threw or silently misbehaved. The behaviours a guard could plausibly have damaged are asserted directly: a callback that merely switches animation keeps playing, ordinary frame advance is unaffected, onCollision returning false still opts out of push-out, and self-removal from a container still works.

@melonjs/planck-adapter and @melonjs/matter-adapter are unaffected and need no update — they work off native engine objects rather than the renderable state involved here, which was verified rather than assumed.

Install

npm install melonjs@20.1.1

Full details in the CHANGELOG.