update block broadcasting and chat system

This commit is contained in:
jwinterm
2025-05-26 08:47:53 -04:00
parent ab67728fb2
commit eb2bdaa825
3 changed files with 188 additions and 52 deletions
+35 -23
View File
@@ -98,29 +98,41 @@
console.log("UI available:", typeof UI !== 'undefined');
console.log("InputHandler available:", typeof InputHandler !== 'undefined');
// Initialize Game
if (typeof Game !== 'undefined') {
console.log("Initializing Game...");
Game.init();
} else {
console.error("Game object not found!");
}
// Initialize SocketHandlers (CRITICAL for receiving game_start events!)
if (typeof SocketHandlers !== 'undefined') {
console.log("Initializing SocketHandlers...");
SocketHandlers.init();
} else {
console.error("SocketHandlers object not found!");
}
// Initialize InputHandler
if (typeof InputHandler !== 'undefined') {
console.log("Initializing InputHandler...");
InputHandler.init();
} else {
console.error("InputHandler object not found!");
}
// Small delay to ensure all modules are fully loaded
setTimeout(function() {
// Initialize Game first
if (typeof Game !== 'undefined') {
console.log("Initializing Game...");
const gameInitResult = Game.init();
console.log("Game initialization result:", gameInitResult);
// Ensure welcome screen is shown after a brief delay
setTimeout(function() {
if (gameInitResult && typeof ScreenManager !== 'undefined' && ScreenManager.drawWelcomeScreen) {
console.log("Ensuring welcome screen is drawn...");
ScreenManager.drawWelcomeScreen();
}
}, 50);
} else {
console.error("Game object not found!");
}
// Initialize SocketHandlers (CRITICAL for receiving game_start events!)
if (typeof SocketHandlers !== 'undefined') {
console.log("Initializing SocketHandlers...");
SocketHandlers.init();
} else {
console.error("SocketHandlers object not found!");
}
// Initialize InputHandler
if (typeof InputHandler !== 'undefined') {
console.log("Initializing InputHandler...");
InputHandler.init();
} else {
console.error("InputHandler object not found!");
}
}, 100); // 100ms delay to ensure everything is loaded
});
</script>
</body>
+46 -1
View File
@@ -25,6 +25,8 @@ const SocketHandlers = {
socket.on('connect', this.onConnect);
socket.on('welcome', this.onWelcome);
socket.on('message', this.onMessage);
socket.on('status_update', this.onStatusUpdate);
socket.on('chat_broadcast', this.onChatBroadcast);
// Game state handlers
socket.on('waiting_status', this.onWaitingStatus);
@@ -33,7 +35,7 @@ const SocketHandlers = {
socket.on('game_over', this.onGameOver);
socket.on('queue_cancelled', this.onQueueCancelled);
// Block height handler
// Block height handler (broadcast to all clients)
socket.on('blockheight', this.onBlockHeight);
},
@@ -59,6 +61,27 @@ const SocketHandlers = {
UI.scrollChat();
},
onStatusUpdate: function(data) {
// Handle player-specific status updates
console.log("📨 STATUS UPDATE received:", data);
// Display status message with appropriate styling
const statusClass = data.type === 'error' ? 'error' : 'status';
$('#messages').append($(`<li class="${statusClass}">`).text(data.message));
UI.scrollChat();
},
onChatBroadcast: function(data) {
// Handle chat messages broadcast to all players
console.log("💬 CHAT BROADCAST received:", data);
const timestamp = new Date(data.timestamp || Date.now()).toLocaleTimeString();
const chatMsg = `[${timestamp}] ${data.username || 'Anonymous'}: ${data.message}`;
$('#messages').append($('<li class="chat">').text(chatMsg));
UI.scrollChat();
},
onWaitingStatus: function(data) {
console.log("🕒 WAITING STATUS received:", data);
@@ -181,6 +204,28 @@ const SocketHandlers = {
if (Game) {
Game._gameActive = false;
}
// Auto-return to title screen after 30 seconds
console.log("⏰ Setting 30-second timer to return to title screen...");
setTimeout(() => {
console.log("⏰ 30 seconds elapsed - returning to title screen");
$('#messages').append($('<li style="color:#888;">').text("Returning to title screen..."));
UI.scrollChat();
// Return to welcome screen
if (Game && Game._drawWelcomeScreen) {
Game._drawWelcomeScreen();
}
// Reset game state
if (typeof GameState !== 'undefined') {
GameState.reset();
}
// Focus chat for new commands
$('#chatInput').focus();
UI.updateFocusIndicator();
}, 30000); // 30 seconds
}
UI.scrollChat();
+107 -28
View File
@@ -50,14 +50,14 @@ app.get('/', function(req, res) {
if (DEBUG_MODE) {
console.log("🐛 DEBUG MODE ENABLED - Simulating blocks every 30 seconds");
// Initial debug block
io.emit('blockheight', debugBlockHeight);
// Initial debug block broadcast
broadcastBlockHeight(debugBlockHeight);
// Debug block height simulator
// Debug block height simulator - advances every 30 seconds
setInterval(function() {
debugBlockHeight++;
console.log(`🐛 DEBUG: New simulated block: ${debugBlockHeight}`);
io.emit('blockheight', debugBlockHeight);
broadcastBlockHeight(debugBlockHeight);
// Start games for waiting players
startGamesForWaiting(debugBlockHeight);
@@ -66,6 +66,13 @@ if (DEBUG_MODE) {
checkGamesTimeout(debugBlockHeight);
}, 30000); // Every 30 seconds
// Regular status broadcasting - sends current block height every 5 seconds
// This ensures all clients stay up-to-date even if they miss a block change
setInterval(function() {
broadcastBlockHeight(debugBlockHeight);
}, 5000); // Every 5 seconds
} else {
// Your original block check code
setInterval(function() {
@@ -107,7 +114,7 @@ function startGamesForWaiting(blockHeight) {
if (currentUser) {
// For queue entry, player gets 3 blocks starting from their entry block
currentUser.blockRec = blockHeight;
console.log(`🕒 QUEUE ENTRY: Player starts on block ${currentUser.blockRec}, will die after block ${currentUser.blockRec + 2}`);
console.log(`🕒 QUEUE ENTRY: Player enters on block ${currentUser.blockRec}, will die when block ${currentUser.blockRec + 1} starts`);
try {
const game = currentUser.startGame(80, 40);
@@ -138,11 +145,11 @@ function checkGamesTimeout(currentHeight) {
activeGames.forEach((game, socketId) => {
const user = getUserBySocket(socketId);
// Player has 3 blocks to complete the dungeon
// If they started on block 2, they must escape before block 5 is found
// Note: Auto-entry players start timing from next block for consistent behavior
if (user && user.blockRec && currentHeight > user.blockRec + 2) {
console.log(`💀 GAME TIMEOUT for player ${socketId}: started timing on block ${user.blockRec}, current block ${currentHeight} (died after block ${user.blockRec + 2})`);
// Players die the block after they enter
// If they entered on block 1, they die when block 2 starts
// If they entered on block 2, they die when block 3 starts
if (user && user.blockRec && currentHeight > user.blockRec) {
console.log(`💀 GAME TIMEOUT for player ${socketId}: entered on block ${user.blockRec}, died on block ${currentHeight}`);
// Game has timed out - player didn't escape in time
game.gameState = 'lost';
@@ -157,6 +164,52 @@ function checkGamesTimeout(currentHeight) {
});
}
// ====== SOCKET EVENT BROADCASTING HELPERS ======
// These functions abstract the broadcasting logic to support future features like spectating
/**
* Send game state update to player and any spectators
* @param {string} playerSocketId - The socket ID of the player
* @param {object} gameState - The game state to broadcast
*/
function sendGameUpdate(playerSocketId, gameState) {
// Send to the player
io.to(playerSocketId).emit('game_update', gameState);
// TODO: Future spectator support
// Get list of spectators for this game and broadcast to them too
// const spectators = getSpectatorsForPlayer(playerSocketId);
// spectators.forEach(spectatorId => {
// io.to(spectatorId).emit('spectator_update', {
// playerSocketId: playerSocketId,
// gameState: gameState
// });
// });
}
/**
* Broadcast block height to all connected clients
* @param {number} blockHeight - Current block height
*/
function broadcastBlockHeight(blockHeight) {
console.log(`📡 Broadcasting block height ${blockHeight} to all clients`);
io.emit('blockheight', blockHeight);
}
/**
* Send status update to a specific player
* @param {string} socketId - The socket ID of the player
* @param {string} type - Type of status (info, error, warning, etc.)
* @param {string} message - The status message
*/
function sendStatusUpdate(socketId, type, message) {
io.to(socketId).emit('status_update', {
type: type,
message: message,
timestamp: Date.now()
});
}
// Socket.io handlers
io.on('connection', function(socket) {
console.log('A user connected');
@@ -175,23 +228,30 @@ io.on('connection', function(socket) {
// Insert into visitors DB
db.insertVisitor(socket.client.id, socket.handshake.address, 0);
// Send welcome message
// Send welcome message and immediate status
io.to(socket.client.id).emit('welcome', socket.client.id);
// Send current block height immediately to new connections
const currentBlock = DEBUG_MODE ? debugBlockHeight : lastBlockHeight;
console.log(`📈 Sending current block height ${currentBlock} to new connection ${socket.id}`);
io.to(socket.id).emit('blockheight', currentBlock);
// Send connection status update (player-specific)
sendStatusUpdate(socket.id, 'connection', 'Connected to Wowngeon server');
socket.on('chat message', function(msg) {
console.log('Message received:', msg);
// Handle game commands (player-specific responses)
if (msg.toLowerCase() == 'hello') {
io.to(`${socket.client.id}`).emit('message',
// Send status update to this player only
sendStatusUpdate(socket.id, 'help',
'Welcome, to enter the dungeon type Enter, and you will be given a Wownero address, ' +
'which you must send between 10-100 WOW to enter. The more you send, the more you can win.');
return; // Don't broadcast commands as chat
}
// ====== GAME ENTRY HANDLERS ======
// There are two ways to enter a game:
// 1. QUEUE ENTRY: Type "enter" and wait for next block (players get 3 blocks from entry block)
// 2. AUTO ENTRY: Type "enter" and start immediately in debug mode (players get 3 blocks from next block)
// Update the 'enter' command handler to start game immediately for testing
// Handle game entry commands
if (msg.toLowerCase() == 'enter') {
console.log(`Player ${socket.id} requested to enter the dungeon - STARTING IMMEDIATELY`);
@@ -203,11 +263,11 @@ io.on('connection', function(socket) {
// Start game immediately for faster testing
try {
// For auto-entry in debug mode, give player 3 blocks starting from NEXT block
// This ensures consistent death timing regardless of entry method
// For auto-entry in debug mode, player enters on current block
// They will die when the next block starts
const currentBlock = debugBlockHeight || lastBlockHeight || 1;
currentUser.blockRec = currentBlock + 1; // Start timing from NEXT block
console.log(`🕒 AUTO-ENTRY: Player starts on block ${currentUser.blockRec}, will die after block ${currentUser.blockRec + 2}`);
currentUser.blockRec = currentBlock; // Player enters on current block
console.log(`🕒 AUTO-ENTRY: Player enters on block ${currentUser.blockRec}, will die when block ${currentUser.blockRec + 1} starts`);
const game = currentUser.startGame(80, 40);
activeGames.set(socket.id, game);
@@ -220,11 +280,12 @@ io.on('connection', function(socket) {
console.log(`Game started immediately for player ${socket.id}`);
} catch (error) {
console.error(`Error creating game:`, error);
io.to(socket.id).emit('message', 'Error starting game: ' + error.message);
sendStatusUpdate(socket.id, 'error', 'Error starting game: ' + error.message);
}
} else {
io.to(socket.id).emit('message', 'Error: Could not start game. Please try again.');
sendStatusUpdate(socket.id, 'error', 'Error: Could not start game. Please try again.');
}
return; // Don't broadcast commands as chat
}
// Add a handler for the 'cancel' command
else if (msg.toLowerCase() == 'cancel') {
@@ -233,16 +294,32 @@ io.on('connection', function(socket) {
if (index !== -1) {
WAITING_PLAYERS.splice(index, 1);
io.to(socket.id).emit('message', 'You have left the queue.');
sendStatusUpdate(socket.id, 'info', 'You have left the queue.');
// Reset to welcome screen
io.to(socket.id).emit('queue_cancelled');
} else {
io.to(socket.id).emit('message', 'You were not in the queue.');
// Send status update to this player only
sendStatusUpdate(socket.id, 'error', 'You were not in the queue.');
}
return; // Don't broadcast commands as chat
}
// For all other messages (actual chat), broadcast to all clients
else {
io.emit('message', msg);
console.log(`💬 Broadcasting chat message from ${socket.id}: "${msg}"`);
// Get user info for chat display
const currentUser = getUserBySocket(socket.id);
const username = currentUser?.username || `User_${socket.id.substr(-4)}`;
// Broadcast chat message to ALL clients
io.emit('chat_broadcast', {
username: username,
message: msg,
timestamp: Date.now(),
socketId: socket.id
});
}
});
@@ -309,12 +386,14 @@ io.on('connection', function(socket) {
// Log before sending to client for debugging
console.log(`Sending game_update to ${socket.id} after player move. Player:`, updatedGameState.player, "Visible tiles keys:", Object.keys(updatedGameState.visibleTiles || {}));
io.to(socket.id).emit('game_update', updatedGameState);
// Send game update to player (current implementation)
// TODO: In future, also broadcast to spectators of this game
sendGameUpdate(socket.id, updatedGameState);
} else {
console.log(`Player move from ${socket.id} was invalid or resulted in no change.`);
// Optionally, send an update even for invalid moves if you want to provide feedback
// const currentGameState = game.getState();
// io.to(socket.id).emit('game_update', currentGameState);
// sendGameUpdate(socket.id, currentGameState);
}
} else {
console.error(`Invalid moveData received from ${socket.id}:`, moveData);