Server-authored message inbox UI
Decision
Quest completion messages and similar notifications use a server-owned data model
and a server-authored UI control. The server decides which message exists, its
stable ID, recipient, text, and read state. It sends a bounded FT_MESSAGE_QUEUE
frame on LAYER_MESSAGE; cl_scrn draws the unread notification controls and the
selected message window.
The server must not send absolute window coordinates or a complete UI frame tree for this feature. Positions, scaling, input hit-testing, stacking, and window decoration belong to the client and can change without changing the gameplay protocol.
Why this matches the engine
FT_MESSAGE_QUEUEfollows the existingFT_BUILDQUEUEspecial-control pattern: the server supplies bounded data and the client screen renderer owns pixels.- The message control is part of the same
svc_layoutstream as the server-owned WoW HUD and quest dialog, so it is redrawn with the authoritative UI state. - Inventory should use the same split: server-authoritative item IDs/counts and permissions; client-owned bag/equipment window layout and drag/drop visuals. Client actions remain requests validated by the server.
Payload
Each visible message is one bounded FT_MESSAGE_QUEUE frame. The server emits all
unread records and the currently open record, making the layer idempotent and easy
to resend after reconnect, map load, or other HUD refreshes.
Each message record should contain only gameplay data needed by the client:
| Field | Owner | Purpose |
|---|---|---|
message_id |
server | Stable per-character ID used by UI actions |
image |
server/data | Notification art resource |
title_font / body_font |
server/data | Text resources |
flags |
server | Unread/open state |
text / tooltip |
server | Title and body strings |
Prefer bounded IDs and data keys over arbitrary client-executable text. If the initial implementation needs literal text for tests, keep it length-delimited and validate its maximum length at the protocol boundary.
Interaction contract
- Server creates or updates a record after the authoritative quest/reward event.
- Server emits the current
LAYER_MESSAGEcontrols throughsvc_layout. cl_scrndraws one notification icon per unread record and the open panel.- Clicking an icon sends
message_open <id>; closing sendsmessage_close. - Server validates the ID, owns open/read state, and emits refreshed controls.
Opening a window is a presentation action; accepting a reward, claiming an item, or acknowledging a quest consequence remains server-authoritative.
Implementation plan
Phase 1 — first visible slice
- Create one message when a quest is rewarded.
- Emit it as an
FT_MESSAGE_QUEUEframe onLAYER_MESSAGE. - Render unread notification icons and the selected message panel in
cl_scrn. - Validate
message_open <id>andmessage_closeon the server.
Phase 2 — reusable client windows
- Introduce a small client-side window registry with IDs, anchors, modal state, and z-order. Keep layout in Lua/FDF rather than in game C.
- Move quest log/dialog and inventory presentation toward this registry while retaining server-authoritative command validation.
- Add keyboard escape, focus, and mouse capture rules once more than one window can be open.
Phase 4 — persistence and richer content
- Persist message records/read state with the character/session model.
- Add item/quest links and localization parameters.
- Replace full snapshots with revisioned deltas only after reconnect and loss behavior is covered by tests.
Constraints
- Do not widen
entityState_torplayerState_tfor inbox state. - Do not let client commands claim rewards or mutate inventory without server validation.
- Do not use unbounded strings or arbitrary script supplied by the server.
- Keep the initial notification count and payload size bounded; log rejected
records with
UIWow:orWoW:diagnostics rather than silently dropping them.
Current implementation status
Implemented in the current tree:
FT_MESSAGE_QUEUEcontrols onLAYER_MESSAGE;- quest reward → unread server message record;
cl_scrnnotification icon and message panel drawing;- server-owned open/read state and
message_open/message_closecommands; - regression coverage for reward delivery and open/close state changes.
Welcome window and gameplay input
Wow_ClientBegin sends the welcome tutorial through UI_WriteWelcomeWindow as a
unique client window. Keep this informational window non-modal: the shared
CL_GameplayInputReady gate blocks keyboard movement and right-mouse camera
look whenever a modal window is open. The welcome window's own bounds still
capture mouse actions through CL_WindowMouseOver, so clicks on its Okay button
stay with the window while WASD and camera input remain available.
The server-authored welcome text and button are built in game/g_ui.c; the
WelcomeFrame.xml test fixture is not the runtime source for this window.