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
All checks were successful
Build and Push Docker Image / docker (push) Successful in 9s
This commit is contained in:
@@ -19,8 +19,12 @@ function showToast(msg) {
|
|||||||
function handleConnected(msg) {
|
function handleConnected(msg) {
|
||||||
state.playerId = msg.playerId;
|
state.playerId = msg.playerId;
|
||||||
if (msg.sessionId) {
|
if (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);
|
cacheSessionId(msg.sessionId);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
const stored = localStorage.getItem('playerName');
|
const stored = localStorage.getItem('playerName');
|
||||||
if (stored) {
|
if (stored) {
|
||||||
if ($nameLobby && $nameLobby.value !== stored) {
|
if ($nameLobby && $nameLobby.value !== stored) {
|
||||||
|
|||||||
@@ -20,11 +20,24 @@ function drawNextTrack(room) {
|
|||||||
export function setupWebSocket(server) {
|
export function setupWebSocket(server) {
|
||||||
const wss = new WebSocketServer({ server });
|
const wss = new WebSocketServer({ server });
|
||||||
wss.on('connection', (ws) => {
|
wss.on('connection', (ws) => {
|
||||||
const id = uuidv4();
|
// Create a tentative player identity, but don't immediately commit or send it.
|
||||||
const sessionId = uuidv4();
|
const newId = uuidv4();
|
||||||
let player = { id, sessionId, name: `Player-${id.slice(0, 4)}`, ws, connected: true, roomId: null };
|
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 {} };
|
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) {
|
function isParticipant(room, pid) {
|
||||||
if (!room) return false;
|
if (!room) return false;
|
||||||
@@ -39,6 +52,7 @@ export function setupWebSocket(server) {
|
|||||||
|
|
||||||
// Allow client to resume by session token
|
// Allow client to resume by session token
|
||||||
if (msg.type === 'resume') {
|
if (msg.type === 'resume') {
|
||||||
|
clearTimeout(helloTimer);
|
||||||
const reqSession = String(msg.sessionId || '');
|
const reqSession = String(msg.sessionId || '');
|
||||||
if (!reqSession) { send('resume_result', { ok: false, reason: 'no_session' }); return; }
|
if (!reqSession) { send('resume_result', { ok: false, reason: 'no_session' }); return; }
|
||||||
let found = null; let foundRoom = null;
|
let found = null; let foundRoom = null;
|
||||||
@@ -61,7 +75,10 @@ export function setupWebSocket(server) {
|
|||||||
// Notify room
|
// Notify room
|
||||||
broadcast(foundRoom, 'room_update', { room: roomSummary(foundRoom) });
|
broadcast(foundRoom, 'room_update', { room: roomSummary(foundRoom) });
|
||||||
}
|
}
|
||||||
|
// Send resume result and an explicit connected that preserves their original session.
|
||||||
send('resume_result', { ok: true, playerId: found.id, roomId: foundRoom?.id });
|
send('resume_result', { ok: true, playerId: found.id, roomId: foundRoom?.id });
|
||||||
|
helloSent = true;
|
||||||
|
send('connected', { playerId: found.id, sessionId: found.sessionId });
|
||||||
return;
|
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 === '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') {
|
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' });
|
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;
|
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 inProgress = room.state.status === 'playing' || room.state.status === 'ended';
|
||||||
const wasParticipant = isParticipant(room, player.id);
|
const wasParticipant = isParticipant(room, player.id);
|
||||||
@@ -161,6 +185,7 @@ export function setupWebSocket(server) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ws.on('close', () => {
|
ws.on('close', () => {
|
||||||
|
clearTimeout(helloTimer);
|
||||||
// Mark player disconnected but keep them in the room for resume
|
// Mark player disconnected but keep them in the room for resume
|
||||||
try {
|
try {
|
||||||
if (player) {
|
if (player) {
|
||||||
@@ -172,7 +197,5 @@ export function setupWebSocket(server) {
|
|||||||
}
|
}
|
||||||
} catch {}
|
} 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) }); } });
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user