[econ] fix room consumable consume

This commit is contained in:
Devin Zuczek
2026-10-26 18:51:49 -04:00
parent eeb652fef7
commit 4709c390fd
4 changed files with 192 additions and 0 deletions
+37
View File
@@ -13,6 +13,7 @@
* - `POST /api/roomconsumables/v1/roomConsumable/awardBulk` gives one to a player
* - `PUT /api/roomconsumables/v1/roomconsumable/{id}/purchase/tokens` and `…/purchase/currency`
* sell one, in tokens or the listing's room currency
* - `PUT /api/roomconsumables/v1/roomConsumable/{id}/consume` uses up one the caller holds
*
* `PurchaseCurrencyId` is a `room_currency`, so a room prices its goods in its own money. It
* is nullable — `Guid?` in the client's model — for a listing that names no currency, and
@@ -344,6 +345,42 @@ export async function awardRoomConsumable(
}
}
/**
* Use up ONE of a consumable a player holds, returning what they hold afterwards — or null
* when they hold none, and nothing was written. `concurrencyCode` is the request's
* `NewConcurrencyCode`, stored to be served back; null leaves the stored one.
*
* A single conditional UPDATE: the `quantity >= 1` guard is what refuses, so two consumes
* racing over the last one cannot both succeed. Here an empty `RETURNING` is exactly the
* answer wanted — "you had none" — unlike the award, where it would be ambiguous.
*/
export async function consumeRoomConsumable(
db: D1Database,
playerId: number,
consumableId: string,
concurrencyCode: string | null = null
): Promise<RoomInventoryHolding | null> {
const row = await db
.prepare(
`UPDATE room_inventory SET
quantity = quantity - 1,
concurrency_code = COALESCE(?3, concurrency_code),
modified_at = ?4
WHERE player_id = ?1 AND consumable_id = ?2 AND quantity >= 1
RETURNING quantity, concurrency_code, modified_at`
)
.bind(playerId, consumableId, concurrencyCode, new Date().toISOString())
.first<{ quantity: number; concurrency_code: string | null; modified_at: string | null }>()
return row
? {
quantity: row.quantity,
concurrencyCode: row.concurrency_code,
modifiedAt: row.modified_at,
}
: null
}
/**
* Everything a player owns of one room's consumables, in the shop's order. A listing they own
* none of is absent — including one they held and spent down to zero.