diff --git a/api/socket.io.js b/api/socket.io.js
deleted file mode 100644
index 96da8fb..0000000
--- a/api/socket.io.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import { Server as SocketServer } from 'socket.io';
-
-const ioHandler = (req, res) => {
- if (!res.socket.server.io) {
- console.log('*First use, starting socket.io');
- const io = new SocketServer(res.socket.server, {
- path: '/api/socketio',
- addTrailingSlash: false,
- });
-
- io.on('connection', (socket) => {
- console.log('A user connected');
- // Your socket event handlers
- });
-
- res.socket.server.io = io;
- } else {
- console.log('socket.io already running');
- }
- res.end();
-}
-
-export const config = {
- api: {
- bodyParser: false
- }
-};
-
-export default ioHandler;
\ No newline at end of file
diff --git a/api/socketio.js b/api/socketio.js
new file mode 100644
index 0000000..0782362
--- /dev/null
+++ b/api/socketio.js
@@ -0,0 +1,28 @@
+import { Server } from "socket.io";
+import { setupSocketHandlers } from "../src/utils/socketHandlers";
+
+const ioHandler = (req, res) => {
+ if (!res.socket.server.io) {
+ console.log("*First use, starting socket.io");
+
+ const io = new Server(res.socket.server, {
+ path: "/api/socketio",
+ addTrailingSlash: false,
+ });
+
+ setupSocketHandlers(io);
+
+ res.socket.server.io = io;
+ } else {
+ console.log("socket.io already running");
+ }
+ res.end();
+};
+
+export const config = {
+ api: {
+ bodyParser: false,
+ },
+};
+
+export default ioHandler;
\ No newline at end of file
diff --git a/public/admin.html b/public/admin.html
index 2b253a2..ee19d65 100644
--- a/public/admin.html
+++ b/public/admin.html
@@ -3,52 +3,49 @@
Game Results
@@ -39,7 +39,7 @@
Game Results
-
+
×
How It Works
@@ -53,10 +53,9 @@ AWS Rekognition
-
+
+
-
-
\ No newline at end of file
diff --git a/public/js/admin.js b/public/js/admin.js
index 07d4620..c723323 100644
--- a/public/js/admin.js
+++ b/public/js/admin.js
@@ -1,6 +1,7 @@
const socket = io({
- transports: ['websocket', 'polling']
- });
+ transports: ['polling']
+});
+
const playerList = document.getElementById('player-list');
const startGameButton = document.getElementById('start-game');
const promptSelect = document.getElementById('prompt-select');
@@ -10,42 +11,45 @@ const endGameButton = document.getElementById('end-game');
const gameResults = document.getElementById('game-results');
const resultsList = document.getElementById('results-list');
const leaderboardLink = document.getElementById('leaderboard-link');
+const syncButton = document.getElementById('sync-button');
let selectedPrompt = null;
-const syncButton = document.getElementById('sync-button');
-syncButton.addEventListener('click', () => {
- socket.emit('syncGameSessions');
-});
+if (syncButton) {
+ syncButton.addEventListener('click', () => {
+ socket.emit('syncGameSessions');
+ });
+}
socket.on('gameSessionsUpdated', (updatedGames) => {
- updateOngoingGames(updatedGames);
+ updateOngoingGames(updatedGames);
});
function updateOngoingGames(games) {
const ongoingGamesElement = document.getElementById('ongoing-games');
+ if (!ongoingGamesElement) return;
ongoingGamesElement.innerHTML = games.map(game => `
Game #${game.id}
Players: ${game.players.join(', ')}
Prompt: ${game.prompt}
Status: ${game.status}
-
+
`).join('');
- }
+}
async function fetchPrompts() {
try {
const response = await fetch(`/api/prompts`);
console.log('Response status:', response.status);
-
+
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
-
+
const data = await response.json();
console.log('Response data:', data);
-
+
displayPrompts(data);
populatePromptSelect(data);
} catch (error) {
@@ -68,41 +72,8 @@ function displayPrompts(prompts) {
}
}
-async function endGame(io, session) {
- try {
- session.status = 'ended';
- session.endTime = new Date();
-
- const results = session.submissions.map(sub => ({
- playerName: sub.playerName,
- score: sub.score
- }));
-
- await session.save();
-
- io.to(session._id.toString()).emit('gameEnded', { results, gameSessionId: session._id });
- io.emit('adminGameEnded', { results, gameSessionId: session._id });
-
- ongoingGames.delete(session._id.toString());
-
- recentResults.unshift({
- id: session._id.toString(),
- prompt: session.prompt.name,
- players: results
- });
- if (recentResults.length > 5) recentResults.pop();
-
- emitLeaderboardUpdate(io);
-
- // Sync game sessions after ending a game
- const updatedGames = await syncGameSessions();
- io.emit('gameSessionsUpdated', updatedGames);
- } catch (error) {
- console.error('Error ending game:', error);
- }
- }
-
function populatePromptSelect(prompts) {
+ if (!promptSelect) return;
promptSelect.innerHTML = '
';
prompts.forEach(prompt => {
const option = document.createElement('option');
@@ -115,73 +86,101 @@ function populatePromptSelect(prompts) {
document.addEventListener('DOMContentLoaded', () => {
fetchPrompts();
const workflowCardsContainer = document.getElementById('workflow-cards-container');
- createWorkflowCards(workflowCardsContainer);
- updateWorkflowStage('player-login');
+ if (workflowCardsContainer && typeof createWorkflowCards === 'function') {
+ createWorkflowCards(workflowCardsContainer);
+ }
+ if (typeof updateWorkflowStage === 'function') {
+ updateWorkflowStage('player-login');
+ }
});
-promptSelect.addEventListener('change', (e) => {
- selectedPrompt = e.target.value;
- startGameButton.disabled = !selectedPrompt;
-});
+if (promptSelect) {
+ promptSelect.addEventListener('change', (e) => {
+ selectedPrompt = e.target.value;
+ if (startGameButton) startGameButton.disabled = !selectedPrompt;
+ });
+}
socket.on('playerJoined', ({ players }) => {
- playerList.innerHTML = players.map(player => `
${player}`).join('');
- updateWorkflowStage('waiting-for-game');
+ if (playerList) {
+ playerList.innerHTML = players.map(player => `
${player}`).join('');
+ }
+ if (typeof updateWorkflowStage === 'function') {
+ updateWorkflowStage('waiting-for-game');
+ }
});
socket.on('drawingAnalyzed', ({ playerName }) => {
console.log(`Drawing from ${playerName} analyzed`);
- updateWorkflowStage('rekognition-analysis');
+ if (typeof updateWorkflowStage === 'function') {
+ updateWorkflowStage('rekognition-analysis');
+ }
});
socket.on('drawingReceived', ({ playerName }) => {
console.log(`Drawing received from ${playerName}`);
- updateWorkflowStage('player-submits');
+ if (typeof updateWorkflowStage === 'function') {
+ updateWorkflowStage('player-submits');
+ }
});
socket.on('adminGameEnded', ({ results, gameSessionId }) => {
const recentResultsElement = document.getElementById('recent-results');
- recentResultsElement.innerHTML = `
-
Most Recent Game Results (Game #${gameSessionId}):
-
- ${results.map(result => `- ${result.playerName}: ${result.score} points
`).join('')}
-
- `;
- });
-
-startGameButton.addEventListener('click', () => {
- if (selectedPrompt) {
- socket.emit('startGame', { promptId: selectedPrompt });
- activeGame.style.display = 'block';
- currentPrompt.textContent = `Current Prompt: ${promptSelect.options[promptSelect.selectedIndex].text}`;
- startGameButton.disabled = true;
- promptSelect.disabled = true;
- console.log('Starting game with prompt ID:', selectedPrompt);
- updateWorkflowStage('game-started');
+ if (recentResultsElement) {
+ recentResultsElement.innerHTML = `
+
Most Recent Game Results (Game #${gameSessionId}):
+
+ ${results.map(result => `- ${result.playerName}: ${result.score} points
`).join('')}
+
+ `;
}
});
-endGameButton.addEventListener('click', () => {
- socket.emit('endGame');
- activeGame.style.display = 'none';
- gameResults.style.display = 'block';
- startGameButton.disabled = false;
- promptSelect.disabled = false;
- leaderboardLink.style.display = 'block'; // Show the leaderboard link
-});
+if (startGameButton) {
+ startGameButton.addEventListener('click', () => {
+ if (selectedPrompt) {
+ socket.emit('startGame', { promptId: selectedPrompt });
+ if (activeGame) activeGame.style.display = 'block';
+ if (currentPrompt && promptSelect) {
+ currentPrompt.textContent = `Current Prompt: ${promptSelect.options[promptSelect.selectedIndex].text}`;
+ }
+ if (startGameButton) startGameButton.disabled = true;
+ if (promptSelect) promptSelect.disabled = true;
+ console.log('Starting game with prompt ID:', selectedPrompt);
+ if (typeof updateWorkflowStage === 'function') {
+ updateWorkflowStage('game-started');
+ }
+ }
+ });
+}
+
+if (endGameButton) {
+ endGameButton.addEventListener('click', () => {
+ socket.emit('endGame');
+ if (activeGame) activeGame.style.display = 'none';
+ if (gameResults) gameResults.style.display = 'block';
+ if (startGameButton) startGameButton.disabled = false;
+ if (promptSelect) promptSelect.disabled = false;
+ if (leaderboardLink) leaderboardLink.style.display = 'block';
+ });
+}
socket.on('gameEnded', (results) => {
- resultsList.innerHTML = results.map(result =>
- `
${result.playerName}: ${result.score} points`
- ).join('');
- gameResults.style.display = 'block';
- activeGame.style.display = 'none';
- updateWorkflowStage('vector-comparison');
+ if (resultsList) {
+ resultsList.innerHTML = results.map(result =>
+ `
${result.playerName}: ${result.score} points`
+ ).join('');
+ }
+ if (gameResults) gameResults.style.display = 'block';
+ if (activeGame) activeGame.style.display = 'none';
+ if (typeof updateWorkflowStage === 'function') {
+ updateWorkflowStage('vector-comparison');
+ }
});
socket.on('newGameSession', () => {
- gameResults.style.display = 'none';
- resultsList.innerHTML = '';
- promptSelect.value = '';
+ if (gameResults) gameResults.style.display = 'none';
+ if (resultsList) resultsList.innerHTML = '';
+ if (promptSelect) promptSelect.value = '';
selectedPrompt = null;
});
\ No newline at end of file
diff --git a/public/js/game.js b/public/js/game.js
index fabf58e..ffbe234 100644
--- a/public/js/game.js
+++ b/public/js/game.js
@@ -1,10 +1,9 @@
const socket = io({
- transports: ['websocket', 'polling']
- });
+ transports: ['polling']
+});
let playerName = localStorage.getItem('playerName') || '';
-// Function to safely get DOM elements
function getElement(id) {
const element = document.getElementById(id);
if (!element) {
@@ -25,7 +24,7 @@ const canvas = getElement('drawing-canvas');
const submitDrawingButton = getElement('submit-drawing');
const undoButton = getElement('undo-button');
const clearButton = getElement('clear-button');
-const ctx = canvas.getContext('2d');
+const ctx = canvas ? canvas.getContext('2d') : null;
const infoButton = getElement('info-button');
const infoModal = getElement('info-modal');
@@ -37,12 +36,19 @@ let currentAction = [];
let currentGameSessionId = null;
document.addEventListener('DOMContentLoaded', () => {
+ if (canvas) {
+ resizeCanvas();
+ initCanvas();
+ }
const workflowCardsContainer = document.getElementById('workflow-cards-container');
- createWorkflowCards(workflowCardsContainer);
- updateWorkflowStage('player-login');
+ if (workflowCardsContainer && typeof createWorkflowCards === 'function') {
+ createWorkflowCards(workflowCardsContainer);
+ }
+ if (typeof updateWorkflowStage === 'function') {
+ updateWorkflowStage('player-login');
+ }
});
-// Info modal functionality
if (infoButton && infoModal) {
infoButton.onclick = function() {
infoModal.style.display = "block";
@@ -62,6 +68,7 @@ window.onclick = function(event) {
}
function resizeCanvas() {
+ if (!canvas) return;
const containerWidth = canvas.parentElement.clientWidth;
canvas.width = containerWidth;
canvas.height = containerWidth;
@@ -69,6 +76,7 @@ function resizeCanvas() {
}
function initCanvas() {
+ if (!ctx) return;
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'black';
@@ -79,7 +87,7 @@ function initCanvas() {
setInterval(() => {
socket.emit('heartbeat');
-}, 30000); // every 30 seconds
+}, 30000);
socket.on('heartbeat', () => {
console.log('Heartbeat received from server');
@@ -97,41 +105,47 @@ socket.on('reconnect', () => {
});
window.addEventListener('resize', resizeCanvas);
-resizeCanvas();
-initCanvas();
if (joinGameButton) {
joinGameButton.addEventListener('click', () => {
if (!playerNameInput) return;
playerName = playerNameInput.value.trim();
if (playerName) {
- localStorage.setItem('playerName', playerName); // Store the player name
+ localStorage.setItem('playerName', playerName);
socket.emit('joinGame', { playerName });
if (loginScreen) loginScreen.style.display = 'none';
if (waitingRoom) waitingRoom.style.display = 'block';
- updateWorkflowStage('waiting-for-game');
+ if (typeof updateWorkflowStage === 'function') {
+ updateWorkflowStage('waiting-for-game');
+ }
}
});
}
socket.on('playerJoined', ({ playerName, players, gameSessionId }) => {
- playerList.innerHTML = players.map(player => `
${player}`).join('');
+ if (playerList) {
+ playerList.innerHTML = players.map(player => `
${player}`).join('');
+ }
currentGameSessionId = gameSessionId;
});
socket.on('gameStarted', ({ promptName, promptDescription, gameSessionId }) => {
- waitingRoom.style.display = 'none';
- gameScreen.style.display = 'block';
- promptElement.innerHTML = `
Draw: ${promptName}
${promptDescription}`;
+ if (waitingRoom) waitingRoom.style.display = 'none';
+ if (gameScreen) gameScreen.style.display = 'block';
+ if (promptElement) {
+ promptElement.innerHTML = `
Draw: ${promptName}
${promptDescription}`;
+ }
resizeCanvas();
initCanvas();
drawingActions = [];
currentGameSessionId = gameSessionId;
- updateWorkflowStage('game-started');
-
+ if (typeof updateWorkflowStage === 'function') {
+ updateWorkflowStage('game-started');
+ }
});
function getPosition(event) {
+ if (!canvas) return { x: 0, y: 0 };
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
@@ -154,43 +168,41 @@ function startDrawing(event) {
const { x, y } = getPosition(event);
currentAction = [{ x, y }];
draw(event);
- updateWorkflowStage('player-draws');
-
+ if (typeof updateWorkflowStage === 'function') {
+ updateWorkflowStage('player-draws');
+ }
}
function draw(event) {
- if (!isDrawing) return;
-
+ if (!isDrawing || !ctx) return;
event.preventDefault();
-
const { x, y } = getPosition(event);
-
ctx.lineTo(x, y);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x, y);
-
currentAction.push({ x, y });
}
function stopDrawing() {
if (!isDrawing) return;
isDrawing = false;
- ctx.beginPath();
+ if (ctx) ctx.beginPath();
if (currentAction.length > 1) {
drawingActions.push(currentAction);
currentAction = [];
}
}
-canvas.addEventListener('mousedown', startDrawing);
-canvas.addEventListener('mousemove', draw);
-canvas.addEventListener('mouseup', stopDrawing);
-canvas.addEventListener('mouseout', stopDrawing);
-
-canvas.addEventListener('touchstart', startDrawing);
-canvas.addEventListener('touchmove', draw);
-canvas.addEventListener('touchend', stopDrawing);
+if (canvas) {
+ canvas.addEventListener('mousedown', startDrawing);
+ canvas.addEventListener('mousemove', draw);
+ canvas.addEventListener('mouseup', stopDrawing);
+ canvas.addEventListener('mouseout', stopDrawing);
+ canvas.addEventListener('touchstart', startDrawing);
+ canvas.addEventListener('touchmove', draw);
+ canvas.addEventListener('touchend', stopDrawing);
+}
function undo() {
if (drawingActions.length === 0) return;
@@ -204,6 +216,7 @@ function clearCanvas() {
}
function redrawCanvas() {
+ if (!ctx || !canvas) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
initCanvas();
drawingActions.forEach(action => {
@@ -216,51 +229,56 @@ function redrawCanvas() {
});
}
-undoButton.addEventListener('click', undo);
-clearButton.addEventListener('click', clearCanvas);
+if (undoButton) undoButton.addEventListener('click', undo);
+if (clearButton) clearButton.addEventListener('click', clearCanvas);
-submitDrawingButton.addEventListener('click', async () => {
- const drawing = canvas.toDataURL('image/png');
- const blob = await (await fetch(drawing)).blob();
- const file = new File([blob], "drawing.png", { type: "image/png" });
+if (submitDrawingButton) {
+ submitDrawingButton.addEventListener('click', async () => {
+ if (!canvas) return;
+ const drawing = canvas.toDataURL('image/png');
+ const blob = await (await fetch(drawing)).blob();
+ const file = new File([blob], "drawing.png", { type: "image/png" });
- const formData = new FormData();
- formData.append('drawing', file);
- updateWorkflowStage('player-submits');
+ const formData = new FormData();
+ formData.append('drawing', file);
+ if (typeof updateWorkflowStage === 'function') {
+ updateWorkflowStage('player-submits');
+ }
- try {
- const response = await fetch('/api/upload', {
- method: 'POST',
- body: formData,
- });
- const data = await response.json();
- if (data.success) {
- socket.emit('submitDrawing', { playerName, filename: data.filename, gameSessionId: currentGameSessionId }, (response) => {
- if (response.success) {
- console.log('Drawing submitted successfully');
- console.log('Labels:', response.labels);
- console.log('Score:', response.score);
- submitDrawingButton.disabled = true;
- submitDrawingButton.textContent = 'Drawing Submitted!';
- } else {
- console.error('Failed to submit drawing:', response.error);
- alert('Failed to submit drawing. Please try again.');
- }
+ try {
+ const response = await fetch('/api/upload', {
+ method: 'POST',
+ body: formData,
});
- } else {
- console.error('Failed to upload drawing:', data.error);
- alert('Failed to upload drawing. Please try again.');
+ const data = await response.json();
+ if (data.success) {
+ socket.emit('submitDrawing', { playerName, filename: data.filename, gameSessionId: currentGameSessionId }, (response) => {
+ if (response.success) {
+ console.log('Drawing submitted successfully');
+ console.log('Labels:', response.labels);
+ console.log('Score:', response.score);
+ submitDrawingButton.disabled = true;
+ submitDrawingButton.textContent = 'Drawing Submitted!';
+ } else {
+ console.error('Failed to submit drawing:', response.error);
+ alert('Failed to submit drawing. Please try again.');
+ }
+ });
+ } else {
+ console.error('Failed to upload drawing:', data.error);
+ alert('Failed to upload drawing. Please try again.');
+ }
+ } catch (error) {
+ console.error('Error uploading drawing:', error);
+ alert('Error uploading drawing. Please try again.');
}
- } catch (error) {
- console.error('Error uploading drawing:', error);
- alert('Error uploading drawing. Please try again.');
- }
-});
+ });
+}
socket.on('drawingReceived', ({ score, labels }) => {
console.log('Drawing received by server. Score:', score);
console.log('Detected labels:', labels);
-
+
const labelsDisplay = document.createElement('div');
labelsDisplay.innerHTML = `
Detected Labels:
@@ -269,48 +287,50 @@ socket.on('drawingReceived', ({ score, labels }) => {
Your drawing has been submitted. Please wait for the game to end.
`;
- updateWorkflowStage('rekognition-analysis');
+ if (typeof updateWorkflowStage === 'function') {
+ updateWorkflowStage('rekognition-analysis');
+ }
const resultsElement = document.getElementById('results');
- resultsElement.innerHTML = '';
- resultsElement.appendChild(labelsDisplay);
-
+ if (resultsElement) {
+ resultsElement.innerHTML = '';
+ resultsElement.appendChild(labelsDisplay);
+ }
+
alert(`Your drawing has been received! Please wait for the game to end.`);
});
socket.on('gameEnded', ({ results, gameSessionId }) => {
console.log('Game ended. Results:', results);
-
- // Hide game screen and show results screen
+
if (gameScreen) gameScreen.style.display = 'none';
if (resultsScreen) resultsScreen.style.display = 'block';
-
+
const resultsElement = getElement('results');
if (resultsElement) {
- // Find this player's score
const playerResult = results.find(result => result.playerName === playerName);
let resultHTML = `
Game Over!
`;
-
+
if (playerResult) {
resultHTML += `
Your Score: ${playerResult.score} points
`;
} else {
resultHTML += `
Your Score: N/A
`;
}
-
- // Display all results
+
resultHTML += '
All Scores:
';
resultHTML += '
' + results.map(result =>
`- ${result.playerName}: ${result.score} points
`
).join('') + '
';
-
+
resultsElement.innerHTML = resultHTML;
}
-
- updateWorkflowStage('vector-comparison');
+
+ if (typeof updateWorkflowStage === 'function') {
+ updateWorkflowStage('vector-comparison');
+ }
currentGameSessionId = null;
- // Notify the player that the game has ended and show their score
const playerResult = results.find(result => result.playerName === playerName);
if (playerResult) {
alert(`The game has ended! Your score: ${playerResult.score} points. Check the results screen for all scores.`);
diff --git a/public/js/leaderboard.js b/public/js/leaderboard.js
index 4b2a9bd..4805bd8 100644
--- a/public/js/leaderboard.js
+++ b/public/js/leaderboard.js
@@ -1,9 +1,12 @@
-const socket = io();
+const socket = io({
+ transports: ['polling']
+});
const ongoingGamesElement = document.getElementById('ongoing-games');
const recentResultsElement = document.getElementById('recent-results');
function updateOngoingGames(games) {
+ if (!ongoingGamesElement) return;
ongoingGamesElement.innerHTML = games.map(game => `
Game #${game.id}
@@ -15,6 +18,7 @@ function updateOngoingGames(games) {
}
function updateRecentResults(results) {
+ if (!recentResultsElement) return;
recentResultsElement.innerHTML = results.map(result => `
Game #${result.id}
@@ -27,18 +31,17 @@ function updateRecentResults(results) {
`).join('');
- // Highlight the most recent game
if (results.length > 0) {
const mostRecentGame = recentResultsElement.firstElementChild;
- mostRecentGame.style.border = '2px solid green';
- mostRecentGame.style.backgroundColor = '#e6ffe6';
+ if (mostRecentGame) {
+ mostRecentGame.style.border = '2px solid green';
+ mostRecentGame.style.backgroundColor = '#e6ffe6';
+ }
}
}
-// Request initial data
socket.emit('getLeaderboardData');
-// Listen for updates
socket.on('leaderboardUpdate', (data) => {
updateOngoingGames(data.ongoingGames);
updateRecentResults(data.recentResults);
diff --git a/public/leaderboard.html b/public/leaderboard.html
index 0535052..464759f 100644
--- a/public/leaderboard.html
+++ b/public/leaderboard.html
@@ -3,52 +3,25 @@
-
Leaderboard - Multiplayer Drawing Game
+
Leaderboard
-
-
-
+
-
-
+