add playersettings, img, more match endpoints

This commit is contained in:
Devin Zuczek
2026-06-12 17:15:18 -04:00
parent 4a97e05866
commit 4d1ea9fe3e
42 changed files with 21397 additions and 2538 deletions
+27 -6
View File
@@ -3,10 +3,11 @@ import { useWorkersLogger } from 'workers-tagged-logger'
import { withNotFound, withOnError } from '@repo/hono-helpers'
import type { App } from './context'
import type { Context } from 'hono'
import { validateAndGetAccountId } from './jwt'
import type { Context } from 'hono'
import type { App } from './context'
/**
* Ported from the C# `MatchmakingController`. Endpoints the C# backs with EF Core
* (`AppDbContext`) are stubbed here — there's no DB binding yet, so room/player
@@ -80,13 +81,33 @@ const app = new Hono<App>()
.notFound(withNotFound())
// ---- Player presence -----------------------------------------------------
// Login is a no-op ack in the C# (`Results.Ok()`).
.post('/player/login', (c) => c.body(null, 200))
.get('/player', (c) => {
// C# loads the account + its active RoomInstance; without a DB binding it
// always falls through to the JSON/getplayer.json default.
// TODO: build the per-account payload once a DB binding exists.
return c.json(DEFAULT_GET_PLAYER)
// The C# reads the `id` query param: a valid id for an existing account
// returns that player's payload; a missing/invalid id (or unknown account)
// falls back to the static JSON/getplayer.json default. With no DB we treat
// any account as existing and synthesize its payload.
const idParam = c.req.query('id')
const accountId = idParam ? Number.parseInt(idParam, 10) : Number.NaN
if (Number.isNaN(accountId)) return c.json(DEFAULT_GET_PLAYER)
// No RoomInstances binding → roomInstance null / isOnline false, matching
// the C# branch where the account exists but owns no room instance.
// TODO: look up the player's active RoomInstance once a DB binding exists.
return c.json([
{
playerId: accountId,
statusVisibility: 0,
deviceClass: 0,
vrMovementMode: 1,
roomInstance: null,
isOnline: false,
appVersion: '',
platform: 0,
},
])
})
.post('/player/heartbeat', async (c) => {
+21 -2
View File
@@ -39,9 +39,26 @@ describe('public endpoints', () => {
expect(res.status).toBe(200)
})
test('GET /player returns the default payload', async () => {
test('GET /player?id=N synthesizes a player payload for that id', async () => {
const res = await exports.default.fetch(`${ORIGIN}/player?id=99`)
expect(res.status).toBe(200)
const players = (await res.json()) as Array<{
playerId: number
isOnline: boolean
appVersion: string
roomInstance: unknown
}>
expect(players[0]).toMatchObject({
playerId: 99,
isOnline: false,
appVersion: '',
roomInstance: null,
})
})
test('GET /player without an id returns the default payload', async () => {
const res = await exports.default.fetch(`${ORIGIN}/player`)
expect(res.status).toBe(200)
const players = (await res.json()) as Array<{ playerId: number; isOnline: boolean }>
expect(players[0]).toMatchObject({ playerId: 1, isOnline: true, appVersion: '20210129' })
})
@@ -107,7 +124,9 @@ describe('auth-gated endpoints', () => {
body: new URLSearchParams({ JoinMode: '2' }).toString(),
})
expect(res.status).toBe(200)
const body = (await res.json()) as { roomInstance: { roomId: number; isPrivate: boolean; name: string } }
const body = (await res.json()) as {
roomInstance: { roomId: number; isPrivate: boolean; name: string }
}
expect(body.roomInstance).toMatchObject({ roomId: 42, isPrivate: true, name: '42' })
})