---
id: world-map-and-portals/013
title: Meeting yourself in the Badlands — one account, one body, per world
epic: world-map-and-portals
state: done
priority: 1
blocked_by: []
estimate: S
created: 2026-08-16
updated: 2026-08-18
claimed_by: null
claimed_at: null
delivers: []
review_artifact: land-of-lor/game/world/world_server.gd
---

## What prompted it

Kris, exploring v5 with the portals finally working, walked into The Badlands and found a
second William 3rd standing beside him — same name, same character, both idle.

Not a leftover verifier character. Both rows in that world's snapshot carried **his** account
id.

## What was actually wrong

Entity ids are minted **per world**, and there are two ways to leave an Area:

- **Through a portal** — transfer step 1 erases you from the origin. Clean.
- **By closing the game** — your body stays standing where you left it. Also correct; that is
  the persistent world working as intended.

Come back into that Area later *through a portal* and you arrive carrying the id you hold in
the world you came **from**. `_handle_hydrate_entity` pointed `entities_by_account` at the
arriving body and simply left the old one sitting in `entities` — still broadcast to every
peer, still written into every snapshot, and never looked at again by anything, because the
account index only ever points at one. A body with no way back to its owner.

Kris's own path was exactly this: he had been in the Village, closed the game, then come
back in through his own front door. The Badlands twin was the same shape one hop further out.

## The fix

`_drop_other_entities_for_account()`, enforcing one invariant — **one account, one body, per
world** — at both points where an entity enters a world:

- **on arrival through a portal**: the arriving body is the only one that account gets here,
  and any older one goes with it;
- **on hydrating a snapshot**: last-one-wins, and earlier bodies for the same account are
  dropped rather than loaded. That is what **heals the snapshots already carrying
  duplicates**, on their next spawn, with no migration to write.

Kris's actual duplicate row was also deleted from production directly, so he didn't have to
wait for the Badlands to next spawn.

## Verified

New test walks his exact path — be in the Village, close the game, come back in through your
own front door — and asserts one body in the Village afterwards. **Confirmed it fails without
the fix** (`expected length 1, got 2`), which is the only thing that makes a regression test
worth having. Director 154 tests, Godot 116. `verify-home-door` and `verify-world-map` both
10/10 against the droplet after deploying.

Server-side only — `world_server.gd` runs on the droplet, so no new APK and no web re-export.
v5 on the phone is still current.