Merge pull request 'feature/reconnect' (#14) from feature/reconnect into dev
Reviewed-on: #14
This commit is contained in:
commit
4154509fdd
9 changed files with 216 additions and 16 deletions
|
|
@ -24,6 +24,8 @@ interface RoundRuntime {
|
||||||
startedAt: number
|
startedAt: number
|
||||||
endsAt: number
|
endsAt: number
|
||||||
data: unknown
|
data: unknown
|
||||||
|
/** Payload public de la manche (pour la reconnexion). */
|
||||||
|
payload: unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GameEngineOptions {
|
export interface GameEngineOptions {
|
||||||
|
|
@ -137,6 +139,22 @@ export class GameEngine implements RoomGameController {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Reconnexion : re-pousse la manche en cours au socket d'un joueur. */
|
||||||
|
resync(socketId: string, playerId: string): void {
|
||||||
|
if (!this.runtime || this.room.status !== "in_round") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const r = this.runtime
|
||||||
|
this.io.to(socketId).emit("round:start", {
|
||||||
|
type: r.round.type,
|
||||||
|
djId: r.djId === playerId ? r.djId : undefined,
|
||||||
|
mine: r.secretPlayerId === playerId ? true : undefined,
|
||||||
|
startsAt: r.startedAt,
|
||||||
|
endsAt: r.endsAt,
|
||||||
|
payload: r.payload,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
private async playRound(config: RoundConfig): Promise<void> {
|
private async playRound(config: RoundConfig): Promise<void> {
|
||||||
const round = this.createRound(config.type)
|
const round = this.createRound(config.type)
|
||||||
const start = round.start(this.room)
|
const start = round.start(this.room)
|
||||||
|
|
@ -154,6 +172,7 @@ export class GameEngine implements RoomGameController {
|
||||||
startedAt,
|
startedAt,
|
||||||
endsAt,
|
endsAt,
|
||||||
data: start.data,
|
data: start.data,
|
||||||
|
payload: start.payload,
|
||||||
}
|
}
|
||||||
this.room.status = "in_round"
|
this.room.status = "in_round"
|
||||||
this.broadcastState()
|
this.broadcastState()
|
||||||
|
|
|
||||||
|
|
@ -73,4 +73,6 @@ export interface RoomGameController {
|
||||||
handleVote(playerId: string, answer: Answer): void
|
handleVote(playerId: string, answer: Answer): void
|
||||||
/** Relais média du DJ (blindtest) : rebroadcast media:sync à toute la room. */
|
/** Relais média du DJ (blindtest) : rebroadcast media:sync à toute la room. */
|
||||||
handleMediaControl(playerId: string, payload: MediaControlPayload): void
|
handleMediaControl(playerId: string, payload: MediaControlPayload): void
|
||||||
|
/** Reconnexion : re-pousse la manche en cours au socket d'un joueur. */
|
||||||
|
resync(socketId: string, playerId: string): void
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ export function registerRoomHandlers(
|
||||||
socket.data.playerId = player.id
|
socket.data.playerId = player.id
|
||||||
socket.data.roomCode = room.code
|
socket.data.roomCode = room.code
|
||||||
socket.join(room.code)
|
socket.join(room.code)
|
||||||
ack({ ok: true, data: { roomCode: room.code, playerId: player.id } })
|
ack({ ok: true, data: { roomCode: room.code, playerId: player.id, token: player.reconnectToken } })
|
||||||
broadcastState(room)
|
broadcastState(room)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
ack({ ok: false, error: toErrorPayload(err) })
|
ack({ ok: false, error: toErrorPayload(err) })
|
||||||
|
|
@ -75,13 +75,46 @@ export function registerRoomHandlers(
|
||||||
socket.data.playerId = player.id
|
socket.data.playerId = player.id
|
||||||
socket.data.roomCode = room.code
|
socket.data.roomCode = room.code
|
||||||
socket.join(room.code)
|
socket.join(room.code)
|
||||||
ack({ ok: true, data: { roomCode: room.code, playerId: player.id } })
|
ack({ ok: true, data: { roomCode: room.code, playerId: player.id, token: player.reconnectToken } })
|
||||||
broadcastState(room)
|
broadcastState(room)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
ack({ ok: false, error: toErrorPayload(err) })
|
ack({ ok: false, error: toErrorPayload(err) })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
socket.on("room:rejoin", (payload, ack) => {
|
||||||
|
const code =
|
||||||
|
typeof payload?.roomCode === "string"
|
||||||
|
? payload.roomCode.trim().toUpperCase()
|
||||||
|
: ""
|
||||||
|
const room = rooms.get(code)
|
||||||
|
const player = room?.players.get(payload?.playerId ?? "")
|
||||||
|
if (!room || !player || player.reconnectToken !== payload?.token) {
|
||||||
|
ack({
|
||||||
|
ok: false,
|
||||||
|
error: { code: "REJOIN_FAILED", message: "Reconnexion impossible." },
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
player.connected = true
|
||||||
|
player.socketId = socket.id
|
||||||
|
socket.data.playerId = player.id
|
||||||
|
socket.data.roomCode = room.code
|
||||||
|
socket.join(room.code)
|
||||||
|
ack({
|
||||||
|
ok: true,
|
||||||
|
data: {
|
||||||
|
roomCode: room.code,
|
||||||
|
playerId: player.id,
|
||||||
|
token: player.reconnectToken,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
io.to(room.code).emit("player:rejoined", { playerId: player.id })
|
||||||
|
broadcastState(room)
|
||||||
|
// Re-pousse la manche en cours au socket reconnecté (si partie en cours).
|
||||||
|
room.game?.resync(socket.id, player.id)
|
||||||
|
})
|
||||||
|
|
||||||
socket.on("player:setName", (payload) => {
|
socket.on("player:setName", (payload) => {
|
||||||
const code = socket.data.roomCode
|
const code = socket.data.roomCode
|
||||||
const playerId = socket.data.playerId
|
const playerId = socket.data.playerId
|
||||||
|
|
@ -291,6 +324,22 @@ export function registerRoomHandlers(
|
||||||
broadcastState(room)
|
broadcastState(room)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
socket.on("room:leave", () => {
|
||||||
|
const code = socket.data.roomCode
|
||||||
|
const playerId = socket.data.playerId
|
||||||
|
if (!code || !playerId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
socket.leave(code)
|
||||||
|
socket.data.roomCode = undefined
|
||||||
|
socket.data.playerId = undefined
|
||||||
|
const room = rooms.leave(code, playerId)
|
||||||
|
if (room) {
|
||||||
|
io.to(room.code).emit("player:left", { playerId })
|
||||||
|
broadcastState(room)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
socket.on("disconnect", () => {
|
socket.on("disconnect", () => {
|
||||||
const affected = rooms.handleDisconnect(socket.id)
|
const affected = rooms.handleDisconnect(socket.id)
|
||||||
if (!affected) {
|
if (!affected) {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ export interface ServerPlayer {
|
||||||
name: string
|
name: string
|
||||||
connected: boolean
|
connected: boolean
|
||||||
socketId: string | null
|
socketId: string | null
|
||||||
|
/** Secret de reconnexion (refresh/coupure). */
|
||||||
|
reconnectToken: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Titre soumis pour le blindtest. `submittedBy` reste secret jusqu'au reveal. */
|
/** Titre soumis pour le blindtest. `submittedBy` reste secret jusqu'au reveal. */
|
||||||
|
|
@ -92,6 +94,28 @@ export class RoomManager {
|
||||||
return this.rooms.get(code)
|
return this.rooms.get(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retire un joueur d'une room (départ volontaire). Réassigne l'hôte si besoin,
|
||||||
|
* supprime la room si plus personne. Renvoie la room (ou null si supprimée).
|
||||||
|
*/
|
||||||
|
leave(code: string, playerId: string): ServerRoom | null {
|
||||||
|
const room = this.rooms.get(code)
|
||||||
|
if (!room) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
room.players.delete(playerId)
|
||||||
|
room.scores.delete(playerId)
|
||||||
|
if (room.players.size === 0) {
|
||||||
|
this.rooms.delete(code)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (room.hostId === playerId) {
|
||||||
|
const named = [...room.players.values()].find((p) => p.name !== "")
|
||||||
|
room.hostId = (named ?? [...room.players.values()][0]).id
|
||||||
|
}
|
||||||
|
return room
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marque le joueur d'un socket comme déconnecté.
|
* Marque le joueur d'un socket comme déconnecté.
|
||||||
* Retourne la room affectée et le playerId, ou null si non rattaché.
|
* Retourne la room affectée et le playerId, ou null si non rattaché.
|
||||||
|
|
@ -138,7 +162,13 @@ export class RoomManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private makePlayer(name: string, socketId: string): ServerPlayer {
|
private makePlayer(name: string, socketId: string): ServerPlayer {
|
||||||
return { id: randomUUID(), name, connected: true, socketId }
|
return {
|
||||||
|
id: randomUUID(),
|
||||||
|
name,
|
||||||
|
connected: true,
|
||||||
|
socketId,
|
||||||
|
reconnectToken: randomUUID(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private generateCode(): string {
|
private generateCode(): string {
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ export function GameEndView({ snapshot }: { snapshot: RoomSnapshot }) {
|
||||||
const finalScores = useRoomStore((s) => s.finalScores)
|
const finalScores = useRoomStore((s) => s.finalScores)
|
||||||
const gameTracks = useRoomStore((s) => s.gameTracks)
|
const gameTracks = useRoomStore((s) => s.gameTracks)
|
||||||
const gameRecap = useRoomStore((s) => s.gameRecap)
|
const gameRecap = useRoomStore((s) => s.gameRecap)
|
||||||
const reset = useRoomStore((s) => s.reset)
|
const leaveRoom = useRoomStore((s) => s.leaveRoom)
|
||||||
const returnToLobby = useRoomStore((s) => s.returnToLobby)
|
const returnToLobby = useRoomStore((s) => s.returnToLobby)
|
||||||
const isHost = snapshot.hostId === playerId
|
const isHost = snapshot.hostId === playerId
|
||||||
|
|
||||||
|
|
@ -284,7 +284,7 @@ export function GameEndView({ snapshot }: { snapshot: RoomSnapshot }) {
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
<Button variant="secondary" className="w-full" onClick={reset}>
|
<Button variant="secondary" className="w-full" onClick={leaveRoom}>
|
||||||
Quitter
|
Quitter
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
||||||
34
apps/web/src/lib/session.ts
Normal file
34
apps/web/src/lib/session.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Session locale pour la reconnexion (refresh / coupure réseau).
|
||||||
|
|
||||||
|
const KEY = "nerdware-session"
|
||||||
|
|
||||||
|
export interface Session {
|
||||||
|
roomCode: string
|
||||||
|
playerId: string
|
||||||
|
token: string
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSession(): Session | null {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(KEY)
|
||||||
|
return raw ? (JSON.parse(raw) as Session) : null
|
||||||
|
} catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setSession(session: Session): void {
|
||||||
|
localStorage.setItem(KEY, JSON.stringify(session))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function patchSession(patch: Partial<Session>): void {
|
||||||
|
const current = getSession()
|
||||||
|
if (current) {
|
||||||
|
setSession({ ...current, ...patch })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearSession(): void {
|
||||||
|
localStorage.removeItem(KEY)
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { Link } from "wouter"
|
import { Link, useLocation } from "wouter"
|
||||||
import { AnimatePresence, motion } from "framer-motion"
|
import { AnimatePresence, motion } from "framer-motion"
|
||||||
|
import { LogOut } from "lucide-react"
|
||||||
import { Button } from "@workspace/ui/components/button"
|
import { Button } from "@workspace/ui/components/button"
|
||||||
import { useRoomStore } from "@/store/room"
|
import { useRoomStore } from "@/store/room"
|
||||||
import { LobbyView } from "@/components/lobby-view"
|
import { LobbyView } from "@/components/lobby-view"
|
||||||
|
|
@ -13,8 +14,10 @@ import { RoundTransition } from "@/components/round-transition"
|
||||||
import { PseudoScreen } from "@/components/pseudo-screen"
|
import { PseudoScreen } from "@/components/pseudo-screen"
|
||||||
|
|
||||||
export function RoomPage({ code }: { code: string }) {
|
export function RoomPage({ code }: { code: string }) {
|
||||||
|
const [, navigate] = useLocation()
|
||||||
const snapshot = useRoomStore((s) => s.snapshot)
|
const snapshot = useRoomStore((s) => s.snapshot)
|
||||||
const roomCode = useRoomStore((s) => s.roomCode)
|
const roomCode = useRoomStore((s) => s.roomCode)
|
||||||
|
const leaveRoom = useRoomStore((s) => s.leaveRoom)
|
||||||
const playerId = useRoomStore((s) => s.playerId)
|
const playerId = useRoomStore((s) => s.playerId)
|
||||||
const connected = useRoomStore((s) => s.connected)
|
const connected = useRoomStore((s) => s.connected)
|
||||||
const pseudoSet = useRoomStore((s) => s.pseudoSet)
|
const pseudoSet = useRoomStore((s) => s.pseudoSet)
|
||||||
|
|
@ -80,10 +83,22 @@ export function RoomPage({ code }: { code: string }) {
|
||||||
>
|
>
|
||||||
<header className="flex items-center justify-between">
|
<header className="flex items-center justify-between">
|
||||||
<RoomCode code={snapshot.code} />
|
<RoomCode code={snapshot.code} />
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
<span
|
<span
|
||||||
className={`size-2.5 rounded-full ${connected ? "bg-green-500" : "bg-red-500"}`}
|
className={`size-2.5 rounded-full ${connected ? "bg-green-500" : "bg-red-500"}`}
|
||||||
title={connected ? "Connecté" : "Déconnecté"}
|
title={connected ? "Connecté" : "Déconnecté"}
|
||||||
/>
|
/>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="secondary"
|
||||||
|
onClick={() => {
|
||||||
|
leaveRoom()
|
||||||
|
navigate("/")
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LogOut /> Quitter
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<AnimatePresence mode="wait">
|
<AnimatePresence mode="wait">
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,14 @@ import {
|
||||||
type VoteAckPayload,
|
type VoteAckPayload,
|
||||||
} from "@nerdware/shared"
|
} from "@nerdware/shared"
|
||||||
import { socket } from "@/lib/socket"
|
import { socket } from "@/lib/socket"
|
||||||
|
import {
|
||||||
|
clearSession,
|
||||||
|
getSession,
|
||||||
|
patchSession,
|
||||||
|
setSession,
|
||||||
|
} from "@/lib/session"
|
||||||
|
|
||||||
|
const saved = getSession()
|
||||||
|
|
||||||
/** Manche en cours côté client (depuis round:start). */
|
/** Manche en cours côté client (depuis round:start). */
|
||||||
export interface ActiveRound {
|
export interface ActiveRound {
|
||||||
|
|
@ -69,6 +77,7 @@ interface RoomState {
|
||||||
submitTrack: (youtubeUrl: string) => Promise<SubmitOkPayload>
|
submitTrack: (youtubeUrl: string) => Promise<SubmitOkPayload>
|
||||||
removeTrack: (trackId: string) => Promise<boolean>
|
removeTrack: (trackId: string) => Promise<boolean>
|
||||||
returnToLobby: () => void
|
returnToLobby: () => void
|
||||||
|
leaveRoom: () => void
|
||||||
vote: (choiceIndex: number) => void
|
vote: (choiceIndex: number) => void
|
||||||
voteText: (text: string) => void
|
voteText: (text: string) => void
|
||||||
voteBlindtest: (answer: BlindtestAnswer) => void
|
voteBlindtest: (answer: BlindtestAnswer) => void
|
||||||
|
|
@ -80,9 +89,9 @@ interface RoomState {
|
||||||
|
|
||||||
export const useRoomStore = create<RoomState>((set, get) => ({
|
export const useRoomStore = create<RoomState>((set, get) => ({
|
||||||
connected: socket.connected,
|
connected: socket.connected,
|
||||||
playerId: null,
|
playerId: saved?.playerId ?? null,
|
||||||
roomCode: null,
|
roomCode: saved?.roomCode ?? null,
|
||||||
pseudoSet: false,
|
pseudoSet: !!saved?.name,
|
||||||
snapshot: null,
|
snapshot: null,
|
||||||
lastError: null,
|
lastError: null,
|
||||||
round: null,
|
round: null,
|
||||||
|
|
@ -103,6 +112,7 @@ export const useRoomStore = create<RoomState>((set, get) => ({
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
socket.emit("room:create", {}, (res) => {
|
socket.emit("room:create", {}, (res) => {
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
|
setSession({ ...res.data, name: "" })
|
||||||
set({
|
set({
|
||||||
playerId: res.data.playerId,
|
playerId: res.data.playerId,
|
||||||
roomCode: res.data.roomCode,
|
roomCode: res.data.roomCode,
|
||||||
|
|
@ -120,6 +130,7 @@ export const useRoomStore = create<RoomState>((set, get) => ({
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
socket.emit("room:join", { roomCode }, (res) => {
|
socket.emit("room:join", { roomCode }, (res) => {
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
|
setSession({ ...res.data, name: "" })
|
||||||
set({
|
set({
|
||||||
playerId: res.data.playerId,
|
playerId: res.data.playerId,
|
||||||
roomCode: res.data.roomCode,
|
roomCode: res.data.roomCode,
|
||||||
|
|
@ -138,6 +149,7 @@ export const useRoomStore = create<RoomState>((set, get) => ({
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
socket.emit("player:setName", { name: name.trim() })
|
socket.emit("player:setName", { name: name.trim() })
|
||||||
|
patchSession({ name: name.trim() })
|
||||||
set({ pseudoSet: true })
|
set({ pseudoSet: true })
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -181,6 +193,11 @@ export const useRoomStore = create<RoomState>((set, get) => ({
|
||||||
socket.emit("lobby:return")
|
socket.emit("lobby:return")
|
||||||
},
|
},
|
||||||
|
|
||||||
|
leaveRoom: () => {
|
||||||
|
socket.emit("room:leave")
|
||||||
|
get().reset()
|
||||||
|
},
|
||||||
|
|
||||||
vote: (choiceIndex) => {
|
vote: (choiceIndex) => {
|
||||||
if (get().hasVoted) {
|
if (get().hasVoted) {
|
||||||
return
|
return
|
||||||
|
|
@ -212,7 +229,8 @@ export const useRoomStore = create<RoomState>((set, get) => ({
|
||||||
boom: () => set((s) => ({ boomKey: s.boomKey + 1 })),
|
boom: () => set((s) => ({ boomKey: s.boomKey + 1 })),
|
||||||
|
|
||||||
clearError: () => set({ lastError: null }),
|
clearError: () => set({ lastError: null }),
|
||||||
reset: () =>
|
reset: () => {
|
||||||
|
clearSession()
|
||||||
set({
|
set({
|
||||||
roomCode: null,
|
roomCode: null,
|
||||||
pseudoSet: false,
|
pseudoSet: false,
|
||||||
|
|
@ -231,11 +249,34 @@ export const useRoomStore = create<RoomState>((set, get) => ({
|
||||||
roundKey: 0,
|
roundKey: 0,
|
||||||
roundKind: "quiz",
|
roundKind: "quiz",
|
||||||
roundModeChanged: false,
|
roundModeChanged: false,
|
||||||
}),
|
})
|
||||||
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Listeners socket → on pousse l'état serveur dans le store (source de vérité).
|
// Listeners socket → on pousse l'état serveur dans le store (source de vérité).
|
||||||
socket.on("connect", () => useRoomStore.setState({ connected: true }))
|
socket.on("connect", () => {
|
||||||
|
useRoomStore.setState({ connected: true })
|
||||||
|
// Reconnexion : on retente de réintégrer la room mémorisée (refresh/coupure).
|
||||||
|
const s = getSession()
|
||||||
|
if (!s) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
socket.emit(
|
||||||
|
"room:rejoin",
|
||||||
|
{ roomCode: s.roomCode, playerId: s.playerId, token: s.token },
|
||||||
|
(res) => {
|
||||||
|
if (res.ok) {
|
||||||
|
useRoomStore.setState({
|
||||||
|
roomCode: res.data.roomCode,
|
||||||
|
playerId: res.data.playerId,
|
||||||
|
pseudoSet: !!s.name,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
useRoomStore.getState().reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
socket.on("disconnect", () => useRoomStore.setState({ connected: false }))
|
socket.on("disconnect", () => useRoomStore.setState({ connected: false }))
|
||||||
socket.on("room:state", (snapshot) =>
|
socket.on("room:state", (snapshot) =>
|
||||||
useRoomStore.setState(
|
useRoomStore.setState(
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,14 @@ export interface RoomCreatePayload {
|
||||||
export interface RoomCreatedPayload {
|
export interface RoomCreatedPayload {
|
||||||
roomCode: string
|
roomCode: string
|
||||||
playerId: string
|
playerId: string
|
||||||
|
/** Jeton secret pour se reconnecter après un refresh/coupure. */
|
||||||
|
token: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RejoinPayload {
|
||||||
|
roomCode: string
|
||||||
|
playerId: string
|
||||||
|
token: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RoomJoinPayload {
|
export interface RoomJoinPayload {
|
||||||
|
|
@ -139,7 +147,9 @@ export type Ack<T> = (result: { ok: true; data: T } | { ok: false; error: ErrorP
|
||||||
export interface ClientToServerEvents {
|
export interface ClientToServerEvents {
|
||||||
"room:create": (payload: RoomCreatePayload, ack: Ack<RoomCreatedPayload>) => void
|
"room:create": (payload: RoomCreatePayload, ack: Ack<RoomCreatedPayload>) => void
|
||||||
"room:join": (payload: RoomJoinPayload, ack: Ack<RoomCreatedPayload>) => void
|
"room:join": (payload: RoomJoinPayload, ack: Ack<RoomCreatedPayload>) => void
|
||||||
|
"room:rejoin": (payload: RejoinPayload, ack: Ack<RoomCreatedPayload>) => void
|
||||||
"player:setName": (payload: SetNamePayload) => void
|
"player:setName": (payload: SetNamePayload) => void
|
||||||
|
"room:leave": () => void
|
||||||
"lobby:updateSettings": (payload: UpdateSettingsPayload) => void
|
"lobby:updateSettings": (payload: UpdateSettingsPayload) => void
|
||||||
"lobby:return": () => void
|
"lobby:return": () => void
|
||||||
"game:start": (ack: Ack<null>) => void
|
"game:start": (ack: Ack<null>) => void
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue