This commit is contained in:
dannuic
2026-04-18 18:37:59 +00:00
committed by GitHub
8 changed files with 240 additions and 153 deletions
+155 -76
View File
@@ -23,7 +23,9 @@
#include <numeric> #include <numeric>
#include <cassert> #include <cassert>
#include <cinttypes> #include <cinttypes>
#include <set>
#include "common/packet_dump.h"
#include "world/sof_char_create_data.h" #include "world/sof_char_create_data.h"
namespace TOB namespace TOB
@@ -144,6 +146,20 @@ namespace TOB
#include "ss_define.h" #include "ss_define.h"
// ENCODE methods // ENCODE methods
ENCODE(OP_AAExpUpdate) {
ENCODE_LENGTH_EXACT(AltAdvStats_Struct);
SETUP_DIRECT_ENCODE(AltAdvStats_Struct, structs::AltAdvStats_Struct);
//later we should change the underlying server to use this more accurate value
//and encode the 330 in the other patches
eq->experience = emu->experience * 100000 / 330;
OUT(unspent);
OUT(percentage);
FINISH_ENCODE();
}
ENCODE(OP_Action) { ENCODE(OP_Action) {
ENCODE_LENGTH_EXACT(Action_Struct); ENCODE_LENGTH_EXACT(Action_Struct);
SETUP_DIRECT_ENCODE(Action_Struct, structs::MissileHitInfo); SETUP_DIRECT_ENCODE(Action_Struct, structs::MissileHitInfo);
@@ -217,19 +233,22 @@ namespace TOB
ENCODE(OP_BlockedBuffs) ENCODE(OP_BlockedBuffs)
{ {
ENCODE_LENGTH_EXACT(BlockedBuffs_Struct); // Blocked buffs are a major change. They are stored in a resizable array in TOB, so this sends size, then
SETUP_DIRECT_ENCODE(BlockedBuffs_Struct, structs::BlockedBuffs_Struct); // spells, then the final two bools -- see 0x140202750
SETUP_VAR_ENCODE(BlockedBuffs_Struct);
for (uint32 i = 0; i < BLOCKED_BUFF_COUNT; ++i) // size is uint32 + count * int32 + uint8 + uint8
eq->SpellID[i] = emu->SpellID[i]; uint32 sz = 6 + emu->Count * 4;
__packet->size = sz;
__packet->pBuffer = new unsigned char[sz];
memset(__packet->pBuffer, 0, sz);
for (uint32 i = BLOCKED_BUFF_COUNT; i < structs::BLOCKED_BUFF_COUNT; ++i) __packet->WriteUInt32(emu->Count);
eq->SpellID[i] = -1; for (int i = 0; i < emu->Count; i++)
__packet->WriteSInt32(emu->SpellID[i]);
OUT(Count); __packet->WriteUInt8(emu->Pet);
OUT(Pet); __packet->WriteUInt8(emu->Initialise);
OUT(Initialise);
OUT(Flags);
FINISH_ENCODE(); FINISH_ENCODE();
} }
@@ -333,6 +352,7 @@ namespace TOB
ENCODE(OP_CastSpell) ENCODE(OP_CastSpell)
{ {
// I don't think the client handles this at all, it only sends the cast packet
ENCODE_LENGTH_EXACT(CastSpell_Struct); ENCODE_LENGTH_EXACT(CastSpell_Struct);
SETUP_DIRECT_ENCODE(CastSpell_Struct, structs::CastSpell_Struct); SETUP_DIRECT_ENCODE(CastSpell_Struct, structs::CastSpell_Struct);
@@ -344,6 +364,7 @@ namespace TOB
//OUT(inventoryslot); //OUT(inventoryslot);
OUT(target_id); OUT(target_id);
LogNetcode("S->C OP_CastSpell {}", DumpPacketToString(__packet));
FINISH_ENCODE(); FINISH_ENCODE();
} }
@@ -453,7 +474,7 @@ namespace TOB
int item_count = in->size / sizeof(EQ::InternalSerializedItem_Struct); int item_count = in->size / sizeof(EQ::InternalSerializedItem_Struct);
if (!item_count || (in->size % sizeof(EQ::InternalSerializedItem_Struct)) != 0) { if (!item_count || (in->size % sizeof(EQ::InternalSerializedItem_Struct)) != 0) {
Log(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d", LogNetcode("[STRUCTS] Wrong size on outbound {}: Got {}, expected multiple of {}",
opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(EQ::InternalSerializedItem_Struct)); opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(EQ::InternalSerializedItem_Struct));
delete in; delete in;
@@ -650,8 +671,8 @@ namespace TOB
} }
SerializeBuffer buffer; SerializeBuffer buffer;
buffer.WriteUInt32(emu->unknown0); buffer.WriteUInt32(0); // This is a string written like the message arrays
buffer.WriteUInt8(0); // Observed buffer.WriteUInt8(emu->unknown0);
buffer.WriteUInt32(emu->string_id); buffer.WriteUInt32(emu->string_id);
buffer.WriteUInt32(emu->type); buffer.WriteUInt32(emu->type);
@@ -939,6 +960,27 @@ namespace TOB
FINISH_ENCODE(); FINISH_ENCODE();
} }
ENCODE(OP_MemorizeSpell) {
ENCODE_LENGTH_EXACT(MemorizeSpell_Struct);
SETUP_DIRECT_ENCODE(MemorizeSpell_Struct, structs::MemorizeSpell_Struct);
// in TOB, 2 is "finish memming" so that becomes 1 in emu and 3 is "unmem" which becomes 2
if (emu->scribing == 1)
eq->scribing = 2;
else if (emu->scribing == 2)
eq->scribing = 3;
else if (emu->scribing == 3)
eq->scribing = 4;
else
OUT(scribing);
OUT(slot);
OUT(spell_id);
OUT(reduction);
FINISH_ENCODE();
}
ENCODE(OP_MobHealth) { ENCODE(OP_MobHealth) {
ENCODE_LENGTH_EXACT(SpawnHPUpdate_Struct2); ENCODE_LENGTH_EXACT(SpawnHPUpdate_Struct2);
SETUP_DIRECT_ENCODE(SpawnHPUpdate_Struct2, structs::MobHealth_Struct); SETUP_DIRECT_ENCODE(SpawnHPUpdate_Struct2, structs::MobHealth_Struct);
@@ -981,6 +1023,7 @@ namespace TOB
ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); }
ENCODE(OP_NewZone) { ENCODE(OP_NewZone) {
// zoneHeader
EQApplicationPacket* in = *p; EQApplicationPacket* in = *p;
*p = nullptr; *p = nullptr;
@@ -2305,7 +2348,6 @@ namespace TOB
eq->container_slot = ServerToTOBSlot(emu->unknown1); eq->container_slot = ServerToTOBSlot(emu->unknown1);
structs::InventorySlot_Struct TOBSlot; structs::InventorySlot_Struct TOBSlot;
TOBSlot.Type = 8; // Observed TOBSlot.Type = 8; // Observed
TOBSlot.Padding1 = 0;
TOBSlot.Slot = 0xffff; TOBSlot.Slot = 0xffff;
TOBSlot.SubIndex = 0xffff; TOBSlot.SubIndex = 0xffff;
TOBSlot.AugIndex = 0xffff; TOBSlot.AugIndex = 0xffff;
@@ -2332,12 +2374,11 @@ namespace TOB
eq->aapoints_assigned[4] = 0; eq->aapoints_assigned[4] = 0;
eq->aapoints_assigned[5] = 0; eq->aapoints_assigned[5] = 0;
for (uint32 i = 0; i < MAX_PP_AA_ARRAY; ++i) for (uint32 i = 0; i < structs::MAX_PP_AA_ARRAY; ++i)
{ {
eq->aa_list[i].AA = emu->aa_list[i].AA; eq->aa_list[i].AA = emu->aa_list[i].AA;
eq->aa_list[i].value = emu->aa_list[i].value; eq->aa_list[i].value = emu->aa_list[i].value;
eq->aa_list[i].charges = emu->aa_list[i].charges; eq->aa_list[i].charges = emu->aa_list[i].charges;
eq->aa_list[i].bUnknown0x0c = false;
} }
FINISH_ENCODE(); FINISH_ENCODE();
@@ -2386,7 +2427,7 @@ namespace TOB
s32 Desc; s32 Desc;
*/ */
buffer.WriteUInt32(emu->id); buffer.WriteUInt32(emu->id); // Index
buffer.WriteUInt8(1); buffer.WriteUInt8(1);
buffer.WriteInt32(emu->upper_hotkey_sid); buffer.WriteInt32(emu->upper_hotkey_sid);
buffer.WriteInt32(emu->lower_hotkey_sid); buffer.WriteInt32(emu->lower_hotkey_sid);
@@ -2903,7 +2944,7 @@ namespace TOB
buf.WriteString(new_message); buf.WriteString(new_message);
auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf); auto outapp = new EQApplicationPacket(OP_SpecialMesg, std::move(buf));
dest->FastQueuePacket(&outapp, ack_req); dest->FastQueuePacket(&outapp, ack_req);
delete in; delete in;
@@ -2934,14 +2975,14 @@ namespace TOB
return; return;
} }
auto outapp = new EQApplicationPacket(OP_ChangeSize, sizeof(ChangeSize_Struct)); auto outapp = new EQApplicationPacket(OP_ChangeSize, sizeof(structs::ChangeSize_Struct));
ChangeSize_Struct* css = (ChangeSize_Struct*)outapp->pBuffer; structs::ChangeSize_Struct* css = (structs::ChangeSize_Struct*)outapp->pBuffer;
css->EntityID = sas->spawn_id; css->EntityID = sas->spawn_id;
css->Size = (float)sas->parameter; css->Size = (float)sas->parameter;
css->Unknown08 = 0; css->CameraOffset = 0;
css->Unknown12 = 1.0f; css->AnimationSpeedRelated = 1.0f;
dest->FastQueuePacket(&outapp, ack_req); dest->FastQueuePacket(&outapp, ack_req);
delete in; delete in;
@@ -3574,18 +3615,28 @@ namespace TOB
DECODE(OP_BlockedBuffs) DECODE(OP_BlockedBuffs)
{ {
DECODE_LENGTH_EXACT(structs::BlockedBuffs_Struct); uint32 count = __packet->ReadUInt32();
SETUP_DIRECT_DECODE(BlockedBuffs_Struct, structs::BlockedBuffs_Struct); std::vector<int32> blocked_spell_ids;
blocked_spell_ids.reserve(count);
for (int i = 0; i < count; ++i)
blocked_spell_ids.push_back(static_cast<int32>(__packet->ReadUInt32()));
for (uint32 i = 0; i < BLOCKED_BUFF_COUNT; ++i) bool pet = __packet->ReadUInt8() == 1;
emu->SpellID[i] = eq->SpellID[i]; bool init = __packet->ReadUInt8() == 1;
IN(Count); __packet->SetReadPosition(0); // reset the packet read to pass it along
IN(Pet);
IN(Initialise);
IN(Flags);
FINISH_DIRECT_DECODE(); __packet->size = sizeof(BlockedBuffs_Struct);
__packet->pBuffer = new unsigned char[__packet->size]{};
BlockedBuffs_Struct* emu = (BlockedBuffs_Struct*)__packet->pBuffer;
memset(emu->SpellID, -1, sizeof(emu->SpellID));
for (int i = 0; i < count; ++i)
emu->SpellID[i] = blocked_spell_ids[i];
emu->Count = count;
emu->Pet = pet;
emu->Initialise = init;
} }
DECODE(OP_CastSpell) DECODE(OP_CastSpell)
@@ -3601,6 +3652,7 @@ namespace TOB
IN(y_pos); IN(y_pos);
IN(x_pos); IN(x_pos);
IN(z_pos); IN(z_pos);
LogNetcode("C->S OP_CastSpell {}", DumpPacketToString(__packet));
FINISH_DIRECT_DECODE(); FINISH_DIRECT_DECODE();
} }
@@ -3794,6 +3846,35 @@ namespace TOB
DECODE_FORWARD(OP_GroupInvite); DECODE_FORWARD(OP_GroupInvite);
} }
DECODE(OP_MemorizeSpell) {
DECODE_LENGTH_EXACT(structs::MemorizeSpell_Struct);
SETUP_DIRECT_DECODE(MemorizeSpell_Struct, structs::MemorizeSpell_Struct);
// TOB sends status 1 here to let the server know that it's started memming, but doesn't want a response
if (eq->scribing == 1) {
// TODO: There should be a timer set here to detect short-mem cheats, and then checked when the 2 packet is sent
// The previous detection will still happen on scribing == 2, the new client just handles it better
__packet->SetOpcode(OP_Unknown);
return;
}
// in TOB, 2 is "finish memming" so that becomes 1 in emu and 3 is "unmem" which becomes 2
if (eq->scribing == 2)
emu->scribing = 1;
else if (eq->scribing == 3)
emu->scribing = 2;
else if (eq->scribing == 4)
emu->scribing = 3;
else
IN(scribing);
IN(slot);
IN(spell_id);
IN(reduction);
FINISH_DIRECT_DECODE();
}
DECODE(OP_MoveItem) DECODE(OP_MoveItem)
{ {
DECODE_LENGTH_EXACT(structs::MoveItem_Struct); DECODE_LENGTH_EXACT(structs::MoveItem_Struct);
@@ -3817,7 +3898,7 @@ namespace TOB
int r; int r;
for (r = 0; r < 29; r++) { for (r = 0; r < 29; r++) {
// Size 68 in TOB // Size 69 in TOB
IN(filters[r]); IN(filters[r]);
} }
@@ -4121,7 +4202,7 @@ namespace TOB
//s32 Variation; //s32 Variation;
//s32 NewArmorId; //s32 NewArmorId;
//s32 NewArmorType; //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(0); //unsupported atm
buffer.WriteUInt32(item->EliteMaterial); buffer.WriteUInt32(item->EliteMaterial);
buffer.WriteUInt32(item->HerosForgeModel); buffer.WriteUInt32(item->HerosForgeModel);
@@ -4239,9 +4320,20 @@ namespace TOB
//u8 SpellDataSkillMask[78]; //u8 SpellDataSkillMask[78];
for (int j = 0; j < 78; ++j) { 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: /* SpellData:
s32 SpellId; s32 SpellId;
u8 RequiredLevel; u8 RequiredLevel;
@@ -4650,55 +4742,42 @@ namespace TOB
//ItemDefinition Item; //ItemDefinition Item;
SerializeItemDefinition(buffer, item); SerializeItemDefinition(buffer, item);
//u32 RealEstateArrayCount;
// buffer.WriteInt32(0);
//s32 RealEstateArray[RealEstateArrayCount];
//bool bRealEstateItemPlaceable;
// buffer.WriteInt8(0);
//u32 SubContentSize; //u32 SubContentSize;
uint32 subitem_count = 0;
int16 SubSlotNumber = EQ::invbag::SLOT_INVALID; int16 SubSlotNumber = EQ::invbag::SLOT_INVALID;
if (slot_id_in <= EQ::invslot::GENERAL_END && slot_id_in >= EQ::invslot::GENERAL_BEGIN) 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) else if (slot_id_in == EQ::invslot::slotCursor)
SubSlotNumber = EQ::invbag::CURSOR_BAG_BEGIN; SubSlotNumber = EQ::invbag::CURSOR_BAG_BEGIN;
else if (slot_id_in <= EQ::invslot::BANK_END && slot_id_in >= EQ::invslot::BANK_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) 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 else
SubSlotNumber = slot_id_in; // not sure if this is the best way to handle this..leaving for now SubSlotNumber = slot_id_in; // not sure if this is the best way to handle this..leaving for now
if (SubSlotNumber != EQ::invbag::SLOT_INVALID) { if (SubSlotNumber != EQ::invbag::SLOT_INVALID) {
std::vector<std::pair<int, EQ::ItemInstance*>> subitems;
for (uint32 index = EQ::invbag::SLOT_BEGIN; index <= EQ::invbag::SLOT_END; ++index) { for (uint32 index = EQ::invbag::SLOT_BEGIN; index <= EQ::invbag::SLOT_END; ++index) {
EQ::ItemInstance* sub = inst->GetItem(index); EQ::ItemInstance* sub = inst->GetItem(index);
if (!sub) if (sub != nullptr)
continue; subitems.emplace_back(index, sub);
++subitem_count;
} }
buffer.WriteUInt32(subitem_count); buffer.WriteUInt32(subitems.size());
for (uint32 index = EQ::invbag::SLOT_BEGIN; index <= EQ::invbag::SLOT_END; ++index) {
EQ::ItemInstance* sub = inst->GetItem(index);
if (!sub)
continue;
// 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); buffer.WriteUInt32(index);
SerializeItem(buffer, subitem, SubSlotNumber, depth + 1, packet_type);
SerializeItem(buffer, sub, SubSlotNumber, (depth + 1), packet_type);
} }
} } else
buffer.WriteUInt32(0); // no subitems, client needs to know that
//bool bCollected; //bool bCollected;
buffer.WriteInt8(0); //unsupported atm buffer.WriteInt8(0); //unsupported atm
//u64 DontKnow; //u64 DontKnow;
buffer.WriteUInt64(0); //unsupported atm buffer.WriteInt64(0); //unsupported atm
//s32 Luck; //s32 Luck;
buffer.WriteInt32(0); //unsupported atm buffer.WriteInt32(0); //unsupported atm
} }
@@ -5019,8 +5098,8 @@ namespace TOB
TOBSlot.Slot = server_slot - EQ::invslot::WORLD_BEGIN; TOBSlot.Slot = server_slot - EQ::invslot::WORLD_BEGIN;
} }
Log(Logs::Detail, Logs::Netcode, "Convert Server Slot %i to TOB Slot [%i, %i, %i, %i]", Log(Logs::Detail, Logs::Netcode, fmt::format("Convert Server Slot {} to TOB Slot [{}, {}, {}, {}]",
server_slot, TOBSlot.Type, TOBSlot.Slot, TOBSlot.SubIndex, TOBSlot.AugIndex); server_slot, TOBSlot.Type, TOBSlot.Slot, TOBSlot.SubIndex, TOBSlot.AugIndex).c_str());
return TOBSlot; return TOBSlot;
} }
@@ -5036,8 +5115,8 @@ namespace TOB
if (TOBSlot.Slot != invslot::SLOT_INVALID) if (TOBSlot.Slot != invslot::SLOT_INVALID)
TOBSlot.Type = invtype::typeCorpse; TOBSlot.Type = invtype::typeCorpse;
Log(Logs::Detail, Logs::Netcode, "Convert Server Corpse Slot %i to TOB Corpse Slot [%i, %i, %i, %i]", Log(Logs::Detail, Logs::Netcode, fmt::format("Convert Server Corpse Slot {} to TOB Corpse Slot [{}, {}, {}, {}]",
server_corpse_slot, TOBSlot.Type, TOBSlot.Slot, TOBSlot.SubIndex, TOBSlot.AugIndex); server_corpse_slot, TOBSlot.Type, TOBSlot.Slot, TOBSlot.SubIndex, TOBSlot.AugIndex).c_str());
return TOBSlot; return TOBSlot;
} }
@@ -5077,8 +5156,8 @@ namespace TOB
} }
} }
Log(Logs::Detail, Logs::Netcode, "Convert Server Slot %i to TOB Typeless Slot [%i, %i, %i] (implied type: %i)", Log(Logs::Detail, Logs::Netcode, fmt::format("Convert Server Slot {} to TOB Typeless Slot [{}, {}, {}] (implied type: {})",
server_slot, TOBSlot.Slot, TOBSlot.SubIndex, TOBSlot.AugIndex, server_type); server_slot, TOBSlot.Slot, TOBSlot.SubIndex, TOBSlot.AugIndex, server_type).c_str());
return TOBSlot; return TOBSlot;
} }
@@ -5086,8 +5165,8 @@ namespace TOB
static inline uint32 TOBToServerSlot(structs::InventorySlot_Struct tob_slot) static inline uint32 TOBToServerSlot(structs::InventorySlot_Struct tob_slot)
{ {
if (tob_slot.AugIndex < invaug::SOCKET_INVALID || tob_slot.AugIndex >= invaug::SOCKET_COUNT) { if (tob_slot.AugIndex < invaug::SOCKET_INVALID || tob_slot.AugIndex >= invaug::SOCKET_COUNT) {
Log(Logs::Detail, Logs::Netcode, "Convert TOB Slot [%i, %i, %i, %i] to Server Slot %i", Log(Logs::Detail, Logs::Netcode, fmt::format("Convert TOB Slot [{}, {}, {}, {}] to Server Slot {}",
tob_slot.Type, tob_slot.Slot, tob_slot.SubIndex, tob_slot.AugIndex, EQ::invslot::SLOT_INVALID); tob_slot.Type, tob_slot.Slot, tob_slot.SubIndex, tob_slot.AugIndex, EQ::invslot::SLOT_INVALID).c_str());
return EQ::invslot::SLOT_INVALID; return EQ::invslot::SLOT_INVALID;
} }
@@ -5209,8 +5288,8 @@ namespace TOB
} }
} }
Log(Logs::Detail, Logs::Netcode, "Convert TOB Slot [%i, %i, %i, %i] to Server Slot %i", Log(Logs::Detail, Logs::Netcode, fmt::format("Convert TOB Slot [{}, {}, {}, {}] to Server Slot {}",
tob_slot.Type, tob_slot.Slot, tob_slot.SubIndex, tob_slot.AugIndex, server_slot); tob_slot.Type, tob_slot.Slot, tob_slot.SubIndex, tob_slot.AugIndex, server_slot).c_str());
return server_slot; return server_slot;
} }
@@ -5227,8 +5306,8 @@ namespace TOB
ServerSlot = TOBToServerCorpseMainSlot(tob_corpse_slot.Slot); ServerSlot = TOBToServerCorpseMainSlot(tob_corpse_slot.Slot);
} }
Log(Logs::Detail, Logs::Netcode, "Convert TOB Slot [%i, %i, %i, %i] to Server Slot %i", Log(Logs::Detail, Logs::Netcode, fmt::format("Convert TOB Slot [{}, {}, {}, {}] to Server Slot {}",
tob_corpse_slot.Type, tob_corpse_slot.Slot, tob_corpse_slot.SubIndex, tob_corpse_slot.AugIndex, ServerSlot); tob_corpse_slot.Type, tob_corpse_slot.Slot, tob_corpse_slot.SubIndex, tob_corpse_slot.AugIndex, ServerSlot).c_str());
return ServerSlot; return ServerSlot;
} }
@@ -5249,8 +5328,8 @@ namespace TOB
static inline uint32 TOBToServerTypelessSlot(structs::TypelessInventorySlot_Struct tob_slot, int16 tob_type) static inline uint32 TOBToServerTypelessSlot(structs::TypelessInventorySlot_Struct tob_slot, int16 tob_type)
{ {
if (tob_slot.AugIndex < invaug::SOCKET_INVALID || tob_slot.AugIndex >= invaug::SOCKET_COUNT) { if (tob_slot.AugIndex < invaug::SOCKET_INVALID || tob_slot.AugIndex >= invaug::SOCKET_COUNT) {
Log(Logs::Detail, Logs::Netcode, "Convert TOB Typeless Slot [%i, %i, %i] (implied type: %i) to Server Slot %i", Log(Logs::Detail, Logs::Netcode, fmt::format("Convert TOB Typeless Slot [{}, {}, {}] (implied type: {}) to Server Slot {}",
tob_slot.Slot, tob_slot.SubIndex, tob_slot.AugIndex, tob_type, EQ::invslot::SLOT_INVALID); tob_slot.Slot, tob_slot.SubIndex, tob_slot.AugIndex, tob_type, EQ::invslot::SLOT_INVALID).c_str());
return EQ::invslot::SLOT_INVALID; return EQ::invslot::SLOT_INVALID;
} }
@@ -5363,8 +5442,8 @@ namespace TOB
} }
} }
Log(Logs::Detail, Logs::Netcode, "Convert TOB Typeless Slot [%i, %i, %i] (implied type: %i) to Server Slot %i", Log(Logs::Detail, Logs::Netcode, fmt::format("Convert TOB Typeless Slot [{}, {}, {}] (implied type: {}) to Server Slot {}",
tob_slot.Slot, tob_slot.SubIndex, tob_slot.AugIndex, tob_type, ServerSlot); tob_slot.Slot, tob_slot.SubIndex, tob_slot.AugIndex, tob_type, ServerSlot).c_str());
return ServerSlot; return ServerSlot;
} }
+2 -2
View File
@@ -200,8 +200,8 @@ namespace TOB
const int16 SLOT_INVALID = IINVALID; const int16 SLOT_INVALID = IINVALID;
const int16 SLOT_BEGIN = INULL; const int16 SLOT_BEGIN = INULL;
const int16 SLOT_END = 9; //254; const int16 SLOT_END = 199;
const int16 SLOT_COUNT = 10; //255; // server Size will be 255..unsure what actual client is (test) const int16 SLOT_COUNT = 200; // server Size will be 200..unsure what actual client is (test)
const char* GetInvBagIndexName(int16 bag_index); const char* GetInvBagIndexName(int16 bag_index);
+3
View File
@@ -1,4 +1,5 @@
//list of packets we need to encode on the way out: //list of packets we need to encode on the way out:
E(OP_AAExpUpdate)
E(OP_Action) E(OP_Action)
E(OP_Animation) E(OP_Animation)
E(OP_ApplyPoison) E(OP_ApplyPoison)
@@ -32,6 +33,7 @@ E(OP_Illusion)
E(OP_ItemPacket) E(OP_ItemPacket)
E(OP_LogServer) E(OP_LogServer)
E(OP_ManaChange) E(OP_ManaChange)
E(OP_MemorizeSpell)
E(OP_MobHealth) E(OP_MobHealth)
E(OP_MoneyOnCorpse) E(OP_MoneyOnCorpse)
E(OP_MoveItem) E(OP_MoveItem)
@@ -83,6 +85,7 @@ D(OP_GMTraining)
D(OP_GroupDisband) D(OP_GroupDisband)
D(OP_GroupInvite) D(OP_GroupInvite)
D(OP_GroupInvite2) D(OP_GroupInvite2)
D(OP_MemorizeSpell)
D(OP_MoveItem) D(OP_MoveItem)
D(OP_RemoveBlockedBuffs) D(OP_RemoveBlockedBuffs)
D(OP_SetServerFilter) D(OP_SetServerFilter)
+35 -33
View File
@@ -12,7 +12,7 @@ namespace TOB {
static const uint32 MAX_PP_UNKNOWN_ABILITIES = 25; static const uint32 MAX_PP_UNKNOWN_ABILITIES = 25;
static const uint32 MAX_RECAST_TYPES = 25; static const uint32 MAX_RECAST_TYPES = 25;
static const uint32 MAX_ITEM_RECAST_TYPES = 100; static const uint32 MAX_ITEM_RECAST_TYPES = 100;
static const uint32 BLOCKED_BUFF_COUNT = 40; static const uint32 BLOCKED_BUFF_COUNT = 60; // this might not be needed?
static const uint32 BUFF_COUNT = 62; static const uint32 BUFF_COUNT = 62;
static const uint32 MAX_PP_LANGUAGE = 32; static const uint32 MAX_PP_LANGUAGE = 32;
#pragma pack(1) #pragma pack(1)
@@ -274,7 +274,6 @@ namespace TOB {
Unknown39, Unknown39,
Unknown40, Unknown40,
Unknown41, Unknown41,
Unknown42,
Birthdate, Birthdate,
EncounterLock EncounterLock
}; };
@@ -288,6 +287,15 @@ namespace TOB {
/*0024*/ /*0024*/
}; };
struct ChangeSize_Struct
{
/*00*/ uint32 EntityID;
/*04*/ float Size;
/*08*/ float CameraOffset;
/*12*/ float AnimationSpeedRelated;
/*16*/
};
struct Spawn_Struct_Bitfields struct Spawn_Struct_Bitfields
{ {
union { union {
@@ -400,14 +408,14 @@ namespace TOB {
/*056*/ float X; /*056*/ float X;
/*060*/ float Z; /*060*/ float Z;
/*064*/ float Heading; /*064*/ float Heading;
/*068*/ float DoorAngle; //not sure if this is actually a float; it might be a uint32 like DefaultDoorAngle /*068*/ float DoorAngle;
/*072*/ uint32 ScaleFactor; //rof2's size /*072*/ uint32 ScaleFactor; //rof2's size
/*076*/ uint32 Unknown76; //client doesn't seem to read this /*076*/ uint32 Unknown76; //client doesn't seem to read this
/*080*/ uint8 Id; //doorid /*080*/ uint8 Id; //doorid
/*081*/ uint8 Type; //opentype /*081*/ uint8 Type; //opentype
/*082*/ uint8 State; //state_at_spawn /*082*/ uint8 State; //state_at_spawn
/*083*/ uint8 DefaultState; //invert_state /*083*/ uint8 DefaultState; //invert_state
/*084*/ int32 Param; //door_param /*084*/ int32 Param; //door_param (spell id?)
/*088*/ uint32 AdventureDoorId; /*088*/ uint32 AdventureDoorId;
/*092*/ uint32 DynDoorID; /*092*/ uint32 DynDoorID;
/*096*/ uint32 RealEstateDoorID; /*096*/ uint32 RealEstateDoorID;
@@ -495,8 +503,8 @@ namespace TOB {
struct ExpUpdate_Struct struct ExpUpdate_Struct
{ {
/*000*/ uint64 exp; //This is exp % / 1000 now; eg 69250 = 69.25% /*000*/ uint64 exp; // This is exp % / 1000 now; eg 69250 = 69.25%
/*008*/ uint64 unknown; //unclear, I didn't see the client actually read this value but i might have missed it /*008*/ uint64 unknown; // if this is the value "2", it opens up the tip window
}; };
struct DeleteSpawn_Struct struct DeleteSpawn_Struct
@@ -508,15 +516,14 @@ namespace TOB {
//OP_SetServerFilter //OP_SetServerFilter
struct SetServerFilter_Struct { struct SetServerFilter_Struct {
uint32 filters[68]; uint32 filters[69];
}; };
// Was new to RoF2, doesn't look changed // Was new to RoF2, doesn't look changed
// The padding is because these structs are padded to the default 4 bytes // The padding is because these structs are padded to the default 4 bytes
struct InventorySlot_Struct struct InventorySlot_Struct
{ {
/*000*/ int16 Type; /*000*/ int32 Type;
/*002*/ int16 Padding1;
/*004*/ int16 Slot; /*004*/ int16 Slot;
/*006*/ int16 SubIndex; /*006*/ int16 SubIndex;
/*008*/ int16 AugIndex; /*008*/ int16 AugIndex;
@@ -549,15 +556,6 @@ namespace TOB {
/*024*/ /*024*/
}; };
struct ChangeSize_Struct
{
/*00*/ uint32 EntityID;
/*04*/ float Size;
/*08*/ uint32 Unknown08; // Observed 0
/*12*/ float Unknown12; // Observed 1.0f
/*16*/
};
struct SpawnHPUpdate_Struct struct SpawnHPUpdate_Struct
{ {
/*00*/ int16 spawn_id; /*00*/ int16 spawn_id;
@@ -677,11 +675,18 @@ namespace TOB {
/*000*/ uint32 spell_id; /*000*/ uint32 spell_id;
/*004*/ uint16 caster_id; /*004*/ uint16 caster_id;
/*006*/ uint32 cast_time; // in miliseconds /*006*/ uint32 cast_time; // in miliseconds
/*010*/ uint32 unknown0a; // I think this is caster effective level but im not sure. live always sends 0 /*010*/ uint32 unknown0a; // I think this is caster effective level but im not sure. live always sends 0. The client uses this for the spell link
/*014*/ uint8 unknown0e; // 0 will short circuit the cast, seen 1 from live usually, maybe related to interrupts or particles or something /*014*/ uint8 unknown0e; // 0 will short circuit the cast, seen 1 from live usually, maybe related to interrupts or particles or something
/*015*/ /*015*/
}; };
struct MemorizeSpell_Struct {
uint32 slot; // Spot in the spell book/memorized slot
uint32 spell_id; // Spell id (200 or c8 is minor healing, etc)
uint32 scribing; // -1 refreshes book, 0 scribe to book, 2 end mem, 1 start mem, 3 unmem, 4 set activated item keyring -- client will send back 2 if a 0 operation updated a memorized spell of the same group + subgroup
uint32 reduction; // lower reuse (only used if scribing is 4)
};
//I've observed 5 s16 that are all -1. //I've observed 5 s16 that are all -1.
//Clicky items don't even trigger this as far as i can tell so not sure what this is for now. //Clicky items don't even trigger this as far as i can tell so not sure what this is for now.
//One of these could have changed to a s32 but im not sure. //One of these could have changed to a s32 but im not sure.
@@ -708,6 +713,13 @@ namespace TOB {
/*39*/ /*39*/
}; };
struct InterruptCast_Struct
{
uint32 spawnid;
uint32 messageid;
char message[0];
};
struct EQAffectSlot_Struct { struct EQAffectSlot_Struct {
/*00*/ int32 slot; /*00*/ int32 slot;
/*04*/ int32 padding; /*04*/ int32 padding;
@@ -749,10 +761,10 @@ namespace TOB {
struct ManaChange_Struct struct ManaChange_Struct
{ {
uint32 new_mana; uint32 new_mana;
uint32 stamina; uint32 stamina; // endurance
uint32 spell_id; uint32 spell_id;
uint32 keepcasting; uint32 keepcasting;
int32 slot; int32 slot; // gem slot
}; };
//This is what we call OP_Action //This is what we call OP_Action
@@ -819,9 +831,8 @@ namespace TOB {
struct AA_Array struct AA_Array
{ {
uint32 AA; uint32 AA;
uint32 value; uint32 value; // points spent
uint32 charges; // expendable charges uint32 charges; // expendable charges
bool bUnknown0x0c; // added test winter 2024; removed sometime in summer 2024
}; };
struct AATable_Struct { struct AATable_Struct {
@@ -834,16 +845,7 @@ namespace TOB {
/*000*/ uint32 experience; /*000*/ uint32 experience;
/*004*/ uint32 unspent; /*004*/ uint32 unspent;
/*008*/ uint8 percentage; /*008*/ uint8 percentage;
/*009*/ uint8 unknown009[3]; /*009*/ uint8 padding[3];
};
struct BlockedBuffs_Struct
{
/*000*/ int32 SpellID[BLOCKED_BUFF_COUNT];
/*120*/ uint32 Count;
/*124*/ uint8 Pet;
/*125*/ uint8 Initialise;
/*126*/ uint16 Flags;
}; };
struct ZonePlayerToBind_Struct { struct ZonePlayerToBind_Struct {
+1 -1
View File
@@ -349,7 +349,7 @@ RULE_STRING(World, MOTD, "", "Server MOTD sent on login, change from empty to ha
RULE_STRING(World, Rules, "", "Server Rules, change from empty to have this be used instead of variables table 'rules' value, lines are pipe (|) separated, example: A|B|C") RULE_STRING(World, Rules, "", "Server Rules, change from empty to have this be used instead of variables table 'rules' value, lines are pipe (|) separated, example: A|B|C")
RULE_BOOL(World, EnableAutoLogin, false, "Enables or disables auto login of characters, allowing people to log characters in directly from loginserver to ingame") RULE_BOOL(World, EnableAutoLogin, false, "Enables or disables auto login of characters, allowing people to log characters in directly from loginserver to ingame")
RULE_BOOL(World, EnablePVPRegions, true, "Enables or disables PVP Regions automatically setting your PVP flag") RULE_BOOL(World, EnablePVPRegions, true, "Enables or disables PVP Regions automatically setting your PVP flag")
RULE_STRING(World, SupportedClients, "RoF2", "Comma-delimited list of clients to restrict to. Supported values are Titanium | SoF | SoD | UF | RoF | RoF2. Example: Titanium,RoF2") RULE_STRING(World, SupportedClients, "RoF2,TOB", "Comma-delimited list of clients to restrict to. Supported values are Titanium | SoF | SoD | UF | RoF | RoF2 | TOB. Example: Titanium,RoF2,TOB")
RULE_STRING(World, CustomFilesKey, "", "Enable if the server requires custom files and sends a key to validate. Empty string to disable. Example: eqcustom_v1") RULE_STRING(World, CustomFilesKey, "", "Enable if the server requires custom files and sends a key to validate. Empty string to disable. Example: eqcustom_v1")
RULE_STRING(World, CustomFilesUrl, "github.com/knervous/eqnexus/releases", "URL to display at character select if client is missing custom files") RULE_STRING(World, CustomFilesUrl, "github.com/knervous/eqnexus/releases", "URL to display at character select if client is missing custom files")
RULE_INT(World, CustomFilesAdminLevel, 20, "Admin level at which custom file key is not required when CustomFilesKey is specified") RULE_INT(World, CustomFilesAdminLevel, 20, "Admin level at which custom file key is not required when CustomFilesKey is specified")
+39 -39
View File
@@ -9,7 +9,7 @@ Below is a status list for the 450 opcodes we currently use on the server for th
| Opcode | Status | Notes | Working On | | Opcode | Status | Notes | Working On |
|:----------------------------------|:--------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------| |:----------------------------------|:--------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------|
| `OP_AAAction` | 🟡 Unverified | | | | `OP_AAAction` | 🟡 Unverified | | |
| `OP_AAExpUpdate` | 🟡 Unverified | | | | `OP_AAExpUpdate` | 🟢 Verified | | |
| `OP_AcceptNewTask` | 🔴 Not-Set | | | | `OP_AcceptNewTask` | 🔴 Not-Set | | |
| `OP_AckPacket` | 🟢 Verified | | | | `OP_AckPacket` | 🟢 Verified | | |
| `OP_Action` | 🟡 Unverified | | | | `OP_Action` | 🟡 Unverified | | |
@@ -63,9 +63,9 @@ Below is a status list for the 450 opcodes we currently use on the server for th
| `OP_BecomeCorpse` | 🔴 Not-Set | | | | `OP_BecomeCorpse` | 🔴 Not-Set | | |
| `OP_BecomeTrader` | 🔴 Not-Set | | | | `OP_BecomeTrader` | 🔴 Not-Set | | |
| `OP_Begging` | 🟡 Unverified | | | | `OP_Begging` | 🟡 Unverified | | |
| `OP_BeginCast` | 🟡 Unverified | | | | `OP_BeginCast` | 🟢 Verified | | |
| `OP_Bind_Wound` | 🟡 Unverified | | | | `OP_Bind_Wound` | 🟡 Unverified | | |
| `OP_BlockedBuffs` | 🟡 Unverified | | | | `OP_BlockedBuffs` | 🟢 Verified | | |
| `OP_BoardBoat` | 🟡 Unverified | | | | `OP_BoardBoat` | 🟡 Unverified | | |
| `OP_BookButton` | 🟡 Unverified | | | | `OP_BookButton` | 🟡 Unverified | | |
| `OP_Buff` | 🟡 Unverified | | | | `OP_Buff` | 🟡 Unverified | | |
@@ -79,17 +79,17 @@ Below is a status list for the 450 opcodes we currently use on the server for th
| `OP_CancelTask` | 🔴 Not-Set | | | | `OP_CancelTask` | 🔴 Not-Set | | |
| `OP_CancelTrade` | 🟡 Unverified | | | | `OP_CancelTrade` | 🟡 Unverified | | |
| `OP_CashReward` | 🟡 Unverified | | | | `OP_CashReward` | 🟡 Unverified | | |
| `OP_CastSpell` | 🟡 Unverified | | | | `OP_CastSpell` | 🟢 Verified | | |
| `OP_ChangeSize` | 🟡 Unverified | | | | `OP_ChangeSize` | 🟢 Verified | | |
| `OP_ChannelMessage` | 🟡 Unverified | | | | `OP_ChannelMessage` | 🟡 Unverified | | |
| `OP_ChangePetName` | 🔴 Not-Set | | | | `OP_ChangePetName` | 🔴 Not-Set | | |
| `OP_CharacterCreate` | 🟢 Verified | Sends heroic type, can be used for something? | | | `OP_CharacterCreate` | 🟢 Verified | Sends heroic type, can be used for something? | |
| `OP_CharacterCreateRequest` | 🟢 Verified | | | | `OP_CharacterCreateRequest` | 🟢 Verified | | |
| `OP_CharInventory` | 🟡 Unverified | | | | `OP_CharInventory` | 🟢 Verified | | |
| `OP_Charm` | 🟡 Unverified | | | | `OP_Charm` | 🟡 Unverified | | |
| `OP_ChatMessage` | 🔴 Not-Set | | | | `OP_ChatMessage` | 🔴 Not-Set | | |
| `OP_ClearAA` | 🟡 Unverified | | | | `OP_ClearAA` | 🟢 Verified | | |
| `OP_ClearBlockedBuffs` | 🟡 Unverified | | | | `OP_ClearBlockedBuffs` | 🟢 Verified | | |
| `OP_ClearLeadershipAbilities` | 🔴 Not-Set | | | | `OP_ClearLeadershipAbilities` | 🔴 Not-Set | | |
| `OP_ClearNPCMarks` | 🔴 Not-Set | | | | `OP_ClearNPCMarks` | 🔴 Not-Set | | |
| `OP_ClearObject` | 🟡 Unverified | | | | `OP_ClearObject` | 🟡 Unverified | | |
@@ -98,20 +98,20 @@ Below is a status list for the 450 opcodes we currently use on the server for th
| `OP_ClickObject` | 🟡 Unverified | | | | `OP_ClickObject` | 🟡 Unverified | | |
| `OP_ClickObjectAction` | 🟡 Unverified | | | | `OP_ClickObjectAction` | 🟡 Unverified | | |
| `OP_ClientError` | 🔴 Not-Set | | | | `OP_ClientError` | 🔴 Not-Set | | |
| `OP_ClientReady` | 🟡 Unverified | | | | `OP_ClientReady` | 🟢 Verified | | |
| `OP_ClientTimeStamp` | 🔴 Not-Set | | | | `OP_ClientTimeStamp` | 🔴 Not-Set | | |
| `OP_ClientUpdate` | 🟡 Unverified | | | | `OP_ClientUpdate` | 🟢 Verified | | |
| `OP_CloseContainer` | 🔴 Not-Set | | | | `OP_CloseContainer` | 🔴 Not-Set | | |
| `OP_CloseTributeMaster` | 🔴 Not-Set | | | | `OP_CloseTributeMaster` | 🔴 Not-Set | | |
| `OP_ColoredText` | 🟡 Unverified | | | | `OP_ColoredText` | 🟢 Verified | | |
| `OP_CombatAbility` | 🟡 Unverified | | | | `OP_CombatAbility` | 🟡 Unverified | | |
| `OP_Command` | 🔴 Not-Set | | | | `OP_Command` | 🔴 Not-Set | | |
| `OP_CompletedTasks` | 🔴 Not-Set | | | | `OP_CompletedTasks` | 🔴 Not-Set | | |
| `OP_ConfirmDelete` | 🟡 Unverified | | | | `OP_ConfirmDelete` | 🟡 Unverified | | |
| `OP_Consent` | 🟡 Unverified | | | | `OP_Consent` | 🟡 Unverified | | |
| `OP_ConsentDeny` | 🟡 Unverified | | | | `OP_ConsentDeny` | 🟡 Unverified | | |
| `OP_ConsentResponse` | 🟡 Unverified | | | | `OP_ConsentResponse` | 🟢 Verified | | |
| `OP_Consider` | 🟡 Unverified | | | | `OP_Consider` | 🟢 Verified | | |
| `OP_ConsiderCorpse` | 🟡 Unverified | | | | `OP_ConsiderCorpse` | 🟡 Unverified | | |
| `OP_Consume` | 🟡 Unverified | | | | `OP_Consume` | 🟡 Unverified | | |
| `OP_ControlBoat` | 🟡 Unverified | | | | `OP_ControlBoat` | 🟡 Unverified | | |
@@ -170,7 +170,7 @@ Below is a status list for the 450 opcodes we currently use on the server for th
| `OP_EnvDamage` | 🟡 Unverified | | | | `OP_EnvDamage` | 🟡 Unverified | | |
| `OP_EvolveItem` | 🔴 Not-Set | | | | `OP_EvolveItem` | 🔴 Not-Set | | |
| `OP_ExpansionInfo` | 🟢 Verified | Updated from u32 to u64 and works now | | | `OP_ExpansionInfo` | 🟢 Verified | Updated from u32 to u64 and works now | |
| `OP_ExpUpdate` | 🟡 Unverified | | | | `OP_ExpUpdate` | 🟢 Verified | | |
| `OP_FaceChange` | 🔴 Not-Set | | | | `OP_FaceChange` | 🔴 Not-Set | | |
| `OP_Feedback` | 🔴 Not-Set | | | | `OP_Feedback` | 🔴 Not-Set | | |
| `OP_FeignDeath` | 🟡 Unverified | | | | `OP_FeignDeath` | 🟡 Unverified | | |
@@ -182,10 +182,10 @@ Below is a status list for the 450 opcodes we currently use on the server for th
| `OP_FinishWindow2` | 🟡 Unverified | | | | `OP_FinishWindow2` | 🟡 Unverified | | |
| `OP_Fishing` | 🟡 Unverified | | | | `OP_Fishing` | 🟡 Unverified | | |
| `OP_Fling` | 🟡 Unverified | | | | `OP_Fling` | 🟡 Unverified | | |
| `OP_FloatListThing` | 🟢 Verified | Movement History. Sent from client, but emu doesn't use it so setting it as varified. Reference is 0x1402FFAD0 | | | `OP_FloatListThing` | 🟢 Verified | Movement History. Sent from client, but emu doesn't use it so setting it as verified. Reference is 0x1402FFAD0 | |
| `OP_Forage` | 🟡 Unverified | | | | `OP_Forage` | 🟡 Unverified | | |
| `OP_ForceFindPerson` | 🔴 Not-Set | | | | `OP_ForceFindPerson` | 🔴 Not-Set | | |
| `OP_FormattedMessage` | 🟡 Unverified | | | | `OP_FormattedMessage` | 🟢 Verified | Some major work to do here -- the client now expects a spell link in the packet, will need to refactor the client work to decouple the stream from the internal representation | |
| `OP_FriendsWho` | 🟡 Unverified | | | | `OP_FriendsWho` | 🟡 Unverified | | |
| `OP_GetGuildMOTD` | 🔴 Not-Set | | | | `OP_GetGuildMOTD` | 🔴 Not-Set | | |
| `OP_GetGuildMOTDReply` | 🔴 Not-Set | | | | `OP_GetGuildMOTDReply` | 🔴 Not-Set | | |
@@ -213,7 +213,7 @@ Below is a status list for the 450 opcodes we currently use on the server for th
| `OP_GMTrainSkillConfirm` | 🟡 Unverified | | | | `OP_GMTrainSkillConfirm` | 🟡 Unverified | | |
| `OP_GMZoneRequest` | 🔴 Not-Set | | | | `OP_GMZoneRequest` | 🔴 Not-Set | | |
| `OP_GMZoneRequest2` | 🔴 Not-Set | | | | `OP_GMZoneRequest2` | 🔴 Not-Set | | |
| `OP_GroundSpawn` | 🟡 Unverified | | | | `OP_GroundSpawn` | 🟢 Verified | | |
| `OP_GroupAcknowledge` | 🔴 Not-Set | | | | `OP_GroupAcknowledge` | 🔴 Not-Set | | |
| `OP_GroupCancelInvite` | 🔴 Not-Set | | | | `OP_GroupCancelInvite` | 🔴 Not-Set | | |
| `OP_GroupDelete` | 🔴 Not-Set | | | | `OP_GroupDelete` | 🔴 Not-Set | | |
@@ -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_Heartbeat` | 🔴 Not-Set | | |
| `OP_Hide` | 🟡 Unverified | | | | `OP_Hide` | 🟡 Unverified | | |
| `OP_HideCorpse` | 🟡 Unverified | | | | `OP_HideCorpse` | 🟡 Unverified | | |
| `OP_HPUpdate` | 🟡 Unverified | | | | `OP_HPUpdate` | 🟢 Verified | | |
| `OP_Illusion` | 🟡 Unverified | | | | `OP_Illusion` | 🟡 Unverified | | |
| `OP_IncreaseStats` | 🟡 Unverified | | | | `OP_IncreaseStats` | 🟡 Unverified | | |
| `OP_InitialHPUpdate` | 🔴 Not-Set | | | | `OP_InitialHPUpdate` | 🔴 Not-Set | | |
@@ -289,7 +289,7 @@ Below is a status list for the 450 opcodes we currently use on the server for th
| `OP_InspectMessageUpdate` | 🔴 Not-Set | | | | `OP_InspectMessageUpdate` | 🔴 Not-Set | | |
| `OP_InspectRequest` | 🔴 Not-Set | | | | `OP_InspectRequest` | 🔴 Not-Set | | |
| `OP_InstillDoubt` | 🟡 Unverified | | | | `OP_InstillDoubt` | 🟡 Unverified | | |
| `OP_InterruptCast` | 🟡 Unverified | | | | `OP_InterruptCast` | 🟢 Verified | Some major work to do here -- the client now expects a spell link in the packet, will need to refactor the client work to decouple the stream from the internal representation | |
| `OP_InvokeChangePetName` | 🔴 Not-Set | | | | `OP_InvokeChangePetName` | 🔴 Not-Set | | |
| `OP_InvokeChangePetNameImmediate` | 🔴 Not-Set | | | | `OP_InvokeChangePetNameImmediate` | 🔴 Not-Set | | |
| `OP_InvokeNameChangeImmediate` | 🔴 Not-Set | | | | `OP_InvokeNameChangeImmediate` | 🔴 Not-Set | | |
@@ -330,7 +330,7 @@ Below is a status list for the 450 opcodes we currently use on the server for th
| `OP_LFPCommand` | 🔴 Not-Set | | | | `OP_LFPCommand` | 🔴 Not-Set | | |
| `OP_LFPGetMatchesRequest` | 🔴 Not-Set | | | | `OP_LFPGetMatchesRequest` | 🔴 Not-Set | | |
| `OP_LFPGetMatchesResponse` | 🔴 Not-Set | | | | `OP_LFPGetMatchesResponse` | 🔴 Not-Set | | |
| `OP_LinkedReuse` | 🟡 Unverified | | | | `OP_LinkedReuse` | 🟢 Verified | | |
| `OP_LoadSpellSet` | 🔴 Not-Set | | | | `OP_LoadSpellSet` | 🔴 Not-Set | | |
| `OP_LocInfo` | 🔴 Not-Set | | | | `OP_LocInfo` | 🔴 Not-Set | | |
| `OP_LockoutTimerInfo` | 🔴 Not-Set | | | | `OP_LockoutTimerInfo` | 🔴 Not-Set | | |
@@ -346,12 +346,12 @@ Below is a status list for the 450 opcodes we currently use on the server for th
| `OP_LootComplete` | 🟡 Unverified | | | | `OP_LootComplete` | 🟡 Unverified | | |
| `OP_LootItem` | 🟡 Unverified | | | | `OP_LootItem` | 🟡 Unverified | | |
| `OP_LootRequest` | 🟡 Unverified | | | | `OP_LootRequest` | 🟡 Unverified | | |
| `OP_ManaChange` | 🟡 Unverified | | | | `OP_ManaChange` | 🟢 Verified | | |
| `OP_ManaUpdate` | 🔴 Not-Set | | | | `OP_ManaUpdate` | 🔴 Not-Set | | |
| `OP_MarkNPC` | 🔴 Not-Set | | | | `OP_MarkNPC` | 🔴 Not-Set | | |
| `OP_MarkRaidNPC` | 🔴 Not-Set | | | | `OP_MarkRaidNPC` | 🔴 Not-Set | | |
| `OP_Marquee` | 🟡 Unverified | | | | `OP_Marquee` | 🟡 Unverified | | |
| `OP_MemorizeSpell` | 🟡 Unverified | | | | `OP_MemorizeSpell` | 🟢 Verified | | |
| `OP_Mend` | 🟡 Unverified | | | | `OP_Mend` | 🟡 Unverified | | |
| `OP_MendHPUpdate` | 🔴 Not-Set | | | | `OP_MendHPUpdate` | 🔴 Not-Set | | |
| `OP_MercenaryAssign` | 🔴 Not-Set | | | | `OP_MercenaryAssign` | 🔴 Not-Set | | |
@@ -379,7 +379,7 @@ Below is a status list for the 450 opcodes we currently use on the server for th
| `OP_MOTD` | 🟢 Verified | | | | `OP_MOTD` | 🟢 Verified | | |
| `OP_MoveCoin` | 🟡 Unverified | | | | `OP_MoveCoin` | 🟡 Unverified | | |
| `OP_MoveDoor` | 🟡 Unverified | | | | `OP_MoveDoor` | 🟡 Unverified | | |
| `OP_MoveItem` | 🟡 Unverified | | | | `OP_MoveItem` | 🟢 Verified | | |
| `OP_MoveMultipleItems` | 🟡 Unverified | | | | `OP_MoveMultipleItems` | 🟡 Unverified | | |
| `OP_MoveLogDisregard` | 🔴 Not-Set | | | | `OP_MoveLogDisregard` | 🔴 Not-Set | | |
| `OP_MoveLogRequest` | 🔴 Not-Set | | | | `OP_MoveLogRequest` | 🔴 Not-Set | | |
@@ -435,7 +435,7 @@ Below is a status list for the 450 opcodes we currently use on the server for th
| `OP_PVPLeaderBoardRequest` | 🔴 Not-Set | | | | `OP_PVPLeaderBoardRequest` | 🔴 Not-Set | | |
| `OP_PVPStats` | 🔴 Not-Set | | | | `OP_PVPStats` | 🔴 Not-Set | | |
| `OP_QueryResponseThing` | 🔴 Not-Set | | | | `OP_QueryResponseThing` | 🔴 Not-Set | | |
| `OP_QueryUCSServerStatus` | 🟡 Unverified | | | | `OP_QueryUCSServerStatus` | 🟢 Verified | | |
| `OP_RaidDelegateAbility` | 🔴 Not-Set | | | | `OP_RaidDelegateAbility` | 🔴 Not-Set | | |
| `OP_RaidClearNPCMarks` | 🔴 Not-Set | | | | `OP_RaidClearNPCMarks` | 🔴 Not-Set | | |
| `OP_RaidInvite` | 🔴 Not-Set | | | | `OP_RaidInvite` | 🔴 Not-Set | | |
@@ -453,7 +453,7 @@ Below is a status list for the 450 opcodes we currently use on the server for th
| `OP_ReclaimCrystals` | 🔴 Not-Set | | | | `OP_ReclaimCrystals` | 🔴 Not-Set | | |
| `OP_ReloadUI` | 🔴 Not-Set | | | | `OP_ReloadUI` | 🔴 Not-Set | | |
| `OP_RemoveAllDoors` | 🟡 Unverified | | | | `OP_RemoveAllDoors` | 🟡 Unverified | | |
| `OP_RemoveBlockedBuffs` | 🟡 Unverified | | | | `OP_RemoveBlockedBuffs` | 🟢 Verified | | |
| `OP_RemoveNimbusEffect` | 🟡 Unverified | | | | `OP_RemoveNimbusEffect` | 🟡 Unverified | | |
| `OP_RemoveTrap` | 🔴 Not-Set | | | | `OP_RemoveTrap` | 🔴 Not-Set | | |
| `OP_Report` | 🟡 Unverified | | | | `OP_Report` | 🟡 Unverified | | |
@@ -465,7 +465,7 @@ Below is a status list for the 450 opcodes we currently use on the server for th
| `OP_RequestKnowledgeBase` | 🔴 Not-Set | | | | `OP_RequestKnowledgeBase` | 🔴 Not-Set | | |
| `OP_RequestTitles` | 🔴 Not-Set | | | | `OP_RequestTitles` | 🔴 Not-Set | | |
| `OP_RespawnWindow` | 🟡 Unverified | | | | `OP_RespawnWindow` | 🟡 Unverified | | |
| `OP_RespondAA` | 🟡 Unverified | | | | `OP_RespondAA` | 🟢 Verified | | |
| `OP_RestState` | 🟡 Unverified | | | | `OP_RestState` | 🟡 Unverified | | |
| `OP_Rewind` | 🟡 Unverified | | | | `OP_Rewind` | 🟡 Unverified | | |
| `OP_RezzAnswer` | 🔴 Not-Set | | | | `OP_RezzAnswer` | 🔴 Not-Set | | |
@@ -478,9 +478,9 @@ Below is a status list for the 450 opcodes we currently use on the server for th
| `OP_SaveOnZoneReq` | 🟡 Unverified | | | | `OP_SaveOnZoneReq` | 🟡 Unverified | | |
| `OP_SelectTribute` | 🔴 Not-Set | | | | `OP_SelectTribute` | 🔴 Not-Set | | |
| `OP_SendAAStats` | 🟡 Unverified | | | | `OP_SendAAStats` | 🟡 Unverified | | |
| `OP_SendAATable` | 🟡 Unverified | | | | `OP_SendAATable` | 🟢 Verified | | |
| `OP_SendCharInfo` | 🟢 Verified | | | | `OP_SendCharInfo` | 🟢 Verified | | |
| `OP_SendExpZonein` | 🟡 Unverified | | | | `OP_SendExpZonein` | 🟢 Verified | | |
| `OP_SendFindableNPCs` | 🔴 Not-Set | | | | `OP_SendFindableNPCs` | 🔴 Not-Set | | |
| `OP_SendGuildTributes` | 🔴 Not-Set | | | | `OP_SendGuildTributes` | 🔴 Not-Set | | |
| `OP_SendLoginInfo` | 🟢 Verified | | | | `OP_SendLoginInfo` | 🟢 Verified | | |
@@ -490,7 +490,7 @@ Below is a status list for the 450 opcodes we currently use on the server for th
| `OP_SendSystemStats` | 🔴 Not-Set | | | | `OP_SendSystemStats` | 🔴 Not-Set | | |
| `OP_SendTitleList` | 🔴 Not-Set | | | | `OP_SendTitleList` | 🔴 Not-Set | | |
| `OP_SendTributes` | 🔴 Not-Set | | | | `OP_SendTributes` | 🔴 Not-Set | | |
| `OP_SendZonepoints` | 🟡 Unverified | | | | `OP_SendZonepoints` | 🟢 Verified | | |
| `OP_SenseHeading` | 🟡 Unverified | | | | `OP_SenseHeading` | 🟡 Unverified | | |
| `OP_SenseTraps` | 🟡 Unverified | | | | `OP_SenseTraps` | 🟡 Unverified | | |
| `OP_ServerListRequest` | 🔴 Not-Set | | | | `OP_ServerListRequest` | 🔴 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_SetGuildMOTD` | 🔴 Not-Set | | |
| `OP_SetGuildRank` | 🔴 Not-Set | | | | `OP_SetGuildRank` | 🔴 Not-Set | | |
| `OP_SetRunMode` | 🟡 Unverified | | | | `OP_SetRunMode` | 🟡 Unverified | | |
| `OP_SetServerFilter` | 🟡 Unverified | | | | `OP_SetServerFilter` | 🟢 Verified | | |
| `OP_SetStartCity` | 🔴 Not-Set | | | | `OP_SetStartCity` | 🔴 Not-Set | | |
| `OP_SetTitle` | 🔴 Not-Set | | | | `OP_SetTitle` | 🔴 Not-Set | | |
| `OP_SetTitleReply` | 🔴 Not-Set | | | | `OP_SetTitleReply` | 🔴 Not-Set | | |
@@ -533,23 +533,23 @@ Below is a status list for the 450 opcodes we currently use on the server for th
| `OP_ShopRetrieveParcel` | 🟡 Unverified | | | | `OP_ShopRetrieveParcel` | 🟡 Unverified | | |
| `OP_ShopParcelIcon` | 🟡 Unverified | | | | `OP_ShopParcelIcon` | 🟡 Unverified | | |
| `OP_ShopRequest` | 🟡 Unverified | | | | `OP_ShopRequest` | 🟡 Unverified | | |
| `OP_SimpleMessage` | 🟡 Unverified | | | | `OP_SimpleMessage` | 🟢 Verified | | |
| `OP_SkillUpdate` | 🟡 Unverified | | | | `OP_SkillUpdate` | 🟡 Unverified | | |
| `OP_Sneak` | 🟡 Unverified | | | | `OP_Sneak` | 🟡 Unverified | | |
| `OP_Some3ByteHPUpdate` | 🔴 Not-Set | | | | `OP_Some3ByteHPUpdate` | 🔴 Not-Set | | |
| `OP_Some6ByteHPUpdate` | 🔴 Not-Set | | | | `OP_Some6ByteHPUpdate` | 🔴 Not-Set | | |
| `OP_SomeItemPacketMaybe` | 🔴 Not-Set | | | | `OP_SomeItemPacketMaybe` | 🔴 Not-Set | | |
| `OP_Sound` | 🟡 Unverified | | | | `OP_Sound` | 🟡 Unverified | | |
| `OP_SpawnAppearance` | 🟡 Unverified | | | | `OP_SpawnAppearance` | 🟢 Verified | | |
| `OP_SpawnDoor` | 🟡 Unverified | | | | `OP_SpawnDoor` | 🟢 Verified | | |
| `OP_SpawnPositionUpdate` | 🔴 Not-Set | | | | `OP_SpawnPositionUpdate` | 🔴 Not-Set | | |
| `OP_SpecialMesg` | 🟡 Unverified | | | | `OP_SpecialMesg` | 🟢 Verified | | |
| `OP_SpellEffect` | 🟡 Unverified | | | | `OP_SpellEffect` | 🟡 Unverified | | |
| `OP_Split` | 🟡 Unverified | | | | `OP_Split` | 🟡 Unverified | | |
| `OP_Stamina` | 🟢 Verified | These values are 0-32k instead of 0-127 | | | `OP_Stamina` | 🟢 Verified | These values are 0-32k instead of 0-127 | |
| `OP_Stun` | 🟡 Unverified | | | | `OP_Stun` | 🟡 Unverified | | |
| `OP_Surname` | 🔴 Not-Set | | | | `OP_Surname` | 🔴 Not-Set | | |
| `OP_SwapSpell` | 🟡 Unverified | | | | `OP_SwapSpell` | 🟢 Verified | | |
| `OP_SystemFingerprint` | 🔴 Not-Set | | | | `OP_SystemFingerprint` | 🔴 Not-Set | | |
| `OP_TargetBuffs` | 🔴 Not-Set | | | | `OP_TargetBuffs` | 🔴 Not-Set | | |
| `OP_TargetCommand` | 🟡 Unverified | | | | `OP_TargetCommand` | 🟡 Unverified | | |
@@ -594,7 +594,7 @@ Below is a status list for the 450 opcodes we currently use on the server for th
| `OP_TributeToggle` | 🔴 Not-Set | | | | `OP_TributeToggle` | 🔴 Not-Set | | |
| `OP_TributeUpdate` | 🔴 Not-Set | | | | `OP_TributeUpdate` | 🔴 Not-Set | | |
| `OP_Untargetable` | 🟡 Unverified | | | | `OP_Untargetable` | 🟡 Unverified | | |
| `OP_UpdateAA` | 🟡 Unverified | | | | `OP_UpdateAA` | 🟢 Verified | | |
| `OP_UpdateAura` | 🔴 Not-Set | | | | `OP_UpdateAura` | 🔴 Not-Set | | |
| `OP_UpdateLeadershipAA` | 🔴 Not-Set | | | | `OP_UpdateLeadershipAA` | 🔴 Not-Set | | |
| `OP_VetClaimReply` | 🔴 Not-Set | | | | `OP_VetClaimReply` | 🔴 Not-Set | | |
@@ -603,8 +603,8 @@ Below is a status list for the 450 opcodes we currently use on the server for th
| `OP_VoiceMacroIn` | 🟡 Unverified | | | | `OP_VoiceMacroIn` | 🟡 Unverified | | |
| `OP_VoiceMacroOut` | 🟡 Unverified | | | | `OP_VoiceMacroOut` | 🟡 Unverified | | |
| `OP_WeaponEquip1` | 🔴 Not-Set | | | | `OP_WeaponEquip1` | 🔴 Not-Set | | |
| `OP_WearChange` | 🟡 Unverified | | | | `OP_WearChange` | 🟢 Verified | | |
| `OP_Weather` | 🟡 Unverified | | | | `OP_Weather` | 🟢 Verified | | |
| `OP_Weblink` | 🟡 Unverified | | | | `OP_Weblink` | 🟡 Unverified | | |
| `OP_WhoAllRequest` | 🟡 Unverified | | | | `OP_WhoAllRequest` | 🟡 Unverified | | |
| `OP_WhoAllResponse` | 🟡 Unverified | | | | `OP_WhoAllResponse` | 🟡 Unverified | | |
@@ -614,7 +614,7 @@ Below is a status list for the 450 opcodes we currently use on the server for th
| `OP_WorldClientReady` | 🟢 Verified | | | | `OP_WorldClientReady` | 🟢 Verified | | |
| `OP_WorldComplete` | 🟢 Verified | | | | `OP_WorldComplete` | 🟢 Verified | | |
| `OP_WorldLogout` | 🔴 Not-Set | | | | `OP_WorldLogout` | 🔴 Not-Set | | |
| `OP_WorldObjectsSent` | 🟡 Unverified | | | | `OP_WorldObjectsSent` | 🟢 Verified | | |
| `OP_WorldUnknown001` | 🟢 Verified | SetServerTime. emu doesn't currently send it so setting it to verified, but the reference is 0x140292550 | | | `OP_WorldUnknown001` | 🟢 Verified | SetServerTime. emu doesn't currently send it so setting it to verified, but the reference is 0x140292550 | |
| `OP_XTargetAutoAddHaters` | 🔴 Not-Set | | | | `OP_XTargetAutoAddHaters` | 🔴 Not-Set | | |
| `OP_XTargetOpen` | 🔴 Not-Set | | | | `OP_XTargetOpen` | 🔴 Not-Set | | |
+2 -2
View File
@@ -93,7 +93,7 @@ OP_ClearAA=0x6093
OP_ClearLeadershipAbilities=0x0000 #removed; leadership abilities are baked in and always on OP_ClearLeadershipAbilities=0x0000 #removed; leadership abilities are baked in and always on
OP_RespondAA=0x4449 OP_RespondAA=0x4449
OP_UpdateAA=0x1655 OP_UpdateAA=0x1655
OP_SendAAStats=0x7416 #i'll be honest i think this was removed at some point but this is the op at the spot in the list OP_SendAAStats=0x7416 # Removed in TOB
OP_AAExpUpdate=0x04c3 #need to look into whether this has changed; exp did OP_AAExpUpdate=0x04c3 #need to look into whether this has changed; exp did
OP_ExpUpdate=0x0e55 OP_ExpUpdate=0x0e55
OP_HPUpdate=0x2723 OP_HPUpdate=0x2723
@@ -222,7 +222,7 @@ OP_KeyRing=0x0000
OP_WhoAllRequest=0x3328 OP_WhoAllRequest=0x3328
OP_WhoAllResponse=0x4dfd OP_WhoAllResponse=0x4dfd
OP_FriendsWho=0x3547 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_Logout=0x46f8
OP_Rewind=0x898a OP_Rewind=0x898a
OP_TargetCommand=0x46bf OP_TargetCommand=0x46bf
+3
View File
@@ -12239,6 +12239,9 @@ void Client::Handle_OP_QueryUCSServerStatus(const EQApplicationPacket *app)
case EQ::versions::ClientVersion::RoF2: case EQ::versions::ClientVersion::RoF2:
ConnectionType = EQ::versions::ucsRoF2Combined; ConnectionType = EQ::versions::ucsRoF2Combined;
break; break;
case EQ::versions::ClientVersion::TOB:
ConnectionType = EQ::versions::ucsTOBCombined;
break;
default: default:
ConnectionType = EQ::versions::ucsUnknown; ConnectionType = EQ::versions::ucsUnknown;
break; break;