refactor: prevent overwriting existing session on reconnect and improve player identity handling
All checks were successful
Build and Push Docker Image / docker (push) Successful in 9s

This commit is contained in:
2025-09-04 19:02:41 +02:00
parent f931d45932
commit 7361174b15
2 changed files with 35 additions and 8 deletions

View File

@@ -19,7 +19,11 @@ function showToast(msg) {
function handleConnected(msg) {
state.playerId = msg.playerId;
if (msg.sessionId) {
cacheSessionId(msg.sessionId);
// Don't clobber an existing session on reconnect; only set if none exists yet.
const existing = localStorage.getItem('sessionId');
if (!existing) {
cacheSessionId(msg.sessionId);
}
}
const stored = localStorage.getItem('playerName');
if (stored) {

View File

@@ -20,11 +20,24 @@ function drawNextTrack(room) {
export function setupWebSocket(server) {
const wss = new WebSocketServer({ server });
wss.on('connection', (ws) => {
const id = uuidv4();
const sessionId = uuidv4();
let player = { id, sessionId, name: `Player-${id.slice(0, 4)}`, ws, connected: true, roomId: null };
// Create a tentative player identity, but don't immediately commit or send it.
const newId = uuidv4();
const newSessionId = uuidv4();
let player = { id: newId, sessionId: newSessionId, name: `Player-${newId.slice(0, 4)}`, ws, connected: true, roomId: null };
const send = (type, payload) => { try { ws.send(JSON.stringify({ type, ...payload })); } catch {} };
send('connected', { playerId: id, sessionId });
// To avoid overwriting an existing session on reconnect, delay the initial
// welcome until we see if the client sends a resume message.
let helloSent = false;
const sendHello = (p) => {
if (helloSent) return;
helloSent = true;
send('connected', { playerId: p.id, sessionId: p.sessionId });
};
// Fallback: if no resume arrives shortly, treat as a fresh connection.
const helloTimer = setTimeout(() => {
sendHello(player);
}, 400);
function isParticipant(room, pid) {
if (!room) return false;
@@ -39,6 +52,7 @@ export function setupWebSocket(server) {
// Allow client to resume by session token
if (msg.type === 'resume') {
clearTimeout(helloTimer);
const reqSession = String(msg.sessionId || '');
if (!reqSession) { send('resume_result', { ok: false, reason: 'no_session' }); return; }
let found = null; let foundRoom = null;
@@ -61,7 +75,10 @@ export function setupWebSocket(server) {
// Notify room
broadcast(foundRoom, 'room_update', { room: roomSummary(foundRoom) });
}
send('resume_result', { ok: true, playerId: found.id, roomId: foundRoom?.id });
// Send resume result and an explicit connected that preserves their original session.
send('resume_result', { ok: true, playerId: found.id, roomId: foundRoom?.id });
helloSent = true;
send('connected', { playerId: found.id, sessionId: found.sessionId });
return;
}
@@ -109,6 +126,13 @@ export function setupWebSocket(server) {
if (msg.type === 'create_room') { const room = createRoom(msg.name, player); player.roomId = room.id; broadcast(room, 'room_update', { room: roomSummary(room) }); return; }
if (msg.type === 'join_room') {
const code = String(msg.code || '').toUpperCase(); const room = rooms.get(code); if (!room) return send('error', { message: 'Room not found' });
// If there's an existing player in this room with the same session, merge to avoid duplicates.
let existing = null;
for (const p of room.players.values()) { if (p.sessionId && p.sessionId === player.sessionId && p.id !== player.id) { existing = p; break; } }
if (existing) {
try { if (existing.ws && existing.ws !== ws) { try { existing.ws.terminate(); } catch {} } } catch {}
existing.ws = ws; existing.connected = true; existing.roomId = room.id; player = existing;
}
room.players.set(player.id, player); player.roomId = room.id; if (!room.state.ready) room.state.ready = {}; if (room.state.ready[player.id] == null) room.state.ready[player.id] = false;
const inProgress = room.state.status === 'playing' || room.state.status === 'ended';
const wasParticipant = isParticipant(room, player.id);
@@ -161,6 +185,7 @@ export function setupWebSocket(server) {
});
ws.on('close', () => {
clearTimeout(helloTimer);
// Mark player disconnected but keep them in the room for resume
try {
if (player) {
@@ -172,7 +197,5 @@ export function setupWebSocket(server) {
}
} catch {}
});
ws.on('close', () => { player.connected = false; if (player.roomId && rooms.has(player.roomId)) { const room = rooms.get(player.roomId); broadcast(room, 'room_update', { room: roomSummary(room) }); } });
});
}