Connection and sync — never pretend to be online
repo: land-of-lor (game/world client + Area server, game/director) and this workspace (board, deliverables, site/play)
· depends on: world-map-and-portalsground-types
Where this stands (2026-10-24)
Offline play is a different thing now (Kris, 2026-10-23): when the realm cannot be reached
the game goes, after seven seconds, to the offline runner — Chrome’s offline dinosaur game
with Spellgrove’s own models (mini-games epic, issue #39, deliverable #37). So the Area
“Connection” property — single_player / multiplayer / either, “Needs a connection / On
your own / Either”, 005 — went completely the same day (issue #38): nothing had ever acted on
it. The column is dropped, the two load-world rules and the editor’s control are gone. Local
play — the walk-only home with no realm (was 006) — is withdrawn, and with it deliverable
#11’s offline-home line (marked there, dated).
Two things this epic owns changed with it: when an Area’s channel gives up, the client now
hangs up the director socket so the session judges the realm (a black-holed network kept it
“open” for minutes); and while the runner is up the realm is looked for every 2 s, forgetting
every cached address after a failed look (Godot kept the IPv6 address a carrier’s DNS64 had
synthesised in the second before Wi-Fi came up, and every look failed against it for good).
docs/handoff/offline-runner.md.
Before that (2026-09-13): six of eight pieces built, live and signed off — 001–003 on
2026-08-18, then 004, 005 and 007 on 2026-08-19 after Kris tested APK v17 in airplane mode: the
client says it cannot reach the realm and stops, rather than pretending. The ship-verify-record
round (was 008) that would flip deliverable #11 from promised to delivered is still held.
No open issues. Past task files (to 2026-09-13): workshop/historic-docs/epics/connection-and-sync/tasks/.
What this is
Kris played v10 and v11 on the phone (APK) and in a browser, and the two views of the Village diverged: the other body stopped moving in one client and never appeared in the other, and the APK walked straight through portal rings — which are entirely server-driven. The server had the APK’s character barely moved. The transport was fine (a native headless client from the laptop, same TLS + Caddy path, gets every move acked, portal hop included). What was wrong was how the client behaves when its channel dies — and, in Kris’s words, the deeper thing: we do want the game to be able to be played offline, that is important, but we don’t ever want to fake that we are online when we are not — which is kind of what the current behaviour looks like.
This epic is the answer to both halves. It comes out of a full review of the client, the Area
server, the director and the platform (three passes, 2026-08-18) whose findings are the task
list; the plan that produced it is summarised in tasks/001 and each task’s own file.
The diagnosis, in short
game/world/client/client_main.gd: expecting_game_close is a sticky file-global, armed on
every reconnect and portal hop and only ever consumed by a disconnect on a peer that has already
been replaced and is never polled again — so the next real drop is swallowed with no rejoin, no
reset, no message. The client keeps predicting into the void, walks through portals, sees nobody.
Any portal arrival arms it; a phone’s radio hiccup or background pause springs it. The 016
liveness watchdog then made it near-deterministic: its receive timestamp is never seeded on
connect, so on any real network it kills a healthy connection first, forcing the reconnect that
arms the flag. Every automated test runs on localhost, where the first packet lands in the same
frame and hides both.
Around that root cause the review found the same species everywhere: silent no-op sends and
misleading logs; kick reasons (TAKEN_OVER, BAD_GRANT) sent and closed in one tick, which this
Godot client cannot read (the very race 012 fixed for portal_result); intents overwritten
per tick with no ack or reject; a peer left unbound answering NOT_HELLO forever with nothing
prompting a re-hello; a portal transfer with no timeout; a snapshot pipeline that wedges
silently; an Area process that exits without a word to its players; and — on the director — an
Area that can be shut down under a live player because presence is counted on the director
socket, not the game one (the 016 heartbeat made that more likely, not less). Fifteen
constructible failure states, most with no self-heal, all indistinguishable in production logs
from “the phone’s network died”.
Principles
- Truth over motion. The avatar moves only on a channel that is demonstrably alive — recent frames and recent acks. A stalled channel freezes the avatar and says so.
- One owner of connection state per channel, an explicit, tested state machine, no sticky globals; every transition logged with a reason.
- Silence and non-acknowledgement are both signals, on both sides.
- Every intent gets an answer; every kick carries its reason and arrives before the close.
- The Area process is the authority on who is present. An Area shuts down when its game peers are gone, never because a director socket died.
- Every failure a player can hit has a sentence on screen, shown by code that doesn’t hide it in the same breath.
Areas declare their connectivity —— withdrawn 2026-10-24 (Kris): no Area is played without the realm; when it is away the game says so and offers the offline runner instead.single_player/multiplayer/either- Faults are reproducible on the laptop before they’re chased on the phone; and the phone reports its own diagnostics.
Decisions (with Kris, 2026-08-18)
Offline home is walk-only— superseded 2026-10-24: there is no offline home; offline play is the offline runner.- A
multiplayerArea may run on a stalled connection ~15 s (avatar frozen from the first second, reconnects running) before play stops with an error until the connection is back. Values:— the property went 2026-10-24 (#38).single_player,multiplayer,either.
Non-goals
- Offline persistence or merging offline changes.
- Anti-cheat beyond what server authority already gives.
- Interest management (radius) — it stays off; the review notes it would need the same truth.
Relevant docs
workshop/historic-docs/epics/world-map-and-portals/tasks/010–016 — the seven earlier bugs of this class.land-of-lor/game/README.md— “Known limits”, the deploy sections.land-of-lor/docs/handoff/connection-and-sync.md— written by 008 with the full diagnosis.
Deliverables (1)
- #11 — Honest connections — the game never pretends to be online, and your home works without the realm (2026-08-18, promised)
Work on this epic
The granular work is GitHub Issues, one label per epic:
epic:connection-and-sync
(private repository — sign-in required).
The dated Where this stands section above names the open ones.
The task files that carried this epic until 2026-09-13 are kept in the repo:
task files to 2026-09-13
.
Ask an agent about this epic
Paste your AI Task Runner API key once. It stays in this browser and is sent only to kris.ai-task-runner.com.
Also available as raw markdown.