opcode(vas): realm-target-list handler + verified list header (P3)

This commit is contained in:
luis
2026-09-04 12:55:09 -03:00
parent 7eda51dcf3
commit 129ec064fb
5 changed files with 34 additions and 13 deletions
@@ -1619,3 +1619,16 @@ void WorldSession::HandleGetVasAccountCharacterList(WorldPackets::BattlePay::Get
SendPacket(result.Write()); SendPacket(result.Write());
} }
void WorldSession::HandleGetVasTransferTargetRealmList(WorldPackets::BattlePay::GetVasTransferTargetRealmList& packet)
{
// The realms a character may be transferred to. The outer wire is verified (3 uint32 + flat count +
// vector), but each realm entry's 6 uint32 are an unlabeled reflected type whose meaning (which field is
// the address the client transfers TO) is not offline-provable - populating entries would mean guessing
// fields that steer a paid transfer. This realm has no configured VAS transfer network, so the honest,
// byte-correct answer is an empty target list; populating it is gated on a live-capture that proves the
// entry semantics.
WorldPackets::BattlePay::GetVasTransferTargetRealmListResult result;
result.Field1 = packet.Field1; // echo the request token for correlation
SendPacket(result.Write());
}
@@ -415,7 +415,6 @@ namespace WorldPackets::BattlePay
_worldPacket << uint32(Field1); _worldPacket << uint32(Field1);
_worldPacket << uint32(Field2); _worldPacket << uint32(Field2);
_worldPacket << uint32(Field3); _worldPacket << uint32(Field3);
_worldPacket << uint32(Field4);
_worldPacket << uint32(Characters.size()); _worldPacket << uint32(Characters.size());
for (VasAccountCharacterInfo const& c : Characters) for (VasAccountCharacterInfo const& c : Characters)
@@ -441,9 +440,12 @@ namespace WorldPackets::BattlePay
WorldPacket const* GetVasTransferTargetRealmListResult::Write() WorldPacket const* GetVasTransferTargetRealmListResult::Write()
{ {
// Outer header is ambiguous between dump versions; a uint32-counted vector of the verified realm entry // Outer = 3 uint32 header + a flat uint32 count + the realm vector (VERIFIED, same shape as the account
// (6 uint32 + a 9-bit-length-prefixed name) is used. Field meanings of the 6 uint32 are an unlabeled // list). Header fields have no proven offline meaning and stay 0; the 6 uint32 per realm entry are an
// reflected type - populated best-effort, live-test-pending. // unlabeled reflected type populated best-effort - live-test-pending.
_worldPacket << uint32(Field1);
_worldPacket << uint32(Field2);
_worldPacket << uint32(Field3);
_worldPacket << uint32(Realms.size()); _worldPacket << uint32(Realms.size());
for (VasTargetRealmInfo const& r : Realms) for (VasTargetRealmInfo const& r : Realms)
@@ -836,9 +836,10 @@ namespace WorldPackets
std::string RealmName; // 9-bit length prefix std::string RealmName; // 9-bit length prefix
}; };
// SMSG_GET_VAS_ACCOUNT_CHARACTER_LIST_RESULT (0x420297). Outer header = 4x uint32 (agreed across both // SMSG_GET_VAS_ACCOUNT_CHARACTER_LIST_RESULT (0x420297). Outer = 3 uint32 header + a FLAT uint32 count +
// dump versions) then the character vector. Header field meanings are unlabeled offline; Field1 echoes // the character vector - VERIFIED (deserializer sub_7FF7290AE4B0; the "4th uint32" earlier dumps showed
// the request token so the client can correlate, the rest are 0 until proven. // is the list count, not a header field). Header field meanings are unlabeled offline; Field1 echoes the
// request token so the client can correlate, the other two are 0 until proven.
class GetVasAccountCharacterListResult final : public ServerPacket class GetVasAccountCharacterListResult final : public ServerPacket
{ {
public: public:
@@ -849,7 +850,6 @@ namespace WorldPackets
uint32 Field1 = 0; uint32 Field1 = 0;
uint32 Field2 = 0; uint32 Field2 = 0;
uint32 Field3 = 0; uint32 Field3 = 0;
uint32 Field4 = 0;
std::vector<VasAccountCharacterInfo> Characters; std::vector<VasAccountCharacterInfo> Characters;
}; };
@@ -867,16 +867,20 @@ namespace WorldPackets
std::string RealmName; // 9-bit length prefix std::string RealmName; // 9-bit length prefix
}; };
// SMSG_GET_VAS_TRANSFER_TARGET_REALM_LIST_RESULT (0x420298). Outer header field count is ambiguous // SMSG_GET_VAS_TRANSFER_TARGET_REALM_LIST_RESULT (0x420298). Outer = 3 uint32 header + a FLAT uint32
// between dump versions; the base (all_smsg) layout of leading uint32s + the realm vector is used and // count + the realm vector - VERIFIED from the deserializer sub_7FF7290AE5B0 (same shape as the account
// is live-test-pending. // list; the earlier "10x uint32" / "u8+bitfield" dumps were miscounts of the element string prefix).
// Header field meanings are unlabeled offline and stay 0 until proven.
class GetVasTransferTargetRealmListResult final : public ServerPacket class GetVasTransferTargetRealmListResult final : public ServerPacket
{ {
public: public:
explicit GetVasTransferTargetRealmListResult() : ServerPacket(SMSG_GET_VAS_TRANSFER_TARGET_REALM_LIST_RESULT, 44) {} explicit GetVasTransferTargetRealmListResult() : ServerPacket(SMSG_GET_VAS_TRANSFER_TARGET_REALM_LIST_RESULT, 16) {}
WorldPacket const* Write() override; WorldPacket const* Write() override;
uint32 Field1 = 0;
uint32 Field2 = 0;
uint32 Field3 = 0;
std::vector<VasTargetRealmInfo> Realms; std::vector<VasTargetRealmInfo> Realms;
}; };
} }
+1 -1
View File
@@ -512,7 +512,7 @@ void OpcodeTable::InitializeClientOpcodes()
DEFINE_HANDLER(CMSG_GET_TROPHY_LIST, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleGetTrophyList); DEFINE_HANDLER(CMSG_GET_TROPHY_LIST, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleGetTrophyList);
DEFINE_HANDLER(CMSG_GET_UNDELETE_CHARACTER_COOLDOWN_STATUS, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleGetUndeleteCooldownStatus); DEFINE_HANDLER(CMSG_GET_UNDELETE_CHARACTER_COOLDOWN_STATUS, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleGetUndeleteCooldownStatus);
DEFINE_HANDLER(CMSG_GET_VAS_ACCOUNT_CHARACTER_LIST, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleGetVasAccountCharacterList); DEFINE_HANDLER(CMSG_GET_VAS_ACCOUNT_CHARACTER_LIST, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleGetVasAccountCharacterList);
DEFINE_HANDLER(CMSG_GET_VAS_TRANSFER_TARGET_REALM_LIST, STATUS_IGNORED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_GET_VAS_TRANSFER_TARGET_REALM_LIST, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleGetVasTransferTargetRealmList);
DEFINE_HANDLER(CMSG_GM_TICKET_ACKNOWLEDGE_SURVEY, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_GM_TICKET_ACKNOWLEDGE_SURVEY, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_GM_TICKET_GET_CASE_STATUS, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleGMTicketGetCaseStatusOpcode); DEFINE_HANDLER(CMSG_GM_TICKET_GET_CASE_STATUS, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleGMTicketGetCaseStatusOpcode);
DEFINE_HANDLER(CMSG_GM_TICKET_GET_SYSTEM_STATUS, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleGMTicketSystemStatusOpcode); DEFINE_HANDLER(CMSG_GM_TICKET_GET_SYSTEM_STATUS, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleGMTicketSystemStatusOpcode);
+2
View File
@@ -132,6 +132,7 @@ namespace WorldPackets
class UpdateVasPurchaseStates; class UpdateVasPurchaseStates;
class VasGetServiceStatus; class VasGetServiceStatus;
class GetVasAccountCharacterList; class GetVasAccountCharacterList;
class GetVasTransferTargetRealmList;
class GetProductList; class GetProductList;
class GetPurchaseList; class GetPurchaseList;
class StartPurchase; class StartPurchase;
@@ -2413,6 +2414,7 @@ public:
void HandleBattlePayGetProductList(WorldPackets::BattlePay::GetProductList& getProductList); void HandleBattlePayGetProductList(WorldPackets::BattlePay::GetProductList& getProductList);
void HandleUpdateVasPurchaseStates(WorldPackets::BattlePay::UpdateVasPurchaseStates& packet); void HandleUpdateVasPurchaseStates(WorldPackets::BattlePay::UpdateVasPurchaseStates& packet);
void HandleVasGetServiceStatus(WorldPackets::BattlePay::VasGetServiceStatus& packet); void HandleVasGetServiceStatus(WorldPackets::BattlePay::VasGetServiceStatus& packet);
void HandleGetVasTransferTargetRealmList(WorldPackets::BattlePay::GetVasTransferTargetRealmList& packet);
void HandleGetVasAccountCharacterList(WorldPackets::BattlePay::GetVasAccountCharacterList& packet); void HandleGetVasAccountCharacterList(WorldPackets::BattlePay::GetVasAccountCharacterList& packet);
void HandleBattlePayGetPurchaseList(WorldPackets::BattlePay::GetPurchaseList& getPurchaseList); void HandleBattlePayGetPurchaseList(WorldPackets::BattlePay::GetPurchaseList& getPurchaseList);
void HandleBattlePayStartPurchase(WorldPackets::BattlePay::StartPurchase& startPurchase); void HandleBattlePayStartPurchase(WorldPackets::BattlePay::StartPurchase& startPurchase);