Interface GameSnapshot
Obtained from the host's snapshot accessor (slice 2 wires this through
Client#snapshot()). All accessors read from the shared-memory
buffer current at the time the snapshot was acquired. The producer flips
buffers each tick, so a snapshot is only safe to use within the same
logical work unit; do not cache across ticks or across acquisitions.
This interface intentionally exposes only what the producer publishes
via SharedLayout.h — nothing here triggers an RPC. Mutations,
config-type lookups, and anything that touches game logic live elsewhere.
-
Nested Class Summary
Nested ClassesModifier and TypeInterfaceDescriptionstatic interfacestatic interfacestatic interfacestatic interfacestatic interfacestatic interface -
Method Summary
Modifier and TypeMethodDescriptiondefault DynamicRegionThe scene's dynamic-region (instance) chunk-descriptor grid (v19+) — the table mapping each instance tile back to the static tile it was copied from.intThe client's own game-cycle counter: one per ~20 ms client main-loop iteration, roughly 30 perserverTick().intGame client state.Ground items table accessor (v15+).Inventory table accessor.default booleanisInterfaceOpen(int ifaceId) WhetherifaceIdis currently mounted injag::InterfaceManager's open-subs hashmap — the engine's canonical "this interface is open right now" signal.default booleanWhether the open-interface list is known to be complete this tick: the client's count was readable and every entry made it into the snapshot.Scene Location table accessor (doors / trees / rocks / scenery).npcs()NPC table accessor.default Optional<OpenInterface> openInterface(int ifaceId) The open sub-interfaceifaceIdwith its open type, or empty when it is not in this snapshot's list.default intHow many open sub-interfaces this snapshot lists — the setisInterfaceOpen(int)searches.default List<OpenInterface> Every open sub-interface this snapshot lists, with how each was opened (wire v23) — the sameopenInterfaceCount()entriesisInterfaceOpen(int)searches, in the producer's order.default intHow many sub-interfaces the client itself reports open this tick, or0when that could not be read.intownIndex()Local player's server index, or negative if not in-game.players()Player table accessor.In-flight projectiles table accessor (v17+).longThe producer's snapshot publish counter, incremented once per ~20 ms republish starting from 1 when the agent attached.intProducer-side scene-shape version.self()The local player, ornullif not currently in-game.intThe server-tick counter: the 0.6-second game-logic step the server runs on.
-
Method Details
-
serverTick
int serverTick()The server-tick counter: the 0.6-second game-logic step the server runs on.This is the clock to pace a script against. Respawn timers, cooldowns and drop cadence are all denominated in these ticks. Returns
-1until the producer has observed one (not yet in a world). -
gameCycle
int gameCycle()The client's own game-cycle counter: one per ~20 ms client main-loop iteration, roughly 30 perserverTick().This is the unit
Projectile.startCycle()andProjectile.endCycle()are stamped in, so it is whatProjectileFilter.inFlightAt(int)expects. Reads0only until the client populates its transmission manager — it is already counting in the lobby, so0does not mean "not in a world";serverTick()== -1is that signal. -
publishSeq
long publishSeq()The producer's snapshot publish counter, incremented once per ~20 ms republish starting from 1 when the agent attached.Useful only for answering "has the snapshot advanced since I last looked". It shares
gameCycle()'s cadence but not its number space, so the two must never be compared or subtracted, and it is not a tick — pacing off it runs ~30x fast. It was misnamedtickId()before wire protocol v18, which is exactly that bug. -
gameState
int gameState() -
ownIndex
int ownIndex()Local player's server index, or negative if not in-game. -
self
LocalPlayer self()The local player, ornullif not currently in-game. InspectgameState()orownIndex()first if you need to distinguish login/lobby states.Read entirely out of shared memory, so
LocalPlayer.currentHealth()/LocalPlayer.maxHealth()come back asLocalPlayer.HEALTH_UNKNOWN— those two live in varps and cost a pipe round-trip. UseGameAPI.getLocalPlayer()when you need health. -
npcs
GameSnapshot.Npcs npcs()NPC table accessor. -
players
GameSnapshot.Players players()Player table accessor. -
locations
GameSnapshot.Locations locations()Scene Location table accessor (doors / trees / rocks / scenery). -
inventories
GameSnapshot.Inventories inventories()Inventory table accessor. -
groundItems
GameSnapshot.GroundItems groundItems()Ground items table accessor (v15+). -
projectiles
GameSnapshot.Projectiles projectiles()In-flight projectiles table accessor (v17+). -
sceneVersion
int sceneVersion()Producer-side scene-shape version. Bumps whenever the streamedloaded_map_squaresidentity changes — region crossings, login, teleport. Use as a cache key on per-region structures derived from snapshot data; a change invalidates anything keyed on the prior scene. -
dynamicRegion
The scene's dynamic-region (instance) chunk-descriptor grid (v19+) — the table mapping each instance tile back to the static tile it was copied from. SeeDynamicRegionfor what that buys you and for the units trap that catches everyone once.The returned region is a flyweight over the snapshot's shared memory and shares its tick lifetime; take a
DynamicRegion.copyOf(DynamicRegion)if it has to outlive the tick.Defaults to
DynamicRegion.STATICso test doubles and hand-rolled stubs don't have to implement it. That default is the correct answer for a scene nobody modelled as an instance, not a placeholder; the live snapshot implementation overrides it. -
isInterfaceOpen
default boolean isInterfaceOpen(int ifaceId) WhetherifaceIdis currently mounted injag::InterfaceManager's open-subs hashmap — the engine's canonical "this interface is open right now" signal. Backed by the v14 SHM snapshot of the open-subs keyset (linear scan over a few dozen entries this tick); no RPC round-trip.A
trueis always conclusive. Afalseis conclusive only whenisOpenInterfaceListComplete()holds: if the producer could not read the table, or the list was truncated, a missing id may still be open.Use this for chain gates that need to wait for a dialog to appear after a click that opens it (lodestone map, max-cape menu, dungeoneering cape teleport list). HUD-style interfaces that are always mounted while in-game (the minimap iface 1465 etc.) return
truethe whole session; a per-component "is this thing clickable" check is a separate question handled byComponent.hidden().Defaults to
falseso test doubles / hand-rolled stubs don't have to implement it; the liveGameSnapshotImploverrides. -
openInterfaceCount
default int openInterfaceCount()How many open sub-interfaces this snapshot lists — the setisInterfaceOpen(int)searches. Never more than the producer's cap, so it can fall short ofopenInterfaceTotal().Defaults to
0for test doubles; the live implementation overrides. -
openInterfaceTotal
default int openInterfaceTotal()How many sub-interfaces the client itself reports open this tick, or0when that could not be read. Compare withopenInterfaceCount(), or useisOpenInterfaceListComplete().Defaults to
0for test doubles; the live implementation overrides. -
isOpenInterfaceListComplete
default boolean isOpenInterfaceListComplete()Whether the open-interface list is known to be complete this tick: the client's count was readable and every entry made it into the snapshot. Only then is afalsefromisInterfaceOpen(int)proof that the interface is closed. -
openInterfaces
Every open sub-interface this snapshot lists, with how each was opened (wire v23) — the sameopenInterfaceCount()entriesisInterfaceOpen(int)searches, in the producer's order.The list is an immutable copy, so it may be kept past the tick. Like the ids, it can be incomplete; see
isOpenInterfaceListComplete().Defaults to an empty list for test doubles; the live implementation overrides.
-
openInterface
The open sub-interfaceifaceIdwith its open type, or empty when it is not in this snapshot's list.api.snapshot().openInterface(517) .map(OpenInterface::isModal)asks whether the bank is open as a modal.Empty is conclusive only when
isOpenInterfaceListComplete()holds, exactly as afalsefromisInterfaceOpen(int)is.Defaults to scanning
openInterfaces().
-