mirror of
https://github.com/djdevin/recflare.git
synced 2026-09-08 14:41:28 -07:00
[api] add outfits endpoint
This commit is contained in:
@@ -65,6 +65,49 @@ export async function getOutfit(
|
||||
return row ? (JSON.parse(row.avatar) as Outfit) : null
|
||||
}
|
||||
|
||||
/**
|
||||
* The most accounts one bulk read may name. D1 caps a prepared statement at 100 bound
|
||||
* parameters and `?1` is the slot, leaving 99 for the ids — so this is the whole query in
|
||||
* one round trip, with no chunking to get wrong. A room holds far fewer players than that,
|
||||
* which is what the caller asks about; the route rejects a longer list rather than silently
|
||||
* answering part of it.
|
||||
*/
|
||||
export const MAX_BULK_OUTFIT_ACCOUNTS = 99
|
||||
|
||||
/**
|
||||
* One slot's outfit for each of several accounts — the bulk read behind
|
||||
* `POST /outfits/bulk`, which the client uses to dress everyone in a room at once.
|
||||
*
|
||||
* Keyed by account id, and an account with nothing saved in that slot is simply ABSENT from
|
||||
* the map rather than present with a null: a map says "no outfit" by not having the key, and
|
||||
* the alternative would be inventing an outfit shape for a player who has never saved one.
|
||||
*
|
||||
* Ids are de-duplicated — the client sends a room's roster, which can repeat — and the
|
||||
* caller must already have held the list to {@link MAX_BULK_OUTFIT_ACCOUNTS}; more than that
|
||||
* overflows D1's parameter cap and fails the query outright. Each outfit is served exactly
|
||||
* as it was stored: see the note at the top of this file — slot 0 written through
|
||||
* `/outfits/me` is the newer envelope, while econ's saved-set writes are the old flat shape,
|
||||
* and neither is projected.
|
||||
*/
|
||||
export async function getOutfitsByAccounts(
|
||||
db: D1Database,
|
||||
accountIds: number[],
|
||||
slot: number
|
||||
): Promise<Map<number, Outfit>> {
|
||||
const ids = [...new Set(accountIds)]
|
||||
if (ids.length === 0) return new Map()
|
||||
|
||||
const placeholders = ids.map((_, n) => `?${n + 2}`).join(', ')
|
||||
const { results } = await db
|
||||
.prepare(
|
||||
`SELECT account_id, avatar FROM outfit WHERE set_id = ?1 AND account_id IN (${placeholders})`
|
||||
)
|
||||
.bind(slot, ...ids)
|
||||
.all<{ account_id: number; avatar: string }>()
|
||||
|
||||
return new Map(results.map((row) => [row.account_id, JSON.parse(row.avatar) as Outfit]))
|
||||
}
|
||||
|
||||
/**
|
||||
* Save an outfit into one of the player's slots, replacing whatever was there. The
|
||||
* upsert is keyed on (account_id, set_id), so re-saving a slot overwrites rather than
|
||||
|
||||
Reference in New Issue
Block a user