Multiplayer: Spielräume mit Code, Ready-Start, geheime Rankings auf jedem Handy (Game-API + Sync-Frontend)
Build & Deploy / deploy (push) Successful in 19s
Build & Deploy / deploy (push) Successful in 19s
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
// ── Client für die Game-API (Multiplayer-Spielräume) ──────
|
||||
|
||||
export type RaumStatus = 'lobby' | 'question' | 'ranking' | 'reveal' | 'scoreboard' | 'finished'
|
||||
|
||||
export interface SpielerInfo {
|
||||
name: string
|
||||
ready: boolean
|
||||
istHost: boolean
|
||||
rankingAbgegeben: boolean
|
||||
}
|
||||
|
||||
export interface PlatzDetail {
|
||||
platz: number
|
||||
person: string
|
||||
stimmen: number
|
||||
punkte: number
|
||||
}
|
||||
|
||||
export interface Ergebnis {
|
||||
mehrheit: string[]
|
||||
punkte: Record<string, number>
|
||||
platzDetails: PlatzDetail[]
|
||||
}
|
||||
|
||||
export interface RaumZustand {
|
||||
code: string
|
||||
status: RaumStatus
|
||||
runde: number
|
||||
anzahlRunden: number
|
||||
frage: string
|
||||
fragesteller: string
|
||||
spieler: SpielerInfo[]
|
||||
minSpieler: number
|
||||
maxSpieler: number
|
||||
alleReady: boolean
|
||||
rankingsFertig: number
|
||||
punkte: Record<string, number>
|
||||
ergebnis: Ergebnis | null
|
||||
ich: string
|
||||
}
|
||||
|
||||
export interface RaumSession {
|
||||
code: string
|
||||
token: string
|
||||
name: string
|
||||
}
|
||||
|
||||
const SESSION_KEY = 'einsbisacht-raum-v1'
|
||||
|
||||
export function ladeSession(): RaumSession | null {
|
||||
try {
|
||||
const roh = localStorage.getItem(SESSION_KEY)
|
||||
return roh ? (JSON.parse(roh) as RaumSession) : null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function speichereSession(s: RaumSession | null) {
|
||||
if (s) localStorage.setItem(SESSION_KEY, JSON.stringify(s))
|
||||
else localStorage.removeItem(SESSION_KEY)
|
||||
}
|
||||
|
||||
class ApiFehler extends Error {}
|
||||
|
||||
async function anfrage(methode: 'GET' | 'POST', pfad: string, body?: object, token?: string): Promise<RaumZustand> {
|
||||
const res = await fetch(`/api/game/room${pfad}`, {
|
||||
method: methode,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(token ? { 'X-Player-Token': token } : {}),
|
||||
},
|
||||
...(body ? { body: JSON.stringify(body) } : {}),
|
||||
})
|
||||
const daten = await res.json().catch(() => ({}))
|
||||
if (!res.ok) throw new ApiFehler(daten.fehler || `Fehler ${res.status}`)
|
||||
if (daten.zustand) return daten.zustand as RaumZustand
|
||||
throw new ApiFehler('Unerwartete Antwort der Spiel-API')
|
||||
}
|
||||
|
||||
/** Raum eröffnen; liefert Session direkt mit */
|
||||
export async function raumErstellen(name: string): Promise<{ session: RaumSession; zustand: RaumZustand }> {
|
||||
const res = await fetch('/api/game/room', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name }),
|
||||
})
|
||||
const daten = await res.json().catch(() => ({}))
|
||||
if (!res.ok || !daten.token) throw new ApiFehler(daten.fehler || 'Raum konnte nicht erstellt werden')
|
||||
const session: RaumSession = { code: daten.code, token: daten.token, name }
|
||||
speichereSession(session)
|
||||
return { session, zustand: daten.zustand }
|
||||
}
|
||||
|
||||
/** Raum beitreten; liefert Session direkt mit */
|
||||
export async function raumBeitreten(code: string, name: string): Promise<{ session: RaumSession; zustand: RaumZustand }> {
|
||||
const res = await fetch(`/api/game/room/${code.toUpperCase()}/join`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name }),
|
||||
})
|
||||
const daten = await res.json().catch(() => ({}))
|
||||
if (!res.ok || !daten.token) throw new ApiFehler(daten.fehler || 'Beitritt fehlgeschlagen')
|
||||
const session: RaumSession = { code: code.toUpperCase(), token: daten.token, name }
|
||||
speichereSession(session)
|
||||
return { session, zustand: daten.zustand }
|
||||
}
|
||||
|
||||
export const zustandLaden = (s: RaumSession) => anfrage('GET', `/${s.code}`, undefined, s.token)
|
||||
export const readySetzen = (s: RaumSession, ready: boolean) => anfrage('POST', `/${s.code}/ready`, { ready }, s.token)
|
||||
export const frageStellen = (s: RaumSession, frage: string) => anfrage('POST', `/${s.code}/question`, { frage }, s.token)
|
||||
export const rankingAbgeben = (s: RaumSession, reihenfolge: string[]) =>
|
||||
anfrage('POST', `/${s.code}/ranking`, { reihenfolge }, s.token)
|
||||
export const naechstePhase = (s: RaumSession) => anfrage('POST', `/${s.code}/next`, {}, s.token)
|
||||
+487
-386
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user