mirror of
https://github.com/djdevin/recflare.git
synced 2026-10-25 23:18:51 -07:00
[econ] fix consumable/skin boxes
This commit is contained in:
@@ -259,6 +259,13 @@ inconsistency here without checking the client first.
|
||||
each stored and pushed as `MessageReceived`. The `RelationshipChanged` frames only
|
||||
refresh relationship state: sent alone, the request "worked" server-side and the target
|
||||
saw nothing. Same pattern as the cheer.
|
||||
- A gift box's `AvatarItemType` (`econ`: the stored box in `GET /api/avatar/v2/gifts`, `buyItem`'s
|
||||
`BalanceUpdates[].Data`, `bulkpurchase`'s `GiftPackage`, and the `GiftPackageReceived*` frame)
|
||||
must be NULL for anything that is not an avatar item — a consumable, a skin, a box. The client
|
||||
routes the box on that field before it reads what else the box names, so a 0 sends it looking
|
||||
for avatar item type 0 and it fails with "can't find avatar item". The 2025 store lists every
|
||||
non-avatar drop with `AvatarItemType: null` for this reason; `boxAvatarItemType` applies it on
|
||||
every surface, so don't coalesce it with `?? 0` anywhere a box is built.
|
||||
- Accessibility is sent as the `RoomAccessibility` enum NAME on
|
||||
`rooms` `PUT /rooms/:id/subrooms/:sid/accessibility` (`accessibility=Private`), not the
|
||||
ordinal the room-level `/rooms/:id/accessibility` takes. The enum has five members
|
||||
|
||||
@@ -1099,6 +1099,21 @@ function truncateGiftMessage(message: string): string {
|
||||
* with nothing but the row to say who sent it or why. They default to Coach and the drop's
|
||||
* own context — a box the server handed over on nobody's behalf.
|
||||
*/
|
||||
/**
|
||||
* `AvatarItemType` as a gift box carries it: the drop's own for an avatar item, NULL for
|
||||
* anything else — a consumable, a skin, a box, a token bundle.
|
||||
*
|
||||
* The client routes a box by this field before it looks at what else the box names: a 0
|
||||
* reads as "an avatar item of type 0", and a consumable or skin box sent with 0 fails with
|
||||
* "can't find avatar item" because there is no such item to find. The 2025 store lists every
|
||||
* non-avatar drop with `AvatarItemType: null` for exactly that reason, and this is what the
|
||||
* old `?? 0` coalescing used to erase. Applied at every surface a box is serialized on — the
|
||||
* stored box, both purchase responses and the hub frame — so they cannot disagree.
|
||||
*/
|
||||
function boxAvatarItemType(drop: StoreGiftDrop): number | null {
|
||||
return drop.AvatarItemDesc !== '' ? (drop.AvatarItemType ?? 0) : null
|
||||
}
|
||||
|
||||
function toGiftContent(
|
||||
giftDrop: StoreGiftDrop,
|
||||
message: string,
|
||||
@@ -1116,7 +1131,7 @@ function toGiftContent(
|
||||
ConsumableMappingId: consumableMappingId,
|
||||
ConsumablePreExistingCount: consumablePreExistingCount,
|
||||
AvatarItemDesc: giftDrop.AvatarItemDesc,
|
||||
AvatarItemType: giftDrop.AvatarItemType,
|
||||
AvatarItemType: boxAvatarItemType(giftDrop),
|
||||
CurrencyType: giftDrop.CurrencyType,
|
||||
Currency: giftDrop.Currency,
|
||||
Xp: giftDrop.Xp ?? 0,
|
||||
@@ -1197,7 +1212,7 @@ async function pushGiftReceived(
|
||||
FromPlayerId: fromPlayerId,
|
||||
ConsumableItemDesc: gift.drop.ConsumableItemDesc,
|
||||
AvatarItemDesc: gift.drop.AvatarItemDesc,
|
||||
AvatarItemType: gift.drop.AvatarItemType ?? 0,
|
||||
AvatarItemType: boxAvatarItemType(gift.drop),
|
||||
EquipmentPrefabName: gift.drop.EquipmentPrefabName,
|
||||
EquipmentModificationGuid: gift.drop.EquipmentModificationGuid,
|
||||
CurrencyType: gift.drop.CurrencyType,
|
||||
@@ -1347,7 +1362,8 @@ function toSkinStoreItem(row: CatalogRow): StoreItem {
|
||||
Tooltip: row.tooltip ?? '',
|
||||
ConsumableItemDesc: '',
|
||||
AvatarItemDesc: '',
|
||||
AvatarItemType: 0,
|
||||
// A skin is no avatar item: null, as the store lists every skin.
|
||||
AvatarItemType: null,
|
||||
EquipmentPrefabName: row.prefab_name ?? '',
|
||||
EquipmentModificationGuid: row.item_key,
|
||||
Rarity: row.rarity,
|
||||
@@ -1668,7 +1684,7 @@ function toBalanceUpdateData(
|
||||
FromPlayerId: fromPlayerId,
|
||||
ConsumableItemDesc: drop.ConsumableItemDesc,
|
||||
AvatarItemDesc: drop.AvatarItemDesc,
|
||||
AvatarItemType: drop.AvatarItemType ?? 0,
|
||||
AvatarItemType: boxAvatarItemType(drop),
|
||||
EquipmentPrefabName: drop.EquipmentPrefabName,
|
||||
EquipmentModificationGuid: drop.EquipmentModificationGuid,
|
||||
CurrencyType: drop.CurrencyType,
|
||||
@@ -1816,7 +1832,7 @@ function toGiftPackage(
|
||||
PlayerId: playerId,
|
||||
FromPlayerId: fromPlayerId,
|
||||
ConsumableItemDesc: drop.ConsumableItemDesc,
|
||||
AvatarItemType: drop.AvatarItemType ?? 0,
|
||||
AvatarItemType: boxAvatarItemType(drop),
|
||||
AvatarItemDesc: drop.AvatarItemDesc,
|
||||
CustomAvatarItemId: null,
|
||||
EquipmentPrefabName: drop.EquipmentPrefabName,
|
||||
|
||||
@@ -2827,7 +2827,7 @@ describe('econ endpoints', () => {
|
||||
Data: Array<{
|
||||
ConsumableItemDesc: string
|
||||
AvatarItemDesc: string
|
||||
AvatarItemType: number
|
||||
AvatarItemType: number | null
|
||||
FromPlayerId: number
|
||||
}>
|
||||
}>
|
||||
@@ -2837,8 +2837,9 @@ describe('econ endpoints', () => {
|
||||
const drop = body.BalanceUpdates[0].Data[0]
|
||||
expect(drop.ConsumableItemDesc).toBe(consumableDesc)
|
||||
expect(drop.AvatarItemDesc).toBe('')
|
||||
// A consumable's AvatarItemType is null in the catalog; the response coalesces it to 0.
|
||||
expect(drop.AvatarItemType).toBe(0)
|
||||
// A consumable's AvatarItemType is NULL, as the store lists it — never 0, which the
|
||||
// client reads as an avatar item and fails on ("can't find avatar item").
|
||||
expect(drop.AvatarItemType).toBeNull()
|
||||
// A self-buy is attributed to the "Coach" system account (id 1).
|
||||
expect(drop.FromPlayerId).toBe(1)
|
||||
|
||||
@@ -4888,7 +4889,9 @@ describe('econ endpoints', () => {
|
||||
FromPlayerId: 1,
|
||||
ConsumableItemDesc: '',
|
||||
AvatarItemDesc: gift.AvatarItemDesc,
|
||||
AvatarItemType: gift.AvatarItemType,
|
||||
// The week's reward is a SKIN, so this is null whatever the block says: a 0 would
|
||||
// send the client looking for an avatar item that does not exist.
|
||||
AvatarItemType: gift.AvatarItemDesc === '' ? null : gift.AvatarItemType,
|
||||
EquipmentPrefabName: gift.EquipmentPrefabName,
|
||||
EquipmentModificationGuid: gift.EquipmentModificationGuid,
|
||||
CurrencyType: 0,
|
||||
|
||||
Reference in New Issue
Block a user