feat: Enhance session persistence by marking players offline in active games and improving rejoin room with server callbacks.
All checks were successful
Build and Deploy / build (push) Successful in 1m11s
All checks were successful
Build and Deploy / build (push) Successful in 1m11s
This commit is contained in:
@@ -151,7 +151,7 @@ io.on('connection', (socket) => {
|
||||
});
|
||||
|
||||
// RE-IMPLEMENTING rejoin_room with playerId
|
||||
socket.on('rejoin_room', ({ roomId, playerId }) => {
|
||||
socket.on('rejoin_room', ({ roomId, playerId }, callback) => {
|
||||
socket.join(roomId);
|
||||
|
||||
if (playerId) {
|
||||
@@ -172,15 +172,29 @@ io.on('connection', (socket) => {
|
||||
resumeRoomTimers(roomId);
|
||||
}
|
||||
|
||||
// Prepare Draft State if exists
|
||||
let currentDraft = null;
|
||||
if (room.status === 'drafting') {
|
||||
const draft = draftManager.getDraft(roomId);
|
||||
if (draft) socket.emit('draft_update', draft);
|
||||
currentDraft = draftManager.getDraft(roomId);
|
||||
if (currentDraft) socket.emit('draft_update', currentDraft);
|
||||
}
|
||||
|
||||
// ACK Callback
|
||||
if (typeof callback === 'function') {
|
||||
callback({ success: true, room, draftState: currentDraft });
|
||||
}
|
||||
} else {
|
||||
// Room found but player not in it? Or room not found?
|
||||
// If room exists but player not in list, it failed.
|
||||
if (typeof callback === 'function') {
|
||||
callback({ success: false, message: 'Player not found in room or room closed' });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Just get room if no playerId? Should rare happen
|
||||
const room = roomManager.getRoom(roomId);
|
||||
if (room) socket.emit('room_update', room);
|
||||
// Missing playerId
|
||||
if (typeof callback === 'function') {
|
||||
callback({ success: false, message: 'Missing Player ID' });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -202,6 +216,29 @@ io.on('connection', (socket) => {
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('kick_player', ({ roomId, targetId }) => {
|
||||
const context = getContext();
|
||||
if (!context || !context.player.isHost) return; // Verify host
|
||||
|
||||
// Get target socketId before removal to notify them
|
||||
// Note: getPlayerBySocket works if they are connected.
|
||||
// We might need to find target in room.players directly.
|
||||
const room = roomManager.getRoom(roomId);
|
||||
if (room) {
|
||||
const target = room.players.find(p => p.id === targetId);
|
||||
if (target) {
|
||||
const updatedRoom = roomManager.kickPlayer(roomId, targetId);
|
||||
if (updatedRoom) {
|
||||
io.to(roomId).emit('room_update', updatedRoom);
|
||||
if (target.socketId) {
|
||||
io.to(target.socketId).emit('kicked', { message: 'You have been kicked by the host.' });
|
||||
}
|
||||
console.log(`Player ${targetId} kicked from room ${roomId} by host.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Secure helper to get player context
|
||||
const getContext = () => roomManager.getPlayerBySocket(socket.id);
|
||||
|
||||
|
||||
@@ -105,18 +105,32 @@ export class RoomManager {
|
||||
const room = this.rooms.get(roomId);
|
||||
if (!room) return null;
|
||||
|
||||
room.players = room.players.filter(p => p.id !== playerId);
|
||||
if (room.status === 'waiting') {
|
||||
// Normal logic: Remove player completely
|
||||
room.players = room.players.filter(p => p.id !== playerId);
|
||||
|
||||
// If host leaves, assign new host from remaining players
|
||||
if (room.players.length === 0) {
|
||||
this.rooms.delete(roomId);
|
||||
return null;
|
||||
} else if (room.hostId === playerId) {
|
||||
const nextPlayer = room.players.find(p => p.role === 'player') || room.players[0];
|
||||
if (nextPlayer) {
|
||||
room.hostId = nextPlayer.id;
|
||||
nextPlayer.isHost = true;
|
||||
// If host leaves, assign new host from remaining players
|
||||
if (room.players.length === 0) {
|
||||
this.rooms.delete(roomId);
|
||||
return null;
|
||||
} else if (room.hostId === playerId) {
|
||||
const nextPlayer = room.players.find(p => p.role === 'player') || room.players[0];
|
||||
if (nextPlayer) {
|
||||
room.hostId = nextPlayer.id;
|
||||
nextPlayer.isHost = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Game in progress (Drafting/Playing)
|
||||
// DO NOT REMOVE PLAYER. Just mark offline.
|
||||
// This allows them to rejoin and reclaim their seat (and deck).
|
||||
const player = room.players.find(p => p.id === playerId);
|
||||
if (player) {
|
||||
player.isOffline = true;
|
||||
// Note: socketId is already handled by disconnect event usually, but if explicit leave, we should clear it?
|
||||
player.socketId = undefined;
|
||||
}
|
||||
console.log(`Player ${playerId} left active game in room ${roomId}. Marked as offline.`);
|
||||
}
|
||||
return room;
|
||||
}
|
||||
@@ -132,6 +146,16 @@ export class RoomManager {
|
||||
return this.rooms.get(roomId);
|
||||
}
|
||||
|
||||
kickPlayer(roomId: string, playerId: string): Room | null {
|
||||
const room = this.rooms.get(roomId);
|
||||
if (!room) return null;
|
||||
|
||||
room.players = room.players.filter(p => p.id !== playerId);
|
||||
|
||||
// If game was running, we might need more cleanup, but for now just removal.
|
||||
return room;
|
||||
}
|
||||
|
||||
addMessage(roomId: string, sender: string, text: string): ChatMessage | null {
|
||||
const room = this.rooms.get(roomId);
|
||||
if (!room) return null;
|
||||
|
||||
Reference in New Issue
Block a user