Refactor to single global lobby
Changes: - Removed room-based architecture, now using single global lobby - Players only need to enter their name to join - Game starts when all players in lobby are ready (min 2, max 8) - Simplified UI - no room name field, shows "Global Lobby" header - Updated all server events to use io.emit instead of io.to(roomName) Files modified: - server/index.js: Replaced rooms Map with single lobby object - public/index.html: Removed room name input, updated button text - public/js/network.js: Renamed joinRoom/leaveRoom to joinLobby/leaveLobby - public/js/ui.js: Simplified join flow, removed room name validation - public/js/app.js: Updated game header to show "GLOBAL LOBBY" - PLAN.md: Marked all phases as complete
This commit is contained in:
+82
-101
@@ -10,8 +10,12 @@ const io = new Server(server);
|
||||
// Serve static files
|
||||
app.use(express.static(path.join(__dirname, '../public')));
|
||||
|
||||
// Game state storage
|
||||
const rooms = new Map();
|
||||
// Single global lobby
|
||||
const lobby = {
|
||||
players: new Map(),
|
||||
gameStarted: false,
|
||||
gameInterval: null
|
||||
};
|
||||
|
||||
const PORT = process.env.PORT || 3000;
|
||||
|
||||
@@ -34,19 +38,8 @@ const GARBAGE_COLOR = '#666666';
|
||||
io.on('connection', (socket) => {
|
||||
console.log('Player connected:', socket.id);
|
||||
|
||||
socket.on('join-room', ({ roomName, playerName }) => {
|
||||
if (!rooms.has(roomName)) {
|
||||
rooms.set(roomName, {
|
||||
name: roomName,
|
||||
players: new Map(),
|
||||
gameStarted: false,
|
||||
gameInterval: null
|
||||
});
|
||||
}
|
||||
|
||||
const room = rooms.get(roomName);
|
||||
socket.join(roomName);
|
||||
socket.data.roomName = roomName;
|
||||
// Join global lobby
|
||||
socket.on('join-lobby', ({ playerName }) => {
|
||||
socket.data.playerName = playerName;
|
||||
|
||||
const player = {
|
||||
@@ -64,60 +57,55 @@ io.on('connection', (socket) => {
|
||||
dropInterval: 1000
|
||||
};
|
||||
|
||||
room.players.set(socket.id, player);
|
||||
lobby.players.set(socket.id, player);
|
||||
|
||||
io.to(roomName).emit('player-joined', {
|
||||
// Broadcast to all clients
|
||||
io.emit('player-joined', {
|
||||
player: { id: player.id, name: player.name },
|
||||
players: getPlayersList(room)
|
||||
players: getPlayersList()
|
||||
});
|
||||
|
||||
console.log(`${playerName} joined room ${roomName}`);
|
||||
console.log(`${playerName} joined global lobby (${lobby.players.size} players)`);
|
||||
});
|
||||
|
||||
socket.on('ready', () => {
|
||||
const roomName = socket.data.roomName;
|
||||
if (!roomName) return;
|
||||
const player = lobby.players.get(socket.id);
|
||||
if (!player) return;
|
||||
|
||||
const room = rooms.get(roomName);
|
||||
const player = room.players.get(socket.id);
|
||||
player.ready = true;
|
||||
|
||||
io.to(roomName).emit('player-joined', {
|
||||
// Broadcast updated player list
|
||||
io.emit('player-joined', {
|
||||
player: { id: player.id, name: player.name, ready: player.ready },
|
||||
players: getPlayersList(room)
|
||||
players: getPlayersList()
|
||||
});
|
||||
|
||||
if (room.players.size >= 2 && room.players.size <= 8) {
|
||||
const allReady = Array.from(room.players.values()).every(p => p.ready);
|
||||
// Check if all players ready and min 2 players
|
||||
if (lobby.players.size >= 2 && lobby.players.size <= 8) {
|
||||
const allReady = Array.from(lobby.players.values()).every(p => p.ready);
|
||||
if (allReady) {
|
||||
startGame(room);
|
||||
startGame();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('player-move', ({ playerId, direction }) => {
|
||||
const roomName = socket.data.roomName;
|
||||
if (!roomName) return;
|
||||
const room = rooms.get(roomName);
|
||||
if (!room || !room.gameStarted) return;
|
||||
if (!lobby.gameStarted) return;
|
||||
|
||||
const player = room.players.get(playerId);
|
||||
const player = lobby.players.get(playerId);
|
||||
if (!player || player.eliminated) return;
|
||||
|
||||
const newX = player.currentPiece.x + (direction === 'left' ? -1 : 1);
|
||||
if (isValidPosition(player.currentPiece, newX, player.currentPiece.y, player.board)) {
|
||||
player.currentPiece.x = newX;
|
||||
broadcastState(room);
|
||||
broadcastState();
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('player-rotate', ({ playerId }) => {
|
||||
const roomName = socket.data.roomName;
|
||||
if (!roomName) return;
|
||||
const room = rooms.get(roomName);
|
||||
if (!room || !room.gameStarted) return;
|
||||
if (!lobby.gameStarted) return;
|
||||
|
||||
const player = room.players.get(playerId);
|
||||
const player = lobby.players.get(playerId);
|
||||
if (!player || player.eliminated) return;
|
||||
|
||||
const originalShape = player.currentPiece.shape;
|
||||
@@ -136,19 +124,16 @@ io.on('connection', (socket) => {
|
||||
if (isValidPosition(player.currentPiece, player.currentPiece.x + kick, player.currentPiece.y, player.board, rotated)) {
|
||||
player.currentPiece.shape = rotated;
|
||||
player.currentPiece.x += kick;
|
||||
broadcastState(room);
|
||||
broadcastState();
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('player-drop', ({ playerId, hard }) => {
|
||||
const roomName = socket.data.roomName;
|
||||
if (!roomName) return;
|
||||
const room = rooms.get(roomName);
|
||||
if (!room || !room.gameStarted) return;
|
||||
if (!lobby.gameStarted) return;
|
||||
|
||||
const player = room.players.get(playerId);
|
||||
const player = lobby.players.get(playerId);
|
||||
if (!player || player.eliminated) return;
|
||||
|
||||
if (hard) {
|
||||
@@ -158,45 +143,41 @@ io.on('connection', (socket) => {
|
||||
dropped++;
|
||||
}
|
||||
player.score += dropped * 2;
|
||||
lockPiece(room, player);
|
||||
lockPiece(player);
|
||||
} else {
|
||||
const newY = player.currentPiece.y + 1;
|
||||
if (isValidPosition(player.currentPiece, player.currentPiece.x, newY, player.board)) {
|
||||
player.currentPiece.y = newY;
|
||||
player.score += 1;
|
||||
broadcastState(room);
|
||||
broadcastState();
|
||||
} else {
|
||||
lockPiece(room, player);
|
||||
lockPiece(player);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
const roomName = socket.data.roomName;
|
||||
if (!roomName) return;
|
||||
const player = lobby.players.get(socket.id);
|
||||
if (player) {
|
||||
console.log(`${player.name} disconnected (${lobby.players.size - 1} remaining)`);
|
||||
|
||||
const room = rooms.get(roomName);
|
||||
if (room) {
|
||||
const player = room.players.get(socket.id);
|
||||
if (player) {
|
||||
console.log(`${player.name} left room ${roomName}`);
|
||||
if (lobby.gameStarted) {
|
||||
player.eliminated = true;
|
||||
broadcastState();
|
||||
checkGameOver();
|
||||
}
|
||||
|
||||
if (room.gameStarted) {
|
||||
player.eliminated = true;
|
||||
broadcastState(room);
|
||||
checkGameOver(room);
|
||||
}
|
||||
lobby.players.delete(socket.id);
|
||||
|
||||
room.players.delete(socket.id);
|
||||
io.to(roomName).emit('player-left', {
|
||||
playerId: socket.id,
|
||||
players: getPlayersList(room)
|
||||
});
|
||||
io.emit('player-left', {
|
||||
playerId: socket.id,
|
||||
players: getPlayersList()
|
||||
});
|
||||
|
||||
if (room.players.size === 0) {
|
||||
if (room.gameInterval) clearInterval(room.gameInterval);
|
||||
rooms.delete(roomName);
|
||||
}
|
||||
// If lobby empty and game running, stop game
|
||||
if (lobby.players.size === 0 && lobby.gameStarted) {
|
||||
if (lobby.gameInterval) clearInterval(lobby.gameInterval);
|
||||
lobby.gameStarted = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -206,8 +187,8 @@ function createEmptyBoard() {
|
||||
return Array(BOARD_HEIGHT).fill(null).map(() => Array(BOARD_WIDTH).fill(0));
|
||||
}
|
||||
|
||||
function getPlayersList(room) {
|
||||
return Array.from(room.players.values()).map(p => ({
|
||||
function getPlayersList() {
|
||||
return Array.from(lobby.players.values()).map(p => ({
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
score: p.score,
|
||||
@@ -251,7 +232,7 @@ function spawnPiece(player) {
|
||||
return isValidPosition(player.currentPiece, player.currentPiece.x, player.currentPiece.y, player.board);
|
||||
}
|
||||
|
||||
function lockPiece(room, player) {
|
||||
function lockPiece(player) {
|
||||
if (!player.currentPiece) return;
|
||||
|
||||
for (let row = 0; row < player.currentPiece.shape.length; row++) {
|
||||
@@ -272,10 +253,10 @@ function lockPiece(room, player) {
|
||||
|
||||
if (!spawnPiece(player)) player.eliminated = true;
|
||||
|
||||
if (rowsCleared > 0) sendGarbage(room, player, rowsCleared);
|
||||
if (rowsCleared > 0) sendGarbage(player, rowsCleared);
|
||||
|
||||
broadcastState(room);
|
||||
checkGameOver(room);
|
||||
broadcastState();
|
||||
checkGameOver();
|
||||
}
|
||||
|
||||
function clearRows(player) {
|
||||
@@ -298,9 +279,9 @@ function clearRows(player) {
|
||||
return rowsCleared;
|
||||
}
|
||||
|
||||
function sendGarbage(room, sender, rowsCleared) {
|
||||
function sendGarbage(sender, rowsCleared) {
|
||||
const garbageRows = rowsCleared >= 4 ? rowsCleared : Math.max(1, rowsCleared - 1);
|
||||
const opponents = Array.from(room.players.values()).filter(p => p.id !== sender.id && !p.eliminated);
|
||||
const opponents = Array.from(lobby.players.values()).filter(p => p.id !== sender.id && !p.eliminated);
|
||||
if (opponents.length === 0) return;
|
||||
for (let i = 0; i < garbageRows; i++) {
|
||||
const target = opponents[Math.floor(Math.random() * opponents.length)];
|
||||
@@ -324,10 +305,10 @@ function addGarbageToPlayer(player) {
|
||||
}
|
||||
}
|
||||
|
||||
function startGame(room) {
|
||||
room.gameStarted = true;
|
||||
function startGame() {
|
||||
lobby.gameStarted = true;
|
||||
|
||||
for (const player of room.players.values()) {
|
||||
for (const player of lobby.players.values()) {
|
||||
player.board = createEmptyBoard();
|
||||
player.score = 0;
|
||||
player.lines = 0;
|
||||
@@ -338,18 +319,18 @@ function startGame(room) {
|
||||
player.nextPiece = getRandomPiece();
|
||||
}
|
||||
|
||||
room.gameInterval = setInterval(() => gameTick(room), 50);
|
||||
lobby.gameInterval = setInterval(() => gameTick(), 50);
|
||||
|
||||
io.to(room.name).emit('game-started', {
|
||||
players: getPlayersList(room),
|
||||
states: getStates(room)
|
||||
io.emit('game-started', {
|
||||
players: getPlayersList(),
|
||||
states: getStates()
|
||||
});
|
||||
|
||||
console.log(`Game started in room ${room.name} with ${room.players.size} players`);
|
||||
console.log(`Game started with ${lobby.players.size} players`);
|
||||
}
|
||||
|
||||
function gameTick(room) {
|
||||
for (const player of room.players.values()) {
|
||||
function gameTick() {
|
||||
for (const player of lobby.players.values()) {
|
||||
if (player.eliminated) continue;
|
||||
|
||||
player.dropCounter += 50;
|
||||
@@ -358,20 +339,20 @@ function gameTick(room) {
|
||||
if (isValidPosition(player.currentPiece, player.currentPiece.x, player.currentPiece.y + 1, player.board)) {
|
||||
player.currentPiece.y++;
|
||||
} else {
|
||||
lockPiece(room, player);
|
||||
lockPiece(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
broadcastState(room);
|
||||
checkGameOver(room);
|
||||
broadcastState();
|
||||
checkGameOver();
|
||||
}
|
||||
|
||||
function broadcastState(room) {
|
||||
io.to(room.name).emit('state-update', getStates(room));
|
||||
function broadcastState() {
|
||||
io.emit('state-update', getStates());
|
||||
}
|
||||
|
||||
function getStates(room) {
|
||||
return Array.from(room.players.values()).map(p => ({
|
||||
function getStates() {
|
||||
return Array.from(lobby.players.values()).map(p => ({
|
||||
playerId: p.id,
|
||||
board: JSON.parse(JSON.stringify(p.board)),
|
||||
currentPiece: p.currentPiece ? JSON.parse(JSON.stringify(p.currentPiece)) : null,
|
||||
@@ -383,15 +364,15 @@ function getStates(room) {
|
||||
}));
|
||||
}
|
||||
|
||||
function checkGameOver(room) {
|
||||
const activePlayers = Array.from(room.players.values()).filter(p => !p.eliminated);
|
||||
function checkGameOver() {
|
||||
const activePlayers = Array.from(lobby.players.values()).filter(p => !p.eliminated);
|
||||
if (activePlayers.length <= 1) {
|
||||
io.to(room.name).emit('game-over', { states: getStates(room) });
|
||||
if (room.gameInterval) {
|
||||
clearInterval(room.gameInterval);
|
||||
room.gameInterval = null;
|
||||
io.emit('game-over', { states: getStates() });
|
||||
if (lobby.gameInterval) {
|
||||
clearInterval(lobby.gameInterval);
|
||||
lobby.gameInterval = null;
|
||||
}
|
||||
room.gameStarted = false;
|
||||
lobby.gameStarted = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user