Skip to main content

Room State

Walking flow:

Client Server Room (all clients incl. walker)
│ │ │
│── [3320] Walk(5,3) ───>│ │
│ │ A* pathfind to (5,3) │
│ │ compute tile path │
│ │ ┌─ game tick fires ──────── │
│ │ │ advance one tile │
│ │ │ set status: mv x,y,z │
│ │ └────────────────────────── │
│ │ │
│ ┌── [1640] UserStatus #1 (tick 1: first step) ──────>│
│ │ statusString="/mv 3,2,0.5/" │
│ │ (body: id, prevX, prevY, prevZ, headRot, bodyRot, │
│ │ statusString - prevX/Y/Z = tile just LEFT) │
│<─┘ │ │
│ │ ┌─ next tick ────────────── │
│ │ │ advance another tile │
│ │ └────────────────────────── │
│ │ │
│ ┌── [1640] UserStatus #2 (tick N: intermediate) ────>│
│ │ statusString="/mv 4,3,0.0/" ← still has mv │
│ │ (one broadcast per tile per tick until goal) │
│<─┘ │ │
│ │ ┌─ final tick: goal reached │
│ │ │ remove "mv" status │
│ │ │ onWalkOn() fires for tile│
│ │ └────────────────────────── │
│ │ │
│ ┌── [1640] UserStatus #3 (arrival) ─────────────────>│
│ │ statusString="/" ← no mv, just final position │
│ │ (prevX/Y/Z = last intermediate tile; │
│ │ client snaps avatar to final position) │
│<─┘ │ │

Why [1640] UserStatus carries prevX/Y/Z, not current position: RoomUserStatus serializes getPreviousLocation() + getPreviousLocationZ(), not the current tile. After serializing it calls setPreviousLocation(getCurrentLocation()) to advance the window. This means each packet tells the client "I was at (prevX,prevY,prevZ) and am now moving toward (next from statusString)". The client animates the transition between previous and next positions at its own frame rate.

S→C 374 - RoomUsers (entities entering/in room)

Alt: RoomEntities

Delivers the list of all entities (users, bots, pets) currently in the room at load time or as they enter.

Body:
count: int
[count user entries]:
userId: int
username: string
motto: string
look: string Figure string
roomUnitId: int Per-room unit ID (used in all subsequent status msgs)
x: int
y: int
z: string Double as string (e.g. "0.0")
bodyRotation: int 0-7 (N=2, NE=3, E=4, SE=5, S=6, SW=7, W=0, NW=1)
entityType: int 1=user, 2=pet, 4=bot (NOTE: bots use 4, NOT 3)
gender: string "M" or "F"
groupId: int (-1 if no group)
groupRank: int (-1 if no group)
groupName: string (empty if no group)
swimmingFigure: string (empty if not swimming)
achievementScore: int

Bot-specific extra fields (only appended when entityType == 4): After isModerator, bots have additional fields serialized by RoomUsers:

ownerId: int Account ID of the bot's owner
ownerName: string Username of the bot's owner
skills: int Always 10 (count of skills shorts that follow)
[10 shorts] Skill slots 0-9 (short values, always 0–9)

S→C 1640 - RoomUserStatus

Pushes position, rotation, and status-string updates for one or more room units (movement, sitting, waving, etc.).

Body:
count: int
[count entries]:
roomUnitId: int
x: int Previous X
y: int Previous Y
z: string Previous Z as string
headRotation: int 0-7
bodyRotation: int 0-7

Status keys used in statusString:

KeyValueMeaning
mvx,y,zMoving to position
sitheightSitting (height as double string)
layheightLying down
wave1Waving
carryitemIdCarrying an item
useitemIdUsing an item
signsignIdShowing a sign (0-17)
swim1Swimming
trd1In a trade
dead1Dead (BattleBanzai)
flatctrllevelHas room rights (level 1-4)

S→C 2661 - RoomUserRemove

Alt: RoomUserLeave

Notifies the client that a specific room unit (user, bot, or pet) has left the room.

Body: (empty)

S→C 339 - RoomOwner

Nitro: ROOM_RIGHTS_OWNER Source: RoomOwnerComposer, RoomOwnerEvent

Body: (empty)

Note: RoomOwner sends no fields. Receiving this packet signals that the receiving client is the room owner. The same information is also conveyed by the isOwner:boolean field in S→C 749 RoomPane; RoomOwner (339) is the older companion signal sent Signals to the client that the current user is the room owner. Carries no fields; ownership is implicit from receipt.