Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ThalissonTMora/shaiya-chat-native-re/llms.txt

Use this file to discover all available pages before exploring further.

The admin bound-whisper system is a three-opcode chain exclusive to GM/admin users in Shaiya Core V9. Opcode 0xF107 binds an admin to a named player, storing the target’s id at CUser+0x5810. Opcode 0xF108 then relays text to that bound partner — the server resolves the target by id, not by name, and emits dual S→C 0xF102 (Pattern C) packets to both parties. Finally, 0xF109 clears the bind and notifies both sides. Stock Game.exe has no send site for 0xF108 and exposes no UI for the bind — the relay is intended for external GM tools only.

Prerequisites

A prior 0xF107 bind is required before 0xF108 will relay anything.
ConditionChecked atConsequence on fail
CUser+0x5810 != 0AdminChat_ProcessIncoming case 0xF108 @ 0x00480462S→C 0x1106, plain size 3, to admin
World_FindUserById returns non-NULLSame case, after id lookupS→C 0x1106, plain size 3, to admin
2 ≤ msg_len ≤ 0x80Length check in same caselen ≤ 1 → silent drop; len > 0x80 → kick FUN_004ec760(9,0) @ 0x004805AF

F107 — Bind Flow

0xF107 associates the sending admin’s session with a target player by name and notifies both parties.

C→S Wire Layout — Pattern H

+0x00  u16      opcode = 0xF107
+0x02  char[21] target_name   // null-terminated; fixed 21-byte region on wire
FieldSizeNotes
Plaintext size0x17 (23)push 0x17 before every SConnection_Send @ 0x004ED0E0
target_name21do/while copy until '\0'; no tail memset
1

Client sends C→S 0xF107 + char[21] name

The admin tool or patched client sends Pattern H with the target player’s name in the fixed 21-byte field.
2

Server clears packet+0x16 before lookup

AdminChat_ProcessIncoming @ 0x0048038E writes 0x00 to packet+0x16 (last byte of the name region) before calling World_FindUserByName @ 0x00414D40. This guards against unterminated input from the wire.
3

Server sets CUser+0x5810

If the target is found and is not already the bound partner, the server sets admin+0x5810 = target+0x128 (the target’s character/session id). If a previous bind exists (+0x5810 != 0), Chat_AdminWhisperHelper @ 0x0047F350 is called first to clear the old bind.
4

Server emits dual S→C 0xF107 — Pattern H

Two SConnection_Send calls @ 0x004803C7 go out:
  • Target receives admin’s name (Pattern H, ecx = target CUser).
  • Admin receives target’s name (Pattern H, ecx = admin CUser).
Both sends use plain size 0x17.
5

Client recv — Handler_Chat_Admin_F107 @ 0x005DE950

The stock client receives the S→C packet, zeroes a 21-byte stack buffer, calls PacketRead_String(..., 0x15), then dispatches via vtable +0x344 @ 0x0056BCB0.
ChatWindow_SetWhisperTarget @ 0x0047C690 is not called by Handler_Chat_Admin_F107 or Handler_Chat_Admin_F109. The stock client does not apply the server-side bind to the whisper UI input field. Whisper target selection on the client is driven locally and independently of S→C F107/F109.

F108 — Bound Relay Flow

0xF108 carries the actual whisper text. The C→S packet carries no target name — the server resolves the destination from CUser+0x5810.

C→S Wire Layout — Pattern I

+0x00  u16  opcode = 0xF108
+0x02  u8   msg_len          // 2 ≤ len ≤ 0x80
+0x03  u8   text[msg_len]    // raw bytes, not null-terminated on wire
FieldSizeNotes
Plaintext sizelen + 3No target name field — partner id sourced from CUser+0x5810
msg_len1Minimum 2, maximum 128 (0x80)

S→C Relay — 0xF102 Pattern C

The server never emits 0xF108 to any client. Both relay sends use opcode 0xF102:
+0x00  u16      opcode = 0xF102
+0x02  u8       dir           // 0 = to bound partner; 1 = echo to admin
+0x03  char[21] name          // null-terminated copy from sender CUser+0x184
+0x18  u8       len
+0x19  u8       text[len]
SendVARecipientdir value
1st0x004804FCBound user (World_FindUserById on admin+0x5810)0
2nd0x00480572Admin (this CUser)1
Plaintext size for each send: len + 0x19 (25). Evidence: add $0x19,%edi @ 0x004804EC / 0x00480564.
1

Admin tool sends C→S 0xF108 + u8 len + text

Only external GM tools or patched clients emit this opcode. Stock Game.exe has zero mov $0xF108 in .text (confirmed by objdump).
2

Server validates bind and length

AdminChat_ProcessIncoming case @ 0x00480462 checks admin+0x5810 != 0 and 2 ≤ len ≤ 0x80. Any violation returns 0x1106 or kicks the connection.
3

Server resolves bound partner by id

World_FindUserById @ 0x00414D10 is called with admin+0x5810. If the partner has gone offline since bind, the server returns 0x1106.
4

Server builds S→C 0xF102 dir=0 to partner

The admin’s display name (from CUser+0x184) is copied into the Pattern C packet and sent to the bound user. Plain size len + 0x19.
5

Optional: monitor log via Chat_BuildOutgoing_F502

If CUser+0x5820 (GM monitor session target id) is non-zero and the session is active, Chat_BuildOutgoing_F502 @ 0x0047F260 is called before the admin echo.
6

Server builds S→C 0xF102 dir=1 to admin

