opcode(vas): implement the two framed responses (P7) - no longer stubs

This commit is contained in:
luis
2026-09-04 14:50:12 -03:00
parent e4da4ed08d
commit 93cf1815dd
3 changed files with 67 additions and 11 deletions
+16 -9
View File
@@ -1714,20 +1714,27 @@ void WorldSession::HandleCharacterCheckUpgrade(WorldPackets::BattlePay::Characte
void WorldSession::HandleCharacterUpgradeManualUnrevokeRequest(WorldPackets::BattlePay::CharacterUpgradeManualUnrevokeRequest& packet)
{
// Undo a revoked boost on a character. That requires a revoked boost entitlement, which exists only after a
// web-side purchase/refund cycle this realm has none of, so there is nothing to unrevoke. The paired result
// SMSG is a framed message whose inner layout is not offline-provable, so a guessed frame is not sent (it
// would risk disconnecting the client); the request is read and logged pending a live capture.
// web-side purchase/refund cycle this realm has none of, so there is nothing to unrevoke. Answer with the
// real result wire (client parser sub_7FF72A663530: a single uint32; no guid) - the Lua handler treats 0 as
// success and any non-zero as failure (ERROR_MANUAL_UNREVOKE_FAILURE), so a non-zero code is the honest
// "nothing to unrevoke" answer rather than falsely reporting success.
TC_LOG_INFO("network", "BattlePay: CharacterUpgradeManualUnrevokeRequest from {}: character={} - no revoked "
"boost to unrevoke.", GetPlayerInfo(), packet.CharacterGUID.ToString());
"boost to unrevoke; returning failure.", GetPlayerInfo(), packet.CharacterGUID.ToString());
WorldPackets::BattlePay::CharacterUpgradeManualUnrevokeResult result;
result.Result = 1; // non-zero == failure (client shows the generic unrevoke-failure dialog)
SendPacket(result.Write());
}
void WorldSession::HandleVasGetQueueMinutes(WorldPackets::BattlePay::VasGetQueueMinutes& packet)
{
// Estimated queue time for a VAS service. This realm processes transfers synchronously (no queue), but the
// response is a framed message whose inner layout is not offline-provable, so a guessed frame is not sent;
// the request is read and logged pending a live capture that proves the response wire.
TC_LOG_DEBUG("network", "BattlePay: VasGetQueueMinutes from {}: context={} - no queue; response wire pending "
"a live capture.", GetPlayerInfo(), packet.Field1);
// Estimated queue time for a VAS service. This realm processes transfers synchronously (the GM/transfer path
// runs inline), so the honest estimate is zero. Wire recovered from the client parser sub_7FF72AE70450:
// { uint64 Handle; uint32 QueueMinutes } - echo the request's correlation handle so the client matches it.
WorldPackets::BattlePay::VasGetQueueMinutesResponse response;
response.Handle = packet.Handle;
response.QueueMinutes = 0;
SendPacket(response.Write());
}
void WorldSession::HandleBattlePayAckFailedResponse(WorldPackets::BattlePay::BattlePayAckFailedResponse& packet)
@@ -388,7 +388,16 @@ namespace WorldPackets::BattlePay
void VasGetQueueMinutes::Read()
{
_worldPacket >> Field1;
// The response echoes a uint64 correlation handle; the request carries it. Read it as a uint64 when the
// body is wide enough, else zero-extend a uint32 - robust against either request width without throwing.
if (_worldPacket.size() - _worldPacket.rpos() >= sizeof(uint64))
_worldPacket >> Handle;
else
{
uint32 low = 0;
_worldPacket >> low;
Handle = low;
}
}
void VasCheckTransferOk::Read()
@@ -396,6 +405,21 @@ namespace WorldPackets::BattlePay
_worldPacket >> Field1;
}
WorldPacket const* VasGetQueueMinutesResponse::Write()
{
_worldPacket << uint64(Handle);
_worldPacket << uint32(QueueMinutes);
return &_worldPacket;
}
WorldPacket const* CharacterUpgradeManualUnrevokeResult::Write()
{
_worldPacket << uint32(Result);
return &_worldPacket;
}
WorldPacket const* BattlePayDistributionAssignVasResponse::Write()
{
_worldPacket << uint32(Field1);
@@ -785,7 +785,32 @@ namespace WorldPackets
void Read() override;
uint32 Field1 = 0;
uint64 Handle = 0; // the correlation token the response echoes (client matches it in its pending table)
};
// SMSG_VAS_GET_QUEUE_MINUTES_RESPONSE (0x4202C1) - flat 12 bytes, VERIFIED from the client parser
// sub_7FF72AE70450: { uint64 Handle; uint32 QueueMinutes }. No sub-frame, no bits.
class VasGetQueueMinutesResponse final : public ServerPacket
{
public:
explicit VasGetQueueMinutesResponse() : ServerPacket(SMSG_VAS_GET_QUEUE_MINUTES_RESPONSE, 12) {}
WorldPacket const* Write() override;
uint64 Handle = 0;
uint32 QueueMinutes = 0;
};
// SMSG_CHARACTER_UPGRADE_MANUAL_UNREVOKE_RESULT (0x42026B) - flat 4 bytes, VERIFIED from the client
// parser sub_7FF72A663530: a single uint32 Result. No character guid on the wire (resolved client-side).
class CharacterUpgradeManualUnrevokeResult final : public ServerPacket
{
public:
explicit CharacterUpgradeManualUnrevokeResult() : ServerPacket(SMSG_CHARACTER_UPGRADE_MANUAL_UNREVOKE_RESULT, 4) {}
WorldPacket const* Write() override;
uint32 Result = 0;
};
// CMSG_VAS_CHECK_TRANSFER_OK (0x400139) - one uint32 (the VAS service/product context to validate).