opcode(vas): parse START_VAS_PURCHASE transfer intent; correct ASSIGN understanding (P8)
This commit is contained in:
@@ -1677,13 +1677,15 @@ void WorldSession::HandleVasCheckTransferOk(WorldPackets::BattlePay::VasCheckTra
|
||||
|
||||
void WorldSession::HandleBattlePayDistributionAssignVas(WorldPackets::BattlePay::BattlePayDistributionAssignVas& packet)
|
||||
{
|
||||
// Assign a purchased VAS (transfer/rename/faction-change) entitlement to a character. VAS entitlements are
|
||||
// created only by the web-side purchase, which this realm has none of, so there is nothing to assign -
|
||||
// answer honestly with "distribution not found" rather than drop the packet and leave the client's VAS UI
|
||||
// waiting. The cross-realm move itself IS implemented (VasTransferMgr / .character transfer); wiring this
|
||||
// opcode to it needs a live capture to prove which request field carries the target realm.
|
||||
TC_LOG_INFO("network", "BattlePay: DistributionAssignVas from {}: token={} - no VAS entitlement to assign.",
|
||||
GetPlayerInfo(), packet.Token);
|
||||
// Assign an ALREADY-PURCHASED VAS distribution to a target. RE of the client serializer (sub_7FF72907F4B0)
|
||||
// showed this opcode carries a u64 DistributionID (+0x28) and target guids but NO realm address - the
|
||||
// realm is chosen earlier, at START_VAS_PURCHASE. The distribution it references only exists after a
|
||||
// web-side purchase settles, which this realm has none of, so there is nothing to assign: answer honestly
|
||||
// with "distribution not found" rather than drop the packet. (The paid-character-transfer flow itself does
|
||||
// NOT use this opcode - it drives START_VAS_PURCHASE, handled above; this is the shop's assign-a-bought-
|
||||
// distribution path.)
|
||||
TC_LOG_INFO("network", "BattlePay: DistributionAssignVas from {}: distributionId-token={} - no VAS "
|
||||
"distribution to assign.", GetPlayerInfo(), packet.Token);
|
||||
|
||||
WorldPackets::BattlePay::BattlePayDistributionAssignVasResponse response;
|
||||
response.Field1 = packet.Token;
|
||||
@@ -1693,12 +1695,24 @@ void WorldSession::HandleBattlePayDistributionAssignVas(WorldPackets::BattlePay:
|
||||
|
||||
void WorldSession::HandleBattlePayStartVasPurchase(WorldPackets::BattlePay::BattlePayStartVasPurchase& packet)
|
||||
{
|
||||
// Begin a paid VAS purchase. Like every real-money purchase in this core the settlement is web-side, so the
|
||||
// realm sends no game response (the client drives the web checkout); the token is logged so the reading can
|
||||
// be confirmed against a real client. The delivered entitlement, when a payment backend confirms it, is what
|
||||
// later reaches the assign path above.
|
||||
TC_LOG_INFO("network", "BattlePay: StartVasPurchase from {}: token={} (settled web-side, no game response).",
|
||||
GetPlayerInfo(), packet.Token);
|
||||
// This is the request the paid-character-transfer flow sends (Lua AssignPCTDistribution): it carries the
|
||||
// selected character, the VAS service type and the TARGET realm's wowRealmAddress. Resolve the target realm
|
||||
// against the realms this server fronts and log the full transfer intent - this is the client-driven entry
|
||||
// point to VasTransferMgr.
|
||||
//
|
||||
// It is not executed here yet for one honest reason: the flow calls twice - a validation pass then a commit
|
||||
// pass - distinguished by an isValidationOnly bool that lives in this packet's bit-packed tail. Reading it
|
||||
// needs the per-string bit-length widths (a small targeted RE still outstanding); executing on the
|
||||
// validation pass would move the character prematurely. Until that bit is read, the reliable execution path
|
||||
// is the .character transfer GM command, which drives the same VasTransferMgr.
|
||||
uint32 targetRealmId = 0;
|
||||
if (packet.TargetRealmAddress)
|
||||
targetRealmId = Battlenet::RealmHandle(packet.TargetRealmAddress).Realm;
|
||||
|
||||
TC_LOG_INFO("network", "BattlePay: StartVasPurchase from {}: seq={} serviceType={} character={} "
|
||||
"targetRealmAddress={:#x} (realm {}) - transfer intent parsed; execution gated on the isValidationOnly bit.",
|
||||
GetPlayerInfo(), packet.SequenceId, packet.ServiceType, packet.Character.ToString(),
|
||||
packet.TargetRealmAddress, targetRealmId);
|
||||
}
|
||||
|
||||
void WorldSession::HandleCharacterCheckUpgrade(WorldPackets::BattlePay::CharacterCheckUpgrade& /*packet*/)
|
||||
|
||||
@@ -508,8 +508,15 @@ namespace WorldPackets::BattlePay
|
||||
|
||||
void BattlePayStartVasPurchase::Read()
|
||||
{
|
||||
_worldPacket >> Token;
|
||||
_worldPacket.rfinish(); // the remaining fields are not modelled (see header); consume them
|
||||
// Wire order from the client builder sub_7FF72AE6E2F0: seq, serviceType, guid, context, targetRealmAddress,
|
||||
// then name/blob strings and further guids we do not need. Read up to the realm address, then consume the
|
||||
// rest so the stream stays aligned.
|
||||
_worldPacket >> SequenceId;
|
||||
_worldPacket >> ServiceType;
|
||||
_worldPacket >> Character;
|
||||
_worldPacket >> Context;
|
||||
_worldPacket >> TargetRealmAddress;
|
||||
_worldPacket.rfinish();
|
||||
}
|
||||
|
||||
void BattlePayDistributionAssignVas::Read()
|
||||
|
||||
@@ -969,10 +969,13 @@ namespace WorldPackets
|
||||
void Read() override { _worldPacket.rfinish(); }
|
||||
};
|
||||
|
||||
// CMSG_BATTLE_PAY_START_VAS_PURCHASE (0x400122) - a large nested struct beginning the paid VAS
|
||||
// purchase (product, target guids, region/appearance choices). Only the leading uint32 (a client token)
|
||||
// is modelled; the rest is consumed. Like a real-money checkout, the purchase itself is settled web-side,
|
||||
// so the realm sends no game response - it is not a request the client blocks a game answer on.
|
||||
// CMSG_BATTLE_PAY_START_VAS_PURCHASE (0x400122) - begins a paid VAS purchase. The leading fields are
|
||||
// resolved from the client builder sub_7FF72AE6E2F0: a request sequence id, the VAS service type, a
|
||||
// guid (the selected character/product), a context uint32, then the TARGET REALM's wowRealmAddress
|
||||
// (copied from the picked JamCliVASTargetRealm[+0]). The tail (name/blob strings, more guids) is not
|
||||
// needed to identify the transfer and is consumed. The per-guid role is not offline-provable, but the
|
||||
// first guid is the selected character in the transfer flow; downstream validation (VasTransferMgr)
|
||||
// rejects a wrong guid safely.
|
||||
class BattlePayStartVasPurchase final : public ClientPacket
|
||||
{
|
||||
public:
|
||||
@@ -980,7 +983,11 @@ namespace WorldPackets
|
||||
|
||||
void Read() override;
|
||||
|
||||
uint32 Token = 0;
|
||||
uint32 SequenceId = 0;
|
||||
uint32 ServiceType = 0;
|
||||
ObjectGuid Character; // first guid (selected character in the PCT flow)
|
||||
uint32 Context = 0;
|
||||
uint32 TargetRealmAddress = 0; // wowRealmAddress of the picked target realm (JamCliVASTargetRealm[+0])
|
||||
};
|
||||
|
||||
// CMSG_BATTLE_PAY_DISTRIBUTION_ASSIGN_VAS (0x400167) - a large nested struct (client token, several
|
||||
|
||||
Reference in New Issue
Block a user