The bound partner’s display name is placed in the echo packet and sent back to the admin. Chat_LogGameLogA @ 0x0047F0E0 records the event (channel 2).
CONFIRMED: PacketDispatcher @ 0x005F1E10 in Game.exe lists cases for 0xF107 and 0xF109 but has no case for 0xF108. Zero instances of mov $0xF108 appear in either bin/Game.exe or bin/ps_game.exe under objdump grep.

Full Call Path

C→S 0xF108  (GM tool — not stock Game.exe)
  └─ CUser_DispatchPacket_Main @ 0x00474940
       └─ (opcode & 0xFF00 == 0xF100) && CUser+0x1818 != 0
            └─ AdminChat_ProcessIncoming @ 0x0047FD10
                 case 0xF108 @ 0x00480462
                   ├─ if admin+0x5810 == 0  → SConnection_Send 0x1106 size 3
                   ├─ validate 2 ≤ len ≤ 0x80
                   ├─ bound = World_FindUserById @ 0x00414D10 (admin+0x5810)
                   ├─ if bound == NULL       → SConnection_Send 0x1106 size 3
                   ├─ build 0xF102 dir=0 + admin name + text → SConnection_Send(bound, len+0x19)
                   ├─ optional Chat_BuildOutgoing_F502 @ 0x0047F260 (monitor log)
                   ├─ build 0xF102 dir=1 + bound name + text → SConnection_Send(admin, len+0x19)
                   └─ Chat_LogGameLogA @ 0x0047F0E0 (admin, channel=2, text)

F109 — Clear Flow

0xF109 is an opcode-only command: it carries no body beyond the 2-byte opcode. It clears CUser+0x5810 and notifies both bound parties.

C→S Wire Layout

+0x00  u16  opcode = 0xF109   // no body; plain size = 2
1

Admin sends C→S 0xF109 (opcode only)

No payload; the server ignores any trailing bytes.
2

Server calls Chat_AdminWhisperHelper @ 0x0047F350

This helper clears admin+0x5810 to 0, then calls World_FindUserById @ 0x00415480 to resolve the previously-bound partner.
3

Server emits dual S→C 0xF109 — Pattern H

Two sends @ 0x0047F390:
  • SConnection_Send(bound, 0x17) @ 0x0047F3B9 — bound user receives admin’s name.
  • SConnection_Send(admin, 0x17) @ 0x0047F3E3 — admin receives partner’s name.
Both use plain size 0x17 (same as F107).

Full Direction Table

DirectionOpcodePlain sizeNotes
C→S0xF1070x17 (23)Pattern H — bind by name
S→C0xF1070x17 (23)Pattern H — dual notify (target + admin)
C→S0xF108len + 3Pattern I — text only; no target name on wire
S→C0xF102len + 0x19Pattern C — relay to partner (dir=0) and admin echo (dir=1)
C→S0xF1092Opcode only — no body
S→C0xF1090x17 (23)Pattern H — dual notify (bound + admin)

F108 vs F102 vs 0x1102 Comparison

Aspect0x11020xF1020xF108
HandlerChat_ProcessIncomingAdminChat_ProcessIncomingAdminChat_ProcessIncoming
C→S target fieldtarget[21] on wiretarget[21] on wireNone — uses +0x5810 bind
Server lookupWorld_FindUserByNameWorld_FindUserByNameWorld_FindUserById
Requires F107 bindNoNoYes
S→C opcode0x11020xF1020xF102 (relay)
Client recv handlerHandler_ChatWhisperHandler_Chat_Admin_F102 @ 0x005E5920(none — server never sends F108)
Client send site (stock)PacketSend_WhisperAdmin chat send pathAbsent

CUser State Fields

OffsetTypeDescription
+0x5810u32Bound whisper-partner id — set to target+0x128 on F107; cleared to 0 on F109 or pre-clear
+0x128u32Character/session id used for bind storage and World_FindUserById lookups
+0x184char[]Display name — source for the name[21] field in all outbound Pattern C relays
+0x5820u32GM monitor session target id — when non-zero enables Chat_BuildOutgoing_F502 log

Error Paths

ConditionServer actionEvidence
admin+0x5810 == 0 (no bind)S→C 0x1106, plain size 3, to adminasm 0x00480587
World_FindUserById returns NULLS→C 0x1106, plain size 3, to adminasm 0x004804910x00480587
len > 0x80Kick FUN_004ec760(9,0) @ 0x004805AFShared admin validation
len ≤ 1Silent return (no send)asm 0x0048047F0x004805B9

Reproduce

# F108 case entry (jump table index 7)
objdump -d --start-address=0x480462 --stop-address=0x480590 bin/ps_game.exe

# S→C relay opcode load (0xF102, not 0xF108)
objdump -d --start-address=0x480490 --stop-address=0x480510 bin/ps_game.exe

# Confirm no F108 send site in either binary
objdump -d bin/ps_game.exe | grep -E 'mov.*\$0xf108'
objdump -d bin/Game.exe    | grep -E 'mov.*\$0xf108'

# Client dispatcher gap (F107 present, F109 present, F108 absent)
grep -n '0xf10' game-chat-native/recv/PacketDispatcher_005f1e10.c | tail -15
The GM tool that emits C→S 0xF108 is not present in this repo. The send-path layout (len+3, no name field) is confirmed by static analysis of AdminChat_ProcessIncoming but is INFERRED rather than CONFIRMED from a live wire capture. Emulators must accept 0xF108 on the admin chat path after a valid 0xF107 bind.

Build docs developers (and LLMs) love