Lock doors, open gates
Lock and unlock the level's own doors, open and close its drawbridges and portcullises, and store them by GUID.
Doors and gates are the level’s own, not spawned: every house door in Bohemia, plus the castle drawbridge and portcullis. The server can lock a door or drive a gate, and the state holds for every player.
// Lock whatever door the player is standing at.async function lockDoorAt(player: Player): Promise<boolean> { const answer = await Door.queryNearest(player); if (!answer.found || !answer.door) return false; answer.door.locked = true; return true;}Find the door a player is at
Section titled “Find the door a player is at”Door.queryNearest(player) always settles and never rejects. It resolves with
found: false and a reason when no door is in sight, the client is in
another level, it did not answer within five seconds, or the player left.
async function describeDoor(player: Player): Promise<void> { const answer = await Door.queryNearest(player); if (!answer.found || !answer.door) { Chat.sendToPlayer(player, `No door: ${answer.reason}`); return; } const label = answer.name || "unnamed"; Chat.sendToPlayer(player, `${label} (${answer.guid}), ${answer.distance?.toFixed(1)} m, locked: ${answer.locked}`);}guid, name, distance, open and locked are only present when found
is true. open and locked are what that client sees now, which can be
fresher than the server’s copy. There is no Door.nearest: only the client
standing there can say which of thousands of doors it is.
Lock and unlock a door
Section titled “Lock and unlock a door”A Door handle has:
| Property | What it is |
|---|---|
guid |
The level’s EntityGuid, sixteen lowercase hex digits. Same on every machine and across restarts. |
locked |
Writable. Reaches every client that has the door, including one standing at it. |
open |
Read-only, as the last client to touch it reported. Bodies push doors open; the server cannot. |
stateToken |
Bumped on every durable change, to tell one change from the next. |
Save doors by GUID
Section titled “Save doors by GUID”Store guid, not id: the id is a replication handle, the GUID is the level’s
identity. Door.find(guid, world?) takes one back, with or without an 0x
prefix.
Door.find returns null for a door not reported yet, and after a restart
none are. There is no “door reported” event, so to restore saved locks, retry
the missing GUIDs on a timer or when a player spawns:
const saved = ["00a1b2c3d4e5f607"]; // loaded from your own storageconst pending = new Set(saved);
setInterval(() => { for (const guid of pending) { const door = Door.find(guid); if (door) { door.locked = true; pending.delete(guid); } }}, 5000);Door.find and Gate.find search the global virtual world unless you pass
another. Each virtual world has its own copy of a door’s state.
Open and close a gate
Section titled “Open and close a gate”There are two gates, one of each
kind: drawbridge and
portcullis. Clients report gates as players near the castles, so
Gate.all() fills in over a session. Because they report the position,
Gate.nearest answers on the
server with no round trip. Use a radius of tens of metres.
const gate = Gate.nearest(player.position, 60, player.virtualWorld);if (gate) { const result = gate.toggle(); Chat.sendToPlayer(player, result.accepted ? `The ${gate.kind} is ${result.opening ? "opening" : "closing"} (${result.seconds.toFixed(1)} s).` : `The ${gate.kind} ${result.reason}.`);}toggle(true) opens, toggle(false) closes, and no argument heads away from
the current end, reversing a moving gate. Every client plays the motion from
the server clock, so late arrivals see the right point. “Open” always means
passable: portcullis raised, drawbridge lowered. Gate.find(guid, world?)
works like Door.find.
| Property | What it is |
|---|---|
cycle |
closed, opening, open or closing. (Not state, which is the entity’s state bag.) |
openness |
0 shut to 1 fully open, moving during a cycle. |
moving |
Whether a cycle is running. |
openDuration, closeDuration |
Clip lengths in seconds. 0 means no clip that way. |
When a call fails
Section titled “When a call fails”| Call | Fails when | Result |
|---|---|---|
Door.queryNearest |
No door in sight, other level, 5 s timeout, player left. | Resolves found: false with a reason. Never rejects. |
Door.find, Gate.find, Gate.nearest, getById |
The server has not learned it. | null. |
gate.toggle() |
Already at that end, or no clip that way. | A GateToggle with accepted: false, a reason that reads after the gate’s name (is already open) and seconds: 0. Never throws. |
Nothing is spawned, so nothing needs cleanup. A lock stays until you change it or the server restarts.
Related
Section titled “Related”- Server vs client authority, why the server asks a client
- Raycasts and nearby entities, to find doors by ray or radius
- Virtual worlds, per-world door state
- Door reference and Gate reference