Skip to content

[diorama-walk] the parked bike teleports back to spawn because the walk controller silently rebuilds #703

Description

@TortoiseWolfe

Environment: /chatt?diorama&walk, reported 2026-08-12 against the #701 spawn fix.

Actual: the bike sometimes relocates on its own — you leave it somewhere, and later it
is not where you left it.

Expected: a parked bike stays parked until you ride it.

Root cause: the walk controller silently rebuilds

src/twin/TwinCanvas.client.tsx, tryBuildWalk():

if (!buildings || !terrain || !groundAt) return;
walkCtrlRef.current?.dispose();          // <- no "already built" guard
const ctrl = EmbodiedController.fromMeshes(...);
...
ctrl.teleport(sx, sy, sz);
ctrl.parkBike(sx, sy, sz);

There is no guard against rebuilding. Every call disposes the live controller and
constructs a new one, which re-runs parkBike(spawn) — so the bike snaps back to the spawn
point. From wherever you had left it, that reads exactly as "it got pulled somewhere else."

tryBuildWalk is invoked from three places, any of which can fire more than once:

#701 added the third trigger. It was necessary (the sampler had to be able to complete
the build when it arrives last, which is what fixed the 33 m underground spawn), but it
increased the number of ways a rebuild can happen. Worth stating plainly rather than
discovering later.

EmbodiedController also seeds bikePos_ from config.spawn ?? {0,0,0} in its constructor
(src/lib/cod/player/EmbodiedController.ts:222-227), and fromMeshes is called with no
spawn, so between construction and parkBike() the bike is briefly at the world origin.
Probably invisible today, but it is a second reason the bike's position is not stable across
a rebuild.

Also worth checking

The player is teleported to spawn by the same code path (ctrl.teleport). If rebuilds are
happening mid-play, the player should be snapping back too — either that is also happening
and has been read as something else, or something about the ordering makes the bike the
visible symptom. Worth confirming which, because it distinguishes "rebuild fires
occasionally" from "only the bike's position is unstable."

Fix sketch

Make the build idempotent: return early when a controller already exists and its inputs have
not changed, rather than dispose-and-rebuild on every callback. If a genuine rebuild is ever
needed (new terrain), preserve the player and bike poses across it instead of re-seeding both
to spawn.

Verification

Park the bike well away from spawn, walk a few hundred metres, wait through any late asset
loads, and confirm it has not moved. Instrument the number of EmbodiedController builds per
session — the expected count is one.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions