Login begins with a 33-byte cleartext greeting. One captured greeting was:
1f00 2c01 0300 330030003000 6e3c795a 885db958 04
2c010000 2c010000 00000000
Parsed fields:
packet payload length: 31
protocol version: 300
subversion: UTF-16LE "300"
IV 1: 6e 3c 79 5a
IV 2: 88 5d b9 58
locale: 4
trailing build words: 300, 300, 0
The IV values are session material; do not assume this example applies to a new connection.
Each encrypted login frame starts with four bytes. Its payload length is:
uint16_le(header[0:2]) XOR uint16_le(header[2:4])
This framing is implemented in:
/home/sdancer/ms/tools/maplestory_classic_server/maple_server/protocol.py
Known packet payload lengths from the first complete official session:
client -> server: 275, 954, then seven packets of 10 bytes
server -> client: 27, 164, 21770, 12, 6, then seven packets of 2 bytes
The second session had the same core sequence and continued producing heartbeat-sized packets until capture stopped.
The protocol-300 payload cipher is implemented in
tools/maplestory_classic_server/maple_server/protocol.py. Each direction has
its own four-byte IV. A frame is decrypted with the current IV, then that IV is
advanced with the recovered Maple shuffle routine. Appended synthetic server
frames must continue from the IV after the last replayed server frame; reactive
client reads must advance the client IV in the same way.
This implementation is validated by observed ciphertext/header vectors and by round-trip tests. The former assumption that the cipher constants were still the main blocker is obsolete.
server 0 bootstrap/login prelude
server 0 local 36-byte diagnostic account-prefix probe
server 3 redacted u8/i32/bool record
server 6 redacted trailing-zero UTF-16 plus uint8 record
server 7 successful character-creation snapshot and compact appearance
server 35 redacted login-prelude text record
server 390 redacted uint8 record
client 9 redacted trailing-zero UTF-16 record
client 10 character-creation name plus eight redacted uint32 values
client 16 select the newly created character
client 255 redacted uint32 record
server 10 empty heartbeat probe
client 6 redacted indexed record set
client 274 fixed 768/74-code-unit redacted text record
client 23 8-byte opaque-token heartbeat response
client 31 three redacted UTF-16 fields and a 48-byte opaque blob
client 13 typed opcode-13 envelope or status message
server 13 typed opcode-13 envelope or three-byte acknowledgment
server 27 counted redacted integer/text ledger
server 28 counted redacted paired-text ledger
server 22 counted redacted indexed-text ledger
server 20 redacted neutral uint32 record
server 21 zero uint8 record
server 23 zero uint32 record
server 161 zero uint8 record
server 1 account/login result
server 2 one world record, or a signed world-id -1 sentinel
client 4 select world (`uint32 world_id`)
server 402 channel transition (12-byte then 8-byte response)
client 5 select channel (`uint8 world`, `uint16 channel`, IPv4)
server 4 character-list response
client 7 select character (`uint32 character_id`)
server 5 world-server handoff
The custom replay acknowledges the client's type-15 opcode-13 status with
plaintext 0d0000. That is enough for the client to continue into the login
controller. It is a local-server behavior, not a claim that the official NGS
proof has been reproduced.
The current local replay also begins with an explicitly patched 36-byte server
opcode-0 diagnostic. It is not a complete AccountLoginResponse: it contains
result 0, a redacted uint32 account id, three zero account flags, one
redacted four-code-unit UTF-16 name, and a 16-byte zero suffix. The fold records
it as a partial local bootstrap probe and does not authenticate the account from
it; the later full opcode-1 account result performs that transition. This
exact live-only shape consumes natively without publishing the id or name.
Five legacy stream-116 records now have neutral, exact boundaries. Generated
server handlers read opcode 3 as uint8 + int32 + bool, opcode 390 as one
uint8, and opcode 6 as trailing-zero counted UTF-16 plus uint8; the
captured records consume exactly under those layouts. Capture-bounded client
opcode 255 is one redacted uint32, while client opcode 9 is one redacted
trailing-zero counted UTF-16 field. The opcode-6 text exactly echoes the
preceding opcode-9 text after 235.214 ms. Opcode 390 follows client opcode
255 after 238.384 ms, but that adjacency is not treated as causal. Safe output
publishes only widths, text length, boolean state, and echo/timing evidence.
Legacy stream 116 then exercises a complete empty-list character-creation
path. Client opcode 10 contains one redacted counted name and eight redacted
uint32 values. After 271.948 ms, generated server opcode 7 reads result zero
and delegates to a body that exactly reuses InitialCharacterSnapshot followed
by the captured compact appearance. The response name and its
gender/face/hair/equipment fingerprint exactly match the request. Client opcode
16 repeats the new character id 3,994.739 ms later, and the existing opcode-5
world handoff repeats it again after 597.284 ms. The request's first uint32,
the response's three style values, and opcode-16's zero-byte role remain
neutral. The separate server opcode-35 prelude is capture-bounded as constants
0/1, one redacted seven-code-unit text field, and a three-byte zero suffix.
Together these records reduce successful stream 116 to zero unknown packets.
The login fold now reuses the world transport heartbeat shapes: server opcode
10 is exactly two bytes and client opcode 23 is exactly ten bytes, carrying
an eight-byte token whose contents remain opaque. Every observed response
immediately follows a probe. Successful stream 83 has one matched pair at
14.107 ms. Stream 116 has four probes, three matched responses at 10.006,
0.349, and 14.445 ms, and one final pending probe. The current local login has
eight matched pairs, zero unmatched/pending responses, and a maximum 2,594.990
ms round trip caused by replay pacing. Safe output exposes only token byte
count, pair counters, and round-trip timing.
Client opcode 6 is a variable indexed record set with an exact shared
boundary across stream 83, stream 116, and the current live login:
uint16 opcode = 6
uint32 neutral_header[9] # redacted; zero-based fields 1 and 5 = 0
uint32 record_count
repeat record_count:
uint16 record_index # complete unique set 0..count-1
uint32 opaque_value # redacted
The total width is therefore 42 + 6 * record_count. Stream 83 carries 299
records in 1,836 bytes; stream 116 and the current live login each carry 152
records in 954 bytes. All three contain every index exactly once, although
their wire order is not sequential. Safe output exposes only the fixed header
width, common zero-field indices, record count, complete-index check, and
aggregate counts. Header and record values—and the higher-level role of the
set—remain neutral.
The sole captured client opcode-274 packet is a fixed 1,698-byte redacted
variant at the start of stream 83: a 768-code-unit trailing-zero UTF-16
field, uint32 2, two uint8 1 flags, and a 74-code-unit trailing-zero UTF-16
field. Both fields contain hex-like text, but the codec deliberately exposes
only their widths and the intervening constants. The contents and higher-level
role remain neutral. Python round-trips the full packet and the native manifest
exact-consumes the isolated record.
Login also reuses the exact server opcode-27 and opcode-28 ledger codecs
already validated in gameplay. Opcode 27 is an i32 count followed by three
redacted i32 values and one trailing-zero UTF-16 field per entry. Opcode 28
is an i32 count followed by two redacted i32 values and two trailing-zero
UTF-16 fields per entry. Stream 83 carries 18/5 entries at 1,056/260
bytes; stream 116 carries 1/4 at 27/164 bytes. The live login independently
replays the same four-entry, 164-byte opcode-28 variant. Every record consumes
and round-trips exactly at full shape coverage. Safe login state exposes only
packet/entry counts and text code-unit totals; all text and numeric values stay
redacted and their higher-level roles remain neutral.
Server opcode 22 carries a uint32 entry count followed by repeated
(trailing-zero counted UTF-16 text, uint16 index) entries. Stream 83 has
299 entries and 16,473 text code units in 34,447 bytes; stream 116 and the
live login share the same 152-entry, 10,502-code-unit, 21,770-byte record.
Every sample exact-consumes, all text trailing bytes are zero, and each index
set is the complete unique range 0..count-1. The later client opcode-6
record uses the same index set in all three sessions, although ordering differs.
The fold reports only counts, code-unit totals, and that correlation; text and
the family's higher-level role stay redacted and neutral.
Four small login-server records also share exact boundaries across stream 83
and stream 116. Opcodes 21 and 161 are each a two-byte opcode followed by
a zero uint8; opcode 23 is followed by a zero uint32; opcode 20 is
followed by a varying neutral uint32. The live login independently contains
the same zero-valued opcode-23 record. Login-specific codecs keep this family
separate from gameplay's fixed-record path, redact the opcode-20 value, and
expose only opcode counts, widths, and zero/nonzero status. All eight reference
records exact-consume natively and round-trip in Python.
Client opcode 31 is a capture-bounded variable record rather than an opaque
width pin. Stream 83, stream 116, and the current live login independently
establish this grammar:
uint16 opcode = 31
byte[20] reserved_prefix = zero
uint8 variant = 2
utf16 opaque_text_1 + trailing zero byte
utf16 opaque_text_2 + trailing zero byte
utf16 opaque_text_3 + trailing zero byte
uint32 opaque_blob_length = 48
byte[48] opaque_blob
byte[3] reserved_suffix = zero
The three packet lengths are 183, 275, and 201 bytes, with text
code-unit patterns 10/0/38, 7/51/36, and 1/51/5. The codec round-trips
the contents but safe packet/state output exposes only the variant, zero-field
checks, text lengths, blob length, and aggregate pattern/count telemetry. The
text, blob contents, and higher-level role remain deliberately neutral.
Opcode 13 has four bounded envelopes in the observed sessions:
acknowledgment (3 bytes)
uint16 opcode = 13
uint8 result
fixed client type-1 envelope (11 bytes)
uint16 opcode = 13
uint8 message_type = 1
byte[8] opaque body
opaque envelope (7 + payload_length bytes)
uint16 opcode = 13
uint8 message_type
uint32 payload_length
byte[payload_length] opaque body
client status message
uint16 opcode = 13
uint8 message_type = 15
uint16 UTF-16 code-unit count
char16[code-unit count] message
uint8 zero trailing byte
The successful reference sends server message type 7 with 27 opaque bytes
after the character list and time. It is followed by three client type-6
messages whose opaque bodies are 176, 184, and 137 bytes, then character
selection opcode 7. A live official-ticket A/B replay reached the same
character-controller loading overlay with or without type 7; neither run
emitted type 6 or opcode 7. The temporal ordering therefore does not prove
that type 7 is a security request or that type 6 is its response. The app
handler path for type 7 also terminates in its UI-dialog utility, so the
decoder deliberately uses the neutral opcode_13_envelope name. Length
prefixes and exact packet boundaries remain validated. Direct placeholder
launches also emit a fully decoded type-15 status message containing “Please
check the network connection status.”
The world-session gameplay fold uses the same neutral family. Stream 92
contains 446 fixed client type-1 envelopes, 104 client type-6 envelopes,
and five client type-13 envelopes. Stream 126 contains 970 fixed client
type-1 envelopes. On the server direction, stream 92 contains 14 type-7,
one type-12, and five type-14 envelopes; stream 114 contains one each of
types 12 and 14. Across login and gameplay, all 23 captured server packets
have a body length that reaches the exact packet end; the automatic dump's
handler confirms the leading discriminator read. The fold emits
direction-specific type/body-length distributions and redaction flags without
exposing any body. Server packets fold as partial
server_opcode_13_envelope observations and
server_opcode_13_message_received events. The payload meanings remain
partial rather than being labeled as security traffic.
Client opcode 43 requests a field transfer. Its leading byte is the active
field epoch: all 45 packets match the folded epoch, and every packet is followed
by the next server opcode-157 field snapshot with no intervening opcode-43.
The two layouts are portal transfer and death respawn:
portal transfer:
uint16 opcode = 43
uint8 field_epoch
int32 destination_map_id = -1 # destination resolved by server/portal
uint16 portal_name_code_units
utf16le[portal_name_code_units] portal_name
uint8 zero_terminator = 0
int16 position_x
int16 position_y
uint16 reserved_value = 0
death respawn:
uint16 opcode = 43
uint8 field_epoch
byte[9] zero_body
server opcode-43 neutral envelope:
uint16 opcode = 43
uint8 message_type
byte[16] opaque_body
Stream 92 contributes nine portal transfers, three death respawns, and three
neutral server packets; all server message types are zero. The three respawns
each follow a same-epoch HP-zero stat update by 2.271..2.918 seconds and use
an all-zero body. Stream 126 contributes 33 portal transfers. Portal-name
lengths are 4:5, 5:9, 6:27, and 8:1 code units across both captures;
all portal requests use map sentinel -1, a zero reserved value, and signed
positions. Safe state/events redact the portal name but expose epoch matching,
variant, position, pending/matched transitions, and latency.
All 45 client requests round-trip as full observations and match their next
field snapshot. Response latency is 28.125..940.035 ms (combined median
399.027 ms). Promoting this family changes stream 92 to
13,505/21,702/0/0 and stream 126 to 27,188/43,912/0/0; stream 114
remains 54/22/0/0, with no warnings beyond stream 126's known six one-HP
combat-model disagreements.
The automatic manifest uses portal and death-respawn candidates rather than the
earlier sequence-keyed switch. Exact packet length makes the candidates
unambiguous, while equality guards enforce map sentinel -1, the zero portal
reserved value, and the death-respawn zero body.
An exact captured 19-byte server envelope was injected into an already active
browser-free custom-server session. The fold added one partial opcode-43
event, core phase/map/player/inventory/progression state stayed unchanged,
matched heartbeats advanced from 173 to 176, and the connection remained
active with zero failures. No client opcode-43 response appeared because
server opcode 43 is not the field-transfer response; the capture-backed
response boundary is the following opcode-157 snapshot. The injection still
proves bounded non-stalling acceptance for the separately neutral server
family only.
All 44 stream-126 packets use one exact variable-width grammar:
uint16 opcode = 114
uint8 control_value
uint16 text_code_units
utf16le[text_code_units] opaque_text
uint8 zero_terminator = 0
uint32 opaque_value
Text lengths 8, 9, and 11 produce total packet lengths 26, 28, and
32; their counts are 2, 8, and 34. The control values span 1..34
across 26 observed values and are nondecreasing in capture order, but that alone
does not establish a sequence or tutorial-step role. There are 13 distinct
redacted strings and 43 distinct trailing values; the trailing values decrease
22 times, so they are not modeled as a monotonic client tick.
Because the counted text and required terminator delimit the record exactly,
redaction and neutral roles do not imply structural opacity. All 44 records now
report full structural coverage and pass the independent manifest validator
with exact consumption; text, trailing-value meaning, and higher-level purpose
remain deliberately unresolved.
Forty-three packets have prior opcode-244/247 tutorial/UI traffic, with 29
within 30 seconds and a median gap of 15.285 seconds. That supports a possible
UI relationship but is too indirect to call the packet an acknowledgement.
The fold therefore emits neutral client_opcode_114_submitted events and
publishes only control and text-length distributions plus a redacted-value
count. Python and native manifest validators consume and re-emit all 44 packets
without ambiguity or failure. Because the family is client-originated and the
active idle level-12 client emits none, no server-to-client live replay or
client-visible effect is claimed.
The final two unknown stream-92 packets are exact same-field portal requests:
uint16 opcode = 115
uint8 field_epoch
uint16 portal_name_code_units
utf16le[portal_name_code_units] portal_name
uint8 zero_terminator = 0
int16 source_x
int16 source_y
int16 destination_x
int16 destination_y
Both requests carry the active field epoch 7 and a four-code-unit portal
name, retained only for exact re-emission and redacted from safe output. Their
source/destination paths are (1050,234) -> (1099,410) and
(1099,411) -> (1040,1007): the second source is within one pixel of the first
destination. The first request is followed by same-epoch mob visibility
removals and entries; the second is followed by remote-player, mob, and drop
visibility removals. No field snapshot or epoch transition occurs.
This structure and role also agree with the older open-source
UseInnerPortalHandler/InnerPortalHandler family, which reads an inner-portal
name plus start/final positions and updates the character within the current
map. Capture-local position chaining and visibility changes remain the primary
version-300 evidence; older layouts are not used to widen the accepted modern
shape. The Python codec accepts the typed variable-name grammar, while the
native manifest deliberately pins the only observed 22-byte/four-code-unit
variant. Both captured records consume and re-emit exactly at full coverage.
Safe state exposes only counts, name length, epoch matches, coordinates,
deltas, and the one-pixel chain result. Stream 92 advances from
13,417/21,788/2/0 to 13,419/21,788/0/0 with no warnings.
The captured server frame at index 3 is a second opcode-0 message with
plaintext result byte 2. It is the direct source of the replayed
account-policy dialog: replacing only this frame with heartbeat 0a00 removes
the dialog. Changing result 2 to 0 instead displays “Logging in, please
wait,” so zero is not yet proven to mean unconditional success for this
message.
The earlier two-byte opcode-1 probe could trigger a controller transition but
bypassed required account state. A bounded 128-byte zero-filled opcode-1
payload lets the original handler run, creates its empty world staging list,
and reaches world selection after an informational modal is dismissed. This is
still a structural probe, not a decoded production account response. Direct
transition probes separately established state 1 as world selection and
state 2 as character selection.
The successful reference uses the same 33-byte version-300, subversion-300,
locale-4 handshake and the same directional cipher masks (3 client,
~300 server). Validate and fold it into typed game state with:
cd /home/sdancer/ms/tools/maplestory_classic_server
python -m maple_server analyze-login \
--pcap /home/sdancer/ms/111.pcapng \
--tcp-stream 83 \
--packets \
--fail-on-invalidThe final state is handoff_ready: five worlds, selected world 4, selected
channel 23, a received character list, a character selection, and a matching
handoff. Numeric account and character identifiers are redacted by default.
The validated server sequence is:
frame 3 opcode 0, 63-byte successful account result
frames 5-9 opcode 2, five 2,183-byte world records
frame 10 opcode 2, signed world-id -1 sentinel
frames 15-16 opcode 402, 12-byte then 8-byte transition results
frame 17 opcode 4, 170-byte typed character-list response
frame 18 opcode 134, 10-byte server time
frame 19 opcode 13, type-7 envelope with 27 opaque bytes
frame 20 opcode 5, 19-byte world handoff
All world packets parse to their exact ends. Worlds 1 through 4 use
visible/online flag 1; world 5 uses flag 2. Each has 60 channels,
event EXP/drop values 100, channel unknown value 200, and no balloons.
This explains why flag-0 test worlds were retained in memory but rendered
with blank labels.
Two independent login captures bound successful server opcode 4: stream
83 in 111.pcapng has one character and a total length of 170 bytes, while
stream 116 in 1-10FS.pcapng has zero characters and a total length of 18
bytes. Both parse and re-emit byte-for-byte as:
uint16 opcode = 4
int8 result = 0
uint32 reserved_1
uint32 reserved_2
uint8 character_count
repeat character_count:
character stat snapshot
uint8 gender
uint8 skin
uint32 face_id
repeated uint8 slot + uint32 item_id, terminated by slot 0xff
repeated masked slot + item_id, terminated by slot 0xff
uint32 cash_weapon_id
uint32 opaque_style_values[7]
uint8 entry_code
bool ranking_present
if ranking_present: int32 ranking_values[4]
uint8 trailer_1
uint8 trailer_2
uint32 trailer_3
The stat snapshot reuses the typed world-entry prefix: character id, data
flags, UTF-16 name, gender/skin/face/hair, companion id, level/job, four base
stats, HP/MP, AP/SP, EXP, fame, map/portal, and two neutral state fields. The
record validates that its repeated appearance identity matches the stat
snapshot. Slot 0 in the first appearance list carries the hair template in
both the parser and emitter. The seven style values and final trailer retain
neutral names because only their widths and positions are independently
established.
The login JSON state reports character count plus level/job/stats, HP/MP,
map, equipment counts, and ranking presence. Character id and name are
"present" unless --show-identifiers is explicitly enabled. A subsequent
client opcode 7 is invalid if its character id was not advertised by this
list. Replay references can append ?character-list to require a full typed
parse and state-driven re-encode before encryption.
The client selects a world with six-byte opcode 4, then a channel with
nine-byte opcode 5, and finally a character with six-byte opcode 7. The
opcode-5 fields are one world byte, a little-endian channel uint16, and four
IPv4 octets.
Timing is part of the channel transition. The first opcode-402 response
arrives about 28 ms after client opcode 4; the second arrives about 2.54 s
later, and client opcode 5 follows immediately. Sending both 402 packets
back-to-back reproduces a live stall after the channel button is clicked.
Both opcode-402 variants are now structurally bounded:
stage 0 (12 bytes)
uint16 opcode = 402
uint16 stage = 0
uint32 transition_value_0
uint32 transition_value_1
stage 1 (8 bytes)
uint16 opcode = 402
uint16 stage = 1
uint32 selected_world_id
The reference stage-0 values are 267748 and 267744; their higher-level
meaning remains unnamed, but no bytes are opaque. The stage-1 world id must
match the triggering client opcode-4 selection. Replaying the captured world
4 after a live world-1 selection is now rejected by the game-state fold and
reproduces the channel-button stall even with correct timing.
The login state machine cannot skip directly from character-list/time to the
handoff. A live bypass sent the valid endpoint-rewritten frame 20 without
frame 19 or the client type-6/opcode-7 sequence. The client accepted the
handoff far enough to blank the scene, but never connected to the local world
port and then exited after the login socket closed. The A/B replay above also
shows that frame 19 alone does not unlock selection.
Replaying the successful stream's six-byte server opcode 23 late is also
insufficient. In the live synthetic-callback probe it produced only a fully
decoded client opcode-13 type-15 status with an empty message; the client
stayed in character_selection and sent no opcode 7. The missing variable
was ordering: non-strict replay consumed fresh-client extra frames as captured
events and emitted captured server frame 4 before the actual opcode 6.
With frame 4 omitted and its plaintext sent reactively after the observed
client opcode 6, a clean unpatched client returned a non-empty type-15
status, completed world/channel/character selection, emitted opcode 7,
accepted the transformed handoff, and opened the local world connection. Thus
the bounded rule is:
client opcode 6 (variable opaque security payload)
server opcode 23 (six-byte captured response)
client opcode 13, type 15 (non-empty completion/status message)
The server response is necessary in this position, while a late duplicate is
not a substitute. The type-7 envelope still is not independently sufficient;
it participates after the ordered NGSX completion rather than causing that
completion itself.
The successful 63-byte account packet is fully bounded as follows. Its three
strings use a uint16 UTF-16 code-unit count without the extra world-string
trailing byte:
uint16 opcode (0 in reference; rewritten to 1 for the local handler)
uint8 result = 0
uint32 account_id
uint8 gender
uint8 administrator
bool8 restricted
string account_name
uint16 unknown
uint8[3] account_flags
int64 created_at_ticks
string secondary_name
string tertiary_name
byte[2] trailing
The current parser model is:
uint16 opcode = 2
int8 world_id
string world_name
uint8 world_flag
string event_description
uint16 event_exp_rate
uint16 event_drop_rate
uint8 channel_count
repeat channel_count:
string channel_name
int32 population
uint8 world_id
uint8 channel_id
uint8 adult_channel
int32 unknown
uint16 balloon_count
repeat balloon_count:
uint16 x
uint16 y
string message
Every string above is encoded as a little-endian uint16 UTF-16 character
count, followed by that many UTF-16LE code units, followed by one additional
byte consumed by the client string reader. Omitting that byte shifts every
subsequent field. A separate three-byte packet 02 00 ff ends the world list.
The current 51-byte test packet describes world 0 named test, with one
channel named test-1, zero population, and no balloons. Do not place the raw
hex in general logs; the replay command in CUSTOM_SERVER.md is the canonical
lab recipe.
Dual transparent process-local trampolines confirm that opcode 2 reaches the
build-specific handler and the world parser without leaving GDB attached. The
51-byte test record decodes exactly as world id 0, name test, one channel
named test-1, and zero-valued remaining fields. Focused Cpp2IL established
that controller field +0xc8 is a wrapper whose List<World> backing field is
at wrapper +0x50; the corrected live dump shows one retained world both
before and after the signed-id -1 sentinel. The former worlds=0 observation
read wrapper +0x18 and was not a list count.
The successful 19-byte shape is fully consumed as:
uint16 opcode = 5
uint16 result = 0
byte[4] IPv4 address (network octet order)
uint16 port (little-endian)
uint32 character_id
byte[5] trailing zeros
The selected character ID must equal the handoff character ID. The replay's
?handoff=127.0.0.1:PORT PCAP-frame transform changes only address and port
after validating this shape.
All three world references use the same exact 66-byte request boundary:
uint16 opcode = 8
uint32 entry_value # neutral; non-identity role
uint32 character_id
byte[56] opaque_ticket
The first uint32 after the opcode was previously mislabeled as the character
id. Cross-checking it against the large opcode-157 snapshot in 111.pcapng
streams 92 and 114, and against 1-10FS.pcapng stream 126, proves that
the second word is the character id. The fold validates that equality while
redacting both the identifier and ticket bytes from normal reports.
The first opcode-157 packet on each validated world connection contains a
typed character/stat prefix followed by a much larger nested state tail. The
stream-92 plaintext is 4,464 bytes and the independent stream-114
plaintext is 4,506 bytes. Both consume the same 112-byte prefix exactly and
round-trip byte-for-byte through InitialFieldSnapshot:
uint16 opcode = 157
uint32 marker = 23
uint8 reserved_flag = 0
uint8 contains_character_data = 1
uint8 character_data_mode = 1
uint16 reserved = 0
uint32 opaque_session_value[3]
int64 sentinel = -1
uint8 character_record_prefix = 0
uint32 character_id
uint32 character_data_flags
string character_name # uint16 count + UTF-16LE + zero byte
uint8 gender
uint8 skin
uint32 face_id
uint32 hair_id
uint64 companion_id
uint8 level
uint16 job_id
uint16 strength
uint16 dexterity
uint16 intelligence
uint16 luck
uint16 current_hp
uint16 max_hp
uint16 current_mp
uint16 max_mp
uint16 ability_points
uint16 skill_points
uint32 experience
int16 fame
uint32 map_id
uint8 portal_index
uint8 opaque_state_flag
uint64 opaque_state_value
byte[] inventory_and_post_inventory_tail
The tail begins with a 30-byte preamble ending in the 1900-01-01 FILETIME
sentinel. It then contains five zero-terminated equipment groups followed by
zero-terminated use, setup, etc, and cash groups. Each item begins with a slot,
record type, template id, cash flag, optional cash id, and expiration. Stack
records additionally expose a uint16 quantity. Equipment-specific metadata
is retained as bounded raw record bytes until its conditional fields are named.
Stream 92 contains group counts 4/1/4/0/0/24/2/17/1; stream 114
contains 4/1/4/0/0/24/2/18/1. The inventory regions are 2,930 and 2,972
bytes respectively, and both are followed by the same 1,422-byte progression
region. Each inventory region and complete initial packet round-trips
byte-for-byte. Reports expose item slot/template/quantity data and the
character-name code-unit count, but not the name text or raw character id. The
gameplay fold checks the embedded character id against client opcode 8, then
seeds player, field, and inventory state and emits a field_snapshot_received
event with variant initial_character_snapshot.
The level-1 capture uses marker 26 rather than marker 23 but retains the
same typed character and inventory grammar. Its 823-byte initial packet seeds
character level 1, job 0, HP/MP maxima, and nine inventory groups. The
537-byte inventory region contains five items and round-trips. Its following
172-byte compact progression is also typed:
uint8 reserved_flag = 0
uint16 skill_count = 1
repeat skill_count: uint32 skill_id, uint32 level # captured (12, 0)
uint16 reserved = 0
uint16 string_property_count = 0
uint16 timestamp_property_count = 0
int64 reserved = 0
uint32 saved_map_id[16]
byte[7] neutral_variant_header
byte[] compact_trailer
The compact trailer reuses the two validated 17-byte blocks, followed by five
empty UTF-16 strings, a zero variant constant, reserved data, the FILETIME
sentinel, server-local FILETIME, and final uint32. The 172-byte region and
complete 823-byte packet round-trip exactly. Reports now expose
snapshot_marker: 26, progression_typed: true, and
progression_shape: compact, and the gamestate fold retains the skill, saved
maps, and server clock.
The 1,422-byte continuation is also structurally complete:
uint8 reserved_flag = 0
uint16 skill_count
repeat skill_count: uint32 skill_id, uint32 level
uint16 reserved = 0
uint16 string_property_count
repeat string_property_count: uint32 key, string value
uint16 timestamp_property_count
repeat timestamp_property_count: uint32 key, int64 ticks
int64 reserved = 0
uint32 saved_map_id[16]
uint8 reserved_flag = 0
uint32 constant = 1
uint8 variant # 1 in stream 92; 2 in stream 114
uint16 extended_property_count
repeat extended_property_count: uint32 key, string value
uint16 reserved = 0
byte[112] fixed_trailer
Both captures contain six skill entries, seven string properties, 35 timestamp
properties, and 12 extended properties. The fixed trailer contains two
validated 17-byte blocks, five UTF-16 strings with code-unit counts
0/1/1/16/0, constant/reserved fields, the 1900-01-01 sentinel, a
server-local FILETIME, and one final uint32. Values of keyed strings and
trailer strings are retained for exact re-encoding but omitted from normal
reports.
The layout is supported by a complete live primitive-reader trace of the
stream-114 packet: 734 observed calls consumed the body through the final
field. The trace also showed that RVA 0x1cd0560 is a direct one-byte reader,
while RVAs 0x1cd0730 and 0x1cd0790 account for the two- and eight-byte gaps
in the typed prefix. The entire packet is now structurally bounded. Semantic
names remain neutral for the five equipment groups, keyed property roles, and
parts of the fixed trailer until independent effects identify them.
The first controlled typed mutation targets current_hp. Replay analyzes the
entire normalized world transcript, requires exactly one valid initial
snapshot, checks 0 <= current_hp <= max_hp, replaces only the nested HP
field, requires an unchanged packet length, and parses the generated bytes back
to the same typed value before patching/re-encrypting the frame. A stream-114
real-client run changed captured HP 50/222 to 1/222. The client HUD showed
HP 1 / 222; the independently captured replay transcript folded to
phase=active, map 101000000, HP 1/222, unchanged inventory/progression,
nine active NPCs, and matched heartbeat traffic. This confirms the field
offset and width as a live effect, not only an offline round trip.
TypedInitialFieldSnapshot is the reusable generator boundary for both large
variants. --generate-initial-field-snapshot materializes the envelope,
character, inventory groups/items, marker-specific progression, and trailer,
then requires byte-identical same-length output when no mutation is requested.
GET /api/v1/status exposes that plan under
protocol.initial_field_snapshot_emitter with frame index, emitter name,
inventory group/item counts, skill count, progression shape/variant,
original/emitted/max HP, prediction, and frames_patched. The existing HP
option performs its bounded mutation through the same generator and exposes
the fields under protocol.initial_player_hp_rewrite.
The baseline generator has also crossed the real-client boundary. In the
2026-08-09 stream-114 run, runtime status reported server frame 3, one
patch, nine inventory groups/54 items, six skills, keyed-property variant 2,
and unchanged HP 50/222. The client rendered level 12, HP 50/222, MP
97/342, and EXP 1464. Its simultaneously captured transcript folds validly
to active with those values, the same inventory/progression state, and 7/7
matched generated heartbeats. Therefore the exact re-emission is accepted by
the live encrypted client path as well as by the offline round-trip checks.
The later 95-byte opcode-157 variant is CompactFieldTransition; it remains
fully decoded and updates transition sequence, map, portal, HP, and server
clock without replacing the initial player-stat model.
The pinned version-300 IL2CPP opcode-189 handler reads a u32 object id, a u8
level, and a counted UTF-16 name before delegating the remaining player body.
The adjacent opcode-190 handler reads exactly one u32 object id and removes
that player:
opcode 189:
uint16 opcode
uint32 object_id # redacted
uint8 level
uint16 name_code_units
utf16le[name_code_units] name # retained only for re-emission
byte[] opaque_player_body
opcode 190:
uint16 opcode
uint32 object_id # redacted
Streams 92/114/126 contain 58/4/52 entries and 29/0/10 leaves. All 153
packets round-trip exactly. The entry prefixes are structurally bounded and
their 36,450 remaining body bytes stay explicit, so the 114 entries are
partial; the 39 exact removals are full. Every removal references a player
introduced in the same field epoch. More importantly, all 563 opcode-202
player-movement broadcasts and all 652 server opcode-217 life-movement
broadcasts now correlate with a prior entry instead of creating players from
movement alone.
The fold emits remote_player_entered_field and
remote_player_left_field, clears active players at field transitions, and
preserves entry metadata when later movement supplies a position. Safe state
exposes only an alias, level, name-code-unit count, position when known, and
opaque-byte count. It never emits the captured object id or name.
A browser-free live A/B/A then composed the restored player's captured
entry/control values with the local player's validated movement path. Opcode
202 moved the white-haired remote sprite to the predicted (629,-2691).
Injecting its typed six-byte opcode-190 removed the sprite and changed folded
active-player state 4 -> 3; replaying the exact opcode-189 plus the composed
movement made it visible again and restored 3 -> 4. The final transcript is
valid with zero unknown leaves. The client remained active on map 101000000
with no connection failures and 209/209 paired heartbeat probes.
The pinned opcode-224 handler reads the remote-player object id directly and
delegates the remaining body. The two sustained gameplay references establish
the complete 22-byte packet:
uint16 opcode = 224
uint32 object_id # active remote player; redacted/aliased
uint8 marker = 0xff
uint32 value # neutral role
uint32 mob_template_id
uint8 flag # observed 0 or 1
uint16 reserved = 0
uint32 repeated_value # must equal value
Stream 126 contains 20 records and stream 92 contains nine; stream 114
contains none. All 29 consume and re-encode byte-for-byte. At each frame the
object id names a remote player introduced by opcode 189, and at least one
currently active mob has the encoded template. The long stream uses player
aliases for two ids and templates 130100, 210100, and 1110100; stream
92 uses four player ids and templates 130100/210100.
The fold emits remote_player_mob_value_received with a session-local player
alias, known-player boolean, active-template count, mob template, neutral
value, and flag. It records unknown-player/inactive-template counters for
future captures but both remain zero in the references. Safe output never
contains the player id. The repeated value and remote-player/mob correlations
are exact; they do not by themselves prove that the value is damage or justify
a live visual/state effect claim.
The pinned version-300 opcode-239 handler reads one selector byte and
dispatches through a larger branch table. The reference captures exercise four
selectors with these exact bodies:
uint16 opcode = 239
uint8 selector
selector 3:
uint8 record_count
repeat record_count:
uint32 key # retained for re-emission; redacted
int32 value
selector 9 or 13:
# no body
selector 21:
uint16 text_code_units
utf16le[text_code_units] text # retained for re-emission; redacted
uint8 text_terminator = 0
uint32 trailing_value
Stream 126 contains 59 packets: 29 selector-3 lists with 38 total records,
27 selector-9 packets, two selector-13 packets, and one selector-21
packet. That string has 14 UTF-16 code units and its trailing value is 1.
Stream 92 contributes one more empty selector-13 packet; stream 114
contains none. All 60 packets consume and re-encode exactly at full coverage.
The fold emits server_opcode_239_received and records selector/count, signed
record-value, text-length, and trailing-value distributions. Safe state,
events, reports, JSON, and HTTP-derived analysis omit all record keys and text.
The fields remain neutral: packet shape and handler dispatch do not establish
whether a branch represents UI, inventory, progression, or another subsystem.
Selectors not observed in the two captures remain unknown rather than being
accepted by one of these codecs.
The pinned version-300 opcode-247 handler reads a terminated counted UTF-16
string, two signed 16-bit values, and a control byte. One control branch reads
two additional signed 32-bit values:
uint16 opcode = 247
uint16 text_code_units
utf16le[text_code_units] text # retained only for re-emission
uint8 text_terminator = 0
int16 value_1
int16 value_2
uint8 control_value
optional int32 extended_value_1
optional int32 extended_value_2
Only stream 126 contains this family: 33 packets of 60 or 62 bytes, with 17
distinct tutorial localization keys. Six strings contain 25 UTF-16 code units
and 27 contain 26. value_1 has distribution 100:7, 110:2, 150:23, 200:1;
value_2 is always 5, the control byte is always 1, and none uses the
extended branch. All 33 parse at full coverage and round-trip exactly. The fold
emits tutorial_ui_instruction_received and exposes only code-unit and numeric
distributions; text never appears in safe state, events, reports, or JSON.
Two exact compact packets were injected into the live level-12 client. Both
were accepted, independently folded at full coverage, and left the client
active on map 101000000 with four remote players and no connection failure.
Framebuffers sampled at 100 ms, 400 ms, and one second showed no new overlay,
so rendering is state-gated in this client state and is not claimed. The live
test validates packet shape, encryption/order, and non-blocking handling only.
The pinned version-300 opcode-244 handler first reads a selector byte. Its
selector-8 branch performs three signed 32-bit reads, exactly matching the
only branch present in the reference captures:
uint16 opcode = 244
uint8 selector = 8
int32 value_1
int32 value_2
int32 value_3
Stream 126 contains 54 such packets, all exactly 15 bytes. They consume and
round-trip exactly at full coverage. value_1, value_2, and value_3 have
28, 19, and 13 distinct values respectively; value_3 is zero in 42 packets.
Those roles remain neutral because width and UI effect do not establish their
higher-level identifiers. Other selector or length branches stay unknown
rather than being forced through the selector-8 codec.
An exact captured packet with values 1036, 2003, 0 was injected into a fresh
level-12 live client. It immediately opened an instructional NPC dialogue:
the 100 ms framebuffer showed partially rendered text and the one-second frame
showed the complete message. The injected packet reappeared in the transcript
as one exact full-coverage instructional_dialogue_requested event. The fold
remained valid and active on map 101000000, all 11 observed heartbeat pairs
matched, and runtime reported no connection failure. This validates the
dialogue-request effect while deliberately leaving its numeric value roles
unnamed.
Server opcodes 322, 320, and 323 are the reactor spawn, state-update, and
removal lifecycle. This is supported by the pinned handler grouping, the exact
wire shapes, the Reactor/{0:D7} and reactorState strings in the build, and
the matching roles in the independent Cosmic
reactor-hit handler
and reactor packet builders.
The version-300 layouts are:
opcode 320:
uint16 opcode
int32 reactor_object_id # aliased/redacted
uint8 state
int16 x
int16 y
uint16 stance
uint8 reserved_value = 0
uint8 frame_delay
opcode 322:
uint16 opcode
int32 reactor_object_id # aliased/redacted
int32 reactor_id
uint8 state
int16 x
int16 y
uint8 spawn_flag
opcode 323:
uint16 opcode
int32 reactor_object_id # aliased/redacted
uint8 state
int16 x
int16 y
Stream 126 contains 12/50/20 packets respectively, all exactly 15/16/11
bytes and all exact full-coverage round trips. Every state update/removal
references an active same-field reactor; removals delete the entity and field
snapshots clear the active reactor map. State/events expose only reactor:N
aliases, reactor template, coordinates, state/flag distributions, and the
request/response correlation. Raw runtime object ids are omitted.
A live exact opcode-322 record at captured coordinates (2609,-372) was
accepted without a visible in-view change. A second typed record changed only
the i16 coordinates to the folded local-player position (633,-2677). At 100
ms the client showed a transient blue 10 directly above the player; it was
gone at one second, HP remained 50/222, and the connection stayed active.
The transcript folds the pair exactly as reactor:1 creation then update,
ending at (633,-2677) with zero unknown updates and 131/131 matched heartbeat
pairs. The blue 10 is consistent with reactor template 2000; the live check
validates placement/acceptance, not gameplay policy for that reactor.
Client opcode 225 is the corresponding reactor-hit request:
uint16 opcode = 225
int32 reactor_object_id # aliased/redacted
int32 character_position # observed 2 or 3
uint16 stance # observed 305 or 393
uint32 reserved_value = 0
All 15 stream-126 requests are 16 bytes. Their five object ids resolve to
active same-epoch reactors and every request follows targetless opcode 50.
FIFO correlation by reactor id matches every request to the next opcode-320
state update (12) or opcode-323 removal (3) in 388.463..1,102.698 ms
(median 406.749 ms). All 12 updates echo the request stance exactly; no
request remains pending. The character_position label is supported by the
independent reactor-hit handler but remains build-specific telemetry here.
Python and native codecs consume/re-emit every request exactly. Promoting the
15 requests while reclassifying the already-full 82 lifecycle packets moves
stream 126 from 27,188/43,912/0/0 to 27,203/43,897/0/0.
Two independently observed selector branches share client opcode 276.
Stream 126 contributes one 210-byte selector-24 packet:
uint16 opcode = 276
uint32 selector = 24
uint32 header_value_1 # retained; redacted
uint32 header_value_2 # retained; redacted
uint32 group_count
repeat group_count:
uint32 group_selector # retained; redacted
uint32 pair_count
repeat pair_count:
uint32 value_1 # retained; redacted
uint32 value_2 # retained; redacted
Its group_count is 5; per-group pair counts are 3,5,5,3,3, totaling 19.
The active local-Wine transcript independently contributes the compact branch:
uint16 opcode = 276
uint32 selector = 17
uint8 reserved[3] = 0
The fold publishes only selector, compact/grouped shape, group count, pair
count, and field epoch. Header, group-selector, and pair values remain present
for exact re-emission but are omitted from safe state and events. Both Python
branches round-trip exactly, the native manifest keeps distinct nine- and
210-byte shapes, and isolated native validation exact-consumes both packets.
The reference grouped record and independently observed compact record now have
full structural coverage; redacted headers, group selectors, pair values, and
higher-level purpose remain neutral.
No request/response, UI, or gameplay meaning is assigned from the shared opcode
alone. The long corpus moves from the opcode-298 checkpoint
26,661/44,429/10/0 to 26,661/44,430/9/0; the held-open live transcript
moves from one unknown packet to zero and closes warning-free after the
configured two-hour hold with all 1,440/1,440 heartbeats matched. Its final
gameplay phase remains map 101000000 at HP 50/222.
All 12 client opcode-298 packets in stream 126 have one exact 76-byte
shape:
uint16 opcode = 298
uint32 control_value = 0
uint32 selection_index
uint32 request_kind # observed 1 -> Use, 2 -> Cash
uint32 item_id
uint32 quantity
uint32 duration_value # observed 0, 10080, or 20160
uint64 expires_at_ticks = 150842304000000000
uint32 serial_value # retained for exact re-emission; redacted
uint32 reserved_values[5] = 0
int32 signed_sentinel_values[2] = -99
uint32 trailing_values[2] = 0
uint8 flag_1 = 0
uint8 flag_2 = 1
The selection indices are 25,24,23,22,10,9,8,7,6,5,3,4; request kinds are
{1:9,2:3}, duration values are {0:8,10080:1,20160:3}, and only the three
kind-2 records carry nonzero serials. The serial field is excluded from safe
packet state, events, and text reports.
Every request is followed in the same field epoch by a server opcode-39
addition with the request-kind inventory and exact item template. The bounded
latencies are 388.332..711.700 ms. All nine Use requests match aggregate
response quantity, including quantity 2 split into two quantity-1 slots.
Two Cash stack additions have response quantity 3 for request quantity 1,
and the Cash equipment-style addition has no quantity. The fold therefore
records 12 item/inventory matches, nine quantity matches, two quantity
mismatches, one quantity-unavailable response, and zero pending requests; it
does not reject the three Cash distinctions or infer what authorizes the
acquisition. Empty opcode-39 change sets immediately before some additions
do not prematurely close a request.
The Python codec, fold, and native manifest consume and re-emit all 12 records
exactly. All 12 now report full structural coverage while neutral control,
selection, duration, serial, sentinel, and flag roles remain neutrally named or
redacted. Safe analysis adds request counts by inventory/kind, neutral duration
distributions, nonzero-serial counts, match/quantity/pending counters, and
last/maximum response latency. Coverage advances from
26,661/44,417/22/0 to 26,661/44,429/10/0.
--reactive-item-acquisition-responses serves only the five permanent Use
transactions for which the capture proves an exact construction rule:
(selection,item,quantity) values (5,2000016,100), (6,2000018,100),
(9,2000079,100), (10,2030059,10), and (22,2000031,300). An admitted
request must retain kind 1, duration 0, serial 0, all fixed fields, and
an item template absent from the modeled Use inventory. The response is one
opcode-39 add with update flag 0, inventory type 2, the lowest positive
free slot, record type 2, requested quantity, zero-length owner, captured
zero metadata, and both permanent/sentinel timestamps. Timed Use and all Cash
requests remain unserved because their expirations, serials, quantities, and
record variants require additional policy evidence.
The encrypted replay integration test drives opcode 298 through the actual
hold-open reader and validates the generated opcode 39, mutable policy state,
runtime events, and immutable transcript fold. The reference account already
owns four of the five permanent templates, leaving only item 2000079
currently eligible. Its ordinary UI did not expose the captured acquisition
action, so the real-client check independently validates the response side:
after a typed removal cleared an earlier permanent-stack probe, the exact
eligible item-2000079, quantity-100, slot-25 addition appeared in the
open Use inventory without disconnecting. The clean transcript
downloads/maple_custom_server_observed/item_acquisition_live_20260812_clean/world/1786499171269565791_replay_12857.jsonl
remained valid and warning-free at 156/133/0/0, folded 25 Use items including
the exact slot/template/quantity, and matched 60/60 heartbeats with none
pending. This proves client acceptance of the response shape, not a genuine
live opcode-298 request; the encrypted integration test is the handler proof.
The pinned version-300 opcode-348 handler performs four common primitive
reads before selector dispatch. The observed branches have this exact grammar:
uint16 opcode = 348
uint8 category
int32 primary_value # retained for re-emission; redacted
uint8 selector
int32 value
uint16 text_code_units
utf16le[text_code_units] text # retained for re-emission; redacted
uint8 text_terminator = 0
if selector == 0:
uint8 control_1
uint8 control_2
Only stream 126 contains opcode 348: 31 packets, 30 distinct payloads, and
six distinct primary values. Category is 4 and value is zero in every packet.
Selectors occur as 0:20, 3:2, 6:7, 17:2. Selector 0 control pairs are
0:0 once, 0:1 nine times, and 1:1 ten times; selectors 3, 6, and 17
end immediately after the string terminator. Packet lengths range from 59 to
391 bytes. Every packet consumes and re-encodes exactly at full coverage.
The fold emits server_opcode_348_received and exposes category, selector,
value, text-code-unit, and control-pair distributions. Safe state, events,
reports, JSON, and HTTP-derived analysis omit the primary value and text.
Their higher-level roles remain neutral, and handler selectors absent from the
captures stay unknown.
Every one of those 31 server packets is followed by exactly one same-selector
client opcode 66 packet, with no pending or unmatched transaction when
processed chronologically. Its capture-bounded grammar is:
uint16 opcode = 66
uint8 selector
uint8 status
if selector == 6 and status == 1:
uint32 optional_value # retained for re-emission; redacted
The four-byte selector/status forms are 0/1 (19), 0/255 (1), 3/1 (2),
6/0 (1), and 17/1 (2). Six 6/1 packets use the eight-byte form; their
optional value is 5 five times and 1 once. Same-selector FIFO round trips
range from 728.174 to 10,436.006 ms with a 1,561.373 ms median. The fold
emits server_opcode_348_acknowledged, exposes only selector/status/shape,
redacted-value presence, pending counts, and timing, and warns on unmatched or
unfinished transactions. All 31 packets parse, re-encode, and validate through
the generated native shape without exposing the text or optional value.
A cross-state live replay does not establish that arbitrary opcode-348
packets are safe. The custom server wrote one exact 63-byte selector-0 packet
from the level-1-to-10 stream to an active level-12 client bootstrapped from
short stream 114. The client emitted no opcode 66; after one more heartbeat
it closed the world connection and displayed a black framebuffer. The launcher
and direct nested-Wayland seat restored the same client to an active field with
719/719 heartbeats. This is negative state/build-gating evidence, not a
contradiction of the capture-local one-to-one transaction correlation.
Stream 126 contains two exact 10-byte requests with this grammar:
uint16 opcode = 64
uint32 npc_object_id # observed 170389 and 11827
int16 player_x
int16 player_y
The two object ids resolve in folded field state to active NPC templates 2003
and 22000. Their player positions (198,275) and (3331,-219) exactly match
the endpoint of the last same-epoch client opcode-47 life-movement path. The
next same-epoch server opcode 348 follows after 439.289 and 396.405 ms,
respectively.
An independent live input control establishes the semantic boundary. The
expanded opcode-385 keyboard map binds action 54 to evdev Space (57). A
physical Space press emitted three repeated opcode-64 requests while held;
all carried object 3294, which was the active field NPC template 1032005,
and player position (677,-2695). The folded NPC position was (740,-2693).
The automatic dump establishes only the opcode enum; the object-id role comes
from this input effect plus the independent active-NPC correlations.
The fold emits npc_interaction_requested, aliases the runtime NPC id, reports
active/unknown target counts and template distributions, validates the player
position against same-epoch movement, and FIFO-correlates the following opcode
348. It warns on an inactive target, position mismatch, or unfinished
request. Both reference requests resolve, match position, receive opcode 348,
and leave nothing pending. The live custom server does not implement that
response, so its three otherwise valid requests remain pending with one
explicit warning. Python and the native manifest consume and re-emit every
record exactly. Stream 126 currently measures 26,664/44,436/0/0
full/partial/unknown/invalid observations.
Three independent gameplay streams share a small fixed-width server-record family. Their complete grammars, including the two-byte opcode, are:
opcode 24, 45, 178: uint16 opcode
opcode 58, 71, 89, 105, 121: uint16 opcode; uint8 value
opcode 56, 72, 74: uint16 opcode; uint16 value
opcode 60: uint16 opcode; int32 value
opcode 112, 131, 301,
386, 388, 389: uint16 opcode; uint32 value
opcode 96: uint16 opcode; uint16 value_1;
uint16 value_2
opcode 76: uint16 opcode; uint32 value_1;
uint32 value_2
opcode 398: uint16 opcode; uint64 value
opcode 11: uint16 opcode; uint32 reserved=0;
uint8 reserved=0
opcode 59: uint16 opcode; uint32 character_id;
uint8 flag=1; uint32 reserved_1=0;
uint32 reserved_2=0;
uint32 reserved_3=0
Opcode 59 is the only member with an established state relationship: its
character id equals the preceding world-entry character id in streams 92,
114, and 126. Normal reports retain only the match boolean. The remaining
values stay semantically neutral. The generated IL2CPP dump proves that opcode
60 performs one direct signed-i32 read; its six stream-126 values are
1037, 1039, 1042, 1043, 1044, 2132. Each appears in a mob-reward packet batch,
but adjacency does not establish the field's meaning. Stream 126 also proves
opcode 388 is not a reserved-zero record (value=0xfde04000). Variable-width
opcodes 156 and 385 are bounded separately below and are not included in
this family.
Short stream 114 contains 21 records: one of every supported opcode except
the sustained-session opcodes 60 and 301. Streams 92 and 126 contain
69 and 94 records respectively, including a second opcode-96, repeated empty
opcode 45, and repeated opcode-301 values later in gameplay. Opcode 190 is now
the remote-player removal described above rather than a neutral numeric
record. All 184 remaining fixed-record observations parse at full
coverage, round-trip exactly, update opcode counters, and emit
fixed_server_record_received or initial_character_context_received events
with the current field epoch.
--generate-fixed-server-records materializes every typed observation,
reparses it, preserves its original length and server-frame index, and rejects
duplicates or explicit-patch conflicts. GET /api/v1/status exposes the safe
plan under protocol.fixed_server_record_emitter; it includes neutral values,
frame indices, field epochs, patch count, and an unchanged player/phase
prediction, but no character id. A browser-free live stream-114 run first
composed the original 11 generated records with the typed initial snapshot and
nine generated NPC spawns; the other ten now-typed records traveled as
unchanged capture bytes in that connection. A fresh run of the expanded
emitter then patched all 21 fixed records. The client reached and rendered map
101000000 with one active connection, no failures, and 13/13 paired heartbeat
probes. The status API reported frames_patched:21 and the exact 21-opcode
plan. The expanded emitter also has exhaustive byte-for-byte PCAP round-trip
coverage.
These nine opcodes recur with capture-bounded layouts in the gameplay streams. Their semantic roles remain neutral, and fields that may carry a character/session value are redacted from safe output:
opcode 69:
uint16 opcode
uint32 header_value
byte[263] opaque_table
opcode 93:
uint16 opcode
uint8 value_count
repeat value_count: uint32 value
opcode 94:
uint16 opcode
bool flag
int32 primary_value
int32 secondary_value
opcode 137:
uint16 opcode
int16 first_value # redacted
int32 second_value # redacted
int32 third_value # redacted
byte[72] opaque_tail
opcode 148:
uint16 opcode
uint8 variant
variant 9: int32 record_count; byte[] records_blob when nonzero
variant 10: no body
variant 12 or 13: int32 primary_value; int32 secondary_value
opcode 201:
uint16 opcode
uint32 primary_value # redacted
uint32 secondary_value # redacted
uint8 flag_a
uint8 flag_b
byte[22] opaque_tail
opcode 205:
uint16 opcode
uint32 primary_value # redacted
uint32 secondary_value
uint64 numeric_value
uint8 trailing_value
opcode 276:
uint16 opcode
bool8 enabled # captured raw byte 0x05 => true
opcode 379, variant 35:
uint16 opcode
uint8 variant
opcode 379, variant 36:
uint16 opcode
uint8 variant
int64 time_1
int64 time_2
int64 time_3
int64 time_4
Streams 92/114/126 contribute 52/6/123 records respectively. By opcode,
the combined counts are 69:50, 93:7, 94:3, 137:3, 148:23, 201:46,
205:42, 276:2, and 379:5. Every
opcode-69 header is 7 and all 263 retained bytes are zero in these
captures. Every opcode-93 packet counts four u32 values. Opcode 205 is
fully bounded, as is the counted opcode-93 vector. The generated handler dump
independently supplies the exact direct-read sequences for opcodes 94, 137,
276, and 379. Opcode 137 directly reads i16/i32/i32; the two stream-92
packets and one stream-126 packet are all 84 bytes, leaving the same 72-byte
capture-bounded tail after that prefix. Both opcode-276 packets are the
three-byte 0x05 true form, both opcode-379 short packets use variant 35,
and its three
four-datetime packets use variant 36. Opcode 148 contributes one empty
variant-9, nine empty variant-10, nine variant-12, three variant-13, and
one nonempty variant-9 packet. The current delegated IL2CPP record mask is
0x9; the legacy nonempty body does not consume under that current parser and
therefore retains 1,632 record bytes as one explicit partial observation.
Together the family provides 81 full and 100 partial observations with 16,010
opaque bytes rather than inventing suffix or record semantics.
The gamestate fold emits neutral_server_record_received, tracks packets by
opcode, typed-value counts, and opaque-byte totals, and exposes only redacted
safe details. All 181 packets reparse and round-trip byte-for-byte.
A typed live replay of captured opcode-94 values (flag=true, primary
2380000, secondary 2) added exactly one neutral event while phase, field
epoch, map, player, inventory, progression, skills, and fixed-record state
remained unchanged. The browser-free client answered the next generated
heartbeat and remained on map 101000000; this proves non-stalling acceptance,
not a higher-level meaning for either integer.
A fresh browser-free direct-Wayland run also accepted a generated three-byte
opcode-148 variant-10 packet. Its transcript added one full
neutral_server_record_received event, left the folded core state unchanged,
advanced matched heartbeat probes from 11 to 18, and retained one active world
connection with zero injection failures. This validates the bounded empty
branch and predicted neutral fold only.
Opcode 276 also exposes why IL2CPP bool cannot be constrained to wire bytes
0 and 1. The generated handler directly calls the pinned boolean reader,
whose ISIL calls BitConverter.ToBoolean; both reference packets carry
0x05, which therefore means true. Native validation now normalizes every
nonzero byte to true for comparisons, while the Python record keeps the raw
byte for exact re-emission. A loopback replay of exact packet 140105 added a
second full opcode-276 event. The core-state digest was unchanged, phase/map
remained active/101000000, the next three heartbeats matched with no pending
probe, and packet injection retained zero failures.
Opcode 137 remains deliberately partial. Its three generated prefix values
and 72-byte tail are retained for exact re-emission but redacted from safe
analysis; only typed-value and tail-length counts are published. All three
packets validate natively and in the state fold. Live replay is deferred because
the generated handler does not name the values or consume the delegated tail,
so cross-session injection would not be a bounded semantic test.
The automatic dump identifies an eight-way selector handler. Native jump-table
inspection is required because its direct-read list is the union of mutually
exclusive branches. Selector 3 lands at 0x180BC3281, calls the pinned
UTF-16 reader once, then exits through the common return:
uint16 opcode = 169
uint8 selector = 3
uint16 text_code_units
utf16 text[text_code_units] # redacted
uint8 trailing_zero = 0
The sole reference occurrence is a 54-byte server packet in
1-10FS.pcapng stream 126; its string has 24 code units and the shape
consumes the frame exactly. ServerOpcode169TextInstruction preserves the
text for exact re-emission but omits it from safe dictionaries, events, JSON,
and text reports. The gamestate fold increments selector/text-length counters
and emits server_opcode_169_text_instruction_received with selector, length,
and field epoch only. The observed event occurs at epoch 31 and moves the
packet from unknown to full coverage. No cross-session replay is claimed until
the client resource effect is independently bounded.
The generated opcode table identifies handler b7bc850c..., but its direct
packet-read list is empty because it constructs a separate ledger object. The
handler passes the PacketReader to constructor 0x180CB4390; native control
flow proves that constructor reads a u8 count and invokes record constructor
0x180CB3F20 once per entry. The record constructor performs the exact ordered
reads below:
uint16 opcode = 29
uint8 entry_count
repeat entry_count:
int32 key # redacted, role unproven
int32 value_1 # redacted, role unproven
uint16 text_code_units
utf16 text[text_code_units] # redacted
uint8 trailing_zero = 0
int32 value_2 # redacted, role unproven
int16 short_value # redacted, role unproven
111.pcapng contains one 327-byte server packet in stream 92 and one in
stream 114. They are byte-identical, have entry_count = 4, and contain
text lengths 30, 36, 31, and 31 code units. The grammar consumes all
327 bytes, both native manifest validations pass, and
ServerOpcode29TextLedger parses and re-emits both payloads byte-for-byte.
Safe state publishes only packet count, entry-count distribution, total and
per-entry text lengths, redaction flags, and field epoch. The fold emits
server_opcode_29_ledger_received and a full
server_opcode_29_text_ledger observation. At that decoder checkpoint, stream
92 coverage changed to 13,412/21,762/33/0 and stream 114 to
51/20/5/0; the level-1-to-10
stream remains 26,660/44,381/59/0. No live replay is claimed because the
obfuscated numeric fields and captured text have not yet been shown safe across
sessions.
The generated opcode table maps server opcode 135 to handler
aecdc2fee9fbe41bb513947bf2cc9b43154d7eb6d73fbc67a79d1f3b2aa4810.
Its flattened read list contains u8, IL2CPP bool, i16, and i32 calls
from mutually nested loops. A non-stalling in-process trace on the local Wine
client recorded every executed non-i16 primitive for the exact captured
packet: 1,305 calls on one packet object, monotonically advancing from framed
cursor 6 to 3729. The only 45 unhooked spans are two bytes each; inserting
the generated i16 primitive at those spans produces this exact grammar:
uint16 opcode = 135
uint8 section_a_entry_count
repeat section_a_entry_count:
bool enabled
int32 value # redacted, role unproven
int32 value_count
repeat value_count:
int32 value # redacted
uint8 section_b_entry_count
repeat section_b_entry_count:
bool enabled
int32 value_1 # redacted, role unproven
uint8 value_2 # redacted, role unproven
int16 pair_count
repeat pair_count:
int32 value_1 # redacted
int32 value_2 # redacted
uint8 section_c_value # redacted, role unproven
int16 section_c_pair_count
repeat section_c_pair_count:
int32 value_1 # redacted
int32 value_2 # redacted
int32 section_d_entry_count
repeat section_d_entry_count:
int32 value # redacted, role unproven
int16 group_1_count
repeat group_1_count:
int32 value_1 # redacted
uint8 value_2 # redacted
int16 group_2_count
repeat group_2_count:
int32 value_1 # redacted
uint8 value_2 # redacted
The sole 111.pcapng stream-114 packet is 3,725 plaintext bytes. Section A
contains two entries and 166 repeated values; section B contains two entries
and 21 pairs; section C contains ten pairs; section D contains 21 entries with
260 members in each nested group. The Python codec consumes all bytes and
re-emits the original packet exactly, while the generated manifest independently
validates the same count grammar. Safe analysis omits every numeric value and
exposes only entry/group counts, enabled counts, redaction flags, and field
epoch.
The fold emits server_opcode_135_ledger_received and a full
server_opcode_135_bootstrap_ledger observation, moving stream 114 to
52/20/4/0. The exact captured plaintext was also sent through loopback-only
POST /api/v1/server-packets while the local Wine client was the sole replay
peer. The client processed the ledger and retained its field connection and
heartbeat exchange. This proves packet shape and non-blocking handling, not the
meaning or cross-session safety of the redacted values.
The automatic packet dump contains the server opcode-394 enum member but no
attributed managed handler, so the shape below comes from exact capture
consumption rather than a flattened read list:
uint16 opcode = 394
uint16 text_code_units = 57
utf16 text[text_code_units] # redacted
uint8 trailing_zero = 0
The sole server packet is 119 bytes. The next client packet, 57.92 ms later, has this exact 120-byte boundary:
uint16 opcode = 279
uint8 control_value # observed 1; neutral role
uint16 text_code_units = 57
utf16 text[text_code_units] # redacted
uint8 trailing_zero = 0
The two strings have equal lengths and 52 of 57 code units are identical; the
only changed span is indices 10..14. Both codecs preserve the text privately
for exact re-emission while safe state, events, and packet observations expose
only lengths, the control byte, changed count/span, temporal correlation, and
the observed gap. FIFO pairing is an analysis correlation, not a causal claim:
injecting the exact server packet through the local replay's loopback HTTP API
did not produce client opcode 279 within seven seconds, and the local Wine
client remained connected and responsive in-field. The model therefore uses
neutral envelope/event names and does not require opcode 394 for login or
gameplay. The two full observations move stream 92 to
13,414/21,782/11/0; stream 114 remains 52/22/2/0 and stream 126 remains
26,660/44,381/59/0.
Client opcode 158 is a shared mode/count envelope, not one uniform field-load
stage packet:
uint16 opcode = 158
uint32 mode
uint32 change_count
repeat change_count:
uint32 key_code
uint8 binding_type
int32 action_id
Modes 1 and 2 carry count zero and remain the repeated field-load
1 -> 2 sequence in streams 92 and 114. Mode 0 carries keymap changes.
All 11 stream-126 mode-0 packets have count one and match the independent
v83 keymap-change grammar. The key codes are Linux evdev values: 29 Left
Ctrl, 42 Left Shift, 71 Home, and 82 keypad zero. Binding types are
0 empty/removal, 1 skill, 2 item, and 5 action. Observed binding values
include learned skills 2001005, item templates 2000013/2000014, and
action 52, while the first packet assigns type 1 value 1000 at key 42.
The fold applies changes to the opcode-385 keyboard snapshot state, emits
keyboard_bindings_changed, and exposes key/type/action values. The exact
counted grammar supports arbitrary multi-change packets even though the
reference corpus contains only count one.
Client opcode 75 is an exact opcode-only marker:
uint16 opcode = 75
It occurs once during the initial field_loading phase in 111.pcapng stream
92 and 1-10FS.pcapng stream 126. The browser-free local-Wine transcript
independently emits the same marker at field epoch 1. No stronger semantic
role is assigned.
Both terminating 111.pcapng world sessions share this client/server sequence:
uint16 opcode = 241 # empty world-exit request
uint16 opcode = 45 or 46 # stream 114 or 92
uint32 value # redacted status value
uint16 opcode = 9
byte[7] opaque_reason # existing terminal server packet
The request is emitted from active. Status opcode 46 follows by 64.396 ms
in stream 92; status opcode 45 follows by 66.699 ms in stream 114. The
final server packet follows the request by 165.073 and 167.004 ms respectively.
The fold enters exit_requested, reports the status value only as redacted,
then enters terminated and records FIFO correlation plus round-trip timing on
opcode 9. Exact packet observations promote all three client boundaries to
full coverage. Stream 92 reaches 13,417/21,782/8/0, stream 114 reaches
54/22/0/0, and stream 126 reaches 26,661/44,381/58/0.
The current local client's game-menu confirmation did not emit opcode 241,
so a terminal injection was intentionally not attempted. Three controlled
direct-Wayland confirmations instead emitted one client opcode-310 packet
each, with no phase transition or later opcode 241. This leaves the captured
transaction exact and independently repeated, but its live UI trigger unproven
in the current replay state.
Four additional outgoing-client families repeat at exact widths in the two sustained gameplay captures. A fifth width is independently bounded by the three controlled local-Wine menu confirmations:
| opcode | packet/body bytes | stream 92 |
stream 126 |
local Wine | bounded observation |
|---|---|---|---|---|---|
100 |
26/24 |
1 | 1 | 1 | counted ability-point allocation request |
307 |
14/12 |
1 | 1 | 28 | two redacted words plus zero trailer near bootstrap |
308 |
74/72 |
2 | 11 | 13 | typed mirrored-value record; approximately 300-second cadence while continuously running |
310 |
41/39 |
0 | 0 | 3 | counted redacted UTF-16 plus zero suffix |
311 |
22/20 |
2 | 6 | 7 | zero-bounded u32; bootstrap-skewed first gap, then approximately 600 seconds |
Opcode 100 is fully modeled as:
uint16 opcode = 100
uint32 client_tick
uint32 allocation_count
repeat allocation_count:
uint32 stat_mask
uint32 increment
Both captures use count 2 and the existing opcode-41 masks for LUK and INT.
Stream 92 requests increments 1/4; 107.555 ms later opcode 41 raises the
two stats by exactly 1/4 and lowers AP from 5 to 0. Stream 126 requests
9/29; 406.248 ms later the response raises the stats by exactly 9/29 and
lowers AP from 38 to 0. The fold correlates both responses with zero
pending, mismatched, or unverified requests and exposes safe per-stat totals and
latency.
The local client's auto-allocation control adds a bounded request variant: it
keeps the same count-2 LUK/INT layout but may encode zero for the unchanged
stat. An exact live request used LUK +0, INT +1. Individual increments are
therefore unsigned u32 values that may be zero, while the request total must
remain positive.
--reactive-ability-point-allocation-responses turns the correlation into an
opt-in hold-open handler. It derives mutable STR/DEX/INT/LUK/AP state from the
validated replay, rejects requests that exceed available AP or overflow a
u16 result, and emits one opcode-41 record with request flag 1, the
requested stat bits plus AP, updated stat/AP values, and the captured zero
tail. Configured opcode-100 replies are mutually exclusive. HTTP telemetry
is published at protocol.ability_point_allocation_responses.
For the live control, one typed opcode-41 injection made AP 1 visible in
the client, and direct nested-Wayland auto-allocation produced LUK +0, INT +1. The responder returned mask 0x00004300, kept LUK at 15, raised INT
57 -> 58, and lowered AP 1 -> 0. The client remained active on map
101000000; runtime recorded 1/1 requests, zero rejection, one response
packet, and 54/54 heartbeat pairs at the proof sample. Independent analysis
of
downloads/maple_custom_server_observed/ability_point_live_20260811/world/1786491988812896059_replay_12857.jsonl
is warning-free at 159/133/0/0, matches the allocation exactly in 0.213 ms,
and leaves none pending.
Opcode 307 decodes as redacted u32 + redacted u32 + zero u32 in two
references and 28 live records. The middle value is zero in 26 samples and all
four nonzero values are page-aligned; that distribution does not establish a
role, so both values remain redacted. Opcode 310 decodes as one counted
redacted UTF-16 value plus zero u32 + zero u8; all three controlled records
use 16 code units and the same text, but neither text nor UI role is exposed or
named. Opcode 308 decodes two redacted f64s, two redacted u64s,
one u32 mirrored by two f64s, controls 50/1, a 0/1 variant, and terminal
controls 1/0. The mirror holds for all 11 stream-126 and 13 latest-live
records; observed (mirror, variant) pairs are reference 59/60,0 and live
3/56/59/60,0/1. Opcode 311 is exactly
zero u64 + redacted u32 + zero u64 for all six reference and seven live
samples. State exposes typed/redacted aggregates without raw values.
Events add opcode, field epoch, phase, and the observed interval after the first
record. Stream 92 observes a
301.474-second opcode-308 gap and a 584.534-second opcode-311 gap. Stream
126 keeps opcode 308 within 299.995..310.551 seconds and opcode 311
within 557.141..610.544 seconds. In the first local run, the first nine
opcode-308 gaps stay within 299.992..300.017 seconds before one later
592.004-second gap; opcode 311 has one bootstrap-skewed 346.476-second gap
followed by approximately 600-second gaps. Cadence is therefore descriptive,
not a guarantee that every interval produces a packet.
Opcodes 307/308/310/311 remain partial observations: numeric structure,
cadence, and controlled UI correlation do not establish field semantics or
replay safety. Opcode 100 is full because the repeated masks and increments
match the authoritative stat/AP deltas in both captures. The automatic packet
manifest replaces the 100/307/308/311 opaque pins with typed shapes and adds
an explicit typed/redacted live-only opcode-310 shape; it does not invent outgoing-client
semantics from incoming handler reads. Current coverage is
13,420/21,787/0/0 for stream 92, remains 54/22/0/0 for stream 114, and
is 26,662/44,438/0/0 for stream 126. The first local transcript folds
all three opcode-310 records with zero unknown packets and an active final
packet state; its socket later timed out without opcode 241. A fresh
browser-free relaunch then traversed world/channel/character selection through
the nested Wayland seat and re-entered map 101000000. Its transcript
positioned_effect_actions_live_20260811/world/1786445521564813241_replay_12857.jsonl
is valid and warning-free at the post-entry 62/41/0/0 sample, remains
active at HP 50/222, and matches all 10 sampled heartbeat probes with none
pending. Runtime status reports one active local world connection, injection
ready, and zero injection failures while the typed initial snapshot,
fixed/variable records, and NPC spawns are generated.
Each opcode occurs once and byte-identically across gameplay streams 92,
114, and 126, immediately after the initial field snapshot and the earlier
fixed/variable bootstrap records. The generated opcode-147 handler directly
reads two rectangles followed by a counted integer vector:
uint16 opcode = 147
int32 rectangle_1[4]
int32 rectangle_2[4]
int32 value_count
repeat value_count:
int32 value
The captured packet is exactly 94 bytes. Its rectangles are
(-300,-370,300,220) and (-300,-405,300,290), and value_count is 14.
The vector values remain neutrally named and are omitted from safe output.
Opcode 272 delegates from its generated top-level handler. A focused live
primitive-reader trace over the exact 1,056-byte captured plaintext supplies
the complete nested grammar:
uint16 opcode = 272
int32 header_value
int64 start_ticks
int64 end_ticks
int32 header_values[5]
int32 entry_count
repeat entry_count:
int32 selector
bool8 flag_1
bool8 flag_2
int32 group_1_count
repeat group_1_count:
int32 values[3]
int32 group_2_count
repeat group_2_count:
int32 values[3]
int32 trailer_value
The reference has 11 entries, both flags false in every entry, 28 total
group-1 triples, 43 total group-2 triples, and trailer zero. Selectors and all
header/triple values are retained only for exact re-emission and redacted from
safe state and events. Counts are bounded before allocation, booleans accept
only wire values 0 and 1, and the decoder requires exact final-cursor
consumption.
The fold emits field_bounds_ledger_received and
field_configuration_ledger_received, records rectangle/count/group/flag and
trailer distributions, and marks both observations full. The native manifest
keeps the original opaque width pins as raw evidence but suppresses them when
the matching semantic opcode/length shape is active, avoiding ambiguity. All
six cross-corpus packets parse and re-emit exactly through both implementations.
Two exact post-bootstrap opcode-272 writes through the opt-in loopback packet
API were accepted by the active browser-free client. The resulting transcript
folds the original plus both injections as three full ledger events, remains
active on map 101000000, and leaves player, inventory, progression, skills,
NPCs, and phase unchanged. Heartbeat responses continued after detach; the
focused debugger pause accounts for the 36.6-second maximum round trip. This
validates packet acceptance and the predicted neutral fold, not a semantic name
for the ledger fields.
These shapes are driven by the current automatic
tools/il2cpp_packet_dump handler output and exact parsing of both gameplay
corpora. The opcode-27 and opcode-28 top-level handlers each read one
signed record count before delegating their entries:
uint16 opcode = 27
int32 entry_count
repeat entry_count:
int32 key
int32 value
int32 control
utf16z text # uint16 code-unit count + UTF-16LE + zero byte
uint16 opcode = 28
int32 entry_count
repeat entry_count:
int32 key
int32 value
utf16z text_1
utf16z text_2
The 1,056-byte opcode-27 form in 111 has 18 records and 390 total text
code units; the 27-byte stream-126 form has one record and three code units.
The 260-byte opcode-28 form has five records and 56/36 code units in its
two text columns; stream 126 uses a 164-byte, four-record form with 18/33
code units. Keys, values, controls, and text are retained only for exact
round-trip encoding and omitted from safe output.
Opcode 142 reads one boolean directly and delegates only when it is true:
uint16 opcode = 142
bool8 enabled
if enabled:
utf16z header_text
int32 entry_count
repeat entry_count:
int32 key
int32 control
utf16z text_1
utf16z text_2
bool8 flag_1
bool8 flag_2
int32 value_1
int32 value_2
The disabled branch ends after three bytes. Both captured packets use the
enabled branch: stream 92 is 254 bytes with four records, nine header code
units, and 65 entry-text code units; stream 126 is 190 bytes with three
records, nine header units, and 45 entry-text units. Every captured flag_1 is
true and every flag_2 is false. The typed decoder preserves each raw byte and
applies the same zero/nonzero truth rule as the IL2CPP reader; the current
ledger captures happen to use canonical 0 and 1. The count remains bounded
before parsing.
Opcode 425 has the following capture-complete neutral shape:
uint16 opcode = 425
uint16 value_count
repeat value_count:
int32 value
int32 trailer[4]
The packets in streams 92, 114, and 126 are byte-identical, 68 bytes
long, have count 12, and end with trailer (0,0,1,1). A focused live trace
independently entered the delegated handler at reader cursor 6, observed its
internally consumed count, and recorded exactly 12 repeated i32 reader calls
at cursors 8,12,...,52. The four trailer words are also identical across all
three captures and required for exact final-cursor consumption. Repeated values
remain redacted; safe analysis publishes only the count and neutral trailer.
The native manifest uses seven fixed-width semantic declarations so each
captured width replaces only its matching opaque pin. All 13 selected
private-regression packets validate natively (9 from the selected 111
streams, including the login duplicates, plus 4 from 1-10FS) and every one
round-trips through the Python codecs. The state fold emits one full ledger
observation/event per packet and reports only record counts, text lengths,
boolean counts, and trailer shapes. That batch raised coverage to
26,659/44,373/68/0 on stream 126, 13,410/21,755/42/0 on stream 92,
and 49/20/7/0 on stream 114.
The loopback-only POST /api/v1/server-packets route accepted one exact
opcode-425 packet while the real client was in the field. The resulting
transcript folds the captured packet and injected copy as two full
server_opcode_425_ledger_received events and keeps phase, map 101000000,
player, inventory, and progression state unchanged. The later process exit
followed the debugger session rather than a synchronous packet rejection; a
fresh browser-free direct-Wayland launch returned to the field with sound
muted and a ready world connection.
These six opcodes are registered on the same generated handler class. Each
handler makes exactly one direct PacketReader call, a u32, then invokes its
local state method without another reader call. The captures contain additional
bytes after that value, so the honest boundary is a typed leading value plus an
ignored, capture-bounded tail:
uint16 opcode
uint32 primary_value
bytes opaque_tail
Only these observed opcode/tail-length combinations are accepted:
| Opcode | Tail bytes | Packet bytes | Occurrences |
|---|---|---|---|
228 |
4 |
10 |
1 in stream 92 |
230 |
1 |
7 |
1 in stream 92, 1 in stream 126 |
230 |
7 |
13 |
1 in stream 126 |
231 |
20 |
26 |
1 in stream 126 |
232 |
16 |
22 |
1 in stream 92 |
234 |
3 |
9 |
1 in stream 92, 2 in stream 126 |
235 |
6 |
12 |
1 in stream 92, 2 in stream 126 |
The shared Python envelope consumes and re-emits all 12 packets exactly.
Safe state and neutral_server_record_received events publish only opcode,
typed-value count, and opaque-tail length; the u32 and tail bytes are
redacted. Observations remain partial because the client handler does not give
the tail bytes a readable role. Seven semantic manifest declarations replace
the five matching 111 opaque pins and add the two widths found only in
1-10FS; targeted native validation passes 12/12 with no unsupported or
consumption failures.
The family moves seven long-stream and five stream-92 observations from
unknown to partial. Strict totals become 26,659/44,380/61/0 for stream 126,
13,410/21,760/37/0 for stream 92, and 49/20/7/0 for stream 114. Live
replay is deferred: the leading value may identify session-local state, and
replaying an untyped ignored tail across sessions would not be a bounded test.
Both opcodes select between a compact and expanded capture variant with the first byte after the opcode. Short-lived GDB primitive-reader traces now bound both expanded branches completely:
uint16 opcode
uint8 variant
opcode 156, variant 0:
end
opcode 156, variant 1:
utf16_packet_string text # u16 units, UTF-16LE units, zero byte
bool8 flag
int32 values[3]
opcode 385, variant 0:
repeat 89:
uint8 selector
int32 value
opcode 385, variant 1:
end
Streams 92 and 114 contain the expanded pair 385:0/156:1, byte-for-byte
identical between the two sessions. Stream 126 contains the compact pair
385:1/156:0. No other variants occur. The captured opcode-385 expanded
packet is 448 bytes: its 89 selectors have counts
0:45, 1:2, 2:3, 4:26, 5:6, 6:7; the signed values range from 0 to
2001005 with 43 distinct values. The captured 21-byte opcode-156 expanded
packet has one text code unit, a false flag, and values (2001004, 0, 0).
The opcode-385 tuple index is a keyboard key code: index 29 is the Linux
evdev Left Ctrl code. Selector 1 is a skill binding and its value is the
skill id. The capture binds key 29 to learned skill 2001005 and key 71
to learned skill 2001002. Selector 0 is an empty binding, opcode-158
changes identify selector 2 as an item binding, and selector 5 is an action
binding. Live controls identify actions 50, 51, 53, and 54 as pickup,
chair sit, jump, and NPC interaction respectively.
An independent legacy-client implementation names the remaining type ids
4/6 as menu/face and action ids 50..54 as pickup, sit, attack, jump, and
interact/harvest in exactly the observed numeric order. Its default map also
places faces 100..106 on F1..F8 and menu actions on ordinary keyboard keys:
KeyType.h,
KeyAction.h,
and UIKeyConfig.h.
The exact Protocol-300 snapshot agrees: selector 4 has 26 menu entries,
selector 6 has seven face-expression entries 100..106, and action 52 is
bound to keypad zero. A focused M input opened the current client's local
GAME MENU without emitting an action packet. A controlled keypad-zero press
likewise emitted no dedicated packet in the empty-platform state; that negative
result limits network claims but does not contradict the source-backed attack
identity. Opcode-156 field meanings remain neutral, and no security meaning
is inferred.
All four forms now have full shape coverage and exact typed round trips. The
fold records opcode/variant counts, 89 selector/value entries per expanded
opcode 385, three typed int32 values per expanded opcode 156, zero opaque
bytes, field epoch, and variable_server_record_received events. Safe output
retains only text length, flag, value/entry counts, and never the opcode-156
text or raw values. Expanded opcode 385 additionally updates the current
keyboard selector distribution plus skill, item, menu, action, and
face-expression maps, validates bound skill ids against initial progression,
exposes the named pickup/sit/attack/jump/NPC-interaction key sets, and emits a
keyboard_bindings_loaded event.
The empty-binding role comes from a one-byte live A/B/A, not the zero value.
The typed keyboard-selector-zero=71 transform changed only key 71's
selector from captured 1 to 0 while preserving value 2001002 and all
other 88 entries. Physical key 71 first emitted opcode 104; under selector
0 it emitted no skill request while an ordinary client opcode-13 packet
followed the input; restoring the exact original record restored opcode
104. Folded selector counts changed 45 -> 46 -> 45, skill-binding counts
2 -> 1 -> 2, and the warning-free active snapshot retained 437/437 matched
heartbeats with none pending. Safe state/events expose empty_binding_count,
the six selector ids, and the five binding maps. Opcode-156 text and raw
int32 values remain suppressed.
Selector 5 has a separate one-entry causal control. The expanded official map
contains six selector-5 entries with values 50,51,53,54,50,52; value 50
is present at evdev key codes 44 and 78. Physical Z at key 44 emitted no
skill request under captured 5/50. Replacing only that entry with selector
1 and learned skill 2001002 produced an authentic opcode-104 request.
Restoring the exact 5/50 entry and presenting an admitted nearby drop
produced authentic pickup opcode 185. Folded snapshots show action-binding
counts 6 -> 5 -> 6, skill-binding counts 2 -> 3 -> 2, and pickup keys
(44,78) -> (78) -> (44,78). This identifies selector 5 as an action binding
and value 50 as pickup. The later controls below assign 51, 53, and 54;
the independent enum assigns the remaining action 52 as attack.
--generate-variable-server-records re-emits every bounded observation at its
original frame index after length/reparse/uniqueness/conflict validation.
protocol.variable_server_record_emitter exposes only frame index, opcode,
variant, text length, flag, value/entry counts, per-family binding counts,
named action key sets, field epoch, patch count, and the predicted unchanged
player/phase state.
A browser-free stream-114 proof regenerated frames 9 and 11, composed
with the initial, fixed, and NPC emitters. A later opt-in HTTP experiment sent
those same two expanded plaintexts through the active cipher state. The real
client remained active on map 101000000 with HP 50/222, MP 97/342, and
110/110 paired generated heartbeats. Its transcript folds validly to four
variable events, 178 typed entries, six typed int32 values, and zero opaque
bytes. This establishes exact generation and post-bootstrap replay acceptance,
not the higher-level purpose of either packet.
A fresh browser-free A/B/A run established the keyboard meaning. Physical
evdev Left Ctrl (29) under the captured value 2001005 emitted a targeted
opcode-52 variant-18 two-hit action with damage [27,32]. One typed packet
changed only that entry's value to another learned skill, 2001004; the next
Left Ctrl emitted variant 17, one hit, damage [65]. Restoring the original
packet restored variant 18, two hits, damage [29,25]. The immutable fold is
valid and warning-free, ends active on map 101000000 at HP 50, records the
binding sequence 2001005 -> 2001004 -> 2001005, and matches 91/91 heartbeats
with no pending or unmatched response. This proves the key/value relationship
without assigning meanings to the other selector families.
Selector-5 action 51, bound to evdev X at key 45, produced a complete
live chair lifecycle boundary without a modeled server response:
client opcode 49 (6 bytes):
uint16 opcode = 49
uint32 item_id
client opcode 82 (2 bytes):
uint16 opcode = 82
client opcode 48 (4 bytes):
uint16 opcode = 48
int16 marker = -1
Physical X rendered Setup-slot-1 item 3010370; the opcode-49 body was the
same 3010370 value. Empty opcode 82 followed 20,006 ms later while the sit
intent remained open. Pressing X again sent opcode 48/-1; a later Right
attempt repeated that exact stand request because the custom server had not
acknowledged the state. Neither reference PCAP contains opcodes 48, 49, or
82 in the client direction, so these shapes are live-bounded rather than
capture-bounded.
The fold exposes full typed observations/events, correlates the requested item
with Setup inventory, and counts recovery/stand requests with or without an
open sit intent. It clears only the modeled client intent on the first stand
request or a field snapshot, and does not claim the server or client has
completed a state change.
Safe state therefore carries server_acknowledgement_modeled: false. The live
transcript is valid, warning-free, and back to zero unknown packets after these
three codecs.
The same unchanged opcode-385 map binds action 53 to evdev Left Alt (56)
and action 54 to Space (57). After restoring real host pointer focus to the
nested client, physical Left Alt produced a visually decisive jump with no
dedicated request packet. Physical Space emitted client opcode 64; its object
id resolved to the active NPC and establishes the request grammar documented
above. Holding Space produced three repeats, so the fold counts raw requests
rather than coalescing input repetition.
The independent KeyAction enum above identifies action 52 as attack. Evdev
keypad zero (82) is the captured binding. Its controlled input did not emit a
dedicated packet in the empty-platform state; the nearby blue 10 recovery
display and alternating client opcode-101 HP/MP recovery requests were
already occurring automatically and are not attributed to that input. Safe
keyboard state publishes sit/attack/jump/NPC-interaction action ids and key
sets while preserving this no-packet observation as a runtime boundary.
The level-1-through-10 capture contains a complete request/update/acknowledgement transaction distinct from skill use:
client opcode 103 (10 bytes):
uint16 opcode = 103
uint32 client_tick
uint32 skill_id
server opcode 46:
uint16 opcode = 46
uint8 flag_a # boolean; semantic role remains neutral
uint8 flag_b # boolean; semantic role remains neutral
int16 record_count # non-negative
repeat record_count:
int32 skill_id
int32 level
int32 auxiliary_value # semantic role remains neutral
uint8 trailing_value # semantic role remains neutral
client opcode 293 (12 bytes):
uint16 opcode = 293
uint32 control_value # 346 in all captured acknowledgements
uint32 client_tick
uint16 trailing_value # zero in all captured acknowledgements
Stream 126 contains eight opcode-103 requests, eight matching one-record
opcode-46 updates, one additional zero-record update, and nine opcode-293
acknowledgements. Every request is followed by an update for the same skill id;
every update is acknowledged. The maximum observed request-to-update interval
is 691.152 ms and the maximum update-to-acknowledgement interval is 16.933
ms. Folding the records yields skill levels {12:0, 1000:1, 2001004:1, 2001005:6} with no pending requests or acknowledgements.
Seven ordinary allocations are preceded by opcode-41 skill-point changes
7 -> 6 through 1 -> 0; each opcode-46 record raises only the requested
skill by one, uses flags 1:0 and auxiliary value 0, and carries trailing
values 4,6,8,10,12,14,16. The earlier beginner request for skill 1000 is
the bounded exception: opcode 41 reasserts 0, yet opcode 46 still creates
level 1 with trailing value 2. Across all eight responses, including that
exception, the trailing byte equals twice the folded sum of skill levels. This
is used only as a capture-bounded construction rule; the field's higher-level
role remains neutral and it is not labeled as skill points.
The fold gives all three packets full structural coverage, emits
skill_level_change_requested, skill_records_updated, and
skill_record_update_acknowledged, and reports request/ack correlations and
timing. Surrounding opcode-41 stat updates independently change skill points;
the opcode-46 trailing byte remains a separate field.
A browser-free real-client stream-114 replay then tested the server packet in
both captured forms. Two zero-record packets and two one-record packets that
reasserted existing skill 2001005 at level 6 all produced client opcode
293 with control 346 and trailing value 0. The four acknowledgements took
11.297 to 864.607 ms; none remained pending or unmatched. The immutable
fold stayed valid and warning-free on map 101000000, with HP 50, skill
levels, player state, inventory, and all other progression unchanged, while
61/61 generated heartbeats were paired. inject-skill-record now automates
typed construction, API submission, acknowledgement correlation, and these
invariant checks for either the empty form or one existing skill.
--reactive-skill-level-change-responses serves the ordinary positive-SP
branch during hold-open. It requires one modeled skill point, a non-negative
int32 skill id and level result, and a twice-level-sum trailing value that fits
uint8; it deliberately rejects the captured zero-SP beginner exception.
An admitted opcode 103 emits opcode 41 first (request flag 0, mask
0x00008000, SP minus one, one-zero tail), then opcode 46 (flags 1:0, one
requested-skill record at level plus one, auxiliary 0, captured trailing
rule). The option cannot share opcode 103 with a configured captured reply,
and telemetry is published at protocol.skill_level_change_responses.
The browser-free real client supplied an independent live proof after a typed
one-point injection. Clicking a skill sent opcode 103 for skill 1001; the
handler moved raw SP 1 -> 0, level 0 -> 1, and emitted trailing value 30.
The visible skill counter changed 5 -> 4, and the client acknowledged opcode
46 with opcode 293, control 346, and tail 0. Independent analysis of
downloads/maple_custom_server_observed/skill_level_live_20260812/world/1786494416311300328_replay_12857.jsonl
is warning-free, matches the response in 0.383 ms and the acknowledgement in
2.762 ms, leaves no request or acknowledgement pending, remains active on map
101000000, and paired 53/53 heartbeats at the proof sample.
The key-71 live control produced a complete fixed-width client request:
uint16 opcode = 104
uint32 client_tick
uint32 skill_id
uint8 skill_level
uint16 trailing_value # observed 0; role remains neutral
The captured and restored 71 -> 2001002 binding each emitted an exact
13-byte opcode-104 packet. Both requests carried skill id 2001002, level
1, and trailing value 0; the level agrees with the initial progression
snapshot. Their client ticks were 393636 and 623660, a delta of 230024
ms. The corresponding transcript timestamps differ by 230039.792 ms, only
15.792 ms more, which supports interpreting the uint32 as a client
millisecond tick.
This result is causal rather than a shape-only label. With the captured
binding, physical evdev key code 71 emitted opcode 104. Changing only the
opcode-385 entry value to learned skill 2001004 made the same key emit the
already-modeled opcode-52 variant-17 attack instead. Restoring the exact
captured binding restored opcode 104 with skill id 2001002. The two
opcode-104 samples are from the controlled live transcript; streams 92,
114, and 126 in the two reference PCAPs contain none.
The gameplay fold gives this packet full structural coverage, correlates the
skill id with both current keyboard bindings and learned skill levels, checks
the submitted level, tracks tick deltas/decreases and neutral trailing values,
and emits client_skill_use_submitted. It does not assign any server packet as
the request's semantic response or name trailing_value beyond the observed
zero.
A later live response-free control provides a narrower server invariant. Two
physical key-71 presses produced same-skill requests 10,684.712 ms apart.
Exactly two periodic server opcode-10 probes occurred between the requests;
no non-heartbeat server packet did. The second request and subsequent matched
heartbeats prove that an immediate gameplay response is not required for repeat
dispatch or connection liveness. They do not prove the skill's client-visible
or authoritative state effect. Each request event now reports its previous
request frame/elapsed time, same-skill relation, and intervening non-heartbeat
server opcode counts. Safe state separately counts same-skill repeats and the
subset with no intervening non-heartbeat packet.
Static client inspection and a bounded live parser trace establish only this prefix:
uint16 opcode = 42
uint32 mask_words[4]
if all mask words are zero:
uint8 zero_mask_flag_a # semantic role unknown
uint8 zero_mask_flag_b # semantic role unknown
int16 zero_mask_trailing # semantic role unknown
bytes opaque_tail # any unmodeled remainder
The opcode-42 client handler at RVA 0xe54150 calls the four-word mask
decoder before processing temporary-stat records. During a live all-zero probe,
the traced reads after that mask were two uint8 values at packet cursors 22
and 23, followed by one int16 ending at cursor 24. Accounting for the
two-byte transport length header outside the plaintext gives an exact 22-byte
minimal plaintext. The analyzer therefore decodes the mask and zero-mask suffix
while preserving every remaining byte; nonzero-mask records remain entirely
opaque after the mask.
This is structural evidence, not a valid skill response. Neither gameplay
stream 92 nor 114 in 111.pcapng, nor level-1-through-10 stream 126 in
1-10FS.pcapng, contains opcode 42. The controlled live run sent a padded
160-byte all-zero probe and then the 22-byte minimal form. Both reached the
socket writer, but heartbeat replies stopped after the padded probe; the later
minimal form was therefore tested only on an already-stalled connection. The
fold emits partial local_temporary_stat_set_header observations and
local_temporary_stat_set_received events, preserves 138 opaque bytes on the
padded form, leaves HP/MP and other modeled state unchanged for a zero mask,
and sets network_progression_proven: false. A preceding opcode-104 request
is exposed only as a non-causal candidate.
A clean browser-free control session subsequently reached map 101000000,
produced one fully decoded opcode-104 request from direct Wayland key 71,
and matched all 16 heartbeat probes/responses with none pending. This separates
normal client progression from the unsafe opcode-42 experiment. Do not replay
opcode 42 as a response until a capture-backed nonzero record grammar and a
fresh-client minimal-packet acceptance test exist.
Server opcode 49 is overloaded. A byte discriminator of 0 selects the
separately modeled pickup-gain notice. Every other observed discriminator uses
the following neutral envelope and is excluded from pickup request/effect
correlation regardless of packet length:
uint16 opcode = 49
uint8 variant
variant 1:
uint32 key
uint8 value_kind
if value_kind == 1:
utf16 text_value
uint8 zero_terminator
if value_kind == 2:
uint64 numeric_value
variant 3:
uint8 record_marker # observed 1
uint32 record_value
bytes opaque_tail # observed lengths 28, 29, or 36
variant 4:
bytes opaque_body # three bytes in both samples
variant 6:
uint64 numeric_value
variant 10:
uint8 reserved_zero
utf16 text_value
uint8 zero_terminator
variant 12:
uint32 key
utf16 text_value
uint8 zero_terminator
Stream 92 contributes variants 3/10/12 = 44/101/7. Level-1-through-10
stream 126 contributes variants 1/3/4/6/10/12 = 128/214/2/1/1/5; variant
1 divides into 98 terminated strings and 30 u64 values. Thus all 503
non-pickup packets parse to their exact ends and round-trip byte-for-byte: 243
move from unknown to full coverage and 260 move from unknown to partial. The
partial records retain 7,607 opaque bytes. The fold emits
server_opcode_49_received and tracks variant, neutral shape, text-code-unit,
and opaque-byte distributions. Text is retained only in the typed object for
exact re-emission and is omitted from safe JSON, events, text reports, and
HTTP-derived analysis.
All 515 opcode-77 samples in the sustained gameplay references share a
one-byte variant discriminator. The text-bearing branches use the standard
uint16 UTF-16LE code-unit count, but reports never expose the decoded text:
uint16 opcode = 77
uint8 variant
variant 3:
utf16 primary_text
uint8 control[3]
variant 4:
bool text_present
if text_present:
utf16 primary_text
uint8 zero_terminator
variant 5:
utf16 primary_text
uint8 prefix_a = 3
uint8 prefix_b = 10
utf16 secondary_text
uint8 zero_terminator
uint8 separator = 10
utf16 tertiary_text
uint8 zero_terminator
uint8 terminal_tag = 2
uint32 terminal_value # role remains neutral
variant 8:
utf16 primary_text
bytes opaque_tail # observed lengths 4 or 117
Variant 3 always has its exact three-byte suffix. Variant 4 has 41 short
false forms and ten true forms with a terminated string. All 22 variant-5
packets use control pattern 03 0a 0a 02 and consume exactly. Variant 8
retains 278 opaque bytes across 13 packets and therefore remains partial;
variants 3, 4, and 5 have full structural coverage.
Stream 92 contributes variants 3/4/5/8 = 152/13/9/6, stream 114
contributes 1/1/0/0, and level-1-through-10 stream 126 contributes
276/37/13/7. Every packet reparses and round-trips exactly: 502 observations
move from unknown to full and 13 move to partial. The fold emits
server_opcode_77_received, tracks variant/control/value and text-length
distributions plus opaque-byte totals, and never copies any of the three text
fields into safe JSON, text reports, events, or HTTP status. The family keeps a
neutral name because the four variants span multiple visible-message forms;
frequency and readable strings alone do not establish one gameplay role.
The level-1-to-10 capture contains two exact 13-byte requests:
uint16 opcode = 79
uint32 client_tick
uint8 inventory_type # 1 equip in both observations
int16 source_slot # observed 2 and 3
int16 destination_slot # observed -11 in both
int16 quantity # observed -1 for both equip moves
The first request is followed 13 combined gameplay frames/1,040.241 ms later
by a single server opcode-39 move for the same inventory and slots. The
second is followed in the next frame/402.275 ms by the same exact transaction
shape. This repeated
field equality is the semantic evidence for the inventory-move name; the
layout alone is not used to infer it. An independent v79
inventory handler
names the leading value as the client tick and the final signed short as
quantity. No unobserved opcode-79 length is accepted by the native manifest.
The state fold queues requests FIFO, correlates only an opcode-39 operation
2 with matching inventory/source/destination, and updates inventory from the
authoritative server packet rather than the request. It emits
inventory_move_requested and inventory_move_confirmed. Safe analysis JSON
reports request counts by inventory, matches, server moves without a request,
pending requests, and last/maximum response milliseconds; plaintext bytes are
never copied into those fields. Both native-manifest records and Python codecs
consume/re-emit exactly. The two requests now have full semantic coverage.
The opt-in --reactive-inventory-move-responses policy turns this correlated
pair into a bounded hold-open handler. It projects initial equipment group 1
onto negative equipped slots, group 3 onto positive Equip-inventory slots,
and overlays later authoritative opcode-39 changes. It admits only opcode
79 inventory type 1, captured quantity -1, and a source slot that
exists in that mutable model. The reply is one exact opcode-39 operation-2
record with captured update_flag=1, request source/destination slots, and
captured move_flag=2; occupied destinations are swapped in policy state.
The handler requires --keep-world-open, cannot share opcode 79 with a
configured captured reply, and publishes observed/served/rejected counts plus
identifier-free item/slot state under protocol.inventory_move_responses.
A fresh local-Wine control supplied an independent destination variant. A UI
move from occupied Equip slot 3 to occupied slot 1 emitted one exact
opcode-79 request with quantity -1; the handler returned one opcode
39 and swapped item templates 1302000/1002053. The live transcript folds
warning-free at 342/323/0/0, matches the request and response with none
pending, remains active on map 101000000, and had 222/222 matched
heartbeat probes at the proof sample. This validates general same-inventory
move/swap handling beyond the two captured moves to equipped slot -11.
The capture-validated packet grammar is:
uint16 opcode = 39
uint8 update_flag # observed 0; role remains neutral
uint8 modification_count
repeat modification_count:
uint8 operation
uint8 inventory_type # 1 equip, 2 use, 3 setup, 4 etc, 5 cash
int16 slot
if operation == 0: complete item_record
if operation == 1: uint16 quantity
if operation == 2: int16 destination_slot; uint8 move_flag
if operation == 3: no operation-specific body
Operation 0 is add, 1 is stack-quantity replacement, 2 is a slot
move/swap, and 3 is remove. The captured add records reuse the
initial-inventory grammar: type 1 carries the equipment record bounded by
its two sentinel timestamps; types 2/3/4 carry a stack item record with
template, cash flag/optional cash id, expiration, quantity, owner string,
bounded metadata, sentinel timestamp, and tail; type 5 carries either the
same stack shape or the complete cash-item variant. Reports retain only
non-sensitive common fields and record length while re-encoding the entire
record. The observed operation-2 records move equipped items from positive
bag slots to negative equipped slots with move flag 2; the fold swaps an
occupied destination and otherwise moves the source item.
Coverage is assigned per change set rather than per opcode. Empty packets and
packets containing only quantity updates, moves, or removals are structurally
complete: the neutral update_flag and move_flag names do not leave bytes
unparsed. Any packet containing an add remains partial because the lossless
item record still retains opaque equipment/stack/Cash metadata. Across streams
126, 92, and 114, this promotes 221 fixed-layout packets
(167 + 53 + 1) to full coverage while all 106 add-bearing packets
(89 + 16 + 1) remain partial. An independent raw-payload walk consumes every
promoted packet at its exact end.
Stream 92 contains 69 packets and 71 modifications: add:16,
update_quantity:40, and remove:15, all under update flag zero. Thirteen
packets have an empty change list. Inventory types are use:20, etc:21, and
cash:30. Fifteen cash remove/add pairs refresh slot 4; the remaining add
creates Etc slot 18, item template 4010003, quantity 1. Every packet
round-trips byte-for-byte, all quantity/removal operations resolve an existing
slot, and the fold ends with Use slot 15 at 27, 24 Use items, 18 Etc, two
Setup, and one Cash item. Stream 114 independently validates one empty packet
and one cash refresh pair with no unknown slots.
Stream 126 expands the grammar to 256 packets and 232 modifications:
add:93, update_quantity:79, move:2, and remove:58. It contains four
equipment adds, two cash-inventory stack adds, and two equip moves. Every
change set round-trips and the fold reports zero unknown-slot modifications.
The resulting stream totals are 67,290 full, 3,810 partial, and zero
unknown or invalid observations; stream 92 reaches 33,573/1,634/0/0 and
stream 114 reaches 61/15/0/0.
The state-driven quantity emitter requires an existing stack item and sends a
single operation-1 change. A real stream-114 client accepted generated
plaintext 2700000101020f000100, changing Use slot 15, item template
2000000, from 27 to 1. The inventory UI displayed quantity 1; the
recorded transcript emitted the same previous/current event, preserved all
item counts and player state, remained active, and matched all 18 generated
heartbeats. The neutral update flag is deliberately not assigned a role.
Stream 126 contains one exact eight-byte record with this capture-bounded
grammar:
uint16 opcode = 111
uint32 neutral_value # observed 425341
int16 slot # observed 3
The next same-epoch server opcode-39 change set arrives after 486.349 ms
and removes then re-adds Cash inventory slot 3. The fold queues the action
and correlates only an opcode-39 modification with inventory type Cash and
the exact signed slot. It reports one match, no pending action, and no warning.
The Python codec and native manifest consume and re-emit the record exactly.
The record now reports full structural coverage without assigning a role to its
neutral u32 or to the higher-level Cash-slot action.
The automatic dump proves only that the opcode enum exists; it supplies no
outgoing client handler or shape. The u32 value, action purpose, and causal
relationship to the authoritative inventory change therefore remain neutral.
With opcode 64 promoted to full coverage, the current stream-126 total is
26,664/44,436/0/0.
The capture-validated request is exactly 12 bytes:
uint16 opcode = 80
uint32 client_tick
int16 use_slot
uint32 item_template_id
Stream 92 contains 17 requests and every packet round-trips byte-for-byte.
Thirteen name Use slot 21, blue potion template 2000014; four name Use slot
15, red potion template 2000000. Each request agrees with the current typed
inventory slot/template. Each is followed by an opcode-39 quantity update for
the same slot and template, and all 17 quantities decrease by exactly one.
Each request is also followed by the captured potion stat effect in opcode
41: red potion increases current HP by 50, while blue potion increases current
MP by 80 with max-MP capping (one observed delta is 79 because MP reaches its
modeled maximum 342). All 17 quantity correlations and all 17 stat-effect
correlations match, with no unknown slots, template mismatches, or pending
requests at capture end. The same independent v79
Use-item handler
passes the leading u32 to the character's tick updater.
Effects for item templates outside the two captured potions remain unknown.
The reactive policy derives mutable inventory and HP/MP state from a validated
world transcript. It accepts only the two evidenced potion templates, checks
slot/template/quantity and maximum-stat bounds, and sends opcode 39 before
opcode 41. It conservatively rejects consuming the final item because the
capture does not establish whether that boundary uses quantity zero or an
operation-3 remove.
A real stream-114 client first accepted a typed red-potion update from
quantity 27 to 2, then emitted opcode 80 for Use slot 15. The generated
responses changed the visible inventory from 2 to 1 and the HUD from HP
50/222 to 100/222. The completed transcript independently folded one
request, one matching inventory response, one matching stat effect, zero
mismatches/pending requests, and 20 matched heartbeat pairs.
The current responder records item_use_request_observed,
item_use_response_completed, and item_use_request_rejected runtime events.
Its fresh browser-free proof used direct Wayland PageUp twice: frames
232/234 applied quantity 2 -> 1 and HP 50 -> 100 with opcodes [39,41],
then frame 250 rejected the second request because the last-item removal
shape remains unvalidated. That rejection emitted no packet and did not close
the client. The folded transcript is valid and warning-free with two requests,
one inventory/effect match, one explicit policy rejection, zero pending item
uses, and 90/90 matched heartbeats.
Server opcode 311 creates or refreshes one field drop. Stream 92 contains
125 packets and 66 unique (field_epoch, drop_object_id) pairs. Fifty-nine
drops appear as an otherwise byte-identical mode-1 then mode-0 pair; seven
item drops use the shorter mode-2 field-load form. All 125 packets parse and
re-encode byte-for-byte. The independent level-1-to-10 corpus adds four exact
mode-2 field-load mesos records.
The shared prefix is:
uint16 opcode = 311
uint8 spawn_mode # observed 0, 1, or 2
uint32 drop_object_id
uint8 drop_kind # 0 item, 1 mesos
uint32 value # item template or mesos amount
uint32 owner_value_1 # role remains neutral
uint32 owner_value_2 # equal to owner_value_1 in all 125
uint8 ownership_flag # zero in all 125
int16 position_x
int16 position_y
uint32 source_mob_object_id
Modes 0 and 1 then carry the animation source and duration:
int16 source_x
int16 source_y
uint16 animation_duration_ms # 450 in all 118 animated records
if drop_kind == 0: int64 expiration_ticks
uint8 final_flag # one in all animated records
That produces 44-byte item records and 36-byte mesos records. Every nonzero
source mob resolves to a field-local mob template retained after that mob's
leave/death packet; none is an unmodeled object. Mode 2 omits the animation
fields:
if drop_kind == 0: int64 expiration_ticks
uint8 final_flag # zero in all seven field-load records
The resulting field-load item record is 38 bytes; a field-load mesos record is
30 bytes. Every captured item expiration is 150842304000000000. The fold
assigns drop:N aliases, treats mode 0 as a refresh of its matching mode-1
record, retains active drops by field epoch, removes them on opcode 312, and
clears them on a field reset.
Spawn mode, owner values/flag, expiration, and final-flag semantics remain
neutral even though their packet boundaries are exact.
The pinned opcode-311 handler independently confirms every direct read and
both conditionals. The analyzer therefore reports full structural coverage for
all four capture-modeled mode/kind branches while retaining those neutral field
names. Across the two reference gameplay streams plus terminal stream 114,
all 562 server opcode-311 packets also pass the independent manifest validator
with exact byte consumption.
All 54 stream-92 opcode-185 requests reference a currently active modeled
drop. The following opcode-49 value matches the spawn value in all 54 cases:
24 item results, 29 mesos results, and the one special result whose value is
the spawning item template. All 54 local reason-5 removals also name that
same active drop; their actor equals both captured owner values and their tail
is zero. This establishes the complete spawn/request/effect/result/removal
chain without assigning a security meaning to the client validation token.
Stream 114 ends with one active mode-2 item drop: template 4000004 at
(-863,-1742). The final folded local-player position is (633,-2677), and
the Etc inventory contains the same template in slot 7, quantity 74.
The owner fields are not a sufficient pickup switch. A typed live replay
rewrote both final mode-2 owner words to the initial player id, and later
controls sent captured-shaped mode-1/mode-0 pairs at the player. The fresh
source-mob controls preserved a known typed mob in field history, tested a
pair less than a millisecond after enter/leave, and repeated it with an
explicit controller-level-0 release before leave. The client stayed healthy
and physical pickup input was verified, but no variant emitted opcode 185 or
compact opcode 222. The bounded transcript snapshot was valid and
warning-free with 141/141 matched heartbeats and zero pickup requests. Runtime
status therefore reports only the modeled owner/source relations: owner
equality, proximity, source-mob presence/history, immediate lifecycle timing,
and controller release are not independently sufficient.
The first capture-timed combat/reward control placed a typed template-210100
mob at the player, observed a real opcode-52 attack, and sent the matching
current-MP update, health 20 -> 0, reason-1 leave, opcode-49 variant-3
record, EXP +10, redacted variant-10 text, and source-matched 4000004
mode-1/mode-0 pair. Its first health response was 92.526 ms after the
attack versus 102.326 ms in that reference family, but pickup input emitted no
request. The exact capture drop in that control was later audited and found to
have no pickup request in its own field epoch. That run therefore bounds only
one capture-authentic packet family; it is not evidence that an officially
admitted drop family failed.
The corrected control starts from the first stream-92 4000004 object with
a proven request. Its official template-210100 attack/death/reward chain has
the first HP response at 58.892 ms, health 12 -> 0, an exact
mode-1/mode-0 pair, a controller-level-0 release 450.451 ms after spawn,
and a pickup request 2,938.908 ms after spawn while the player is 24 horizontal
and one vertical unit from the drop. The live replay retained that packet
family and animated-source offset, rewrote only current MP/EXP and position,
observed an active-target opcode-52 attack, delivered its first HP response
at 53.681 ms, and released control after approximately 450 ms. Physical input
then produced a base opcode-185 request 1,517.335 ms after the live spawn at
Manhattan distance three. The client retried every 3,000 ms until one
[39,49,312] response advanced Etc slot 7 from 74 to 75 and removed the
drop. At the validation snapshot the fold is valid and warning-free: 62 raw
requests form one admitted chain plus 61 retries, its effect/result/removal all
match, and all 900 heartbeat probes have responses. Typed PCAP transforms
rewrite only the sole captured opcode-41 stat value and opcode-311
destination/animated-source positions; every other byte-bearing field is
preserved.
A second proven 4000004 object isolates the drop boundary from that prefix.
Its official pair has animated source offset (+10,-3), a controller release
398.819 ms after spawn, and a request 1,591.279 ms after spawn at horizontal
distance seven. The live control sent only this exact mode-1/mode-0 pair,
retargeted it to the current player while preserving the offset, and sent the
same controller release in 394.459 ms. No mob spawn, attack, health, leave,
reward record, or fresh input preceded the first opcode-185 request, which
arrived in 1,584.485 ms; the fold consequently marks the source mob unknown.
This establishes that the admitted pair does not require the combat/reward
prefix. A reason-1 cleanup interrupted that deliberately unanswered
ten-attempt chain. Reinjecting the same pair produced
another request in 1,595.243 ms before the deliberately delayed new input and
one [39,49,312] response changed 75 -> 76. The aggregate live fold is
warning-free with 76 raw requests, three admitted chains, 73 retries, two
matched completions, one interruption, zero pending pickups, and 1,064/1,064
heartbeats.
A cold-client control initially appeared to exercise a neutral-state boundary.
The exact second admitted pair received a controller release 394.575 ms after
spawn and capture-timed physical pickup input but generated no request. The first
admitted combat/death/reward family was replayed next. A calibrated repeat
delivered its first HP update 64.085 ms after the real opcode-52 attack,
released control 450.850 ms after spawn, and scheduled pickup input at the
previously successful live drop age of 1,517.335 ms; it also generated no
opcode 185 or 222. After removing the injected object, the fresh fold is
active, valid, and warning-free with zero pickup requests/chains/pending work,
one baseline field-load drop, and 180/180 heartbeats. A later coordinate audit
showed that these controls used the global folded trailer (633,-2677) rather
than the same movement record's final absolute command (633,-2693), so the
negative result is not evidence of a missing client-side admission state.
The typed live injector now prevents that ambiguity. It scans backward for the
latest same-field client opcode-182 observation and retargets both
opcode-311 records to its last absolute command final_x/final_y, while
preserving the global folded trailer as fallback and exposing both positions in
safe output. It retains the animated-source offset, allocates new drop/source
ids, reproduces the 398.819-ms release and 1,591.279-ms input schedule, and
waits for the matching aliased request before sending [39,49,312].
The selector is capture-supported rather than specific to one live failure.
Across 54 stream-92 pickup requests, the preceding movement command-final
has mean absolute X/Y deltas 35.815/6.296, maxima 140/28, and three exact
matches; the trailer has 77.093/12.167, maxima 229/39, and no exact match.
Across 197 stream-126 requests, command-final has mean deltas
28.695/3.249, maxima 120/69, and 17 exact matches, versus trailer
41.878/5.756, maxima 240/79, and 13 exact matches.
The primed live client had moved to (675,-2693) while earlier probes still
used (633,-2677); the first live-fold injection produced opcode 185 after
1,607.298 ms and completed 75 -> 76. The decisive control used another
untouched client whose sole opcode-182 record reported command-final
(633,-2693) and trailer (633,-2677). With no movement, key-map, or skill
preflight, command-final placement produced authentic opcode 185 after
1,572.761 ms at request position (633,-2694) and completed 74 -> 75. The
suspected readiness transition was therefore a coordinate-source bug.
The capture-validated client request has a 23-byte base form and a 35-byte extended form:
uint16 opcode = 185
uint32 control_value # zero in all captured requests
uint8 field_epoch
uint32 client_tick
int16 position_x
int16 position_y
uint32 drop_object_id
uint32 item_validation_token # role remains neutral
byte[0 or 12] optional_proof # contents remain opaque
Stream 92 contains 54 requests: 48 base and six extended. Every field_epoch
equals the fold's current field epoch, the ticks preserve request ordering, and
all 54 packets round-trip. Normal reports replace drop_object_id with a
field-local drop:N alias, expose only token presence, and report the optional
proof length rather than its bytes.
Stream 126 adds six exact 19-byte opcode-222 requests that omit only the
opcode-185 control word and optional-proof branch:
uint16 opcode = 222
uint8 field_epoch
uint32 client_tick
int16 position_x
int16 position_y
uint32 drop_object_id
uint32 item_validation_token = 0 # role remains neutral
One request carries field epoch 8; the five-request burst carries epoch 9.
Every epoch equals folded state, the ticks and signed positions track the local
player, and all six object ids resolve to active same-epoch drops. Each request
matches its opcode-39 inventory or opcode-41 mesos effect, opcode-49 gain
notice, and exact-id opcode-312 removal. All six removals use captured reason
2, while the opcode-185 local chains use reason 5; the source and
behavioral meaning of that shape/reason distinction remain neutral. The fold
therefore validates all 203 long-corpus pickup chains with zero unknown drops,
epoch/effect/result/removal mismatches, or pending requests. Safe output adds a
compact shape/counter while continuing to alias runtime drop ids and omit the
validation token itself.
Across both reference gameplay streams, the 234 base, 17 extended, and six
compact requests all pass independent manifest validation with exact byte
consumption. These 257 records now report full structural coverage while the
control, validation-token, and proof meanings remain neutral and redacted.
Requests repeating for the same (field_epoch, drop_object_id) before the
effect/result/removal completes are retry attempts on one logical chain, not
independent pickups. The fold preserves item_pickup_requests as the raw wire
count and separately reports item_pickup_request_chains,
item_pickup_request_retries, item_pickup_admitted_drops, admitted drop-kind
counts, and admitted item-template counts. Each request event carries its
one-based attempt number and retry flag. Effect/result/removal events correlate
to the latest attempt for latency while retaining the first request frame and
total attempt count. Stream 92 remains 54 chains/zero retries and proves four
admitted 4000004 objects; stream 126 remains 203 chains/zero retries. The
first live admitted control is one chain/61 retries, not 62 incomplete pickups.
A removal whose reason differs from the request's local-result reason and
arrives before any effect/result closes that chain as
item_pickup_interrupted_chains. It is neither a successful pickup removal nor
a mismatch and cannot remain pending. The expected local removal without its
effect/result remains a mismatch. The second live cut exercises one interrupted
ten-attempt chain followed by a completed four-attempt chain.
Each request event now includes drop_spawn_frame and drop_age_ms from the
first mode-1 spawn, plus source_controller_release_frame and
source_controller_release_age_ms when the source has released control.
Controller-release events expose aliased source_drop_release_delays_ms;
mode-0 refreshes preserve rather than restart the first-spawn clock. The four
official template-4000004 requests have drop ages 2,938.908, 1,591.279,
4,124.092, and 2,378.920 ms. Their corresponding release ages at request
are 2,488.457, 1,192.460, 4,124.092, and 1,988.300 ms. The third source
release precedes its drop spawn, so that family has no post-spawn release. The
three primed live admissions have drop ages 1,517.335, 1,584.485, and
1,595.243 ms, while the fresh controls expose their matched release delays
without inventing a request event.
The corresponding short server opcode-49 records have three exact variants:
uint16 opcode = 49
uint8 result_flag # zero in all 54 records
uint8 kind
kind 0, item (12 bytes total):
uint32 item_template_id
uint32 quantity
kind 1, mesos (15 bytes total):
uint8 subkind # zero in all 29 records
uint64 amount
uint16 tail # zero in all 29 records
kind 2, special (8 bytes total):
uint32 special_value # behavioral role remains neutral
There are 24 item, 29 mesos, and one special result. For item results, the fold
attaches the immediately preceding positive opcode-39 add/quantity delta.
For mesos, it attaches the positive opcode-41 previous/current delta. The
initial snapshot does not yet seed mesos, so the first result anchors an
inferred prior balance of 4100; the other 28 are independently checked as
direct deltas. The special result has no inventory or stat effect. All 54
result chains match. Request-to-result latency is 27.801-111.964 ms, averaging
68.239 ms.
The pinned opcode-49 handler independently confirms this result discriminator
and all three kind-selected branches. Consequently, neutral meanings for the
result flag, mesos subtype/tail, and special value no longer make the
byte-complete packet partial: all 257 pickup-result records across both
reference gameplay streams report full structural coverage and pass exact
independent manifest validation.
Field-drop removal opcode 312 has three exact widths:
7 bytes: uint16 opcode, uint8 reason, uint32 drop_object_id
11 bytes: uint16 opcode, uint8 reason, uint32 drop_object_id,
uint32 actor_id
15 bytes: uint16 opcode, uint8 reason, uint32 drop_object_id,
uint32 actor_id, uint32 trailing_value
All 100 records round-trip: 25 are seven-byte records (reason 0 or 1), 10
are 11-byte records (reason 2), and 65 are 15-byte records (reason 5, zero
tail). Exact drop-id correlation identifies 54 of the 15-byte records as the
removals completing local pickup requests; the other 11 belong to other
actors. All 54 local removals follow a matching opcode-49 result and leave no
pending pickup. Request-to-removal latency is 27.829-111.964 ms, averaging
79.723 ms. The behavioral meaning of the reason, actor, validation-token, and
optional-proof fields remains deliberately neutral.
The pinned opcode-312 handler confirms the reason-selected 7/11/15-byte
branches, so neutral role names no longer make these structurally complete
records partial. All 545 reference-corpus removals now report full coverage and
pass the independent manifest validator with exact byte consumption.
For state-driven replay, a separate validated evidence transcript supplies the
deterministic item effect. Stream 92 proves template 4000004 four times as
an Etc quantity delta of one and an item gain notice quantity of one. The
reactive policy therefore accepts only a known active item drop, the current
field epoch, a deterministic captured template effect, and exactly one
existing stack with capacity. It emits opcodes 39, 49, and 312 in that
order and removes the drop from mutable state. The response mirrors the
request form: opcode 185 receives the captured 15-byte reason-5 removal,
while opcode 222 receives the captured 11-byte reason-2 removal. Mesos
pickups, special results, new-slot insertion, ambiguous stacks, and unknown
templates remain rejected.
Runtime annotations record pickup request, completed response, or rejection;
an exact rejection consumes its matching pending request without disconnecting
the client. An annotation without a matching observed request is invalid.
The capture-validated prefix and conditional-value grammar is:
uint16 opcode = 41
bool request_flag # role remains neutral
uint32 stat_mask
if stat_mask & 0x00000010: uint8 character_level
if stat_mask & 0x00000020: uint16 job_id
if stat_mask & 0x00000040: uint16 strength
if stat_mask & 0x00000080: uint16 dexterity
if stat_mask & 0x00000100: uint16 intelligence
if stat_mask & 0x00000200: uint16 luck
if stat_mask & 0x00000400: uint16 current_hp
if stat_mask & 0x00000800: uint16 max_hp
if stat_mask & 0x00001000: uint16 current_mp
if stat_mask & 0x00002000: uint16 max_mp
if stat_mask & 0x00004000: uint16 ability_points
if stat_mask & 0x00008000: uint16 skill_points
if stat_mask & 0x00010000: uint32 experience
if stat_mask & 0x00040000: uint64 mesos
bool trailing_flag # role remains neutral
if trailing_flag: uint8 trailing_value
Conditional values occur in ascending mask-bit order. The pinned opcode-41
handler reads request_flag as a boolean, delegates the full mask/value body to
parser 0x1812ED210, then reads trailing_flag as a boolean and one additional
u8 only when that flag is true. This types the former bounded tail without
assigning either trailing field a behavioral role. Both capture corpora
exercise false and true flags; true trailing values are
1,2,3,5,7,9,11,13,15,17.
Stream 92 contains 333 packets. Their masks/counts are 0x0:14,
0x400:35, 0x1000:207, 0x4300:1, 0x10000:44, 0x10400:3, and
0x40000:29; request flags are 0:315 and 1:18. All packets and all 324
conditional values parse and re-encode exactly. The combined 0x4300 packet
decodes INT 57, LUK 15, AP 0, while 0x10400 concatenates current HP
and EXP. The fold applies each field independently, records previous/current
values in player_stats_updated events, and ends with HP 50, MP 97, EXP
1464, and mesos 4567 after all field resets and deltas.
Stream 126 contains 841 packets and exercises every field listed above. It
adds nine character-level updates, the job change to 200, primary-stat and
max-HP/max-MP updates, ten SP updates, AP updates, EXP, and mesos. All packets
round-trip; the final fold reaches level 10, HP 114/194, MP 158/285,
STR 4, DEX 4, INT 49, LUK 13, EXP 980, and mesos 1472.
With the handler-proven booleans and conditional tail, all 333 stream-92, 841
stream-126, and one stream-114 stat packets are full structural coverage.
The independent manifest validates all 841 stream-126 packets and all 334
opcode-41 packets in its stream-83/92/114 corpus with exact byte
consumption and no failures.
The state-driven replay emitter uses request flag false, current-HP mask
0x00000400, a bounded HP value, and trailing flag false. A real stream-114
client accepted generated plaintext 29000000040000010000: its HUD changed
from 50/222 to 1/222, the observed transcript folded the event as
previous:50 -> current:1, MP/EXP/map/inventory/progression stayed unchanged,
and heartbeat responses continued. This validates the predicted effect without
assigning behavioral semantics to the two flags.
The later live typed-injection validator repeated the smaller reversible
experiment against an already active browser-free client. Its plan predicted
only current_hp 50 -> 49, one player_stats_updated event, and no changes to
max HP, phase, field epoch, map, inventory, or progression. The real HUD showed
49/222; transcript frame 775 decoded the exact opcode-41 mask and the fold
matched every invariant. A second independently planned operation observed the
current baseline and restored 49 -> 50 at frame 777, again with all
invariants matched. The API's runtime injection annotation is separate from
the one predicted domain event.
The complete spawn packet is 22 bytes:
uint16 opcode = 300
uint32 object_id
uint32 template_id
int16 x
int16 cy
uint8 facing_value
uint16 foothold_id
int16 range_left
int16 range_right
uint8 hidden # boolean 0/1
The facing field is not boolean. Streams 92, 114, and 126 preserve the
observed values 0, 1, 2, 4, and 5; all 78 stream-126 spawns parse
and round-trip. The fold exposes the neutral byte as facing_value and keeps
the hidden field separately boolean.
--generate-field-npc-spawns turns those folded entities back into replay
frames. The planner requires a valid world fold, selects every npc_spawn
observation, emits and reparses the 22-byte record, preserves its server-frame
index, and rejects duplicate indices or explicit-patch conflicts. Its runtime
API key is protocol.npc_spawn_emitter; records expose session aliases,
templates, positions, footholds, ranges, facing, hidden state, and field epoch,
but never the runtime object ids. Stream 114 generates nine frames at server
indices 20..28; stream 92 generates 53 across seven populated epochs.
The 2026-08-09 browser-free live run composed those nine frames with the typed
initial snapshot emitter. The client entered map 101000000 and rendered the
expected visible NPCs. The simultaneously recorded transcript folds validly to
active with nine active/spawned NPCs and 10/10 matched generated heartbeats.
This demonstrates client acceptance of the reconstructed entity packets rather
than only capture-side parsing.
Opcode 302 starts with a one-byte control and the field-local NPC object id.
The captured control-1 branch is 23 bytes and reuses the complete spawn body
from opcode 300 after that id:
uint16 opcode = 302
uint8 control = 1
uint32 object_id
uint32 template_id
int16 x
int16 cy
uint8 facing_value
uint16 foothold_id
int16 range_left
int16 range_right
uint8 hidden # boolean 0/1
All 36 stream-126 records use that shape. Removing the opcode, control, and
object id leaves the same 16 bytes produced by NpcSpawn after its opcode and
object id; all 36 parse and re-encode exactly. They now emit full-coverage
npc_lifecycle_spawn observations, enter the entity into current-field NPC
state, and allow later opcode-303 updates to resolve against the same alias.
That raises the long-corpus NPC spawn-event count from 78 direct spawns to 114
total without changing the distinction between opcode families.
The pinned client handler independently reads u8 control and i32 object_id.
When the control equals its runtime branch constant, it invokes a helper with
the packet reader and consumes the spawn body; the alternate helper receives
only the object id and cannot consume further bytes. The model uses control
0 as the canonical compact encoding for that alternate branch:
uint16 opcode = 302
uint8 control = 0
uint32 object_id
It removes the aliased NPC, tracks known versus unknown removals, and emits a
full-shape npc_lifecycle_removal observation. This seven-byte form is an
exact typed and unit-tested hypothesis, not capture evidence: no reference
PCAP contains it, and the first live attempt reached the configured one-hour
world hold boundary before the removal could be sent.
The same live session did accept a 23-byte control-1 composition using the
captured object/template fields and the typed current-map position, foothold,
and range. Its transcript remained valid and active, folded nine direct NPCs
plus this lifecycle spawn, and matched 360/360 heartbeat pairs until the replay
closed at its configured one-hour boundary. This validates client acceptance
and the predicted spawn-state delta; it does not establish visible rendering
or the compact removal effect.
The pinned opcode-285 and opcode-286 handlers belong to the same client
type and each directly reads one signed 32-bit object id before delegating the
body. Stream 92 supplies the only reference instances: ten 33-byte set
records and five 23-byte reset records. The captured single-bit forms are:
uint16 opcode = 285
uint32 mob_object_id # field-local; redacted/aliased
uint32 mask_words[4] = [0, 0, 0, 0x80] # enabled bit index 103
uint16 value = 1
uint32 source_skill_id
uint16 source_level # neutral captured field name
uint16 duration_value # units not yet proven
uint8 flag = 1
uint16 opcode = 286
uint32 mob_object_id # field-local; redacted/aliased
uint32 mask_words[4] = [0, 0, 0, 0x80]
uint8 flag = 1
All 15 packets consume and re-encode byte-for-byte. Every object id names an
active template-3210800 mob. Each set follows within one or two server frames
of an opcode-219 relay that targets the same mob and carries the same source
skill id, 3101005; this correlation is 10/10. The set records use source
levels 5/6 and ten distinct duration values from 859 through 1142, so
the model retains both names neutrally and does not assert time units.
The fold stores bit 103 on the field-local mob entity. Ten sets include three
refreshes. Of five resets, three remove a modeled active bit and two occur
before a corresponding set is visible in the bounded capture. Four remaining
active bits clear when their mobs leave, and the final active count is zero.
Events and reports expose the aliased mob, mask, source skill, neutral values,
and relay match without exposing the object id. Other masks, values, flags, or
packet widths remain unknown rather than inheriting this capture-bounded
grammar.
The typed live validator now joins this semantic model to the independently
generated IL2CPP shape dump. The pinned dump/export/validation covered all
35,316 packets from 111.pcapng streams 83, 92, and 114, with zero
unsupported packets or consumption failures. The validator selected the
captured base-spawn/set/reset sequence at server direction indices
11006/11438/11506, matched generated shapes
mob_enter_field_short_status, server_opcode_285, server_opcode_286, and
mob_leave_field, then rewrote only a redacted runtime object id and current-
map placement.
On the live map-101000000 transcript, the base-spawn run folded opcode 279
at frame 1516, bit-103 set at 1518, reset at 1522, and leave at 1523.
The active-status count followed 0 -> 1 -> 0; the mob was present for set and
reset and absent after leave; all predicted counter deltas matched; phase,
field epoch, map, player state, inventory, and progression were unchanged; and
the control remained healthy at 1,424/1,424 heartbeats. The client visibly
rendered the spawned template and removed it after leave. Sampled spawn-only
and set-active frames showed the same sprite pose and no unique status marker,
so a specific visible immobilization/status meaning for bit 103 remains
unproven even though client acceptance and the folded lifecycle are now live-
validated.
The complete update is seven bytes:
uint16 opcode = 293
uint32 mob_object_id
uint8 health_percentage # inclusive 0..100
Stream 92 contains 208 updates and stream 126 contains 399. Every
long-corpus object id resolves to an active mob in the current field epoch;
189 values are zero and no value increases during one mob lifecycle. The fold
stores current/previous percentages and emits mob_health_percentage_updated.
A zero value does not itself remove the entity: membership still changes only
on the separate opcode-280 leave packet.
When targeted client opcode 50/52 damage is pending for the same mob, each
nonzero damage word is one pending hit. One opcode-293 packet consumes one
hit in order and adds the request frame, hit index/count, selected damage word,
complete damage array, response time, and count of modeled relay hits observed
for that mob since submission to the event. Zero-damage words do not need an
update. This accounts for all 399 stream-126 health packets and all 208
stream-92 packets; lifecycle removal clears 21 and 11 terminal hits,
respectively, and both folds finish with no pending effects.
For the 11 templates actually attacked in the two references, the model uses
version-specific info/maxHP values extracted from the official client's
json_27ed12ab55c4464e7db01cade1a2e593.bundle WZJS-v5 records:
| template | max HP | template | max HP |
|---|---|---|---|
100100 |
8 | 100101 |
15 |
120100 |
20 | 130100 |
40 |
130101 |
40 | 210100 |
50 |
1110100 |
250 | 1130100 |
300 |
1210100 |
75 | 1210102 |
80 |
9300018 |
8 |
The authoritative byte uses integer floor percentage:
health_percentage = floor(current_hp * 100 / max_hp)
Therefore one percentage maps to a bounded integer-HP interval rather than an
exact value. The fold stores that interval on the mob and, when a previous
sample exists, subtracts the correlated hit from both bounds to predict the
next percentage range. Stream 92 matches all 161 testable predictions; floor
also explains seven cases that ceil/nearest quantization cannot represent.
Stream 126 matches 203/209 exactly. The six outliers are each exactly one HP
from the predicted interval and have response delays of 0.389..0.460
seconds, so they are reported as semantic warnings instead of invalid shapes.
Subtracting the observed post-update HP bounds from the previous bounds yields
an inferred authoritative-damage interval on every testable event. All six
outliers have exact authoritative-minus-submitted deltas: five +1, one -1.
None has an intervening modeled opcode-218/219 hit for the target.
The opt-in runtime policy intentionally has a narrower contract than captured
official combat. It requires an active mob with exact integer HP and a template
whose max HP is in the referenced table above. A later typed opcode-279
spawn initializes that mob at max HP; an opcode-293 can be adopted only when
its percentage maps to one exact integer HP. The policy accepts only targeted
client opcode 50/52 actions with a decoded damage array and no high-bit
damage marker.
For each damage word, in packet order:
damage == 0 -> emit nothing
current_hp == 0 -> skip this already-terminal hit
otherwise -> current_hp = max(0, current_hp - damage)
emit server 293 with
floor(current_hp * 100 / max_hp)
new current_hp == 0 -> after the 293, emit server 280 reason 1
and remove the mob from active state
This exact rule is a custom-server state transition, not a claim that the six
official ±1 HP adjustments have been explained. It also does not synthesize
server attack relays 218/219. Unknown or inactive targets, missing damage,
ambiguous HP, and high-bit damage are counted as rejected and receive no
modeled response; the held-open transport continues so ordinary untargeted
swings do not disconnect the client.
The real-client proof injected a typed template-100100 spawn with 8/8 HP
at the stream-114 player position. The client submitted opcode 52, variant
18, with damage words [27,32]. The server predicted and emitted opcode
293 percentage 0, then opcode 280 reason 1; the second hit was skipped
because the first was terminal. The observed fold contains one attack, one
zero-health update, one leave, zero active mobs, and zero pending effects while
the connection and generated heartbeat exchange remained active.
Recorded custom-server runs also emit identifier-free runtime policy events;
these do not alter the packet protocol. The live targeted opcode-52 at frame
85 records observed damage [27,32]. After generated opcode 293/280
frames drain, frame 87 records the completed 8 -> 0 transition,
percentage [0], removal, and one skipped terminal hit. A later untargeted
opcode-52 records observation and rejection together at frame 99 without a
server response. The combined packet/policy fold is valid and warning-free
with two attacks, 59 submitted damage, one matched health effect, no pending
effect, and 17/17 matched heartbeats.
The client submits a field-local mob object, a sequence number, and one typed movement path:
uint16 opcode = 207
uint32 mob_object_id
uint16 sequence
uint8 option_flags
int8 activity_code
uint8 skill_id
uint8 skill_level
uint8 action_auxiliary_1
uint8 action_auxiliary_2
uint8 control_marker
uint32 control_value_1
uint32 control_value_2
uint32 control_value_3
int16 reference_x
int16 reference_y
uint8 command_count # nonzero
repeat command_count: mob_movement_command
uint8 trailer_marker = 0
int16 path_start_x
int16 path_start_y
int16 path_end_x
int16 path_end_y
The observed command tags are 0, 1, and 2. Type 0 has signed position
and velocity pairs, uint16 foothold, stance, and duration; types 1 and 2
have signed relative velocity, stance, and duration. All 12,100 stream-92
submissions, 40,090 commands, and nine-byte trailers parse and re-encode
exactly; stream 126 validates the same boundaries across another 22,855
submissions. The six-byte action prefix is also typed. The signed activity is
predominantly -1, with captured 12, 13, and 24 values; option flags are
captured as 0, 1, and 17. The two auxiliary bytes retain neutral names,
and the following 13 bytes split exactly as one marker plus three little-endian
u32 values. The marker is always zero; control_value_1 is 0 or 1; and
both final values are the stable captured 0x00ffddcc. Their behavioral roles
remain neutral.
This split follows the original v83 client writer, which emits six one-byte
arguments and then reserves 13 bytes before the reference point. Independent
v83 server handlers agree on option/activity and skill-id/level placement, but
disagree on whether the final pair is one uint16 option or two independent
bytes, so the model does not assign those two bytes behavioral meaning.
Original client writer,
Guida83 handler,
and gms083 handler
provide the independent source anchors. Guida83 independently reads this tail
at the same u8 + u32 + u32 + u32 boundary. With every byte structurally
typed, all 12,100 stream-92 and 22,855 stream-126 submissions are full
coverage.
Server opcode 282 broadcasts movement for one field-local mob without the
client sequence or nine-byte trailer:
uint16 opcode = 282
uint32 mob_object_id
bool control_flag_1
bool control_flag_2
uint8 control_selector
uint32 control_value
int16 reference_x
int16 reference_y
uint8 command_count # nonzero
repeat command_count: mob_movement_command
The pinned opcode-282 receive handler delegates the body to method
c593a76f..., whose first reads after the object id are exactly
bool, bool, u8, u32. The six observed seven-byte combinations therefore no
longer need an opaque prefix. Across streams 92 and 126, all 13,366
broadcasts have control_flag_1 = false, selectors are 0x0c, 0x0d, or
0xff, and control_value is zero; control_flag_2 exercises both values.
Those neutral names describe only the proven wire types, not behavioral roles.
The three command tags are also scalar-typed rather than width-only blobs, so
all 5,284 stream-92 and 8,082 stream-126 broadcasts now have full structural
coverage and pass independent manifest validation with exact byte consumption.
Stream 92 contains 18,874 commands and every referenced object is active.
The control combination false,false,0xff,0 occurs 5,230 times. Of those,
1,509 packets use the exact one-command stationary placement shape: the
reference equals the absolute command position, velocity is zero, and duration
is 1,080 ms. Stances 2, 4, and 5 are all captured; stance 4 has 1,055
exact examples.
The complete acknowledgement is 13 plaintext bytes:
uint16 opcode = 283
uint32 mob_object_id
uint16 sequence
uint8 status_flag # boolean 0/1
uint16 status_value
uint8 status_auxiliary_1
uint8 status_auxiliary_2
All 11,949 stream-92 acknowledgements correlate with a prior submission.
The flag is exactly whether option_flags is nonzero, both auxiliary
bytes are always zero, and the status value is deterministic for every
field-local template: 100100 -> 0, 130100 -> 30, 210100 -> 35,
1110100 -> 25, 1130100 -> 30, 2110200 -> 35, 3210800 -> 100, and
9999999 -> 0. The behavioral name of the uint16 remains unknown.
The custom server can derive this mapping from one validated evidence stream
while using another transcript's final field. Typed later opcode-279 entries
and opcode-281 controller assignments populate field-local template state;
opcode 280 removes active membership but retains that identity until the next
field epoch, matching official post-leave acknowledgements. Unknown objects or
templates without deterministic evidence receive no guessed reply.
The real-client proof replayed stream 114, derived values from stream 92,
and injected matching typed entry/controller packets for template 100100 at
the final player position. The client produced 205 opcode-207 submissions;
the server generated 205 opcode-283 packets, and the independent transcript
fold matched all 205 with flag/value/auxiliary rules exact and no pending
movement. Heartbeats and the world connection remained active.
The current replay also records each reactive submission and outcome as a
safe runtime event. Request details retain template, sequence, command count,
the typed action prefix, four neutral control values, and reference/start/end
coordinates, but omit the runtime object id. A fresh browser-free run produced
13 alternating
mob_movement_submission_observed and
mob_movement_acknowledgement_completed events. All 58 generated opcode-283
packets independently matched their opcode-207 submissions; the valid,
warning-free fold has no pending/unmatched movement and 7/7 heartbeats.
Unknown-object unit coverage records a nonfatal
mob_movement_submission_rejected event instead of inventing a response.
The state-driven broadcast proof used stream 114 as the held-open field and
stream 92 as shape evidence. A typed opcode-279 introduced one
template-100100 snail at (433,-2677) on foothold 635; the custom server
then generated one opcode-282 stationary placement for (833,-2677), the
same foothold, and stance 4. The real client rendered the snail at the
predicted target. The independent observed fold is valid and ends with that
exact mob position/stance, one known broadcast, one command, and 11/11 matched
heartbeats. This validates the stationary placement effect.
The multi-command proof selects stream-92 server-direction frame 18818,
whose template-100100 opcode 282 moves from reference (249,2024) through
five absolute commands to (297,2024) in 1,080 ms. The command positions are
(277,2024), (278,2024), (285,2022), (289,2022), and (297,2024);
their velocity, stance, and duration fields are preserved exactly. The planner
translates every position relative to the reference, requires the active mob
to be at that translated reference on the requested foothold, and rewrites
each command foothold to that continuous live foothold.
The real-client run translated this captured path to reference (785,-2677)
and endpoint (833,-2677) for the injected snail on foothold 635. The client
rendered the snail at the predicted endpoint. The recorded transcript folds
validly with one known broadcast, five type-0 commands, final stance 2, and
9/9 matched heartbeats. This validates replay of a specific captured path and
its predicted state effect; it is not yet an autonomous path-selection policy.
The next layer removes the frame choice while retaining a strict capture boundary. Given the active template, current position, requested endpoint, and foothold, the server catalogs captured multi-command absolute paths with the dominant control prefix. It keeps only paths whose endpoint-minus-reference equals the requested displacement, then requires exactly one distinct relative position/velocity/stance/duration shape. Repeated evidence for that same shape is allowed; different matching shapes are an explicit ambiguity error.
For template 100100 and displacement (48,0), stream 92 contains exactly
one matching path and one shape: server-direction frame 18818. The automatic
mode therefore generated the same predicted packet without a frame hint. The
real client again rendered the snail at (833,-2677), and the new observed
transcript folds validly with one five-command broadcast, no unknown mob, and
13/13 matched heartbeats.
Bounded composition uses those same unique displacement primitives rather than
inventing longer command arrays. The planner excludes every displacement that
has multiple relative motion shapes, then performs breadth-first search up to
the requested 2..8 step bound. Every step must strictly reduce Manhattan
distance to the endpoint, every intermediate absolute position must fit in
int16, and exactly one shortest displacement sequence must remain. Each
selected primitive becomes its own opcode 282, so the ordinary gameplay fold
validates every intermediate state as well as the final endpoint.
For template 100100, stream 92 provides 167 usable unique displacement
shapes and 38 ambiguous displacements that are excluded. No direct (96,0)
path exists, but the unique shortest composition is (48,0) + (48,0), using
frame 18818 twice. Starting at (785,-2677), the generated references were
(785,-2677) and (833,-2677), with endpoints (833,-2677) and
(881,-2677). The real client rendered the snail at final (881,-2677).
The independent transcript folds validly with two known broadcasts, ten
type-0 commands, final foothold 635/stance 2, and 11/11 matched
heartbeats.
At runtime those two packets are no longer represented only by a startup plan
and final sent counter. A per-connection scheduler validates that every step
retains the same entity/template/field/object and that each previous
position/foothold/stance equals the prior predicted result. It advances its
current state only after the expected encrypted write drains, retains the
confirmed opcode-282 prefix for later planning folds, and exposes
planned, in_progress, or complete state plus last/next safe step reports.
Out-of-order plaintext is rejected without changing the model.
The live pacing proof repeated the (48,0) + (48,0) route with a dedicated
10-second movement-step delay. Runtime status observed the intermediate
(833,-2677) state at 1/2 sent rather than prematurely reporting the final
target, then completed at (881,-2677) and 2/2. The independently folded
events are 10.002838 seconds apart and retain exact continuity:
(785,-2677, stance 3) -> (833,-2677, stance 2) -> (881,-2677, stance 2) on foothold 635. The transcript remains valid with
two broadcasts, ten type-0 commands, and 10/10 heartbeats.
The next runtime boundary is a bounded decision queue, not a new packet shape.
Up to eight follow-up MAX_STEPS:X:Y:FOOTHOLD targets are fixed at startup.
After the current schedule's last opcode 282 write drains, the queue exposes
planning and invokes the same composition algorithm with the scheduler's
original baseline plus its confirmed broadcast prefix. No untransmitted
prediction enters that fold. The capture analysis is dispatched off the
asyncio event loop; GET /api/v1/status remains responsive and no HTTP
mutation route exists. During planning, the sent count includes the drained
prefix, packets_remaining is zero because the next packet count is not yet
known, and decision_queue.planning_decision_index identifies the pending
decision.
The real-client proof began with one automatically selected (48,0) packet,
then queued a two-step composed target. Runtime state progressed
planned (785, 0/1) -> planning (833, 1/1 known) -> in_progress (833, 1/3) -> in_progress (881, 2/3) -> complete (929, 3/3). The independent transcript is
valid and preserves exact foothold-635/stance-2 continuity across all
three five-command broadcasts. Its movement events occur at 14.644652,
57.145568, and 67.147278 seconds: the 42.500916-second first gap includes
worker-backed evidence analysis plus the ten-second movement pace, while the
next gap is 10.001710 seconds. The final fold reports fifteen type-0 commands
and 8/8 matched heartbeats, and the real client rendered the snail at the
predicted final endpoint (929,-2677).
That first implementation rebuilt the same immutable evidence analysis inside
the follow-up call. The planner now receives a validated
MobMovementPlanningContext containing the replay analysis, evidence analysis,
5,284 parsed captured paths/broadcasts, and stationary-shape counts. Only the
connection-local confirmed prefix remains mutable. Building the stream-92
context once took 10.432799 seconds; using it took 0.000360 seconds for the
automatic first decision and 0.005125 seconds for the composed follow-up. The
context's safe cache counts are visible in runtime status so reuse is
inspectable.
The cached real-client transcript places the three movement events at
14.642526, 24.651246, and 34.653303 seconds. The resulting 10.008720- and
10.002057-second gaps are the configured ten-second movement pace plus normal
scheduling jitter; the prior 32.5-second repeated fold is absent. The fold is
again valid with exact 785 -> 833 -> 881 -> 929 continuity, three broadcasts,
fifteen type-0 commands, final foothold 635/stance 2, and 6/6 matched
heartbeats.
The first bounded gameplay policy derives relative targets from confirmed
state rather than storing endpoint arguments. Its inputs are a 1..8 decision
count, a 2..8 composition bound, signed (dx,dy), and a foothold. Once a
decision completes, the policy adds that displacement to the scheduler's
current coordinates, validates the result as int16, and submits the derived
target to the unchanged capture-backed composed planner. It cannot coexist
with the explicit target queue. This changes scheduling policy only; every
wire packet remains the same typed opcode 282 shape and every intermediate
state remains independently foldable.
The live policy used two (+96,0) decisions after an initial automatic
placement. It derived 929 only from confirmed 833, then derived 1025 only
from confirmed 929. Runtime completed three decisions, five planned/sent
packets, and final state (1025,-2677) on foothold 635/stance 2. The client
rendered that predicted endpoint. The frozen movement events preserve exact
continuity 785 -> 833 -> 881 -> 929 -> 977 -> 1025; their four gaps are
3.011704, 3.000499, 3.008745, and 3.001274 seconds. The valid transcript has
five broadcasts, twenty-five type-0 commands, and 6/6 matched heartbeats.
Policy scheduling can now consume the already-modeled heartbeat correlation
as its trigger rather than chaining immediately. In matched-heartbeat mode,
only a client opcode 23 observed while a server opcode 10 probe is pending
authorizes one follow-up decision. The initial movement remains unconditional,
the first opcode 282 of each authorized decision is immediate, and any later
opcode 282 packets in that same composed decision retain their configured
step delay. This is a runtime scheduling invariant and does not change any wire
shape.
The real-client proof used five-second probes and a one-second movement-step
delay. The first two matches authorized the two (+96,0) decisions; a third
matched response after completion was observed but ignored. The frozen event
order is heartbeat response frame 78 -> movement frame 79, then response
frame 83 -> movement frame 84. Exact movement gaps are 5.326966, 1.000527,
3.999752, and 1.000549 seconds, separating the two event gates from each
decision's internal pace. The transcript remains valid with exact
785 -> 833 -> 881 -> 929 -> 977 -> 1025 continuity, five broadcasts,
twenty-five type-0 commands, and 21/21 matched heartbeats.
Event-trigger scheduling also has an optional shared cooldown invariant. A
configured 0..3600-second window begins only when all opcode-282 packets
in one authorized decision have drained. A qualifying heartbeat, served mob
submission, or proximity entry observed before expiry is counted and rejected
without planning or emitting a decision; the first qualifying event after
expiry may authorize exactly one. Cooldown configuration is invalid for the
immediate trigger. The HTTP model exposes the configured duration, rejection
count, last event outcome, and last observed remaining duration without adding
any field to the wire protocol.
In the five-second live proof, heartbeat response frame 78 authorized
opcode-282 frames 79/80. Response frames 82, 84, and 86 arrived
inside the window and have no intervening movement; frame 88 arrived after
expiry and authorized frames 89/90. The runtime completion sample recorded
two started/completed decisions and three cooldown rejections. The independent
fold is valid with no warnings, exact
785 -> 833 -> 881 -> 929 -> 977 -> 1025 continuity, five broadcasts,
twenty-five type-0 commands, and 16/16 matched heartbeats.
An optional connection-local event budget is a separate scheduling invariant.
Its bound is 1..8, and it applies only to event-driven triggers. Every
accepted trigger consumes one unit before planning; after the remaining count
reaches zero, later qualifying events are observed and rejected without
planning or emitting opcode 282, even when no cooldown is active. Safe HTTP
state exposes configured/used/remaining counts plus
events_rejected_by_budget. Runtime rejection annotations use
reason: event_budget; no wire field or identifier is added. Encrypted test
coverage sends two valid heartbeat matches to a two-decision policy with a
budget of one and proves that exactly one follow-up decision drains.
Because trigger acceptance is server policy rather than a Maple wire packet,
observed replay JSONL can include a separate runtime_event record. Its
nanosecond timestamp, safe kind, and JSON-safe details carry the trigger mode,
decision index, outcome reason, and cooldown duration/remaining time. The
gameplay analyzer orders these annotations alongside decoded packet events,
marks their direction as runtime, and aligns each to the preceding packet
frame without treating it as protocol bytes. The writer caps annotations at
16,384 and places written/dropped counts in the close record; a nonzero drop
count becomes an explicit analysis warning.
The live annotation transcript directly emits observation/start events at
response frame 78, completion at movement frame 80, cooldown rejections at
frames 82/84/86, re-arm/start at frame 88, and final completion at
frame 90. The rejection records preserve exact remaining times
4.006931, 2.007733, and 0.006772 seconds. Its independent packet fold is
valid and warning-free at (1025,-2677) with five broadcasts, twenty-five
type-0 commands, and 15/15 matched heartbeats.
A second event mode consumes the existing typed mob-controller protocol rather
than the heartbeat. served-mob-movement accepts only an opcode 207
submission that the capture-derived acknowledgement policy validates for a
known active mob. The server first drains its typed opcode 283 response and
only then starts one pending relative decision. A rejected submission has no
scheduling effect. While no accepted event exists, explicit awaiting_event
telemetry distinguishes the gate from capture-backed path planning. Again,
this changes packet order and policy only, not the 207, 283, or 282 wire
shapes.
In the real-client proof, the initial opcode 282 ended at 833 in frame
77; authentic sequence-1 opcode 207 arrived in frame 78, its matched
opcode 283 was frame 79, and the newly authorized opcode 282 packets were
frames 80 and 81. They ended at 881 and 929, with 0.008418 seconds from
the initial broadcast to the first gated broadcast and 1.000714 seconds inside
the composed decision. The valid fold reports three broadcasts/fifteen
type-0 commands, one submitted/acknowledged/matched movement, zero pending or
unmatched movement, and 4/4 independently matched heartbeats.
The third event mode is a bounded predicate over an already typed packet.
player-proximity parses each live local-player opcode 182, takes its
explicit trailer path_end_x/path_end_y, and computes Manhattan distance to
the scheduler's confirmed mob coordinates. A required radius is limited to
1..4096. Only an outside-to-inside edge (including the first observation
when it is inside) authorizes a decision; repeated in-radius submissions do
not. The server exposes only safe coordinates, distance, boolean result, and
observation/entry counts. This adds no new interpretation to movement command
payloads and changes no wire shape.
The live negative observations ended at (633,-2673) and (548,-2652), both
outside radius 64; the latter was distance 310 from confirmed mob
(833,-2677), and neither sent an opcode 282. After four rightward
submissions, frame 107 ended at (855,-2695), distance 40, and crossed the
predicate. Opcode-282 frames 108/109 followed, ending at 881/929; the
entry-to-first-broadcast gap was 0.009526 seconds and the configured internal
pace was 1.001263 seconds. The valid fold has seven player submissions, three
mob broadcasts/fifteen type-0 commands, and 11/11 independently matched
heartbeats.
Local-player movement submissions have this capture-validated shape:
uint16 opcode = 182
uint32 control_value
int16 reference_x
int16 reference_y
uint8 command_count
repeat command_count: movement_command
uint8 trailer_marker = 0
int16 path_start_x
int16 path_start_y
int16 path_end_x
int16 path_end_y
Remote-player broadcasts use opcode 202, insert a uint32 object_id before
the control value, and end immediately after the same reference/count/command
stream; they do not carry the nine-byte client trailer. Reports replace the
object id with a stable player:N alias.
The command discriminants and payload boundaries are exact across both directions:
type 0: int16 position_x/y, int16 velocity_x/y,
uint16 foothold_id, uint8 stance, uint16 duration_ms # 13 bytes
type 1: int16 velocity_x/y, uint8 stance, uint16 duration_ms # 7 bytes
type 3: int16 position_x/y, int16 neutral_value,
uint8 stance, int16 duration_ms # 9 bytes
type 4: same 9-byte fields as type 3
type 5: same 13-byte fields as type 0
Stream 92 contains 531 client submissions with 3,606 commands
(0:3506, 1:53, 3:20, 4:20, 5:7) and 113 server broadcasts with 675 commands
(0:642, 1:18, 3:4, 4:4, 5:7). All 644 packets and 4,281 commands round-trip
byte-for-byte. Stream 126 independently exact-consumes 2,435 packets and
11,147 commands (0:10736, 1:236, 3:61, 4:61, 5:53). The type-3/4
layout is pinned to the current client's shared IL2CPP movement parser; its
third signed value is zero in both captures, so its role remains neutral. All
client control values are zero in stream 92; the two stream-114 broadcasts
demonstrate values zero and one, so that field also remains neutrally named.
The fold uses the client trailer endpoint for general local position and the
last positioned command for each observed remote player,
emitting typed events for both directions. Proximity-sensitive live pickup
placement is deliberately narrower: it prefers the latest same-field client
command-final coordinate and falls back to the trailer only when no such
observation exists. Stream 114 ends at local path endpoint (633,-2677) with
two identifier-safe remote-player aliases.
This is a separate counted movement family. The client packet is:
uint16 opcode = 47
uint8 local_object_index
uint32 client_token # redacted from safe reports
uint32 control_value # neutral role
int16 reference_x
int16 reference_y
uint8 command_count
repeat command_count:
uint8 command_type
byte[command_payload_length(command_type)] command_payload
uint8 tail_type
uint8 tail_state_values[tail_payload_length(tail_type)] # neutral per-byte state
uint8 tail_marker # neutral role
int16 path_start_x
int16 path_start_y
int16 path_end_x
int16 path_end_y
The server packet ends after the shared movement path:
uint16 opcode = 217
uint32 object_id
int16 reference_x
int16 reference_y
uint8 command_count
repeat command_count: command_type + fixed command payload
Capture-derived command payload sizes, excluding the one-byte tag, are exact:
type: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
bytes: 13 7 7 9 9 13 7 9 9 9 1 9 7 7 9 15 7 13 7 7 3 3 7
The exact v83 MovementParser
and its Movement record
independently name the supported layouts:
types 0/5/17: position x/y, last x/y, foothold, stance, duration
types 1/2/6/12/13/16: relative delta x/y, stance, duration
type 10: equipment-change value
type 11: chair position x/y, neutral u16, stance, trailing i16
type 15: jump-down position x/y, two vectors, two neutral u16s,
stance, trailing i16
types 3/4/7/8/9/14: teleport-like position x/y plus neutral middle/trailing values
Guida's v83
AbstractMovementPacketHandler
confirms every captured payload width and identifies the nine-byte family as
teleport-like. It disagrees with the other parser about some middle/trailing
labels, so the codec intentionally does not promote those disputed words.
Client tail types 17, 18, 21, and 24 carry 8, 8, 10, and 11
neutral byte-sized state values respectively. Stream 92 validates 963 client
packets with 3,869 commands and 305 server packets with 1,441 commands; it
observes every tail type and command tags 0/1/2/3/4/10/11/14/15. Stream 126
validates another 2,585 client packets with 8,189 commands and 347 server
packets with 1,399 commands. In the long corpus, all 347 server object ids name
players already active when the packet arrives. The fold
records that correlation and advances an already known remote-player alias to
the last positioned command. It emits redacted submission/broadcast events and
tracks decoded command, tail-type, tail-state-value, and tail-marker
distributions. All captured packets contain typed command tags and therefore
receive full structural coverage. Disputed command words, tail/control roles,
and unobserved tags 18..22 deliberately retain neutral names; a future packet
using one of those opaque tags remains partial.
The client-to-server opcode is a separate family from the server-to-client
life-movement opcode 217 above. Stream 126 proves that it submits the same
NPC state body later echoed by server opcode 303. It has two variants:
compact:
uint16 opcode = 217
uint32 npc_object_id
uint8 action
uint8 parameter
movement:
uint16 opcode = 217
uint32 npc_object_id
uint8 action
uint8 parameter
int16 reference_x
int16 reference_y
uint8 command_count # 1..255
repeat command_count:
uint8 command_type # observed 0 or 2
type 0: int16 position_x/y, int16 velocity_x/y,
uint16 foothold_id, uint8 stance, uint16 duration_ms
type 2: int16 velocity_x/y, uint8 stance, uint16 duration_ms
uint8 trailer_marker = 0
int16 path_start_x
int16 path_start_y
int16 path_end_x
int16 path_end_y
server echo:
uint16 opcode = 303
byte[...] client_body_without_final_9_byte_movement_trailer
Stream 126 contains 937 instances. Of these, 345 are compact and 592 are
movement submissions. They carry 1,653 commands: 1,596 type 0 commands and
57 type 2 commands. Observed command counts per packet are:
count: 1 2 3 4 5 6 7 8 9 10 11 12 13 14
packets: 332 71 21 18 37 40 27 22 11 1 4 5 1 2
Every client packet and all 1,279 server opcode-303 packets parse to their
exact end and re-encode byte-for-byte. Exact FIFO matching pairs 930 of the 937
client submissions with later server updates: 339 compact and 591 movement
pairs. For every pair, the server response changes only the opcode and, for the
movement variant, removes the client-only final nine-byte marker/start/end
trailer. The remaining seven requests have no same-epoch captured response and
are cleared by later field changes; 349 server updates are independent of a
client submission. The fold records exact matches, latency, active-NPC
admission, command distributions, and final absolute positions. Higher-level
action/parameter intent remains neutral. The command field layouts match the
current client's pinned movement parser and make both variants full structural
coverage without assigning those higher-level action semantics.
The opt-in --reactive-npc-state-responses policy admits only object ids active
in the replay's final field, validates the two captured variants and command
types, and emits the exact typed opcode-303 transformation. NPC spawn and
lifecycle packets keep the runtime admission set current.
Promoting the 592 movement-bearing requests and 683 movement-bearing updates
adds 1,275 full observations. Current stream 126 coverage is
53,785/17,315/0/0 (full/partial/unknown/invalid). Streams 92 and 114
remain 26,266/8,941/0/0 and 57/19/0/0.
The long gameplay corpus establishes six exact client-to-server shapes:
uint16 opcode = 122
uint8 selector
repeat captured_value_count(selector, packet_length):
uint32 opaque_value # redacted from safe output
The capture-bounded selector/count matrix is:
selector u32 values packets additional invariant
1 2 2 none
1 3 26 none
2 3 1 final value = 0xffffffff
2 4 25 final value = 0xffffffff
4 3 6 none
5 3 2 none
All 62 stream-126 packets consume exactly and re-encode byte-for-byte. No
opcode-122 packet occurs in gameplay streams 92 or 114. Selector 1 and
selector 2 frequently appear as a pair with the same first value, and the
remaining words include coordinate-like packed values, but those correlations
do not establish field roles or a safe replay effect. The codec therefore
retains every u32 only for lossless re-emission while reports, events, JSON,
and HTTP-derived state expose selector, value count, shape, and terminal-
sentinel presence. The fold emits client_opcode_122_submitted; any selector/
count combination outside the matrix remains unknown instead of being parsed
through an observed variant.
This family is exactly two opcode-only packets:
server -> client: uint16 opcode = 426
client -> server: uint16 opcode = 309
The temporal relationship is exact in every gameplay reference. Stream 126
has 299 pairs, stream 92 has 61, and stream 114 has one. In all 361 pairs,
the server notification arrives first, the client acknowledgement consumes the
only pending notification, and no unmatched packet remains at capture end.
The long-stream delay is 0.2681-4,643.4658 ms (13.3547 ms median); stream 92
is 0.6815-83.8679 ms (28.0082 ms median), and stream 114 is 82.534 ms.
Pinned IL2CPP handler code independently confirms direction: the handler
registered for server opcode 426 constructs an outgoing opcode 309 packet,
writes no body fields, and sends it. The fold therefore names only the proven
notification/acknowledgement relationship, records matched/unmatched/pending
counts and round-trip times, and keeps the higher-level purpose distinct from
the separately modeled opcode-10/23 heartbeat.
The client packet is exactly 11 bytes:
uint16 opcode = 101
uint8 reserved_prefix = 0
uint8 request_type = 20
uint16 reserved_value = 0
uint16 hp_recovery
uint16 mp_recovery
uint8 reserved_tail = 0
The old byte/u32/byte/u16/byte grouping hid the invariant boundary: every
packet starts 00 14 00 00, ends in zero, and puts exactly one non-zero amount
in the two intervening u16 fields. Stream 126 has 33 HP-10 and 113 MP-3
requests. All 146 match a following authoritative opcode-41 update in the
same field: 136 apply the requested amount exactly, six HP updates are capped
by max HP, and four occur before a prior HP baseline is known. None remains
pending; maximum response time is 1,543.604 ms.
Stream 92 independently has seven HP-10 and 66 MP-5 requests. Every one
of its 73 following opcode-41 updates applies the exact amount, none remains
pending, and response time is at most 105.781 ms. Stream 114 contains none.
Python and isolated native validation consume and re-emit all 219 records
exactly. Promoting the family to full coverage moves stream 126 to
26,810/44,290/0/0 and stream 92 to 13,493/21,714/0/0.
An earlier active local run supplies an independent no-response control. At
that checkpoint it had automatically emitted 178 HP-10 and 274 MP-5
requests alongside the visible recovery cadence. Because that replay did not
serve opcode-41 recovery updates, all 452 stay explicitly pending without
being treated as malformed. The same cadence was already running before the
controlled keypad-zero/action-52 input; neither the packets nor the blue
recovery number are evidence for that still-unnamed action.
The opt-in --reactive-client-recovery-responses policy derives current/max HP
and MP from a validated world transcript. For each exact opcode-101 request
it emits one typed opcode-41 current-stat update whose value is
min(maximum, current + requested). It cannot share opcode 101 with a
captured-reply rule. Runtime/HTTP telemetry reports source correlation counts,
observed/served/packet totals, the last safe response, and mutable HP/MP state.
A fresh browser-free stream-114 login validated the generated direction and
cap behavior against the real client. The server served 39/39 requests with
one opcode-41 response each; the independently folded live transcript has
38 exact increments, one capped HP 220 -> 222 increment, zero pending
requests, no issues or warnings, and phase active on map 101000000. The HUD
reached HP 222/222, while heartbeat status remained matched at 42/42.
The fold emits full client_recovery_request observations, HP/MP amount
distributions, authoritative stat-update matches, exact/capped/unverified
amount classes, pending counts, and response timing. A field snapshot clears
old-epoch pending requests.
Client opcodes 50 and 52 share this exact prefix:
uint16 opcode = 50 or 52
uint8 local_object_index
uint8 variant
uint32 client_token # redacted from safe reports
uint32 control_value # neutral role
byte[5] opaque_common_state
uint32 value_1 # neutral role
uint32 value_2 # mob object id in extended variants
if variant >> 4 == 1:
byte[14] opaque_target_prefix
repeat (variant & 0x0f):
uint32 raw_damage
damage_value = raw_damage & 0x7fffffff
high_bit_marker = raw_damage >> 31
byte[8] opaque_target_tail # opcode 50
byte[9] opaque_target_tail # opcode 52
else:
byte[variant_suffix_length] opaque_suffix
The accepted capture-bounded variants are:
opcode variant suffix bytes total bytes target in value_2
50 1 0 25 no
50 17 26 51 yes
52 1 1 26 no
52 2 1 26 no
52 17 27 52 yes
52 18 31 56 yes
Stream 126 contains 552 opcode-50 actions (264 variant 1, 288 variant
17) and 130 opcode-52 actions (16/8/80/26 variants 1/2/17/18). Stream
92 contains 128 opcode-52 actions (15 variant 2, 113 variant 18). Every
extended action's value_2 is a known mob object id. The extended suffix
decomposes exactly into the 14-byte prefix, one damage word per low-nibble hit,
and the opcode-specific tail above. Stream 126 contains 420 damage words with
low-31-bit magnitudes 1..42, total 6964, and stream 92 contains 226 with
magnitudes 0..49, total 4864. No client damage word in either capture sets
the high bit. Prefix and tail field roles remain neutral.
Opcode 54 is an exact 24-byte member of the same action family:
uint16 opcode = 54
uint32 control_value
uint8 flag_1
uint8 flag_2
uint32 value_1
uint32 value_2
uint32 target_object_id # offset 16
uint32 tail_value
All 120 stream-126 and 31 stream-92 opcode-54 packets carry a known mob
object id at offset 16. This replaces the earlier neutral value_3
interpretation. Across stream 126, targeted opcode-50, opcode-52, and
opcode-54 actions are followed by same-mob health or leave traffic often
enough to establish the attack-action family; stream 92 independently
confirms the targeted opcode-52 shape. All 961 actions consume exactly and
round-trip byte-for-byte.
The fixed scalar opcode-54 branch now reports full structural coverage for all
151 records and passes independent manifest validation. Opcode 50/52
actions remain partial because their target prefix/tail bytes are still opaque;
the shared fold does not promote them.
The fold emits client_attack_submitted, aliases the mob target, distinguishes
currently active from previously known targets, and records per-mob damage/hit
totals. Each nonzero opcode-50/52 damage word is queued separately. The
following same-mob opcode-293 updates consume those hits in order, including
both responses to a two-hit action; terminal hits can instead be cleared by
opcode 280 or a field transition. Stream 126 correlates 399 hit responses
and clears 21 terminal hits. Stream 92 correlates 208, clears 11, and skips
seven zero-damage words. Both finish with zero pending effects. The fold does
not expose client tokens or raw target ids.
Both server families use this capture-bounded envelope:
uint16 opcode = 218 or 219
uint32 player_object_id # aliased in safe reports
uint8 packed_counts
target_count = packed_counts >> 4
hit_count = packed_counts & 0x0f
uint8 relay_tag
uint8 skill_level # zero in every captured opcode-218
if opcode == 219 and skill_level != 0:
uint32 skill_id
uint8 unknown_value # zero in both sustained captures
uint8 display
uint8 facing_flags # captured 0 or 0x80
uint8 attack_speed
if opcode == 218 and this is the short form:
# metadata ends here; target must be the all-zero placeholder below
else:
uint8 mastery
if opcode == 218:
uint32 auxiliary_value # zero in every captured full form
else:
uint32 projectile_id
repeat target_count:
uint32 mob_object_id # aliased; zero in five 218 placeholders
uint8 hit_action # 6 for every nonzero captured target
repeat hit_count:
uint32 raw_damage
damage_value = raw_damage & 0x7fffffff
high_bit_marker = raw_damage >> 31
if opcode == 219:
int16 position_x
int16 position_y
Opcode 218 has observed total lengths 18, 22, and 27; opcode 219 has
lengths 22, 26, 31, 35, 39, 44, 53, and 62. Stream 126
contains 41 opcode-218 and 99 opcode-219 relays. Stream 92 contains one
and 42. Every outer player object id is observed somewhere in the
same capture. The nibble split is supported by the manifest's packed
attack-count prefix and by body-length scaling. Given those counts, every body
decomposes exactly into the typed attack metadata, repeated target records, and
the opcode-219 signed position.
All 42 close-range relays share the same typed six-byte metadata prefix. Stream
126 has 36 full forms and five short forms; stream 92 adds one full form.
Every skill level and unknown byte is zero. Full forms append mastery 0 and
auxiliary u32 0. Each short form has packed counts 0x11 and exactly one
target record whose object id, hit action, and damage word are all zero. The
decoder treats that as a distinct capture-backed placeholder shape and rejects
a six-byte prefix paired with any nonzero or differently counted target. Stream
126 observes relay tags 8/16, displays 5, 6, 7, 9, 11, 16,
and 17, facing flags 0/0x80, and speeds 4/6; stream 92 adds relay
tag 14 with display 17, facing 0x80, and speed 6.
All 141 ranged relays obey the conditional skill-id rule: the prefix is 11
bytes when skill_level == 0 and 15 bytes otherwise. Stream 126 contains 51
basic attacks, 45 level-8 skill 4001344 attacks, and three level-1 skill
3001005 attacks. Its projectiles are 2060000 (54) and 2070000 (45).
Stream 92 independently contributes skill ids 3001005, 3101005, and
4001344, with projectile ids 2060000, 2070009, and 2070015. These
values are emitted as numeric protocol fields; item/skill names are not inferred
by the decoder.
The final four bytes decode as two plausible signed field coordinates. All 99
stream-126 ranged relays and 30 of 42 stream-92 relays have an actor whose
last movement position was already observed. The fold records the packet
position and its delta from that prior position; common vertical differences
cluster around -22..-28, while larger differences coincide with stale remote
movement state. This establishes the coordinate shape and supports an attack-
position interpretation without treating that position as an authoritative
movement update.
Stream 126 has 123 target records and 166 damage words. Of those records,
118 name known mobs and use hit action 6; five short opcode-218 records are
all-zero placeholders. Low-31-bit damage magnitudes range from 0 to 80,
and 12 raw words set the high bit. Stream 92 has 71 target records, all known
mobs with hit action 6, and 88 damage words ranging from 1 to 366; 20 set
the high bit. Thus all 194 target records and 254 damage words across the two
sustained captures consume exactly and re-encode as part of their relay.
The fold emits server_attack_relay_received, aliases the actor, records the
packed target/hit distributions, aliases each nonzero mob, and reports damage
magnitudes plus a neutral high-bit marker. Active mob entities accumulate the
observed relay hit/damage totals without replacing the authoritative opcode-
293 health percentage. Raw ids and raw body bytes remain hidden. These
captures validate action-to-health/leave correlations and damage array
boundaries. Client-side max HP now predicts 364/370 testable percentage
transitions exactly and bounds the remaining six to a one-HP difference.
Relay-tag/unknown/auxiliary roles, the damage high bit, client target
prefix/tail fields, and the cause of those delayed one-HP differences remain
unresolved. The custom server therefore does not yet generate or replay
attacks.
The client sent a stable HTTP/1.1 request:
GET / HTTP/1.1
Host: 54.238.121.146:58880
Cache-Control: no-cacheThe response was 218 bytes and had the fixed body:
aewwawuiaryatp
Date and cache/entity metadata varied. The role appears to be a probe or handoff rather than the encrypted login channel.
This is the complete observed HTTP surface; there is no JSON management API.
Custom-server control is CLI-based and observations are mode-0600 JSONL. In
the successful 111.pcapng flow, the actual encrypted world handoff is instead
43.142.194.150:8587.
An additional TLS connection was captured at 54.65.46.47:5050, with roughly
842 client bytes and 4,544 server bytes in the first observed exchange. It has
not yet been decoded.
The two repository-root PCAP references are deliberately kept outside Git:
./111.pcapng successful login (stream 83), long gameplay (92), short field (114)
./1-10FS.pcapng level 1 through 10 gameplay (stream 126)
1-10FS.pcapng stream 126 is a 55-minute protocol-300 world session from
96.62.155.120:12324. The transport prefixes 14 server bytes and 28 client
bytes before the ordinary 33-byte Maple greeting; PCAP normalization locates
the greeting, discards only those preludes, and records both byte counts in
transcript metadata. The resulting session contains 71,100 decrypted frames
(31,345 client and 39,755 server), one marker-26 initial snapshot, 35 later
field snapshots, 841 stat updates, 256 inventory change sets, 78 direct NPC
spawns, 36 NPC lifecycle spawns,
436 drop-spawn packets, 203 pickup requests, eight skill-level requests, nine
skill-record updates, and nine skill-record acknowledgements.
All 203 pickup requests resolve to a known active drop and match their field
epoch after the marker-26 initial snapshot is folded. The 197 opcode-185
requests also target a final mode-0 spawn whose two owner words equal the
initial player id. The four mode-2 field-load mesos records are exact 30-byte
shapes. Opcode 303 NPC-state movement paths and client opcode 158 mode 0
keymap changes are now fully typed. Strict validation succeeds
across all 71,100 frames with 53,785
full, 17,315 partial, zero unknown, and zero invalid packet
observations. Stream 92 independently reaches 26,266 full, 8,941 partial,
zero unknown, and zero invalid; stream 114 reaches 57/19/0/0. The long fold
reaches level 10 and reports no unknown inventory-slot
modifications; its one remaining warning is a cross-packet state correlation:
an aggregate warning for six delayed combat predictions that differ by one
HP. That warning also records the
{-1: 1, +1: 5} inferred damage-delta histogram and that all six lack an
intervening modeled relay hit.
Both spawn-bearing opcodes delegate to the same client parser. Opcode 279
places the object id directly before the spawn body; opcode 281 places a
one-byte controller level before the object id and omits the spawn body when
that level is zero. The complete shared body is:
uint8 spawn_marker = 1
uint32 template_id
uint32 temporary_status_mask[4]
if temporary_status_mask[3] & 0x00000080:
int16 value
int32 source_skill_id
int16 duration_units
int32 status_control_value
bool status_flag_1
bool status_flag_2
int16 x
int16 y
uint8 stance
uint16 foothold_id
uint16 origin_foothold_id
int8 appear_type
uint8 team
int32 effect_item_id
The conditional status tuple accounts for the exact 42/50-byte shared
body and 48/56-byte opcode-279 packet widths. A live GDB trace against
the pinned client injected one captured packet of each width and observed the
optional reads as exactly i16 + i32 + i16, followed in both branches by
i32 + bool + bool. The suffix trace and disassembly identify the former
signed-i16 “spawn effect” as separate signed appear-type and team bytes, then
one client-read i32 effect item id. All 1,884 spawn-bearing packets across
reference streams 92 and 126 parse and re-emit exactly; the mask/control
and optional tuple retain neutral behavioral names.
The independent manifest validator consumes every opcode-279/281 packet
in both corpora without a failure. Promoting the 1,884 spawn-bearing records
from partial to full coverage moves stream 126 to 68,527/2,573/0/0 and
stream 92 to 34,220/987/0/0; stream 114 remains 61/15/0/0. The same
live trace transcript folds injected common and extended opcode-279 packets
at full coverage and remains valid with no unknown observations.
Primary captures live in:
/home/sdancer/ms/downloads/maple_protocol_captures/
Important files:
1786118307321677094_13.115.120.13_10282.jsonl
1786118506764535468_52.193.141.80_10282.jsonl
1786118312530175497_54.238.121.146_58880.jsonl
1786118511968891795_54.238.121.146_58880.jsonl
1786118473811233268_54.65.46.47_5050.jsonl
The fresh HK SOCKS5 official reference is:
hk_official_reference_20260808/1786166652482700663_35.73.142.21_10282.jsonl
hk_official_reference_20260808/1786166661254491571_54.238.121.146_58880.jsonl
It contains 1,377 client bytes and 22,092 server bytes on 10282. Decryption
produces server opcodes 27, 28, 22, 0, 23, followed by ten opcode-10
heartbeats; the client produces opcodes 31, 6, followed by ten opcode-23
frames. The 58880 exchange contains 77 client bytes and 221 server bytes.
- Gameplay framing is complete across reference gameplay streams
92,114, and126; all packets are typed or capture-bounded and none remain unknown or invalid. - The successful account shape is decoded, but the regional opcode mapping
differs (
0in the successful capture,1for the local handler), and several fields still have unknown semantics. - Successful character-list records are typed and losslessly re-emitted. The seven appearance-style values, entry code, six-byte trailer roles, ranked branch semantics, and non-success response bodies remain neutrally named or opaque pending independent variants.
- The 19-byte handoff and large initial world snapshot are structurally
validated; equipment-specific metadata, keyed-property roles, parts of the
fixed trailers, the marker-
26seven-byte variant header, and several one-time field bootstrap opcodes remain semantically neutral. - The purpose and required state for the TLS
5050connection remain unknown. - The exact semantics of captured opcode-
0result values other than the observed policy result2remain unknown.