Fix host reconnection losing player state and first-answer bonus
- Persist firstCorrectPlayerId to backend during state sync - Restore full players array (not just host) on host reconnect - Add default values to WELCOME payload for new/unmatched players - Add migration for first_correct_player_id column in game_sessions
This commit is contained in:
parent
32696ad33d
commit
035ea57274
4 changed files with 22 additions and 3 deletions
|
|
@ -71,7 +71,8 @@ export const useGame = () => {
|
||||||
const gameConfigRef = useRef<GameConfig>(DEFAULT_GAME_CONFIG);
|
const gameConfigRef = useRef<GameConfig>(DEFAULT_GAME_CONFIG);
|
||||||
const gamePinRef = useRef<string | null>(null);
|
const gamePinRef = useRef<string | null>(null);
|
||||||
const hostSecretRef = useRef<string | null>(null);
|
const hostSecretRef = useRef<string | null>(null);
|
||||||
const gameStateRef = useRef<GameState>('LANDING');
|
const gameStateRef = useRef<GameState>("LANDING");
|
||||||
|
const firstCorrectPlayerIdRef = useRef<string | null>(null);
|
||||||
|
|
||||||
useEffect(() => { timeLeftRef.current = timeLeft; }, [timeLeft]);
|
useEffect(() => { timeLeftRef.current = timeLeft; }, [timeLeft]);
|
||||||
useEffect(() => { playersRef.current = players; }, [players]);
|
useEffect(() => { playersRef.current = players; }, [players]);
|
||||||
|
|
@ -81,6 +82,7 @@ export const useGame = () => {
|
||||||
useEffect(() => { gamePinRef.current = gamePin; }, [gamePin]);
|
useEffect(() => { gamePinRef.current = gamePin; }, [gamePin]);
|
||||||
useEffect(() => { hostSecretRef.current = hostSecret; }, [hostSecret]);
|
useEffect(() => { hostSecretRef.current = hostSecret; }, [hostSecret]);
|
||||||
useEffect(() => { gameStateRef.current = gameState; }, [gameState]);
|
useEffect(() => { gameStateRef.current = gameState; }, [gameState]);
|
||||||
|
useEffect(() => { firstCorrectPlayerIdRef.current = firstCorrectPlayerId; }, [firstCorrectPlayerId]);
|
||||||
|
|
||||||
const generateGamePin = () => Math.floor(Math.random() * 900000) + 100000 + "";
|
const generateGamePin = () => Math.floor(Math.random() * 900000) + 100000 + "";
|
||||||
|
|
||||||
|
|
@ -98,6 +100,7 @@ export const useGame = () => {
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
gameState: gameStateRef.current,
|
gameState: gameStateRef.current,
|
||||||
currentQuestionIndex: currentQuestionIndexRef.current,
|
currentQuestionIndex: currentQuestionIndexRef.current,
|
||||||
|
firstCorrectPlayerId: firstCorrectPlayerIdRef.current,
|
||||||
players: playersRef.current,
|
players: playersRef.current,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
@ -296,6 +299,7 @@ export const useGame = () => {
|
||||||
setQuiz(hostData.quiz);
|
setQuiz(hostData.quiz);
|
||||||
setGameConfig(hostData.gameConfig);
|
setGameConfig(hostData.gameConfig);
|
||||||
setCurrentQuestionIndex(hostData.currentQuestionIndex || 0);
|
setCurrentQuestionIndex(hostData.currentQuestionIndex || 0);
|
||||||
|
setFirstCorrectPlayerId(hostData.firstCorrectPlayerId || null);
|
||||||
|
|
||||||
const hostPlayer = (hostData.players || []).find((p: Player) => p.id === 'host');
|
const hostPlayer = (hostData.players || []).find((p: Player) => p.id === 'host');
|
||||||
if (hostPlayer) {
|
if (hostPlayer) {
|
||||||
|
|
@ -303,7 +307,7 @@ export const useGame = () => {
|
||||||
setCurrentPlayerName('Host');
|
setCurrentPlayerName('Host');
|
||||||
setCurrentPlayerScore(hostPlayer.score);
|
setCurrentPlayerScore(hostPlayer.score);
|
||||||
setCurrentStreak(hostPlayer.streak);
|
setCurrentStreak(hostPlayer.streak);
|
||||||
setPlayers([hostPlayer]);
|
setPlayers(hostData.players || []);
|
||||||
|
|
||||||
if (hostPlayer.lastAnswerCorrect !== null) {
|
if (hostPlayer.lastAnswerCorrect !== null) {
|
||||||
setHasAnswered(true);
|
setHasAnswered(true);
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ const runMigrations = () => {
|
||||||
game_state TEXT NOT NULL DEFAULT 'LOBBY',
|
game_state TEXT NOT NULL DEFAULT 'LOBBY',
|
||||||
current_question_index INTEGER NOT NULL DEFAULT 0,
|
current_question_index INTEGER NOT NULL DEFAULT 0,
|
||||||
players_data TEXT NOT NULL DEFAULT '[]',
|
players_data TEXT NOT NULL DEFAULT '[]',
|
||||||
|
first_correct_player_id TEXT,
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
|
|
@ -52,6 +53,13 @@ const runMigrations = () => {
|
||||||
`);
|
`);
|
||||||
console.log("Migration: Created game_sessions table");
|
console.log("Migration: Created game_sessions table");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const sessionTableInfo = db.prepare("PRAGMA table_info(game_sessions)").all() as { name: string }[];
|
||||||
|
const hasFirstCorrect = sessionTableInfo.some(col => col.name === "first_correct_player_id");
|
||||||
|
if (!hasFirstCorrect) {
|
||||||
|
db.exec("ALTER TABLE game_sessions ADD COLUMN first_correct_player_id TEXT");
|
||||||
|
console.log("Migration: Added first_correct_player_id to game_sessions");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
runMigrations();
|
runMigrations();
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ CREATE TABLE IF NOT EXISTS game_sessions (
|
||||||
game_state TEXT NOT NULL DEFAULT 'LOBBY',
|
game_state TEXT NOT NULL DEFAULT 'LOBBY',
|
||||||
current_question_index INTEGER NOT NULL DEFAULT 0,
|
current_question_index INTEGER NOT NULL DEFAULT 0,
|
||||||
players_data TEXT NOT NULL DEFAULT '[]',
|
players_data TEXT NOT NULL DEFAULT '[]',
|
||||||
|
first_correct_player_id TEXT,
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ interface GameSession {
|
||||||
game_state: string;
|
game_state: string;
|
||||||
current_question_index: number;
|
current_question_index: number;
|
||||||
players_data: string;
|
players_data: string;
|
||||||
|
first_correct_player_id: string | null;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
}
|
}
|
||||||
|
|
@ -112,6 +113,7 @@ router.get('/:pin/host', (req: Request, res: Response) => {
|
||||||
gameState: session.game_state,
|
gameState: session.game_state,
|
||||||
currentQuestionIndex: session.current_question_index,
|
currentQuestionIndex: session.current_question_index,
|
||||||
players: JSON.parse(session.players_data),
|
players: JSON.parse(session.players_data),
|
||||||
|
firstCorrectPlayerId: session.first_correct_player_id,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error getting host session:', err);
|
console.error('Error getting host session:', err);
|
||||||
|
|
@ -123,7 +125,7 @@ router.patch('/:pin', (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const { pin } = req.params;
|
const { pin } = req.params;
|
||||||
const hostSecret = req.headers['x-host-secret'] as string;
|
const hostSecret = req.headers['x-host-secret'] as string;
|
||||||
const { hostPeerId, gameState, currentQuestionIndex, players } = req.body;
|
const { hostPeerId, gameState, currentQuestionIndex, players, firstCorrectPlayerId } = req.body;
|
||||||
|
|
||||||
if (!hostSecret) {
|
if (!hostSecret) {
|
||||||
res.status(401).json({ error: 'Host secret required' });
|
res.status(401).json({ error: 'Host secret required' });
|
||||||
|
|
@ -156,6 +158,10 @@ router.patch('/:pin', (req: Request, res: Response) => {
|
||||||
updates.push('players_data = ?');
|
updates.push('players_data = ?');
|
||||||
values.push(JSON.stringify(players));
|
values.push(JSON.stringify(players));
|
||||||
}
|
}
|
||||||
|
if (firstCorrectPlayerId !== undefined) {
|
||||||
|
updates.push("first_correct_player_id = ?");
|
||||||
|
values.push(firstCorrectPlayerId);
|
||||||
|
}
|
||||||
|
|
||||||
if (updates.length === 0) {
|
if (updates.length === 0) {
|
||||||
res.status(400).json({ error: 'No updates provided' });
|
res.status(400).json({ error: 'No updates provided' });
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue