From 6a7baf8f1c9c445c6c5116912b578baccc11f3c9 Mon Sep 17 00:00:00 2001 From: dannuic Date: Fri, 17 Apr 2026 11:56:46 -0600 Subject: [PATCH] Validated through OP_SetServerFilter --- common/patches/tob.cpp | 56 +++++++++++++++++------------------- common/patches/tob_structs.h | 6 ++-- tob/opcodes.md | 16 +++++------ utils/patches/patch_TOB.conf | 2 +- 4 files changed, 39 insertions(+), 41 deletions(-) diff --git a/common/patches/tob.cpp b/common/patches/tob.cpp index 93e9a66f2..c0ea35bda 100644 --- a/common/patches/tob.cpp +++ b/common/patches/tob.cpp @@ -3845,7 +3845,7 @@ namespace TOB int r; for (r = 0; r < 29; r++) { - // Size 68 in TOB + // Size 69 in TOB IN(filters[r]); } @@ -4149,7 +4149,7 @@ namespace TOB //s32 Variation; //s32 NewArmorId; //s32 NewArmorType; - buffer.WriteUInt32(item->Material); + buffer.WriteUInt32(item->Material); // this isn't labeled well, material is material *type* buffer.WriteUInt32(0); //unsupported atm buffer.WriteUInt32(item->EliteMaterial); buffer.WriteUInt32(item->HerosForgeModel); @@ -4267,9 +4267,20 @@ namespace TOB //u8 SpellDataSkillMask[78]; for (int j = 0; j < 78; ++j) { - buffer.WriteUInt8(0); //unsure what this is exactly + buffer.WriteUInt8(0); // TODO: collection of ints for bitfield for each skill required to use. reads 19 ints byte by byte in the client, leave like this for further investigation } + + /* There are a static 7 spell data entries on an item: + Clicky + Proc + Worn + Focus + Scroll + Focus2 + Blessing + */ + /* SpellData: s32 SpellId; u8 RequiredLevel; @@ -4678,55 +4689,42 @@ namespace TOB //ItemDefinition Item; SerializeItemDefinition(buffer, item); - //u32 RealEstateArrayCount; - // buffer.WriteInt32(0); - //s32 RealEstateArray[RealEstateArrayCount]; - - //bool bRealEstateItemPlaceable; - // buffer.WriteInt8(0); - //u32 SubContentSize; - uint32 subitem_count = 0; - int16 SubSlotNumber = EQ::invbag::SLOT_INVALID; if (slot_id_in <= EQ::invslot::GENERAL_END && slot_id_in >= EQ::invslot::GENERAL_BEGIN) - SubSlotNumber = EQ::invbag::GENERAL_BAGS_BEGIN + ((slot_id_in - EQ::invslot::GENERAL_BEGIN) * EQ::invbag::SLOT_COUNT); + SubSlotNumber = EQ::invbag::GENERAL_BAGS_BEGIN + (slot_id_in - EQ::invslot::GENERAL_BEGIN) * EQ::invbag::SLOT_COUNT; else if (slot_id_in == EQ::invslot::slotCursor) SubSlotNumber = EQ::invbag::CURSOR_BAG_BEGIN; else if (slot_id_in <= EQ::invslot::BANK_END && slot_id_in >= EQ::invslot::BANK_BEGIN) - SubSlotNumber = EQ::invbag::BANK_BAGS_BEGIN + ((slot_id_in - EQ::invslot::BANK_BEGIN) * EQ::invbag::SLOT_COUNT); + SubSlotNumber = EQ::invbag::BANK_BAGS_BEGIN + (slot_id_in - EQ::invslot::BANK_BEGIN) * EQ::invbag::SLOT_COUNT; else if (slot_id_in <= EQ::invslot::SHARED_BANK_END && slot_id_in >= EQ::invslot::SHARED_BANK_BEGIN) - SubSlotNumber = EQ::invbag::SHARED_BANK_BAGS_BEGIN + ((slot_id_in - EQ::invslot::SHARED_BANK_BEGIN) * EQ::invbag::SLOT_COUNT); + SubSlotNumber = EQ::invbag::SHARED_BANK_BAGS_BEGIN + (slot_id_in - EQ::invslot::SHARED_BANK_BEGIN) * EQ::invbag::SLOT_COUNT; else SubSlotNumber = slot_id_in; // not sure if this is the best way to handle this..leaving for now if (SubSlotNumber != EQ::invbag::SLOT_INVALID) { + std::vector> subitems; for (uint32 index = EQ::invbag::SLOT_BEGIN; index <= EQ::invbag::SLOT_END; ++index) { EQ::ItemInstance* sub = inst->GetItem(index); - if (!sub) - continue; - - ++subitem_count; + if (sub != nullptr) + subitems.emplace_back(index, sub); } - buffer.WriteUInt32(subitem_count); - - for (uint32 index = EQ::invbag::SLOT_BEGIN; index <= EQ::invbag::SLOT_END; ++index) { - EQ::ItemInstance* sub = inst->GetItem(index); - if (!sub) - continue; + buffer.WriteUInt32(subitems.size()); + // This must be guaranteed to have subitem_count members, where the index is the correct index. The client doesn't loop through all slots here + for (const auto& [index, subitem] : subitems) { buffer.WriteUInt32(index); - - SerializeItem(buffer, sub, SubSlotNumber, (depth + 1), packet_type); + SerializeItem(buffer, subitem, SubSlotNumber, depth + 1, packet_type); } - } + } else + buffer.WriteUInt32(0); // no subitems, client needs to know that //bool bCollected; buffer.WriteInt8(0); //unsupported atm //u64 DontKnow; - buffer.WriteUInt64(0); //unsupported atm + buffer.WriteInt64(0); //unsupported atm //s32 Luck; buffer.WriteInt32(0); //unsupported atm } diff --git a/common/patches/tob_structs.h b/common/patches/tob_structs.h index 64642c78b..c92d271ca 100644 --- a/common/patches/tob_structs.h +++ b/common/patches/tob_structs.h @@ -516,7 +516,7 @@ namespace TOB { //OP_SetServerFilter struct SetServerFilter_Struct { - uint32 filters[68]; + uint32 filters[69]; }; // Was new to RoF2, doesn't look changed @@ -748,10 +748,10 @@ namespace TOB { struct ManaChange_Struct { uint32 new_mana; - uint32 stamina; + uint32 stamina; // endurance uint32 spell_id; uint32 keepcasting; - int32 slot; + int32 slot; // gem slot }; //This is what we call OP_Action diff --git a/tob/opcodes.md b/tob/opcodes.md index 8a11d5107..67cdf20a9 100644 --- a/tob/opcodes.md +++ b/tob/opcodes.md @@ -85,7 +85,7 @@ Below is a status list for the 450 opcodes we currently use on the server for th | `OP_ChangePetName` | 🔴 Not-Set | | | | `OP_CharacterCreate` | 🟢 Verified | Sends heroic type, can be used for something? | | | `OP_CharacterCreateRequest` | 🟢 Verified | | | -| `OP_CharInventory` | 🟡 Unverified | | | +| `OP_CharInventory` | 🟢 Verified | | | | `OP_Charm` | 🟡 Unverified | | | | `OP_ChatMessage` | 🔴 Not-Set | | | | `OP_ClearAA` | 🟢 Verified | | | @@ -98,9 +98,9 @@ Below is a status list for the 450 opcodes we currently use on the server for th | `OP_ClickObject` | 🟡 Unverified | | | | `OP_ClickObjectAction` | 🟡 Unverified | | | | `OP_ClientError` | 🔴 Not-Set | | | -| `OP_ClientReady` | 🟡 Unverified | | | +| `OP_ClientReady` | 🟢 Verified | | | | `OP_ClientTimeStamp` | 🔴 Not-Set | | | -| `OP_ClientUpdate` | 🟡 Unverified | | | +| `OP_ClientUpdate` | 🟢 Verified | | | | `OP_CloseContainer` | 🔴 Not-Set | | | | `OP_CloseTributeMaster` | 🔴 Not-Set | | | | `OP_ColoredText` | 🟡 Unverified | | | @@ -279,7 +279,7 @@ Below is a status list for the 450 opcodes we currently use on the server for th | `OP_Heartbeat` | 🔴 Not-Set | | | | `OP_Hide` | 🟡 Unverified | | | | `OP_HideCorpse` | 🟡 Unverified | | | -| `OP_HPUpdate` | 🟡 Unverified | | | +| `OP_HPUpdate` | 🟢 Verified | | | | `OP_Illusion` | 🟡 Unverified | | | | `OP_IncreaseStats` | 🟡 Unverified | | | | `OP_InitialHPUpdate` | 🔴 Not-Set | | | @@ -346,7 +346,7 @@ Below is a status list for the 450 opcodes we currently use on the server for th | `OP_LootComplete` | 🟡 Unverified | | | | `OP_LootItem` | 🟡 Unverified | | | | `OP_LootRequest` | 🟡 Unverified | | | -| `OP_ManaChange` | 🟡 Unverified | | | +| `OP_ManaChange` | 🟢 Verified | | | | `OP_ManaUpdate` | 🔴 Not-Set | | | | `OP_MarkNPC` | 🔴 Not-Set | | | | `OP_MarkRaidNPC` | 🔴 Not-Set | | | @@ -503,7 +503,7 @@ Below is a status list for the 450 opcodes we currently use on the server for th | `OP_SetGuildMOTD` | 🔴 Not-Set | | | | `OP_SetGuildRank` | 🔴 Not-Set | | | | `OP_SetRunMode` | 🟡 Unverified | | | -| `OP_SetServerFilter` | 🟡 Unverified | | | +| `OP_SetServerFilter` | 🟢 Verified | | | | `OP_SetStartCity` | 🔴 Not-Set | | | | `OP_SetTitle` | 🔴 Not-Set | | | | `OP_SetTitleReply` | 🔴 Not-Set | | | @@ -543,7 +543,7 @@ Below is a status list for the 450 opcodes we currently use on the server for th | `OP_SpawnAppearance` | 🟢 Verified | | | | `OP_SpawnDoor` | 🟢 Verified | | | | `OP_SpawnPositionUpdate` | 🔴 Not-Set | | | -| `OP_SpecialMesg` | 🟡 Unverified | | | +| `OP_SpecialMesg` | 🟢 Verified | | | | `OP_SpellEffect` | 🟡 Unverified | | | | `OP_Split` | 🟡 Unverified | | | | `OP_Stamina` | 🟢 Verified | These values are 0-32k instead of 0-127 | | @@ -603,7 +603,7 @@ Below is a status list for the 450 opcodes we currently use on the server for th | `OP_VoiceMacroIn` | 🟡 Unverified | | | | `OP_VoiceMacroOut` | 🟡 Unverified | | | | `OP_WeaponEquip1` | 🔴 Not-Set | | | -| `OP_WearChange` | 🟡 Unverified | | | +| `OP_WearChange` | 🟢 Verified | | | | `OP_Weather` | 🟢 Verified | | | | `OP_Weblink` | 🟡 Unverified | | | | `OP_WhoAllRequest` | 🟡 Unverified | | | diff --git a/utils/patches/patch_TOB.conf b/utils/patches/patch_TOB.conf index c718d5de4..de96f6841 100644 --- a/utils/patches/patch_TOB.conf +++ b/utils/patches/patch_TOB.conf @@ -222,7 +222,7 @@ OP_KeyRing=0x0000 OP_WhoAllRequest=0x3328 OP_WhoAllResponse=0x4dfd OP_FriendsWho=0x3547 -OP_ConfirmDelete=0x14a8 +OP_ConfirmDelete=0x14a8 # This is sent fromt the client after a movement update (with just spawn ID as the content) OP_Logout=0x46f8 OP_Rewind=0x898a OP_TargetCommand=0x46bf