Files
eqemu-server/tob/opcodes.md
T
dannuic 168995a5b5
Build / Linux (push) Has been cancelled
Build / Windows (push) Has been cancelled
Full Packet Review for Known Conversion (#5100)
2026-06-07 00:12:57 -07:00

642 lines
172 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
### Status
Below is a status list for the 450 opcodes we currently use on the server for the TOB client. Currently uses 3 status levels (let me know if we should do more):
- 🔴 Not-Set (Opcode not set in the patch file)
- 🟠 Missing (Opcode is 0x0000 in TOB patch but has a known value in RoF2 — needs investigation)
- 🟡 Unverified (Opcode set but structure hasn't been verified as completely working)
- 🟢 Verified (Opcode set and structure is working)
### World/Zone Opcode Implementation Status
| Opcode | Status | Notes | Working On |
|:----------------------------------|:--------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------|
| `OP_AAAction` | 🟢 Verified | Client→Server only. 16-byte AA_Action struct (action/ability/target_id/exp_value) matches exactly across all 4 send sites. Passthrough correct. action=7 (set restore) and action=10 (shift-buy) unhandled by emu server by design. | |
| `OP_AAExpUpdate` | 🟢 Verified | Server→Client only. Handler: `msg_altexpup` @ `0x1402087b0`. Dispatched via `cmp [mem], 4C3h` @ `0x1401e4219` (memory-form, not switch table). Encoder correctly scales experience ×100000/330 and zero-extends unspent uint16→uint32. All 3 fields confirmed from raw ASM. | |
| `OP_AcceptNewTask` | 🟠 Missing | | |
| `OP_AckPacket` | 🟢 Verified | Client→Server only. Sent from `CDisplay::SetViewActor` @ `0x1401a0b40` (hton @ `0x1401a0c9d`). 4-byte uint32 payload = spawn ID of newly viewed actor (0 if local player or no match). Server ignores payload on both zone and world paths. No encoder/decoder needed. Also used as `signature.ignore_eq_opcode` in zone-in detection. | |
| `OP_Action` | 🟢 Verified | Server→Client only. Dispatched via `cmp [mem], 7D28h` @ `0x1401f4245`. Inline case block: constructs CUnSerializeBuffer, calls MissileHitInfo::Deserialize @ `0x1402024C0`. Encoder correct: spell/effect_flag widened, instrument_mod→float, effective_casting_level/unknown1/damage hardcoded to 0 (intentional). Field offsets confirmed from case block and init function. | |
| `OP_Action2` | 🔴 Not-Set | | |
| `OP_AddNimbusEffect` | 🟢 Verified | Server→Client only. Dispatched via `cmp @ 0x1401F3C89` → case block `0x1401F8C36`. Case block extracts spawnid [+0] and nimbus_effect [+4] as dwords, calls `ActivateStateFlavorSpellParticleEffect` @ `0x1401E2060`. Wire layout matches `RemoveNimbusEffect_Struct` exactly. Passthrough correct. | |
| `OP_AdventureData` | 🟠 Missing | | |
| `OP_AdventureDetails` | 🟠 Missing | | |
| `OP_AdventureFinish` | 🟠 Missing | | |
| `OP_AdventureInfo` | 🟠 Missing | | |
| `OP_AdventureInfoRequest` | 🟠 Missing | | |
| `OP_AdventureLeaderboardReply` | 🟠 Missing | | |
| `OP_AdventureLeaderboardRequest` | 🟠 Missing | | |
| `OP_AdventureMerchantPurchase` | 🟠 Missing | | |
| `OP_AdventureMerchantRequest` | 🟠 Missing | | |
| `OP_AdventureMerchantResponse` | 🟠 Missing | | |
| `OP_AdventureMerchantSell` | 🟠 Missing | | |
| `OP_AdventurePointsUpdate` | 🟠 Missing | | |
| `OP_AdventureRequest` | 🟠 Missing | | |
| `OP_AdventureStatsReply` | 🟠 Missing | | |
| `OP_AdventureStatsRequest` | 🟠 Missing | | |
| `OP_AdventureUpdate` | 🟠 Missing | | |
| `OP_AggroMeterLockTarget` | 🟠 Missing | | |
| `OP_AggroMeterTargetInfo` | 🟠 Missing | | |
| `OP_AggroMeterUpdate` | 🟠 Missing | | |
| `OP_AltCurrency` | 🟠 Missing | | |
| `OP_AltCurrencyMerchantReply` | 🟠 Missing | | |
| `OP_AltCurrencyMerchantRequest` | 🟠 Missing | | |
| `OP_AltCurrencyPurchase` | 🟠 Missing | | |
| `OP_AltCurrencyReclaim` | 🟠 Missing | | |
| `OP_AltCurrencySell` | 🟠 Missing | | |
| `OP_AltCurrencySellSelection` | 🟠 Missing | | |
| `OP_Animation` | 🟢 Verified | Inline case block @ `0x1401f7ab2` (cmp-dispatch). 4-byte struct verified field-by-field. Encoder/decoder correctly handle `action`/`speed` field-order swap between server and TOB structs. | |
| `OP_AnnoyingZoneUnknown` | 🔴 Not-Set | | |
| `OP_ApplyPoison` | 🟢 Verified | Handler `msg_apply_poison` @ `0x140208810`. Fixed: struct changed from `TypelessInventorySlot_Struct` (8B) to `InventorySlot_Struct` (12B); slot fns updated to `ServerToTOBSlot`/`TOBToServerSlot`. | |
| `OP_ApproveName` | 🟢 Verified | Bidirectional. S→C: 1-byte response code, `HandleNameApprovalResponse` @ `0x14039bff0` (26 cases). C→S: decoder maps name/race_id/class_id; deity_id+heroic_type dropped (TODO, enhancement only). | |
| `OP_ApproveWorld` | 🟠 Missing | | |
| `OP_ApproveZone` | 🔴 Not-Set | | |
| `OP_Assist` | 🟢 Verified | Inline case block @ 0x1401EF76D. Reads entity_id (dword +0), sets g_pTargetPlayer; triggers SetAutoAttack if attack_on_assist set. No encoder/decoder needed. | |
| `OP_AssistGroup` | 🟢 Verified | Passthrough. EntityId_Struct (4B uint32 entity_id). S→C inline case @ 0x1401ef76d (Pattern B): lookup player, set target, optional auto-attack. C→S via do_assist() @ 0x140223710 (/assist group only). | |
| `OP_AugmentInfo` | 🟢 Verified | Bidirectional. Encoder: itemid/window/augment_info[64] ok; TOB-extra unknown072 (+0x48) and unknown076 (+0x4C) not set. S->C inline @ 0x1401EFB0D. C->S CItemDisplayWnd::SetItem @ 0x1404314E0. | |
| `OP_AugmentItem` | 🟢 Verified | C→S only. 4 send sites: InsertAugmentRequest, RemoveAugmentRequest, SwapAugmentRequest, DialogResponse. 40-byte TOB struct (InventorySlot_Struct slots) decoded correctly via TOBToServerSlot. | |
| `OP_AutoAttack` | 🟢 Verified | C→S only. No struct, no decoder needed. Client sends bAutoAttack as uint32 (0/1) in 4-byte payload from DoPassageOfTime@0x1400EC3BD; server reads pBuffer[0]. Passthrough correct. | |
| `OP_AutoAttack2` | 🟢 Verified | C→S only. Client sends uint32 auto-attack state (0/1) from DoPassageOfTime when bAutoAttack changes. Send @ 0x1400ec45f. No encoder/decoder needed; opcode_dispatch.h IN(uint32) correct. Handler stub. | |
| `OP_AutoFire` | 🟢 Verified | C→S only. No S→C handler (IDA showed fallback case). Client sends 6-byte packet (2B opcode + 4B uint32 payload) from DoPassageOfTime @ 0x1400ec65a. No encoder/decoder needed; passthrough correct. | |
| `OP_AvaliableTask` | 🟠 Missing | | |
| `OP_Bandolier` | 🟠 Missing | | |
| `OP_BankerChange` | 🟢 Verified | Inline case block @ 0x1401e6d98. Bank fields (0x100x1C) confirmed correct under passthrough; SetBankPlatinum/Gold/Silver/Copper called at matching offsets. No encoder needed. C→S: header-only click. | |
| `OP_Barter` | 🟠 Missing | | |
| `OP_Bazaar` | 🔴 Not-Set | | |
| `OP_BazaarInspect` | 🔴 Not-Set | | |
| `OP_BazaarSearch` | 🟠 Missing | | |
| `OP_BecomeCorpse` | 🔴 Not-Set | | |
| `OP_BecomeTrader` | 🟠 Missing | | |
| `OP_Begging` | 🟢 Verified | Encoder correct. Result cast uint32→uint8 (values 04). StringSize=0 and Lucky=0 hardcoded (no server struct fields). Handler sub_1402088C0 @ 0x1402088c0; cmp-dispatch at 0x1401f346a. No decoder (passthrough). | |
| `OP_BeginCast` | 🟢 Verified | Handler: CEverQuest::StartCasting@0x140293100. Direct 15-byte memcpy into local struct. TOB struct reorders fields (spell_id int32 first) vs server. Encoder correctly maps all fields; unknown0e hardcoded 1 (matches live). | |
| `OP_Bind_Wound` | 🟢 Verified | Passthrough via tob_ops.h. Client sends 8-byte BindWound_Struct (target field [r14+0x168] → to+unknown2, type+unknown6 = 0). S→C handler @ 0x1401ef3f3 (ja-dispatch, inline Pattern B). C→S send @ 0x140101c99 in sub_140101A90. Struct matches server struct exactly. | |
| `OP_BlockedBuffs` | 🟢 Verified | Bidirectional. Dynamic-length wire (uint32 count + int32[count] + uint8 Pet + uint8 Init). ENCODE/DECODE correct. Case block @ `0x1401f1dac`, Pattern D (inline CUnSerializeBuffer → sub_140202750). 4 C→S send sites. | |
| `OP_BoardBoat` | 🟢 Verified | C->S only. Passthrough. Client sends null-terminated boat name string (6-64 bytes). Send @ DoPassageOfTime 0x1400ebf30. Server reads pBuffer directly via memcpy. | |
| `OP_BookButton` | 🟢 Verified | C->S only. Added DECODE + TOB struct (16-byte wire: +0 int16 invslot, +4 int16 unknown, +8 int32 target_id). Was broken (size mismatch 16 vs 6); scribing from book window now functional. | |
| `OP_BuffDefinition` | 🟢 Verified | Bidirectional. S→C: EQAffectPacket_Struct (168B), inline case 0x1401e8994. C→S: added DECODE to tob.cpp/tob_ops.h mapping EQAffectPacket_Struct→SpellBuffPacket_Struct; fixes buff click-off size mismatch.| |
| `OP_BuffRemoveRequest` | 🟢 Verified | C→S only. Two send sites: `RemoveBuffEffect` @ `0x1402ec560` (char buffs), `RemovePetEffect` @ `0x1402ec9c0` (pet buffs). Decoder correctly applies `TOBToServerBuffSlot` on SlotID; EntityID passthrough. | |
| `OP_Bug` | 🟢 Verified | C→S passthrough: `sub_140391580` @ `0x140391580` sends exact `BugReport_Struct` (8740B); server size-checks before cast. S→C: case `0x1401f2d5e` (cmp/ja dispatch) reads one length-prefixed URL string via CUnSerializeBuffer; handler `sub_140391470` shows popup + LaunchWebPage; EQEmu never sends S→C so no encoder needed. | |
| `OP_BuyerItems` | 🟠 Missing | | |
| `OP_CameraEffect` | 🟢 Verified | S→C only. Passthrough — Camera_Struct (8B: +0 uint32 duration, +4 float intensity). Inline case 0x1401e8431 reads directly, calls CCameraShakeManager::AddShake @ 0x140537F10. | |
| `OP_Camp` | 🟢 Verified | Bidirectional, no encoder/decoder. C→S: empty (size 0) from `CEverQuest::Camp` @ `0x140267240`. S→C: 1 byte @ case `0x1401f9cc7` (cmp-dispatch); byte=0 starts countdown, byte≠0 cancels camp with AA warning. | |
| `OP_CancelSneakHide` | 🟢 Verified | S→C only. Zero-byte signal, no struct/encoder/decoder. Handler `CEverQuest::CancelSneakHide` @ `0x1402678e0` (cmp-dispatch @ `0x1401e8c86`). Client responds with OP_Hide (0x4F10) + OP_SpawnAppearance. | |
| `OP_CancelTask` | 🟠 Missing | | |
| `OP_CancelTrade` | 🟢 Verified | sub_140209C70 @ 0x140209c70 (cmp-dispatch). C→S field names inverted: fromid=trade target SpawnID, action=local SpawnID. No change needed. | |
| `OP_CashReward` | 🟢 Verified | Passthrough. `sub_140209DB0` @ `0x140209DB0` (Pattern A). 4×uint32 matches `CashReward_Struct`. | |
| `OP_CastSpell` | 🟢 Verified | C→S only (client ignores S→C). Send: `CharacterZoneClient::CastSpell` @ `0x1400d5350`, hton @ `0x1400d7e09`. Fixed decoder: `inventoryslot` now uses `TOBToServerSlot(TOBCastingInventorySlotToInventorySlot(eq->inventory_slot))`. | |
| `OP_ChangeSize` | 🟢 Verified | Inline Pattern B @ `0x1401f20d4`; calls `ChangeHeight` @ `0x14030ffc0`; bidirectional — client sends via `/changesize` GM cmd (`sub_140234D90` @ `0x140234d90`); passthrough correct, layouts match. | |
| `OP_ChannelMessage` | 🟢 Verified | Fixed decoder: `emu->sender` now uses `Sender` not `Target` (tob.cpp:3669). Encoder correct. Handler @ `0x1401f0875` (cmp-dispatch), UnSerialize @ `0x1406a6c70`. Pattern D (CommunicationManagerMessage). | |
| `OP_ChangePetName` | 🔴 Not-Set | | |
| `OP_CharacterCreate` | 🟢 Verified | C→S only. Handler sub_14039B900 @ 0x14039b900 (738 bytes). TOB struct 168 bytes with 72-byte padding prefix. All 23 server fields verified correct. heroic_type at +0xa4 not in server struct (existing TODO). | |
| `OP_CharacterCreateRequest` | 🟢 Verified | Bidirectional. S→C: handler `HandleStartingLocationResponse` @ `0x14039c5a0`, cmp/jz dispatch @ `0x1401e5987`. Encoder widens `ExpansionRequired` uint32→uint64. C→S: empty trigger, no decoder needed. | |
| `OP_CharInventory` | 🟢 Verified | Handler: msgCompItems @ 0x1402051b0; shared with OP_BankItems; cmp-dispatch @ 0x1401e4679; client sends zero-byte ACK (no decoder needed) | |
| `OP_Charm` | 🟢 Verified | Handler: sub_140217E40 @ 0x140217E40; cmp-dispatch @ 0x1401e4a43; TOB wire 13B (owner_id,pet_id,charmer_id[TOB-only +0x08 set to 0],command[uint8 at +0x0C]); encoder added; release(0) was ok, make-pet(1) was broken | |
| `OP_ChatMessage` | 🔴 Not-Set | | |
| `OP_ClearAA` | 🟢 Verified | Zero-payload trigger; case @ 0x1401E83CD calls CAltAdvManager::Reset() (0x1401A9DF0) with no packet fields read. Passthrough correct, no struct. | |
| `OP_ClearBlockedBuffs` | 🟢 Verified | Passthrough, no encode/decode. 1-byte packet: `uint8 pet`. C→S: `/blockspell clear`, hton@0x14023053a in sub_140230370. S→C: QueuePacket echo → msg_blocked_spells_clear@0x140209a40 clears INI slots. | |
| `OP_ClearLeadershipAbilities` | 🟠 Missing | | |
| `OP_ClearNPCMarks` | 🟠 Missing | | |
| `OP_ClearObject` | 🟢 Verified | Passthrough. Handler `msg_clear_world_con` @ `0x14020a520` (Pattern A). Server always sends Clear=1 → ClearWorldContainerItems(). Struct wire-compatible. | |
| `OP_ClearSurname` | 🟠 Missing | | |
| `OP_ClickDoor` | 🟢 Verified | C→S only. Send @ EQSwitch::UseSwitch 0x140260da0. Added item_id and picklockskill decoding; doorid and player_id were already correct. TOB struct unknowns renamed to match server fields. | |
| `OP_ClickObject` | 🟢 Verified | Bidirectional passthrough. C→S: LMouseUp @ 0x14027b4f0. S→C: inline Pattern B @ 0x1401f6e8e; body at loc_1401F6EAD reads drop_id(+0) as serial and player_id(+4) to gate pending-counter dec. | |
| `OP_ClickObjectAction` | 🟢 Verified | Handler `msg_world_container` @ `0x1402197A0`. Added `OUT(unknown24)` (ItemNumber). Slots hardcoded to 10 by client; `unknown16=0` OK. C→S opcode-only, no payload (camp cmd + close container). | |
| `OP_ClientError` | 🔴 Not-Set | | |
| `OP_ClientReady` | 🟢 Verified | Zero-length bidirectional. S→C @ `0x1401F46BC` (cmp-dispatch); sets ReadyEnterWorld=1. C→S zero-payload. | |
| `OP_ClientTimeStamp` | 🔴 Not-Set | | |
| `OP_ClientUpdate` | 🟢 Verified | Bidirectional. S→C: `ProcessUpdateStats` @ `0x140200010` (Pattern A); spawn_id, vehicle_id, 9 position fields all correct. C→S: 4 send sites in DoMainLoop/DoTeleportB; decoder correct; pitch not forwarded (no server field). | |
| `OP_CloseContainer` | 🔴 Not-Set | | |
| `OP_CloseTributeMaster` | 🔴 Not-Set | | |
| `OP_ColoredText` | 🟢 Verified | Passthrough; sub_140205060 @ 0x140205060; color @ +0x00, msg @ +0x04; newline-split, calls DisplayChatText | |
| `OP_CombatAbility` | 🟢 Verified | C→S only passthrough. Client sends CombatAbility_Struct (m_target/m_atk/m_skill, 12 bytes) unchanged. S→C hits HWM fallback — server never sends this. Send @ sub_140246750 (0x140246750). | |
| `OP_Command` | 🔴 Not-Set | | |
| `OP_CompletedTasks` | 🟠 Missing | | |
| `OP_ConfirmDelete` | 🟢 Verified | C→S only. Client sends 2-byte spawn_id from ProcessUpdateStats @ 0x140200010 when spawn not found locally. Server handler is a stub (returns immediately). Passthrough correct, no decoder needed. | |
| `OP_Consent` | 🟢 Verified | C→S only. Client sends null-terminated player name from sub_140221550 @ 0x140221550 (/consent cmd). No real S→C handler (fallback case). Passthrough correct, struct matches wire format. | |
| `OP_ConsentDeny` | 🟢 Verified | C→S only. `/deny` cmd. Send fn `sub_1402217E0` @ `0x1402217E0`. Passthrough — `Consent_Struct{char name[1]}` matches wire format. Not found in HandleWorldMessage (no S→C handler). | |
| `OP_ConsentResponse` | 🟢 Verified | Passthrough S→C. Handler `sub_14020A550` @ `0x14020A550` (Pattern A). All 4 struct fields verified: grantname+0x00, ownername+0x40, permission+0x80, zonename+0x81. No encoder/decoder needed. | |
| `OP_Consider` | 🟢 Verified | Bidirectional. S→C handler `CEverQuest::Consider` @ `0x1402687e0`; client reads only targetid (+4), ignores faction/level/HP — consider UI uses locally-cached player state. C→S send @ `0x14021faf0`; sends playerid+targetid only. Dead targets send OP_ConsiderCorpse from same fn. Encoder/decoder correct. | |
| `OP_ConsiderCorpse` | 🟢 Verified | C→S only. `sub_14021FAF0` @ `0x14021faf0`; hton @ `0x14021fe60`. Sent when target spawn-type ≥ 2 (corpse). Decoder forwards to OP_Consider; reads playerid + targetid correctly. | |
| `OP_Consume` | 🟢 Verified | C→S only. 4 send sites in DoDrinkEatPoison+sub_1402E1DD0. TOB wire: InventorySlot_Struct(12B)+unknown(-1)+type(0/1)+mode(0/1)=20B. Added DECODE: TOBToServerSlot, mode→auto_consumed, type+1. | |
| `OP_ControlBoat` | 🟢 Verified | Passthrough. Inline case block @ 0x1401f0384 (cmp-dispatch). Reads boatId [+0] uint32 + TakeControl [+4] bool. C→S: 3 send sites in ProcessControls + RightClickedOnPlayer. Struct matches exactly. | |
| `OP_CorpseDrag` | 🟢 Verified | C→S only. Decoder reads two length-prefixed strings → CorpseDrag_Struct. No real S→C handler (fallback at 0x1401f2dfb). No hton send site found; corpse drag may not be initiated by TOB client. | |
| `OP_CorpseDrop` | 🟢 Verified | C→S only. Passthrough. Client sends null-terminated target name (g_pTargetPlayer+0xB4, max 64 chars) when releasing a dragged corpse. Send @ sub_140172C80 (0x140172C80). No struct in eq_packet_structs.h. | |
| `OP_CrashDump` | 🔴 Not-Set | | |
| `OP_CrystalCountUpdate` | 🟠 Missing | | |
| `OP_CrystalCreate` | 🟠 Missing | | |
| `OP_CrystalReclaim` | 🟠 Missing | | |
| `OP_CustomTitles` | 🟠 Missing | | |
| `OP_Damage` | 🟢 Verified | handler `msg_successful_hit` @ `0x140216190`; TOB 48-byte struct (int64 damage at +0x08, type at +0x28) verified; decoder fixed (added `force` and `hit_pitch`) | |
| `OP_Death` | 🟢 Verified | Handler: `CEverQuest::ReportDeath` @ `0x1402866c0` (Pattern A). Fixed: added `OUT(corpseid)` to encoder (client reads +0x08 for `ReportSuccessfulHit` on skill-231 kills). TOB struct is 40 bytes. | |
| `OP_DelegateAbility` | 🟠 Missing | | |
| `OP_DeleteCharacter` | 🟢 Verified | C→S only. No S→C handler. Client sends char[64] name + uint32 padding (always 0) from `sub_1400CF9A0`. World handler `HandleDeleteCharacterPacket` reads `app->pBuffer` as char* directly — no struct needed, passthrough correct. | |
| `OP_DeleteCharge` | 🟢 Verified | ENCODE_FORWARD(OP_MoveItem). Client handler @ 0x1401eac01 (cmp-dispatch). Calls msg_move_spell_charge @ 0x1402102e0 — looks up item by from_slot, applies negative number_in_stack as charge delta. | |
| `OP_DeleteItem` | 🟢 Verified | Handler `sub_140204D00` @ `0x140204D00`; cmp-dispatch S→C; client reads from_slot+number_in_stack; to_slot sent but not read S→C; C→S always sends to_slot=-1 (trash); encoder/decoder correct. | |
| `OP_DeletePetition` | 🔴 Not-Set | | |
| `OP_DeleteSpawn` | 🟢 Verified | Struct correct (5 bytes). Byte 4 (Decay): 0=deferred decay animation path (sets field_133Ch=1 on corpse-type spawns), 1=immediate PrepForDestroyPlayer. Also gates duel-end logic. | |
| `OP_DeleteSpell` | 🟢 Verified | Passthrough. S→C @ 0x1401f097e → DeleteSpellFromBook @ 0x1404f26a0. C→S: DialogResponse @ 0x1404f27b0. | |
| `OP_DenyResponse` | 🟢 Verified | S→C only. Handler sub_14020A550 @ 0x14020a550. Corpse drag consent response: char[64] requester (+0), char[64] owner (+0x40), bool granted (+0x80), zone name (+0x81). Client picks message by name match. | |
| `OP_Disarm` | 🟢 Verified | Client→Server only. 16-byte struct (source/target/skill/unknown) matches Disarm_Struct exactly. No encoder/decoder needed; passthrough is correct. | |
| `OP_DisarmTraps` | 🟢 Verified | C→S only. 2-byte opcode, no payload. Sent by `CharacterZoneClient::UseSkill` (skill 17) when target is a valid NPC. Passthrough — no struct, no decoder needed. hton @ `0x140101740`. | |
| `OP_DisciplineTimer` | 🟢 Verified | S→C. Inline handler @ 0x1401f4b5d (cmp-dispatch secondary table). Calls SetMeleeSpellReuseTimer(TimerID,Duration,computed). Encoder sends 16-byte TOB struct with ServerTime for client latency-adjust calculation. | |
| `OP_DisciplineUpdate` | 🟢 Verified | S→C. Encoder sends 1200-byte packet (300×uint32); client at 0x1401e885b validates size via GetCombatAbilitySize()=1200, then rep movsb into CurrentProfile+0x4700. Server fills entries 099, zeros 100299. | |
| `OP_DiscordMerchantInventory` | 🔴 Not-Set | | |
| `OP_DoGroupLeadershipAbility` | 🟠 Missing | | |
| `OP_DuelDecline` | 🟠 Missing | | |
| `OP_DuelAccept` | 🟠 Missing | | |
| `OP_DumpName` | 🔴 Not-Set | | |
| `OP_Dye` | 🟠 Missing | | |
| `OP_DynamicWall` | 🔴 Not-Set | | |
| `OP_DzAddPlayer` | 🟠 Missing | | |
| `OP_DzChooseZone` | 🟠 Missing | | |
| `OP_DzChooseZoneReply` | 🟠 Missing | | |
| `OP_DzCompass` | 🟠 Missing | | |
| `OP_DzExpeditionEndsWarning` | 🟠 Missing | | |
| `OP_DzExpeditionInfo` | 🟠 Missing | | |
| `OP_DzExpeditionInvite` | 🟠 Missing | | |
| `OP_DzExpeditionInviteResponse` | 🟠 Missing | | |
| `OP_DzExpeditionLockoutTimers` | 🟠 Missing | | |
| `OP_DzListTimers` | 🟠 Missing | | |
| `OP_DzMakeLeader` | 🟠 Missing | | |
| `OP_DzMemberList` | 🟠 Missing | | |
| `OP_DzMemberListName` | 🟠 Missing | | |
| `OP_DzMemberListStatus` | 🟠 Missing | | |
| `OP_DzPlayerList` | 🟠 Missing | | |
| `OP_DzQuit` | 🟠 Missing | | |
| `OP_DzRemovePlayer` | 🟠 Missing | | |
| `OP_DzSetLeaderName` | 🟠 Missing | | |
| `OP_DzSwapPlayer` | 🟠 Missing | | |
| `OP_Emote` | 🟠 Missing | | |
| `OP_EndLootRequest` | 🟢 Verified | Bidirectional passthrough. S→C: client calls CLootWnd::EndLootingSession @ 0x1404426e0, no body read. C→S: 4-byte body = corpse entity ID (uint32); server reads low 2 bytes as uint16 spawn ID. | |
| `OP_EnduranceUpdate` | 🟠 Missing | | |
| `OP_EnterChat` | 🔴 Not-Set | | |
| `OP_EnterWorld` | 🟢 Verified | C→S only. Decoder copies name correctly but hardcodes tutorial=0, return_home=0. TOB sends zoneID (EverQuest_EnterZoneReason; -1=last zone) instead of flags. Enter Tutorial/Return Home buttons broken. | |
| `OP_EnvDamage` | 🟢 Verified | C→S only. Send @ 0x1400E6012 (sub_1400E5DC0). TOB wire=58 bytes. Decoder added: entity_id=wire+0 (u16→u32), damage=wire+16 (i64→u32), dmgtype=wire+56. Needs in-game test (fall/drown/lava). | |
| `OP_EvolveItem` | 🔴 Not-Set | | |
| `OP_ExpansionInfo` | 🟢 Verified | Handler `msg_expansions` @ 0x14020B970 (12 bytes). Pattern A. Client reads uint64 at offset 0x40; 64-byte Unknown000 prefix zeroed by alloc. OUT(Expansions) correct (u32→u64 zero-extend). | |
| `OP_ExpUpdate` | 🟢 Verified | Handler sub_14020B980 @ 0x14020B980 (95 bytes); Pattern A; encoder converts exp 0330→0100000; unknown=0 (no tip window); aaxp intentionally omitted (handled via OP_AAExpUpdate). | |
| `OP_FaceChange` | 🟠 Missing | | |
| `OP_Feedback` | 🟠 Missing | | |
| `OP_FeignDeath` | 🟢 Verified | Passthrough. No struct defined. Wire: spawn_id(4)+last_hitter(4)+fd(1). Inline case block @ 0x1401f55eb; C→S send in UseSkill::0x19 @ 0x140101388. fd always 0 from client; S→C checks nonzero for name-lookup. | |
| `OP_FellowshipUpdate` | 🔴 Not-Set | | |
| `OP_FindPersonReply` | 🟠 Missing | | |
| `OP_FindPersonRequest` | 🟠 Missing | | |
| `OP_FinishTrade` | 🟢 Verified | Passthrough, zero-payload notification. Handler `msg_do_trade` @ `0x14020b4b0` reads no packet data; client finalises trade UI on receipt. No struct. | |
| `OP_FinishWindow` | 🟢 Verified | Zero-payload S→C notification. Handler @ 0x1401f4ae1 (ja-dispatch; missed by validate_opcode.py). Calls DecItemPending() — decrements pending item window counter. No struct needed. | |
| `OP_FinishWindow2` | 🟢 Verified | Zero-payload S→C signal, always paired with OP_FinishWindow in trade/object-close flows. No struct. Passthrough correct. TOB client has no handler — packet silently discarded on receipt (harmless). | |
| `OP_Fishing` | 🟢 Verified | C→S only. Zero-payload cast signal (opcode only, no struct). Client validates pole/bait/land/level before sending. Send @ sub_140102510+0x140102510. Passthrough — no decoder needed. | |
| `OP_Fling` | 🟢 Verified | Encoder added. TOB wire layout differs from server fling_struct — fields reordered + extra radius (+16, set 0.0f) and padding (+20). Handler sub_1403135C0 @ 0x1403135c0 (Pattern A). fall_damage bit semantics TBD (see packet_analysis/OP_Fling.md). | |
| `OP_FloatListThing` | 🟢 Verified | C→S only. Movement History telemetry; EQEmu ignores it, no decoder needed. `PlayerClient::SendMovementHistory` @ `0x1402FFAD0` (0x1DF bytes). Throttled to 1000ms; payload via `CMovementHistory::BuildPacket`. | |
| `OP_Forage` | 🟢 Verified | C→S only; zero-payload forage trigger (skill 27). hton @ 0x140101740 in UseSkill. No S→C handler in HWM. | |
| `OP_ForceFindPerson` | 🔴 Not-Set | | |
| `OP_FormattedMessage` | 🟢 Verified | Handler: msgTokenTextParam @ 0x140207ce0 (Pattern D, inline CUnSerializeBuffer). No encoder needed — MessageComponent::Formatted() (tob.cpp:5795) builds the TOB wire format directly (uint32=0 + uint8 from_world + uint32 string_id + uint32 color + 9x WriteLengthString). Spell links handled via ServerToTOBConvertLinks before serialization. | |
| `OP_FriendsWho` | 🟢 Verified | C→S only; sends comma-separated friends string (flag+'0'/'1' + buddy names). No S→C handler. Zone reads as raw char*. hton @ 0x1402992d9 in CEverQuest::Who (0x140298b70). Passthrough correct. | |
| `OP_GetGuildMOTD` | 🟠 Missing | | |
| `OP_GetGuildMOTDReply` | 🟠 Missing | | |
| `OP_GetGuildsList` | 🔴 Not-Set | | |
| `OP_GiveMoney` | 🔴 Not-Set | | |
| `OP_GMApproval` | 🟠 Missing | | |
| `OP_GMBecomeNPC` | 🟠 Missing | | |
| `OP_GMDelCorpse` | 🟠 Missing | | |
| `OP_GMEmoteWorld` | 🟠 Missing | | |
| `OP_GMEmoteZone` | 🟠 Missing | | |
| `OP_GMEndTraining` | 🟢 Verified | C→S only. Passthrough. CTrainWnd::AboutToHide @ 0x140529b30. Payload: npcid+playerid = GMTrainEnd_Struct exactly. | |
| `OP_GMEndTrainingResponse` | 🔴 Not-Set | | |
| `OP_GMFind` | 🟠 Missing | | |
| `OP_GMGoto` | 🟠 Missing | | |
| `OP_GMHideMe` | 🟠 Missing | | |
| `OP_GMKick` | 🟠 Missing | | |
| `OP_GMKill` | 🟠 Missing | | |
| `OP_GMLastName` | 🟠 Missing | | |
| `OP_GMNameChange` | 🟠 Missing | | |
| `OP_GMSearchCorpse` | 🟠 Missing | | |
| `OP_GMServers` | 🟠 Missing | | |
| `OP_GMSummon` | 🟠 Missing | | |
| `OP_GMToggle` | 🟠 Missing | | |
| `OP_GMTraining` | 🟢 Verified | Bidirectional. S→C handler `msg_req_guildmaster` @ `0x140212e20` (Pattern A). Reads npcid[+0], flag[+0x198], passes skills[+8] and languages[+0x199] to CTrainWnd::SetGMData. Encoder/decoder correct. | |
| `OP_GMTrainSkill` | 🟢 Verified | C→S only. Send fn `sub_14052ADE0` @ `0x14052ade0`. Wire: uint32 npcid, skillbank, skill_id. Passthrough. | |
| `OP_GMTrainSkillConfirm` | 🟢 Verified | Encoder maps all 4 fields correctly. Inline handler @ cmp-dispatch 0x1401f37b4→0x1401f98c5. SkillID≤99=normal skill, >99=language (GetLangDesc). Unknown073[3] trailing padding not read by client. | |
| `OP_GMZoneRequest` | 🟠 Missing | | |
| `OP_GMZoneRequest2` | 🟠 Missing | | |
| `OP_GroundSpawn` | 🟢 Verified | S→C encoder verified. Pattern D: HWM inline alloc EQGroundItem, CSB in sub_14021EA40 @ 0x14021EA40. 13 wire fields match client reads. C→S is zero-byte signal (no decoder needed). | |
| `OP_GroupAcknowledge` | 🟠 Missing | | |
| `OP_GroupCancelInvite` | 🟠 Missing | | |
| `OP_GroupDelete` | 🟠 Missing | | |
| `OP_GroupDisband` | 🟢 Verified | C→S only; `CEverQuest::Disband` @ `0x14026c220`; decoder correctly copies name1/name2 from 154-byte TOB struct (26 extra bytes zeroed/ignored); no S→C handler. | |
| `OP_GroupDisbandOther` | 🟠 Missing | | |
| `OP_GroupDisbandYou` | 🟠 Missing | | |
| `OP_GroupFollow` | 🟠 Missing | | |
| `OP_GroupFollow2` | 🟠 Missing | | |
| `OP_GroupInvite` | 🟢 Verified | Handler sub_14029A040@0x14029A040 (PatternA). Fixed GroupGeneric_Struct to 168B (was 154); added ENCODE using SETUP_DIRECT_ENCODE. Client reads GroupRequestId at offset 168 (1B past buf) — server has no equivalent field, reads 0. | |
| `OP_GroupInvite2` | 🔴 Not-Set | | |
| `OP_GroupLeaderChange` | 🟠 Missing | | |
| `OP_GroupLeadershipAAUpdate` | 🟠 Missing | | |
| `OP_GroupMakeLeader` | 🟠 Missing | | |
| `OP_GroupMentor` | 🟠 Missing | | |
| `OP_GroupRoles` | 🟠 Missing | | |
| `OP_GroupUpdate` | 🟠 Missing | | |
| `OP_GroupUpdateB` | 🟠 Missing | | |
| `OP_GroupUpdateLeaderAA` | 🔴 Not-Set | | |
| `OP_GuildBank` | 🟠 Missing | | |
| `OP_GuildBankItemList` | 🟠 Missing | | |
| `OP_GuildCreate` | 🟠 Missing | | |
| `OP_GuildDelete` | 🟠 Missing | | |
| `OP_GuildDeleteGuild` | 🟠 Missing | | |
| `OP_GuildDemote` | 🟠 Missing | | |
| `OP_GuildInvite` | 🟠 Missing | | |
| `OP_GuildInviteAccept` | 🟠 Missing | | |
| `OP_GuildLeader` | 🟠 Missing | | |
| `OP_GuildManageAdd` | 🔴 Not-Set | | |
| `OP_GuildManageBanker` | 🟠 Missing | | |
| `OP_GuildManageRemove` | 🔴 Not-Set | | |
| `OP_GuildManageStatus` | 🔴 Not-Set | | |
| `OP_GuildMemberLevelUpdate` | 🔴 Not-Set | | |
| `OP_GuildMemberList` | 🟠 Missing | | |
| `OP_GuildMemberUpdate` | 🟠 Missing | | |
| `OP_GuildMemberLevel` | 🟠 Missing | | |
| `OP_GuildMemberRankAltBanker` | 🟠 Missing | | |
| `OP_GuildMemberPublicNote` | 🟠 Missing | | |
| `OP_GuildMemberAdd` | 🟠 Missing | | |
| `OP_GuildMemberRename` | 🟠 Missing | | |
| `OP_GuildMemberDelete` | 🟠 Missing | | |
| `OP_GuildMemberDetails` | 🟠 Missing | | |
| `OP_GuildRenameGuild` | 🟠 Missing | | |
| `OP_GuildMOTD` | 🟠 Missing | | |
| `OP_GuildPeace` | 🟠 Missing | | |
| `OP_GuildPromote` | 🟠 Missing | | |
| `OP_GuildPublicNote` | 🟠 Missing | | |
| `OP_GuildRemove` | 🟠 Missing | | |
| `OP_GuildSelectTribute` | 🟠 Missing | | |
| `OP_GuildModifyBenefits` | 🟠 Missing | | |
| `OP_GuildTributeToggleReq` | 🟠 Missing | | |
| `OP_GuildTributeToggleReply` | 🟠 Missing | | |
| `OP_GuildOpenGuildWindow` | 🟠 Missing | | |
| `OP_GuildOptInOut` | 🟠 Missing | | |
| `OP_GuildSaveActiveTributes` | 🟠 Missing | | |
| `OP_GuildSendActiveTributes` | 🟠 Missing | | |
| `OP_GuildTributeFavorAndTimer` | 🟠 Missing | | |
| `OP_GuildsList` | 🟠 Missing | | |
| `OP_GuildStatus` | 🟠 Missing | | |
| `OP_GuildTributeInfo` | 🔴 Not-Set | | |
| `OP_GuildUpdate` | 🟠 Missing | | |
| `OP_GuildTributeDonateItem` | 🟠 Missing | | |
| `OP_GuildTributeDonatePlat` | 🟠 Missing | | |
| `OP_GuildWar` | 🟠 Missing | | |
| `OP_Heartbeat` | 🔴 Not-Set | | |
| `OP_Hide` | 🟢 Verified | C→S only. 4-byte payload: uint32 (1=movement check passed, requesting hide roll; 0=cancel). 3 send sites: sub_140102A50 @ 0x140102A50, CancelHide @ 0x140267670, CancelSneakHide @ 0x1402678E0. Fixed: data==0 returns early, data==1 falls through to full server hide roll. | |
| `OP_HideCorpse` | 🟢 Verified | C→S only passthrough. Send sites: cmd@0x140222bf0, DoMainLoop, loadOptions, ALWAYS-mode@0x140441b70. | |
| `OP_HPUpdate` | 🟢 Verified | S→C only. Cmp-dispatch @ 0x1401f3571 → inline case block 0x1401f4448 (Pattern B). Reads spawn_id(+0,w), cur_hp(+2,q), max_hp(+0xA,q); calls ProcessHitpointMessage@0x1401fefd0. Encoder+struct match. | |
| `OP_Illusion` | 🟢 Verified | S→C: msg_change_form@0x14020a080 (Pattern A); encoder maps all common fields; TOB-only fields (class_, armorProperties, armorTints) zero-init (correct for NPC illusions). C→S: /becomenpc sends 332-byte TOB struct; no decoder, server gets garbled data past charname — GM-only. | |
| `OP_IncreaseStats` | 🟢 Verified | Handler sub_140208290 @ 0x140208290 (Pattern A). TOB wire: {uint32 spawn_id, uint32 stat_type 06, uint32 value}. Encoder added; spawn_id sourced from unknown13[0..1] stashed by Client::IncStats/SetStats (GetID()). Primary stats only; resists not handled by TOB client. | |
| `OP_InitialHPUpdate` | 🔴 Not-Set | | |
| `OP_InitialMobHealth` | 🔴 Not-Set | | |
| `OP_InspectAnswer` | 🟠 Missing | | |
| `OP_InspectBuffs` | 🟠 Missing | | |
| `OP_InspectMessageUpdate` | 🟠 Missing | | |
| `OP_InspectRequest` | 🟠 Missing | | |
| `OP_InstillDoubt` | 🟠 Missing | Was duplicate of OP_Fishing (both 0x3cdb). Fixed: set to 0x0000. IDA confirms 0x3cdb is fishing only (sub_140102510); no Instill Doubt send site found in TOB binary. Real TOB opcode unknown. | |
| `OP_InterruptCast` | 🟢 Verified | S→C passthrough. Handler sub_1401E27C0 @ 0x1401E27C0 (Pattern A). Reads spawnid+4, messageid+4, message[0]+8. messageid used for StringTable lookup + orderedStringExpansion. Struct matches exactly. | |
| `OP_InvokeChangePetName` | 🔴 Not-Set | | |
| `OP_InvokeChangePetNameImmediate` | 🔴 Not-Set | | |
| `OP_InvokeNameChangeImmediate` | 🔴 Not-Set | | |
| `OP_InvokeNameChangeLazy` | 🔴 Not-Set | | |
| `OP_ItemAdvancedLoreText` | 🟠 Missing | | |
| `OP_ItemLinkClick` | 🟠 Missing | | |
| `OP_ItemLinkResponse` | 🟠 Missing | | |
| `OP_ItemLinkText` | 🔴 Not-Set | | |
| `OP_ItemName` | 🔴 Not-Set | | |
| `OP_ItemPacket` | 🟢 Verified | Handler @ `0x1401f0bf3` (inline+CSB). Fixed: added `ItemPacketParcel` to `ServerToTOBItemPacketType` (srv 0x73→TOB 0x74) and corrected parcel `SerializeItem` to use `pms.slot_id`. Note: client discards note field. | |
| `OP_ItemPreview` | 🟠 Missing | | |
| `OP_ItemPreviewRequest` | 🔴 Not-Set | | |
| `OP_ItemRecastDelay` | 🟢 Verified | Handler @ `0x1401E8638` (inline, Pattern B). Encoder added: InventorySlot_Struct (zeroed, TODO server struct) + recast_delay (+0x0C) + recast_type (+0x10). SetCoreItemRecastTimer fires; per-item timer needs server slot. | |
| `OP_ItemVerifyReply` | 🟢 Verified | Handler `0x1401e652c` (inline Pattern B). Encoder added: expands 12→20 bytes; adds unknown0 (+0x0C exit gate) and recast_time (+0x10 timestamp, zeroed). Autobook-scribe (spell==0x407) needs server-side recast_time. | |
| `OP_ItemVerifyRequest` | 🟢 Verified | C→S only. TOB sends 16-byte packet (InventorySlot_Struct 12 bytes + target uint32). DECODE added using TOBToServerSlot; tob_structs.h entry added. Matches RoF2 layout with int32 Type instead of int16. | |
| `OP_ItemViewUnknown` | 🟠 Missing | | |
| `OP_Jump` | 🟢 Verified | C→S only; 0-byte payload, no struct. Client sends on jump; server deducts endurance (`Handle_OP_Jump`). Passthrough correct. Send @ `0x140272773` in `DoMainLoop`. | |
| `OP_KeyRing` | 🟠 Missing | | |
| `OP_KickPlayers` | 🟢 Verified | C→S only (no HWM handler). DialogResponse case 140. Sends full KickPlayers_Struct (72 bytes): char_name[64]+unknown064+kick_expedition+kick_task+padding. Passthrough correct, struct matches exactly. | |
| `OP_KnowledgeBase` | 🔴 Not-Set | | |
| `OP_LDoNButton` | 🟠 Missing | | |
| `OP_LDoNDisarmTraps` | 🔴 Not-Set | | |
| `OP_LDoNInspect` | 🔴 Not-Set | | |
| `OP_LDoNOpen` | 🟢 Verified | C→S only. Zero-payload packet (opcode only, no data body). Client sends from `do_open` @ `0x14022b160`; server handler reads no packet data — uses `GetTarget()`. No encoder/decoder needed. | |
| `OP_LDoNPickLock` | 🟢 Verified | C→S only. Zero-payload packet (opcode only, no data body). Client sends from `UseSkill` @ `0x140100e60` (case 0x23, skill 35); server handler reads no packet data — uses `GetTarget()`. No encoder/decoder needed. | |
| `OP_LDoNSenseTraps` | 🔴 Not-Set | Not set in patch_TOB.conf; no server struct or encoder/decoder found. | |
| `OP_LeadershipExpToggle` | 🟠 Missing | | |
| `OP_LeadershipExpUpdate` | 🟠 Missing | | |
| `OP_LeaveAdventure` | 🟠 Missing | | |
| `OP_LeaveBoat` | 🟢 Verified | C→S only. Zero-payload signal packet (no struct). Client sends opcode-only header from DoPassageOfTime @ 0x1400edf50 to notify server of vehicle dismount. No encoder/decoder needed. | |
| `OP_LevelAppearance` | 🟢 Verified | Passthrough. Case block @ 0x1401f61ff → UpdateAnimVariation @ 0x1402ff3d0. 5 parm/slot/flag triplets; all offsets match LevelAppearance_Struct exactly. valueNb low-byte-only read is benign. | |
| `OP_LevelUpdate` | 🟢 Verified | Passthrough. Handler sub_14020F110 @ 0x14020F110 (Pattern A). All 3 fields (level/level_old/exp) read correctly from wire offsets 0/4/8. No encoder/decoder needed. | |
| `OP_LFGAppearance` | 🔴 Not-Set | | |
| `OP_LFGCommand` | 🟠 Missing | | |
| `OP_LFGGetMatchesRequest` | 🟠 Missing | | |
| `OP_LFGGetMatchesResponse` | 🟠 Missing | | |
| `OP_LFGResponse` | 🔴 Not-Set | | |
| `OP_LFGuild` | 🟠 Missing | | |
| `OP_LFPCommand` | 🟠 Missing | | |
| `OP_LFPGetMatchesRequest` | 🟠 Missing | | |
| `OP_LFPGetMatchesResponse` | 🟠 Missing | | |
| `OP_LinkedReuse` | 🟢 Verified | Inline case @ 0x1401f4c23. Added encoder + TOB struct (16 bytes): extra DWORD at +0x04, end_time at +0x08, start_time at +0x0C. Server sent 12 bytes; client read past end — reuse timers were corrupted. | |
| `OP_LoadSpellSet` | 🟠 Missing | | |
| `OP_LocInfo` | 🔴 Not-Set | | |
| `OP_LockoutTimerInfo` | 🔴 Not-Set | | |
| `OP_Login` | 🔴 Not-Set | | |
| `OP_LoginAccepted` | 🔴 Not-Set | | |
| `OP_LoginComplete` | 🔴 Not-Set | | |
| `OP_LoginExpansionPacketData` | 🔴 Not-Set | | |
| `OP_LoginUnknown1` | 🔴 Not-Set | | |
| `OP_LoginUnknown2` | 🔴 Not-Set | | |
| `OP_Logout` | 🟢 Verified | C→S passthrough (empty, `INz`). S→C handler @ `0x1401ea526` reads 2 bools (bool[0]=cancel-camp/AA, bool[1]=exit-confirm) but EQEmu never sends S→C; uses `OP_LogoutReply` instead. | |
| `OP_LogoutReply` | 🔴 Not-Set | | |
| `OP_LogServer` | 🟢 Verified | Inline case block @ 0x1401eda66, Pattern B; 1932-byte packet manually assembled; all 23 client-read fields correctly set; dead: worldshortname (+0x15), voicemacros (+0x5F4), tutorial (+0x5F8) | |
| `OP_LootComplete` | 🟢 Verified | S→C only. Zero-payload signal; client calls CLootWnd::EndLootingSession @ 0x1404426e0. No struct, no encoder/decoder needed. Passthrough confirmed. | |
| `OP_LootItem` | 🟢 Verified | Bidirectional. C→S decoder correct. Added S→C encoder: server 16-byte→TOB 20-byte; slot_id via ServerToTOBCorpseMainSlot; unknown16 (quantity) set to 0 (no server-side quantity field). Handler: `CLootWnd::SlotLooted` @ 0x140443d40. | |
| `OP_LootRequest` | 🟢 Verified | C→S only. Raw uint32 payload (corpse spawn ID at PlayerClient+0x168). No struct, no decoder needed. Server checks app->size == sizeof(uint32) and calls entity_list.GetID(). Passthrough confirmed. | |
| `OP_ManaChange` | 🟢 Verified | S→C handler `msg_stop_casting` @ 0x140215f70 (Pattern A, 531 bytes). Updates mana+endurance, clears gem ETA, optionally stops cast. Encoder correct; keepcasting uint8→uint32 safe (client reads byte). C→S opcode-only (no payload). | |
| `OP_ManaUpdate` | 🟠 Missing | | |
| `OP_MarkNPC` | 🟠 Missing | | |
| `OP_MarkRaidNPC` | 🟠 Missing | | |
| `OP_Marquee` | 🟢 Verified | Passthrough S→C. Inline case 0x1401F00CD reads all 6 uint32 fields (+0x00+0x14) + msg ptr (+0x18) directly; forwards to CBroadcast::BroadcastString @ 0x1400BCEA0. | |
| `OP_MemorizeSpell` | 🟢 Verified | Bidir. Handler sub_14020ECB0 @ 0x14020ecb0 (Pattern A). Switch on scribing -1..4. Encoder/decoder correct; scribing=1 suppress intentional. 6 C→S send sites confirmed. | |
| `OP_Mend` | 🟢 Verified | C→S only, no payload. UseSkill case 0x20 (Mend/skill 32) @ hton 0x140101740. No struct or decoder needed. | |
| `OP_MendHPUpdate` | 🔴 Not-Set | | |
| `OP_MercenaryAssign` | 🔴 Not-Set | | |
| `OP_MercenaryCommand` | 🟠 Missing | | |
| `OP_MercenaryDataRequest` | 🟠 Missing | | |
| `OP_MercenaryDataResponse` | 🟠 Missing | | |
| `OP_MercenaryDataUpdate` | 🟠 Missing | | |
| `OP_MercenaryDataUpdateRequest` | 🟠 Missing | | |
| `OP_MercenaryDismiss` | 🟠 Missing | | |
| `OP_MercenaryHire` | 🟠 Missing | | |
| `OP_MercenarySuspendRequest` | 🟠 Missing | | |
| `OP_MercenarySuspendResponse` | 🟠 Missing | | |
| `OP_MercenaryTimer` | 🟠 Missing | | |
| `OP_MercenaryTimerRequest` | 🟠 Missing | | |
| `OP_MercenaryUnknown1` | 🟠 Missing | | |
| `OP_MercenaryUnsuspendResponse` | 🟠 Missing | | |
| `OP_MerchantBulkItems` | 🔴 Not-Set | | |
| `OP_MobEnduranceUpdate` | 🟠 Missing | | |
| `OP_MobHealth` | 🟢 Verified | S→C only. Handler via cmp-dispatch @ `0x1401f43ce``ProcessHitpointMessage` @ `0x1401fefd0`. 6-byte packet: int16 spawn_id + uint32 hp%. Encoder correct; max hardcoded 100 at call site. | |
| `OP_MobManaUpdate` | 🟠 Missing | | |
| `OP_MobRename` | 🟠 Missing | | |
| `OP_MobUpdate` | 🟠 Missing | | |
| `OP_MoneyOnCorpse` | 🟢 Verified | Handler `sub_140443110` @ `0x140443110`. Pattern B inline; TOB struct (24 B) matches client reads. flags=0 hardcoded (no server field); type 0/1/2 map correctly from emu->response. | |
| `OP_MoneyUpdate` | 🟢 Verified | Passthrough. Handler sub_140213940 @ 0x140213940 (Pattern A). All 4 fields (platinum/gold/silver/copper) read directly into LocalPC. S→C only. | |
| `OP_MOTD` | 🟢 Verified | Passthrough. Raw null-terminated string (no struct). Handler sub_140219DA0 @ 0x140219DA0 stores MOTD in EverQuest_motd and calls display update if changed. Client never sends. | |
| `OP_MoveCoin` | 🟢 Verified | C→S only. Passthrough (no encoder/decoder). 2 send sites: CEverQuest::MoveMoney @ 0x14027e000, PcZoneClient::DestroyHeldItemOrMoney @ 0x1402e6a10. No S→C handler in HWM. 20-byte struct matches. | |
| `OP_MoveDoor` | 🟢 Verified | Passthrough. Inline Pattern B @ 0x1401ED4A9. Reads doorid (+0) to look up EQSwitch via sub_14025FE50, then action (+1) → EQSwitch::ChangeState. Struct matches exactly. S→C only. | |
| `OP_MoveItem` | 🟢 Verified | Bidirectional. Encoder/decoder use ServerToTOBSlot/TOBToServerSlot (uint32↔InventorySlot_Struct). S→C cmp-dispatch @ 0x1401EAE97; normal path calls sub_14020FAF0; invalid to_slot+Type3/4 destroys item via sub_140204D00. 12 C→S send sites. | |
| `OP_MoveMultipleItems` | 🟢 Verified | Bidirectional. Passthrough. S→C handler sub_1402CD630 @ 0x1402cd630; reads count (+0) then 36-byte MultiMoveItemSub_Struct entries. C→S send in MultipleItemMoveManager::ProcessMove @ 0x1402cd830. | |
| `OP_MoveLogDisregard` | 🔴 Not-Set | | |
| `OP_MoveLogRequest` | 🔴 Not-Set | | |
| `OP_MultiLineMsg` | 🔴 Not-Set | | |
| `OP_NewSpawn` | 🟢 Verified | Deprecated in TOB client — 0x053d handler @ 0x1401eedcc calls fdebug("Received deprecated...") only. Encoder forwards to OP_ZoneSpawns which sends OP_ZoneEntry (0x713d). No struct parsing by client. | |
| `OP_NewTitlesAvailable` | 🟠 Missing | | |
| `OP_NewZone` | 🟢 Verified | Handler: zoneHeader_Deserialize @ 0x140203d30, Pattern D. Fixed npc_aggro_max_dist (was hardcoded 600). All other fields correct. | |
| `OP_NPCMoveUpdate` | 🟠 Missing | | |
| `OP_OnLevelMessage` | 🟢 Verified | Handler `CServerTextWnd::HandleShowDialogMessage` @ `0x1404ed300`. Pattern D (Hybrid CUnSerializeBuffer). Encoder matches client reads. Fields 910 hardcoded 0 (no server struct equivalent); correct. | |
| `OP_OpenContainer` | 🟢 Verified | C→S only. Server handler is empty no-op by design (all clients). Send @ 0x1403bad8d in CContainerMgr::OpenContainer. Sends 12-byte ItemGlobalIndex payload; server ignores contents. | |
| `OP_OpenDiscordMerchant` | 🔴 Not-Set | | |
| `OP_OpenGuildTributeMaster` | 🟠 Missing | | |
| `OP_OpenInventory` | 🔴 Not-Set | | |
| `OP_OpenTributeMaster` | 🟠 Missing | | |
| `OP_PDeletePetition` | 🔴 Not-Set | | |
| `OP_PetCommands` | 🟠 Missing | | |
| `OP_PetCommandState` | 🟠 Missing | | |
| `OP_PetHoTT` | 🟠 Missing | | |
| `OP_Petition` | 🟠 Missing | | |
| `OP_PetitionBug` | 🔴 Not-Set | | |
| `OP_PetitionCheckIn` | 🔴 Not-Set | | |
| `OP_PetitionCheckout` | 🔴 Not-Set | | |
| `OP_PetitionCheckout2` | 🔴 Not-Set | | |
| `OP_PetitionDelete` | 🔴 Not-Set | | |
| `OP_PetitionQue` | 🔴 Not-Set | | |
| `OP_PetitionRefresh` | 🔴 Not-Set | | |
| `OP_PetitionResolve` | 🔴 Not-Set | | |
| `OP_PetitionSearch` | 🔴 Not-Set | | |
| `OP_PetitionSearchResults` | 🔴 Not-Set | | |
| `OP_PetitionSearchText` | 🔴 Not-Set | | |
| `OP_PetitionUnCheckout` | 🔴 Not-Set | | |
| `OP_PetitionUpdate` | 🔴 Not-Set | | |
| `OP_PickPocket` | 🟢 Verified | S→C encoder + C→S decoder via structs::PickPocket_Struct. Handler sub_140215B60@0x140215B60 (Pattern D). Wire: uint32 to/from/myskill, uint8 type, uint32 coin(unaligned@0xD), length-prefixed itemname, uint8 luckily. | |
| `OP_PickZone` | 🔴 Not-Set | | |
| `OP_PickZoneWindow` | 🔴 Not-Set | | |
| `OP_PlayerProfile` | 🟢 Verified | S→C only. Case block `0x1401f124e``sub_1402E01A0` @ `0x1402e01a0`. Decompresses, CRC32-verifies, calls `ClientNetPcData::UnSerialize()`. ~1040-line manual SerializeBuffer encoder. Buffs/armor hardcoded empty; coin written twice. | |
| `OP_PlayerStateAdd` | 🟢 Verified | Passthrough. S→C handler sub_1401FE640 @ 0x1401FE640 (Pattern A): reads spawn_id+0x00 and state+0x04, ORs state into PlayerState. 5 C→S send sites in do_sheathe, do_aggressive, UpdateItemSlot. | |
| `OP_PlayerStateRemove` | 🟢 Verified | Passthrough. S→C handler sub_1401FE670 @ 0x1401FE670 (Pattern A): reads spawn_id+0x00 and state+0x04, ANDs ~state into PlayerState. 5 C→S send sites in do_sheathe, do_aggressive, UpdateItemSlot. | |
| `OP_PlayEverquestRequest` | 🔴 Not-Set | | |
| `OP_PlayEverquestResponse` | 🔴 Not-Set | | |
| `OP_PlayMP3` | 🟢 Verified | Passthrough. Inline case block @ 0x1401f7b7e (cmp-dispatch). Packet body = raw filename char*; passed directly to EqSoundManager::PlayScriptMp3. Volume sourced client-side from CLargeDialogWnd. | |
| `OP_Poll` | 🔴 Not-Set | | |
| `OP_PollResponse` | 🔴 Not-Set | | |
| `OP_PopupResponse` | 🟢 Verified | C→S only. `SendDialogResponseToServer` @ `0x1404ed680`. Sends `EDialogResponse` enum + popup ID (8-byte payload). Wire layout = `PopupResponse_Struct` exactly. Passthrough correct, no decoder needed. | |
| `OP_PostEnterWorld` | 🟢 Verified | Unused — marked `# unused` in patch conf; IDA confirms no S→C handler in HWM and no C→S hton send sites; no struct defined anywhere | |
| `OP_PotionBelt` | 🟠 Missing | | |
| `OP_PreLogoutReply` | 🔴 Not-Set | | |
| `OP_PurchaseLeadershipAA` | 🟠 Missing | | |
| `OP_PVPLeaderBoardDetailsReply` | 🟠 Missing | | |
| `OP_PVPLeaderBoardDetailsRequest` | 🟠 Missing | | |
| `OP_PVPLeaderBoardReply` | 🟠 Missing | | |
| `OP_PVPLeaderBoardRequest` | 🟠 Missing | | |
| `OP_PVPStats` | 🟠 Missing | | |
| `OP_QueryResponseThing` | 🔴 Not-Set | | |
| `OP_QueryUCSServerStatus` | 🟢 Verified | C→S only. Client polls every ~30s via AddPlayerTimer::Stop (0x14009de50). Server ignores payload; responds with OP_SetChatServer+OP_SetChatServer2. TOB handled (ucsTOBCombined). Passthrough, no struct. | |
| `OP_RaidDelegateAbility` | 🟠 Missing | | |
| `OP_RaidClearNPCMarks` | 🟠 Missing | | |
| `OP_RaidInvite` | 🟠 Missing | | |
| `OP_RaidJoin` | 🔴 Not-Set | | |
| `OP_RaidUpdate` | 🟠 Missing | | |
| `OP_RandomNameGenerator` | 🟢 Verified | S→C only; no hton sends. Handler: CCharacterCreation::NameGenerated @ 0x14039cb30 (Pattern A, 128 bytes). Packet: 8-byte prefix (skipped) + null-terminated name; calls SetName() and enables Accept button. No server struct; emu never sends this opcode. | |
| `OP_RandomReply` | 🟢 Verified | Passthrough. Inline HWM (0x1401F4C85/0x1401F4CD4). BeingIgnored gate on name[+0xC]; reads low[+0], high[+4], result[+8] via _ltoa; self vs other roll via strncmp. Struct matches exactly. | |
| `OP_RandomReq` | 🟢 Verified | C→S only. No S→C handler. Passthrough. Client sends sorted low/high pair from /random cmd; matches RandomReq_Struct exactly. Send @ sub_1402266E0 (0x1402266E0). | |
| `OP_ReadBook` | 🟢 Verified | Bidirectional. ENCODE+DECODE added. BookRequest_Struct in tob_structs.h. TOB wire: window(u32)+type(u32)+target_id(u32)+invslot(8B)+can_cast(1B)+txtfile[8194]. Handler: msg_note_text@0x140211740. | |
| `OP_RecipeAutoCombine` | 🟢 Verified | Fixed S→C encoder: container_slot now serialized as 10B (no Padding2), eliminating 2-byte misalignment. Added C→S DECODE for 56B wire layout (HandleCombine). New RecipeAutoCombine_CS_Struct in tob_structs.h. | |
| `OP_RecipeDetails` | 🟢 Verified | Bidirectional passthrough (no encoder/decoder). S→C: sub_140521980@0x140521980 (Pattern A, 422B). 10-slot big-endian format. Fixed: appended htonl(trivial) at end of SendTradeskillDetails packet (zone/tradeskills.cpp); client was reading 4 garbage bytes at recipe[+84]. | |
| `OP_RecipeReply` | 🟢 Verified | Passthrough. Handler sub_14051F590@0x14051F590 (Pattern A). All 5 uint32 fields + recipe_name[64] read at correct offsets; matches RecipeReply_Struct exactly. | |
| `OP_RecipesFavorite` | 🟢 Verified | C→S only. Passthrough. Client sends TradeskillFavorites_Struct (2008 B): object_type + some_id + favorite_recipes[500]. Sent by CTradeskillWnd::ShowFavorites @ 0x140522960. Server size-validates. | |
| `OP_RecipesSearch` | 🟢 Verified | C→S only. Client sends 80-byte struct (object_type, some_id, mintrivial, maxtrivial, query[56], 2 unknowns). Send @ sub_14051FC10 (0x14051FC10). Passthrough; struct matches exactly. | |
| `OP_ReclaimCrystals` | 🔴 Not-Set | | |
| `OP_RefreshBuffs` | 🟢 Verified | S→C. CBuffWindow::RefreshBuffs @ 0x14038fda0. mob_id & refresh_type skipped by client; tic_time, full_flag, count, per-buff fields, suspended all verified ok. | |
| `OP_RefreshPetBuffs` | 🟢 Verified | Bidi. CPetInfoWnd::RefreshPetBuffs @ 0x1404a0ae0. hit_number skipped by pet window (harmless); mob_id/refresh_type skipped; C→S is 6-byte UI-reload request, no server decoder. | |
| `OP_RefreshTargetBuffs` | 🟢 Verified | S→C. CTargetWnd::RefreshTargetBuffs @ 0x14050e510. Pattern D. Built by BuffComponent::RefreshBuffs; no ENCODE/DECODE. mob_id and hit_number sent but discarded by client. All fields verified ok. | |
| `OP_ReloadUI` | 🔴 Not-Set | | |
| `OP_RemoveAllDoors` | 🟢 Verified | Zero-byte signal; case block @ `0x1401ED50E` calls `EqSwitchManager::DeleteAll` @ `0x14025FBD0`; no packet payload, no encoder/decoder needed. | |
| `OP_RemoveBlockedBuffs` | 🟢 Verified | Bidirectional; encoder/decoder forward to `OP_BlockedBuffs`; wire = `uint32 Count + Count×int32 SpellID + uint8 Pet + uint8 Initialise`; S→C case block @ `0x1401f1e47`, deserializer `sub_140202750`. | |
| `OP_RemoveNimbusEffect` | 🟢 Verified | Passthrough. Case block 0x1401F8C65, handler sub_1401E2890 (24B); reads spawnid+0, nimbus_effect+4; calls CParticleSystemInterface vtbl[0x70]. | |
| `OP_RemoveTrap` | 🟠 Missing | | |
| `OP_Report` | 🟢 Verified | C→S only. Passthrough. Two send sites: `/report` cmd (sub_14021EE20 @ 0x14021ee20) sends `target|reporter|chatlog\0`; CMail::ReportInappropriateMail (0x1401680e0) sends mail body. BugReport_Struct does NOT match wire format. | |
| `OP_ReqClientSpawn` | 🟢 Verified | C→S only. Zero-byte signal packet, no struct or decoder needed. Sent from DoMainLoop@0x14026e670 (hton@0x14026e992) during world init ("Requesting initialization data."). | |
| `OP_ReqNewZone` | 🟢 Verified | S→C passthrough; emu never sends it, handler is dead. Inline case @ 0x1401e7ee6 via cmp-dispatch. Reads [Src+0xF4] (string-table ID, default 0x563) and [Src+0xF8] (class ID) from internal zone struct; displays zone-entry chat msg. Dead branch at loc_1401E804E (compiler artifact). | |
| `OP_RequestClientZoneChange` | 🟢 Verified | handler @ 0x1401f7040 (inline Pattern B); cont. @ 0x1401f713a calls DoTeleportB; zone_id/instance_id/y/x/z/heading pass correctly; type hardcoded 0x0b (ignores server field); message string at pkt+29 sent as zeros; tob_structs.h unknown032 offset annotation wrong (should be /*029*/ not /*032*/) | |
| `OP_RequestDuel` | 🟠 Missing | | |
| `OP_RequestGuildTributes` | 🟠 Missing | | |
| `OP_RequestKnowledgeBase` | 🔴 Not-Set | | |
| `OP_RequestTitles` | 🟠 Missing | | |
| `OP_ResetAA` | 🟢 Verified | C→S only. Zero-payload signal packet; client sends opcode header only. Server handler reads no fields — opcode arrival triggers admin AA reset. Send fn sub_140174980 @ 0x140174980, hton @ 0x1401749a2. | |
| `OP_RespawnWindow` | 🟢 Verified | Passthrough correct. Inline CUnSerializeBuffer handler @ `0x1401eb890`: reads selected_bind_id, time_remaining, ui_flag, total_binds, then loops reading bind_number+WorldLocation(20B)+name+validity. Client sends empty 0x5c ACK. | |
| `OP_RespondAA` | 🟢 Verified | Handler sub_140217A60 @ 0x140217a60 (Pattern A). TOB adds aapoints_assigned[6] header; aa_list expanded to 300 entries vs server's 240. Fixed OOB read: loop now uses MAX_PP_AA_ARRAY (240); entries 240-299 zero-filled. | |
| `OP_RestState` | 🟢 Verified | Passthrough. No struct. Variable-length: 1B (0x01=combat) or 5B (0x00+uint32 timer). Handler sub_1402DA8E0 @ 0x1402da8e0; manual CSB reads. Updates LocalPC+0x2E2C/28/20. S→C only. | |
| `OP_Rewind` | 🟢 Verified | C→S only; zero-payload signal (no struct); send @ sub_1402326F0 (0x1402326F0); guard: bBeingFlung at [player+0x18F]; no S→C handler | |
| `OP_RezzAnswer` | 🟠 Missing | | |
| `OP_RezzComplete` | 🟠 Missing | | |
| `OP_RezzRequest` | 🟠 Missing | | |
| `OP_Sacrifice` | 🟢 Verified | Passthrough. S→C: handler sub_140214100 @ 0x140214100 (Pattern A) reads CasterID (+0x00) only; pops sacrifice confirm dialog (token 9054). C→S: zero-payload send from DialogResponse case 107 (user accepted). | |
| `OP_SafeFallSuccess` | 🟢 Verified | C→S only. Zero-byte notification sent by TakeFallDamage @ 0x140100520 when SafeFall skill (0x27) reduces fall damage. No struct, no decoder needed — passthrough correct. | |
| `OP_SafePoint` | 🔴 Not-Set | | |
| `OP_Save` | 🟢 Verified | C→S only. Passthrough — no encoder/decoder. `Save_Struct` is opaque (192 unknown bytes). Client sends 476-byte save blob via `CEverQuest::SavePC` @ `0x14028d640`. No S→C handler. | |
| `OP_SaveOnZoneReq` | 🟢 Verified | C→S only. Zero-byte packet sent by sub_14028DF20 @ 0x14028DF20 when zoning. Server Handle_OP_SaveOnZoneReq → Handle_OP_Save → Save(); payload ignored entirely. No decoder needed. | |
| `OP_SelectTribute` | 🟠 Missing | | |
| `OP_SendAAStats` | 🟢 Verified | Removed in TOB — no S→C handler in HandleWorldMessage, no C→S send sites, no server struct. Value 0x7416 retained in patch conf as documentation only; server must not send this opcode to TOB clients. | |
| `OP_SendAATable` | 🟢 Verified | S→C: msg_send_alt_data@0x140214200 → UnPackNetBuffer@0x1401a80d0. Pattern A. Encoder verified field-by-field. Effects base/limit zero-extend int32→int64 (positive values only in practice). No decoder. | |
| `OP_SendCharInfo` | 🟢 Verified | Handler: msg_send_characters@0x1402142a0, sub_140203150@0x140203150. Pattern D (inline CUnSerializeBuffer). Variable-length name, 9 equip slots, PreFTP=1 hardcoded to bypass FTP checks. No changes needed.| |
| `OP_SendExpZonein` | 🟢 Verified | Bidirectional handshake; S→C inline @ `0x1401f4923` sets `EverQuest_ReceivedWorldObjects=1`; client echoes 0-byte C→S response. No struct, passthrough. | |
| `OP_SendFindableNPCs` | 🟠 Missing | | |
| `OP_SendGuildTributes` | 🟠 Missing | | |
| `OP_SendLoginInfo` | 🟢 Verified | C→S only. Send @ `0x1402bfec8` in WorldAuthenticate (`0x1402bfb40`). No S→C handler. Passthrough — no encoder/decoder. sub_140246F50 packs login+passwd+empty as NUL-terminated strings; zoning=EverQuest_EnterZone; 0xCC hardcoded at offset 192. | |
| `OP_SendMaxCharacters` | 🟢 Verified | Handler `msg_player_info_header` @ `0x140211ae0` (938B, Pattern A). All 16 client-read fields set by encoder; values hardcoded (server struct has no TOB-field equivalents). add_marketplace_chars/add_unknown both 0 so conditional 0x76C4 responses never triggered. | |
| `OP_SendMembership` | 🟢 Verified | S→C only. FreeToPlayClient::UnSerialize @ 0x14067d6f0. Wire: uint8 membership, uint32 races/classes, uint32 entrysize, int32[33] entries. TOB struct packed; encoder correct; entries hardcoded to max. | |
| `OP_SendMembershipDetails` | 🟢 Verified | Handler: FreeToPlay::UnSerialize @ 0x14067d540 (Pattern D). Encoder fully hardcoded — 96 settings (4 tiers × 24 IDs), 17 race + 17 class entries, exit_url_length=0. Wire format matches client reads. | |
| `OP_SendSystemStats` | 🔴 Not-Set | | |
| `OP_SendTitleList` | 🟠 Missing | | |
| `OP_SendTributes` | 🟠 Missing | | |
| `OP_SendZonepoints` | 🟢 Verified | S→C. Handler: msg_teleport_index @ 0x14029A9B0 (63B). Pattern A. XMM bulk copy, 32B/entry. TOB adds unknown024/028 (zeroed). Client cap: 127 entries. Encoder correct. | |
| `OP_SenseHeading` | 🟢 Verified | C→S only. Client sends 0-byte packet when Sense Heading skill succeeds. No payload, no decoder needed. Send: `sub_140103050` @ `0x140103050`, hton @ `0x1401031a0`. No S→C handler. | |
| `OP_SenseTraps` | 🟢 Verified | C→S only. Client sends 0-byte packet when Sense Traps skill (ID 62) is used. No payload, no decoder needed. Send: `UseSkill` @ `0x140100e60`, hton @ `0x140101740`. No S→C handler. | |
| `OP_ServerListRequest` | 🔴 Not-Set | | |
| `OP_ServerListResponse` | 🔴 Not-Set | | |
| `OP_SessionReady` | 🔴 Not-Set | | |
| `OP_SetChatServer` | 🟠 Missing | | |
| `OP_SetChatServer2` | 🟢 Verified | S→C only. Raw CSV text packet — no binary struct, no encoder/decoder. Handler sub_140208110 @ 0x140208110 parses 5 comma-sep fields; wire has 4 (3 commas), 5th absent→always false. Calls UniversalChatProxyConnect. | |
| `OP_SetFace` | 🟠 Missing | | |
| `OP_SetGroupTarget` | 🟠 Missing | | |
| `OP_SetGuildMOTD` | 🟠 Missing | | |
| `OP_SetGuildRank` | 🟠 Missing | | |
| `OP_SetRunMode` | 🟢 Verified | C→S only. Client sends when run mode changes in DoPassageOfTime @ 0x1400ec06f. uint32 payload (mode clamped 0/1) matches SetRunMode_Struct exactly. No S→C handler exists; no encoder/decoder needed. | |
| `OP_SetServerFilter` | 🟢 Verified | C→S only; no S→C handler. Decoder copies filters[0..28] from 69-entry TOB packet (276 bytes); remaining 40 TOB-only filters correctly dropped. send_update_filters @ 0x1402a0b00. | |
| `OP_SetStartCity` | 🟠 Missing | | |
| `OP_SetTitle` | 🟠 Missing | | |
| `OP_SetTitleReply` | 🟠 Missing | | |
| `OP_SharedTaskMemberList` | 🟠 Missing | | |
| `OP_SharedTaskAddPlayer` | 🟠 Missing | | |
| `OP_SharedTaskRemovePlayer` | 🟠 Missing | | |
| `OP_SharedTaskMakeLeader` | 🟠 Missing | | |
| `OP_SharedTaskMemberInvite` | 🔴 Not-Set | | |
| `OP_SharedTaskInvite` | 🟠 Missing | | |
| `OP_SharedTaskInviteResponse` | 🟠 Missing | | |
| `OP_SharedTaskAcceptNew` | 🟠 Missing | | |
| `OP_SharedTaskMemberChange` | 🟠 Missing | | |
| `OP_SharedTaskPlayerList` | 🟠 Missing | | |
| `OP_SharedTaskSelectWindow` | 🟠 Missing | | |
| `OP_SharedTaskQuit` | 🟠 Missing | | |
| `OP_TaskTimers` | 🟠 Missing | | |
| `OP_Shielding` | 🟠 Missing | | |
| `OP_ShopDelItem` | 🟢 Verified | Passthrough. Handler `sub_14020FA30`@`0x14020FA30``sub_140476CF0`@`0x140476CF0`. Client reads no payload fields; packet arrival triggers merchant window refresh only. Dispatched via cmp/jz sub-base. | |
| `OP_ShopEnd` | 🟢 Verified | Passthrough. S→C: inline case at 0x1401f6ff5; no body read, calls g_pMerchantWnd vtable[0x138](true) to close window. C→S: AboutToHide sends 8B (NPC spawn_id+player spawn_id); server uses INr (raw). | |
| `OP_ShopEndConfirm` | 🟢 Verified | Passthrough. Inline case @ 0x1401F61A2; no packet fields read. Calls g_pMerchantWnd->vtable[0x27](1) to close merchant window. Client never sends. | |
| `OP_ShopItem` | 🔴 Not-Set | | |
| `OP_ShopPlayerBuy` | 🟢 Verified | S→C: handler sub_1401E3E20@0x1401e3e20, encoder correct. C→S: fixed Merchant_Sell_Request_Struct from 20→24 bytes (added unknown20); client sends 24-byte payload, DECODE_LENGTH_EXACT was rejecting all | |
| `OP_ShopPlayerSell` | 🟢 Verified | Bidirectional. C→S decoder correct. Fixed S→C encoder: removed npcid from Merchant_Purchase_Response_Struct (client sub_14047A3A0@0x14047A3A0 reads TypelessInventorySlot at byte 0 and quantity at [rsi+8]; npcid was offsetting both). | |
| `OP_ShopSendParcel` | 🟢 Verified | Bidirectional. C→S: 224B RoF2 Parcel_Struct; inherited RoF2 decoder correct (TypelessInventorySlot→item_slot, send_to, note). S→C: no encoder; server sends empty (success) or 220B item_slot=0xFFFF (cancel). Handler sub_14020FAB0@0x14020FAB0→sub_14047B380@0x14047B380; unconditional UpdateSlots covers all cases. | |
| `OP_ShopDeleteParcel` | 🟢 Verified | S→C passthrough. Handler msg_merchant_mail_clearslot@0x14020fa40 reads [rcx+8] (parcel_slot_id+parcel_item_id as int64) → CMerchantWnd::ClearMailSlot. ParcelRetrieve_Struct matches. | |
| `OP_ShopRespondParcel` | 🔴 Not-Set | | |
| `OP_ShopRetrieveParcel` | 🟢 Verified | Passthrough. S→C empty ack (SendParcelRetrieveAck). C→S uses ParcelRetrieve_Struct (merchant+player entity id, parcel_slot_id, parcel_item_id). Handler @ client_packet.cpp:17225. | |
| `OP_ShopParcelIcon` | 🟢 Verified | Passthrough S→C. ParcelIcon_Struct (uint32 status: 0=off,1=on,2=overlimit) matches client reads exactly. Inline case @ 0x1401f277f; drives show/hide+overlimit on CPlayerWnd (sub_140201A20/A40) and updates LocalPC (sub_140201690). | |
| `OP_ShopRequest` | 🟢 Verified | S→C handler sub_1401E3E60 @ 0x1401E3E60 (cmp-dispatch). Fixed close case: OUT(npc_id) always; player_id=0 for close triggers client close path. Decoder correct (4-byte npc_id only). | |
| `OP_SimpleMessage` | 🟢 Verified | Passthrough. Handler `msgTokenText` @ `0x140207BF0` (Pattern A). Reads `string_id`/`color`/`unknown8` as dwords at +0,+4,+8. String IDs 469/471/14261 also open CTipWnd. | |
| `OP_SkillUpdate` | 🟢 Verified | Handler sub_140214AB0 @ 0x140214ab0 (676B). Pattern A. Handles skills 0-99 and languages 100-131. active=1 hardcoded correctly; server struct omits active by design. | |
| `OP_Sneak` | 🟢 Verified | C→S only. Empty packet (size=0) sent to activate sneak. No struct or decoder needed. Send @ `sub_1401032A0` (0x1401032A0). S→C not handled by client. | |
| `OP_Some3ByteHPUpdate` | 🔴 Not-Set | | |
| `OP_Some6ByteHPUpdate` | 🔴 Not-Set | | |
| `OP_SomeItemPacketMaybe` | 🟠 Missing | | |
| `OP_Sound` | 🟢 Verified | Passthrough. Inline handler @ `0x1401e9799` (Pattern B). Reads mob_id (+0x00), copper/silver/gold/platinum (+0x140x20) at correct offsets; plays sound WavePlay(0x8D). Ignores target_id, exp, faction, items. | |
| `OP_SpawnAppearance` | 🟢 Verified | Handler `msg_stat_change` @ `0x140215060` (cmp-dispatch @ `0x1401f3b90`). Fixed encoder: now sets both `eq->parameter` and `eq->lock_id` — half the TOBAppearance types (MaxHealth, HP, PVP, Sneak, Linkdead) read value from lock_id (offset +16). | |
| `OP_SpawnDoor` | 🟢 Verified | Handler @ 0x1401ED624 (inline Pattern B); EQSwitch ctor @ 0x14025E990 reads 132-byte _EQClientSwitch array; all server fields correct; extra TOB-only fields (AdventureDoorId, DynDoorID, RealEstateDoorID, speeds, flags) sent as 0 | |
| `OP_SpawnPositionUpdate` | 🔴 Not-Set | | |
| `OP_SpecialMesg` | 🟢 Verified | Inline @ `0x1401f6c6e` (Pattern B); encoder correct; link conversion applied to message | |
| `OP_SpellEffect` | 🟢 Verified | Passthrough. Handler sub_1402C0BE0 @ 0x1402c0be0. All struct fields matched. Unknown025 (+0x19) is a gate flag (0 = skip); Unknown026 (+0x1A) not read by client. | |
| `OP_Split` | 🟢 Verified | Passthrough. Bidirectional. S→C inline case @ 0x1401F1420 (Pattern B): reads platinum/gold/silver/copper at +0,+4,+8,+C; calls BuildMoneyText then displays chat. C→S: DoSplit @ 0x1402755E0. | |
| `OP_Stamina` | 🟢 Verified | Passthrough. Handler: msg_fwater_update @ 0x14020bc30. Pattern A. food@+0, water@+4 (both uint32). Client clamps to [0,32000]; original 0-127 comment reflects older protocol range. | |
| `OP_Stun` | 🟢 Verified | S→C only. Handler: `CharacterZoneClient::StunMe` @ `0x1401003C0` (Pattern A). Encoder correctly passes `duration`; `unknown004` sent as 0 (no server field); offsets +5/+6 hardcoded but not read by client. | |
| `OP_Surname` | 🟠 Missing | | |
| `OP_SwapSpell` | 🟢 Verified | Passthrough. Case block @ 0x1401eabca reads from_slot (+0) and to_slot (+4) inline → CSpellBookWnd::SwapSpellBookSlots @ 0x1404f6170. Send: HandleRightClickOnSpell @ 0x1404f40e0. | |
| `OP_SystemFingerprint` | 🔴 Not-Set | | |
| `OP_TargetCommand` | 🟢 Verified | Passthrough. Bidirectional 4-byte SpawnID. S→C inline case @ `0x1401ef810` sets `g_pTargetPlayer`. C→S send sites in AdvancedLootWnd, `/target` handler (`sub_140235E60`), and zero-byte un-target in HWM. | |
| `OP_TargetHoTT` | 🟠 Missing | | |
| `OP_TargetMouse` | 🟢 Verified | Passthrough. C→S only. Sends `uint32 new_target` (spawn_id from `[g_pTargetPlayer+0x168]`, 0 to un-target). 3 send sites: DoPassageOfTime, CTargetWnd ctor, CTargetWnd::Init. Matches `ClientTarget_Struct`. | |
| `OP_TargetReject` | 🔴 Not-Set | | |
| `OP_TaskActivity` | 🟠 Missing | | |
| `OP_TaskActivityComplete` | 🟠 Missing | | |
| `OP_TaskDescription` | 🟠 Missing | | |
| `OP_TaskHistoryReply` | 🟠 Missing | | |
| `OP_TaskHistoryRequest` | 🟠 Missing | | |
| `OP_TaskRequestTimer` | 🟠 Missing | | |
| `OP_TaskSelectWindow` | 🟠 Missing | | |
| `OP_Taunt` | 🟢 Verified | C→S passthrough. Client sends target spawn_id (uint32 at [g_pTargetPlayer+0x168]) matching ClientTarget_Struct. Send @ sub_140102DF0 (0x140102DF0). Pre-send: range/LOS/inanimate checks. | |
| `OP_TestBuff` | 🟠 Missing | | |
| `OP_TGB` | 🟠 Missing | | |
| `OP_TimeOfDay` | 🟢 Verified | Passthrough S→C; case 0x1401edc8b → sub_1401E07F0; reads {hour,minute,day,month:uint8, year:uint32}; exact match with TimeOfDay_Struct. | |
| `OP_Track` | 🟢 Verified | Added ENCODE: uint16 count + variable-length null-terminated names per entry (entityid, distance, level, is_npc, name, is_pet, is_merc). Identical to RoF2. Handler: sub_14051CF50 @ 0x14051CF50. | |
| `OP_TrackTarget` | 🟢 Verified | C→S only. Passthrough. Client sends uint32 EntityID at +0x00 matching TrackTarget_Struct exactly. Two send sites: CTrackingWnd::NotifyServerOfTrackingTarget (0x14051cc10) and UpdateUsingSkill (0x14024a6d0). | |
| `OP_TrackUnknown` | 🟢 Verified | Zero-length C→S companion to OP_Track (skill 53, Tracking). Server handler is a no-op; no struct payload. | |
| `OP_TradeAcceptClick` | 🟢 Verified | Passthrough. S→C handler sub_140212A90 @ 0x140212a90 reads byte ptr [rcx+4] (flag from unknown4) to set trade-accepted state. C→S sends 8-byte struct from ClickedTradeButton/WndNotification. No issues. | |
| `OP_TradeBusy` | 🟢 Verified | Passthrough. S→C handler sub_140212C30 @ 0x140212C30: reads from_mob_id (+4) and type byte (+8). C→S send @ 0x140213020: sends to/from mob IDs + type. Type encoding consistent; no translation needed. | |
| `OP_TradeCoins` | 🟢 Verified | Passthrough. S→C only. Handler sub_140216740 @ 0x140216740. Reads slot (+4, byte, 03=copper/silver/gold/platinum) and amount (+8, dword). trader field ignored (client uses g_pTradeTarget). | |
| `OP_TradeMoneyUpdate` | 🟢 Verified | Passthrough. S→C only. Handler sub_140216670 @ 0x140216670. Reads type (byte +4, 03=copper/silver/gold/platinum) and amount (dword +8), adds to LocalPC wallet. trader field (+0) unused by client. | |
| `OP_Trader` | 🟠 Missing | | |
| `OP_TraderBulkSend` | 🟠 Missing | | |
| `OP_TraderBuy` | 🔴 Not-Set | | |
| `OP_TraderDelItem` | 🟠 Missing | | |
| `OP_TradeRequest` | 🟢 Verified | Bidirectional passthrough. S→C handler `sub_140213020 @ 0x140213020` reads `from_mob_id` at [rbx+4]; opens trade window or sends `OP_TradeBusy` (0x43B8) with reason code. C→S send in `sub_140275D30`. | |
| `OP_TradeRequestAck` | 🟢 Verified | Bidirectional passthrough. S→C handler `sub_140208C50 @ 0x140208C50` (cmp-dispatch); reads `from_mob_id` at [rcx+4] to open trade window. C→S send in `sub_140528740 @ 0x140528971`; 8-byte `TradeRequest_Struct` matches exactly. | |
| `OP_TraderItemUpdate` | 🔴 Not-Set | | |
| `OP_TraderShop` | 🟠 Missing | | |
| `OP_TradeSkillCombine` | 🟢 Verified | Fixed: TOB struct now 28 bytes (added unknown0x18 at +24). DECODE_LENGTH_EXACT now accepts client's 28-byte packet. container_slot and guildtribute_slot decoded correctly. S→C: 0-byte ack, client calls DecItemPending. | |
| `OP_TradeSkillRecipeInspect` | 🔴 Not-Set | | |
| `OP_Translocate` | 🟢 Verified | Passthrough correct. Handler sub_140217410 @ 0x140217410. Pattern D (manual reads + GetString). All 8 struct fields read at correct offsets. Complete=1→immediate teleport; Complete=0→popup dialog (DialogResponse sends 0x0611 back). | |
| `OP_TributeInfo` | 🟠 Missing | | |
| `OP_TributeItem` | 🟠 Missing | | |
| `OP_TributeMoney` | 🟠 Missing | | |
| `OP_TributeNPC` | 🔴 Not-Set | | |
| `OP_TributePointUpdate` | 🟠 Missing | | |
| `OP_TributeTimer` | 🟠 Missing | | |
| `OP_TributeToggle` | 🟠 Missing | | |
| `OP_TributeUpdate` | 🟠 Missing | | |
| `OP_UnderWorld` | 🟢 Verified | C→S only. Client sends position (x,y,z floats) + uint32 at player+0x168 on underworld fall; 18-byte wire. After send, client auto-teleports via zoneHdr_fallThroughWorldTeleportId. No server decoder. | |
| `OP_Untargetable` | 🟢 Verified | Passthrough. Inline case block @ 0x1401EF975. Reads id (uint32 +0) via GetPlayerByID, targetable_flag (byte ptr +4) via SetTargetable vtable. Clears g_pTargetPlayer if flag=0 and player is target. | |
| `OP_UpdateAA` | 🟢 Verified | C→S only zero-payload trigger. Client sends during connect phase; server calls SendAlternateAdvancementPoints(). Send fn sub_1404EFE70 @ 0x1404efe70. No struct, no decoder needed. | |
| `OP_UpdateAura` | 🟠 Missing | | |
| `OP_UpdateLeadershipAA` | 🟠 Missing | | |
| `OP_VetClaimReply` | 🟠 Missing | | |
| `OP_VetClaimRequest` | 🟠 Missing | | |
| `OP_VetRewardsAvaliable` | 🟠 Missing | | |
| `OP_VoiceMacroIn` | 🟢 Verified | C→S only. 3 send sites: do_vtell/do_vraid/do_vgroup @ 0x140229950/CF0/A030. Wire format matches VoiceMacroIn_Struct exactly. Unknown132=Voice ID (GetMyVoice). Passthrough correct. | |
| `OP_VoiceMacroOut` | 🟢 Verified | Passthrough. Handler: VoiceManager::HandleMessage @ 0x14033d570. Reads From[64], Type, Voice, MacroNumber; all match server struct. Unknown068 unused; Unknown080 passed to sub_140242E90 for Tell type. | |
| `OP_WeaponEquip1` | 🟠 Missing | | |
| `OP_WearChange` | 🟢 Verified | Inline S→C @ 0x1401f5f48. All 8 fields confirmed: spawn_id (+0), wear_slot_id (+4), armor_id..new_armor_type (+8..+24 via rep movsb), color (+28). Encoder/decoder correct. | |
| `OP_Weather` | 🟢 Verified | Handler @ 0x1401ECE65, sub_14031BE00 @ 0x14031BE00. TOB wire is 16 bytes (4 uint32s); server struct 12 bytes. Client skips +0x08, reads mode at +0x0C. Added TOB struct + encoder to remap mode. | |
| `OP_Weblink` | 🟢 Verified | Deprecated in TOB client — not implemented. No handler found in HandleWorldMessage or sub-base scan, no C→S send. Passthrough is a no-op. | |
| `OP_WhoAllRequest` | 🟢 Verified | C→S only. TOB sends 176B (RoF2 sends 156B): guildid split into flag+id at 0x94/0x98, type moved to 0xA0. Decoder added; /who and /who all now functional. Trader/Buyer filters TODO (NaN sentinel unmapped). | |
| `OP_WhoAllResponse` | 🟢 Verified | Handler @ sub_1402182A0 (0x1402182a0). TOB client expects extra uint32 (0xFFFFFFFF) between PIDMSGID and Name per player. Encoder added (mirrors RoF2): inserts extra field, sets PIDMSGID=0 (no surname). | |
| `OP_World_Client_CRC1` | 🟢 Verified | C→S only. No struct/decoder. SendExeChecksum @ 0x14024aa10 sends 0x808-byte payload: crc32 + filesize + 256 sampled (offset, byte) pairs. World-server anti-cheat; EQEmu discards it. | |
| `OP_World_Client_CRC2` | 🟢 Verified | C→S only. No struct/decoder. SendBaseDataChecksum @ 0x14024a7b0 sends 0x808-byte payload: crc32 + filesize of Resources\BaseData.txt + 256 sampled (DWORD-idx, DWORD-val) pairs. EQEmu discards it. | |
| `OP_World_Client_CRC3` | 🟢 Verified | C→S only. No struct/decoder. SendSkillCapschecksum @ 0x14024ae10 sends 0x808-byte payload: crc32 + filesize of Resources\SkillCaps.txt + 256 sampled (DWORD-idx, DWORD-val) pairs. EQEmu discards it. | |
| `OP_WorldClientReady` | 🟢 Verified | C→S only. Zero-payload notification sent from DoCharacterSelection @ 0x14026cec0 (hton @ 0x14026cf8d). No struct, no decoder needed. Passthrough correct. | |
| `OP_WorldComplete` | 🟢 Verified | C→S only. 0-byte payload (opcode-only ACK). Client sends after receiving zone-connect data. Send @ 0x1401eeaaf in HandleWorldMessage. No S→C handler. No encoder/decoder needed. | |
| `OP_WorldLogout` | 🔴 Not-Set | | |
| `OP_WorldObjectsSent` | 🟢 Verified | Empty packet both ways; handler @ `0x1401f47e4` checks g_world, acks with C→S empty send at `0x1401f4882`. | |
| `OP_WorldUnknown001` | 🟢 Verified | SetServerTime (S→C). Handler `CEverQuest::SetServerTime` @ 0x140292550. Seeds CPacketScrambler with seeds at +0x00/+0x10; stores ServerTimeBase at +0x08. EQEmu doesn't use scrambler so packet never sent. | |
| `OP_XTargetAutoAddHaters` | 🟠 Missing | | |
| `OP_XTargetOpen` | 🟠 Missing | | |
| `OP_XTargetOpenResponse` | 🟠 Missing | | |
| `OP_XTargetRequest` | 🟠 Missing | | |
| `OP_XTargetResponse` | 🟠 Missing | | |
| `OP_YellForHelp` | 🟢 Verified | Passthrough. S→C: handler sub_140219A80 reads yeller spawn_id (+0) and target spawn_id (+4); server sends only 4 bytes so "with [target]" display is non-functional (server limitation, not TOB issue). | |
| `OP_ZoneChange` | 🟢 Verified | Bidirectional (100-byte TOB struct). S→C via ServerStatusPacketHandler (not HWM). Encoder/decoder correct; 3 extra unknown fields (068/072/096) are 0-filled/ignored. success-=1 for negative codes. | |
| `OP_ZoneComplete` | 🔴 Not-Set | | |
| `OP_ZoneEntry` | 🟢 Verified | Bidir. S→C: ENCODE_FORWARD(OP_ZoneSpawns); handler msgEQAddPlayer@0x140205610 (Pattern D). C→S: 92-byte; decoder extracts char_name only; unknown00/68-88 are crc32/SpellFileCRC/fingerprint, unused. | |
| `OP_ZoneGuildList` | 🔴 Not-Set | | |
| `OP_ZoneInUnknown` | 🔴 Not-Set | | |
| `OP_ZonePlayerToBind` | 🟢 Verified | Inline CSB in HWM case @ 0x1401eb3bc. WorldLocation (20B: ZoneBoundID+Y+X+Z+Heading), zone_name string, then 3 ints (HP/End/mana — hardcoded 60/0/51). Extra WriteUInt32(41) not read by client; remove it | |
| `OP_ZoneServerInfo` | 🟢 Verified | S→C only. Inline Pattern B @ `0x1401ee9cc`. `rep movsb ecx=0x82` copies 130 bytes; port read at offset 128. Struct matches exactly. Client acks with OP_WorldComplete (0x1223) to world server. | |
| `OP_ZoneServerReady` | 🔴 Not-Set | | |
| `OP_ZoneSpawns` | 🟢 Verified | Deprecated in TOB client — case block @ 0x1401EEDDD calls fdebug only, no parsing. Encoder fans out each Spawn_Struct as individual OP_ZoneEntry (0x713D) packets. Client never receives this directly. | |
| `OP_ZoneUnavail` | 🟢 Verified | Passthrough. Inline case @ 0x1401EE4CC: sets global ZoneUnavailable=1, clears PendingCharacterName[0]. Client reads no packet fields — opcode arrival is the entire signal. | |