mirror of
https://github.com/djdevin/recflare.git
synced 2026-10-27 16:08:58 -07:00
[econ] room key award
This commit is contained in:
@@ -2,15 +2,19 @@
|
||||
* Room keys — a room's own admission tickets.
|
||||
*
|
||||
* A creator lists a key for their room: a named, priced thing a player can hold to get
|
||||
* through a door the room locks. This module is the LISTING — its name, its price, which room
|
||||
* it opens. Nothing sells a key yet, so who holds one is not recorded anywhere. The endpoints:
|
||||
* through a door the room locks. `room_key` is the LISTING — its name, its price, which room
|
||||
* it opens — and `room_key_player` is who HOLDS one. Nothing sells a key yet; a key reaches
|
||||
* a player by being awarded. The endpoints:
|
||||
* - `POST /api/roomkeys/v1/create` lists one
|
||||
* - `GET /api/roomkeys/v1/room?roomId=` lists a room's
|
||||
* - `POST /api/roomkeys/v1/awardbulk` awards the caller some
|
||||
* - `POST /api/roomkeys/v1/owns/bulk` says who holds which
|
||||
*
|
||||
* This worker (`econ`) owns the table and its migrations — see apps/econ/migrations/0024.
|
||||
* This worker (`econ`) owns the tables and their migrations — see apps/econ/migrations/0024
|
||||
* and 0025.
|
||||
*/
|
||||
|
||||
/** Schema DDL — also builds the table in tests. Mirrors migration 0024. */
|
||||
/** Schema DDL — also builds the tables in tests. Mirrors migrations 0024 and 0025. */
|
||||
export const ROOM_KEY_SCHEMA_DDL: string[] = [
|
||||
// `room_key_id` is an AUTOINCREMENT rather than a GUID, unlike a room currency's: the
|
||||
// client's own model names a key by a numeric `RoomKeyId` (see the `notify` worker's
|
||||
@@ -37,6 +41,15 @@ export const ROOM_KEY_SCHEMA_DDL: string[] = [
|
||||
)`,
|
||||
// Every read is "this room's keys" — the whole access pattern.
|
||||
`CREATE INDEX IF NOT EXISTS idx_room_key_room ON room_key (room_id)`,
|
||||
// Who holds a key: one row per (key, player). A key is held or it isn't — there is no
|
||||
// quantity — so the pair is the primary key and a repeat award changes nothing.
|
||||
`CREATE TABLE IF NOT EXISTS room_key_player (
|
||||
room_key_id INTEGER NOT NULL,
|
||||
account_id INTEGER NOT NULL,
|
||||
awarded_at TEXT NOT NULL,
|
||||
PRIMARY KEY (room_key_id, account_id)
|
||||
)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_room_key_player_account ON room_key_player (account_id)`,
|
||||
]
|
||||
|
||||
/**
|
||||
@@ -146,3 +159,48 @@ export async function getRoomKeys(db: D1Database, roomId: number): Promise<RoomK
|
||||
|
||||
return results.map(toRoomKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* Award a player a room key, answering when they came to hold it — now, or the first award's
|
||||
* time when they already did (a key is held once; re-awarding is a no-op). Null when
|
||||
* `roomKeyId` names no listed key: a held key that cannot be described is never written.
|
||||
*/
|
||||
export async function awardRoomKey(
|
||||
db: D1Database,
|
||||
accountId: number,
|
||||
roomKeyId: number
|
||||
): Promise<string | null> {
|
||||
const [, held] = await db.batch<{ awarded_at: string }>([
|
||||
db
|
||||
.prepare(
|
||||
`INSERT OR IGNORE INTO room_key_player (room_key_id, account_id, awarded_at)
|
||||
SELECT room_key_id, ?2, ?3 FROM room_key WHERE room_key_id = ?1`
|
||||
)
|
||||
.bind(roomKeyId, accountId, new Date().toISOString()),
|
||||
db
|
||||
.prepare('SELECT awarded_at FROM room_key_player WHERE room_key_id = ?1 AND account_id = ?2')
|
||||
.bind(roomKeyId, accountId),
|
||||
])
|
||||
return held.results[0]?.awarded_at ?? null
|
||||
}
|
||||
|
||||
/** A (player, key) pair — one question put to `POST /api/roomkeys/v1/owns/bulk`. */
|
||||
export interface RoomKeyHolding {
|
||||
AccountId: number
|
||||
RoomKeyId: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether each player holds each key — one answer per pair, in the order asked. A pair
|
||||
* naming an unknown key or player is simply not held.
|
||||
*/
|
||||
export async function ownsRoomKeys(db: D1Database, pairs: RoomKeyHolding[]): Promise<boolean[]> {
|
||||
if (pairs.length === 0) return []
|
||||
const held = db.prepare(
|
||||
'SELECT 1 AS held FROM room_key_player WHERE room_key_id = ?1 AND account_id = ?2'
|
||||
)
|
||||
const results = await db.batch<{ held: number }>(
|
||||
pairs.map((pair) => held.bind(pair.RoomKeyId, pair.AccountId))
|
||||
)
|
||||
return results.map((r) => r.results.length > 0)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user