Send the shop catalog every time it is opened.

The client drops its product list when the shop closes and asks again. Serving the database catalog only once per session left every later open empty.
This commit is contained in:
luis
2026-09-23 12:04:40 -03:00
parent 7e0bf614d4
commit 1cb0bb3f18
3 changed files with 3 additions and 27 deletions
+3 -14
View File
@@ -123,9 +123,9 @@ namespace
} }
} }
// In-game Shop (BattlePay). P0: reply to the catalog request with the captured, client-validated // In-game Shop (BattlePay). The client asks for the product list every time the shop opens and
// product list so the shop opens and displays real products. If no catalog blob is loaded we send // drops the list it already had. Answering only the first request leaves every later open empty.
// nothing (shop opens empty) rather than fabricating wire. // The blob is the catalog built from shop_product at startup and on `.reload shop_catalog`.
void WorldSession::HandleBattlePayGetProductList(WorldPackets::BattlePay::GetProductList& /*getProductList*/) void WorldSession::HandleBattlePayGetProductList(WorldPackets::BattlePay::GetProductList& /*getProductList*/)
{ {
if (!sWorld->getBoolConfig(CONFIG_SHOP_ENABLED)) if (!sWorld->getBoolConfig(CONFIG_SHOP_ENABLED))
@@ -137,20 +137,9 @@ void WorldSession::HandleBattlePayGetProductList(WorldPackets::BattlePay::GetPro
return; return;
} }
// Throttle the 58 KB blob: serve it at most once per catalog generation for this session. The client
// re-requests on every shop open, so without this a STATUS_AUTHED session could pull it repeatedly.
// A `.reload shop_catalog` bumps the generation, so a fresh catalog still reaches the next request.
uint32 const generation = sBattlePayMgr->GetCatalogGeneration();
if (_battlePayCatalogGeneration == generation)
{
TC_LOG_DEBUG("network", "BattlePay: GetProductList from {} already served catalog gen {}.", GetPlayerInfo(), generation);
return;
}
WorldPackets::BattlePay::ProductListResponse response; WorldPackets::BattlePay::ProductListResponse response;
response.RawData = &sBattlePayMgr->GetProductListBlob(); response.RawData = &sBattlePayMgr->GetProductListBlob();
SendPacket(response.Write()); SendPacket(response.Write());
_battlePayCatalogGeneration = generation;
} }
// Drives the purchase to completion for a given productID: validate, charge (gold or a token item), // Drives the purchase to completion for a given productID: validate, charge (gold or a token item),
@@ -1352,15 +1352,6 @@ void WorldSession::HandlePlayerLoginOpcode(WorldPackets::Character::PlayerLogin&
m_playerLoginRPE = playerLogin.RPE; m_playerLoginRPE = playerLogin.RPE;
//WowCommunity //WowCommunity
// The Shop catalog is served at most once per catalog generation per session, but the client
// throws its store state away when it leaves character select, so the copy it fetched there is
// gone by the time the in-game Shop opens. Without this reset the in-world
// CMSG_BATTLE_PAY_GET_PRODUCT_LIST is silently swallowed by that throttle and the Shop shows an
// empty frame ("bad argument #1 to GetProducts" in Blizzard_StoreUI, because it has no product
// groups). Clearing the marker on login gives each context exactly one copy, which is what the
// throttle was actually meant to do.
_battlePayCatalogGeneration = 0;
TC_LOG_DEBUG("network", "Character {} logging in (RPE={})", playerLogin.Guid.ToString(), playerLogin.RPE); TC_LOG_DEBUG("network", "Character {} logging in (RPE={})", playerLogin.Guid.ToString(), playerLogin.RPE);
if (!IsLegitCharacterForAccount(playerLogin.Guid)) if (!IsLegitCharacterForAccount(playerLogin.Guid))
-4
View File
@@ -2828,10 +2828,6 @@ public:
// Packets cooldown // Packets cooldown
time_t _calendarEventCreationCooldown; time_t _calendarEventCreationCooldown;
// In-game Shop: last catalog generation this session was served the product-list blob for
// (0 = never). Throttles the 58 KB blob to once per generation; see BattlePayMgr.
uint32 _battlePayCatalogGeneration = 0;
// In-game Shop: pending purchase awaiting the client's confirmation response (two-step flow, // In-game Shop: pending purchase awaiting the client's confirmation response (two-step flow,
// Shop.PurchaseConfirmation). _battlePayConfirmToken 0 = nothing pending. // Shop.PurchaseConfirmation). _battlePayConfirmToken 0 = nothing pending.
uint32 _battlePayPendingProductID = 0; uint32 _battlePayPendingProductID = 0;