Spaces:
Build error
Build error
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Colyseus Top-Down Client</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| body { | |
| font-family: 'Inter', sans-serif; | |
| background-color: #1a202c; | |
| color: #e2e8f0; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| min-height: 100vh; | |
| margin: 0; | |
| padding: 20px; | |
| box-sizing: border-box; | |
| } | |
| canvas { | |
| background-color: #2d3748; | |
| border: 2px solid #4a5568; | |
| border-radius: 10px; | |
| box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5); | |
| display: block; | |
| touch-action: none; | |
| } | |
| .container { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| width: 100%; | |
| max-width: 900px; | |
| } | |
| .controls { | |
| display: flex; | |
| gap: 10px; | |
| margin-top: 20px; | |
| flex-wrap: wrap; | |
| justify-content: center; | |
| } | |
| .chat-container { | |
| width: 100%; | |
| max-width: 800px; | |
| margin-top: 20px; | |
| background-color: #2d3748; | |
| border-radius: 10px; | |
| padding: 15px; | |
| box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5); | |
| } | |
| .chat-messages { | |
| height: 150px; | |
| overflow-y: auto; | |
| border: 1px solid #4a5568; | |
| padding: 10px; | |
| border-radius: 5px; | |
| margin-bottom: 10px; | |
| background-color: #1a202c; | |
| } | |
| .chat-input-group { | |
| display: flex; | |
| gap: 10px; | |
| } | |
| .message-box-overlay { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: rgba(0, 0, 0, 0.7); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| z-index: 1000; | |
| opacity: 0; | |
| visibility: hidden; | |
| transition: opacity 0.3s ease, visibility 0.3s ease; | |
| } | |
| .message-box-overlay.show { | |
| opacity: 1; | |
| visibility: visible; | |
| } | |
| .message-box { | |
| background: #2d3748; | |
| padding: 25px; | |
| border-radius: 10px; | |
| box-shadow: 0 5px 15px rgba(0, 0, 0, 0.6); | |
| text-align: center; | |
| max-width: 400px; | |
| width: 90%; | |
| color: #e2e8f0; | |
| } | |
| .message-box h3 { | |
| margin-top: 0; | |
| color: #a0aec0; | |
| } | |
| .message-box button { | |
| background-color: #4299e1; | |
| color: white; | |
| padding: 10px 20px; | |
| border: none; | |
| border-radius: 5px; | |
| cursor: pointer; | |
| font-size: 1rem; | |
| transition: background-color 0.2s ease; | |
| margin-top: 15px; | |
| } | |
| .message-box button:hover { | |
| background-color: #3182ce; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-900 text-gray-100 flex flex-col items-center justify-center min-h-screen p-5"> | |
| <div class="container"> | |
| <h1 class="text-3xl font-bold mb-6 text-blue-400">Colyseus Top-Down Game</h1> | |
| <canvas id="gameCanvas" width="800" height="600" class="rounded-lg shadow-xl"></canvas> | |
| <div class="controls mt-6"> | |
| <p class="text-lg font-semibold">Move with: <span class="text-yellow-400">Arrow Keys</span></p> | |
| </div> | |
| <div class="chat-container"> | |
| <h2 class="text-xl font-semibold mb-3 text-blue-300">Chat</h2> | |
| <div id="chatMessages" class="chat-messages text-sm"> | |
| </div> | |
| <div class="chat-input-group"> | |
| <input type="text" id="chatInput" placeholder="Type your message..." | |
| class="flex-grow p-3 rounded-md bg-gray-700 text-gray-100 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500"> | |
| <button id="sendChat" | |
| class="px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-bold rounded-md shadow-lg transition duration-200 ease-in-out transform hover:scale-105"> | |
| Send | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="messageBoxOverlay" class="message-box-overlay"> | |
| <div class="message-box"> | |
| <h3 id="messageBoxTitle"></h3> | |
| <p id="messageBoxContent"></p> | |
| <button id="messageBoxClose">OK</button> | |
| </div> | |
| </div> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/colyseus.js/0.15.10/Colyseus.min.js"></script> | |
| <script> | |
| function showMessageBox(title, message) { | |
| const overlay = document.getElementById('messageBoxOverlay'); | |
| const boxTitle = document.getElementById('messageBoxTitle'); | |
| const boxContent = document.getElementById('messageBoxContent'); | |
| const closeButton = document.getElementById('messageBoxClose'); | |
| boxTitle.textContent = title; | |
| boxContent.textContent = message; | |
| overlay.classList.add('show'); | |
| closeButton.onclick = () => { | |
| overlay.classList.remove('show'); | |
| }; | |
| } | |
| const canvas = document.getElementById('gameCanvas'); | |
| const ctx = canvas.getContext('2d'); | |
| let client; | |
| let room; | |
| let currentPlayerId = null; | |
| const chatMessagesDiv = document.getElementById('chatMessages'); | |
| const chatInput = document.getElementById('chatInput'); | |
| const sendChatButton = document.getElementById('sendChat'); | |
| const PLAYER_SIZE = 30; | |
| const PLAYER_SPEED = 5; | |
| const keys = { | |
| ArrowUp: false, | |
| ArrowDown: false, | |
| ArrowLeft: false, | |
| ArrowRight: false | |
| }; | |
| async function connectAndJoin() { | |
| try { | |
| // Defensive check to ensure Colyseus is loaded | |
| if (typeof Colyseus === 'undefined' || !Colyseus.Client) { | |
| throw new Error("Colyseus library not loaded. Please check network connection or CDN availability."); | |
| } | |
| // Initialize client here, after Colyseus library is loaded | |
| const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; | |
| client = new Colyseus.Client(`${protocol}//${window.location.host}`); | |
| room = await client.joinOrCreate("my_room"); | |
| currentPlayerId = room.sessionId; | |
| showMessageBox("Connected!", `Joined room: ${room.name} as ${room.sessionId}`); | |
| console.log("Joined successfully", room); | |
| room.onStateChange((state) => { | |
| console.log("New state:", state.toJSON()); | |
| drawGame(state); | |
| }); | |
| room.state.players.onAdd = (player, sessionId) => { | |
| console.log(`Player ${sessionId} added at (${player.x}, ${player.y})`); | |
| player.onChange = (changes) => { }; | |
| }; | |
| room.state.players.onRemove = (player, sessionId) => { | |
| console.log(`Player ${sessionId} removed.`); | |
| }; | |
| room.onMessage("welcome", (message) => { | |
| console.log("Server welcome message:", message); | |
| }); | |
| room.onMessage("chat", (message) => { | |
| addChatMessage(message); | |
| }); | |
| room.onLeave((code) => { | |
| console.log("Left room with code:", code); | |
| showMessageBox("Disconnected", "You have been disconnected from the room. Please refresh to reconnect."); | |
| }); | |
| } catch (e) { | |
| console.error("Failed to connect or join:", e); | |
| showMessageBox("Connection Error", `Failed to connect to the server or join room: ${e.message}. Make sure the server is running and the Colyseus library is loaded.`); | |
| } | |
| } | |
| window.onload = connectAndJoin; | |
| function drawGame(state) { | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| state.players.forEach((player, sessionId) => { | |
| ctx.fillStyle = player.color; | |
| ctx.strokeStyle = sessionId === currentPlayerId ? '#FFFFFF' : '#000000'; | |
| ctx.lineWidth = 2; | |
| ctx.beginPath(); | |
| ctx.arc(player.x, player.y, PLAYER_SIZE / 2, 0, Math.PI * 2); | |
| ctx.fill(); | |
| ctx.stroke(); | |
| ctx.fillStyle = '#FFFFFF'; | |
| ctx.font = '12px Inter'; | |
| ctx.textAlign = 'center'; | |
| ctx.textBaseline = 'middle'; | |
| ctx.fillText(sessionId.substring(0, 4), player.x, player.y + PLAYER_SIZE / 2 + 10); | |
| }); | |
| } | |
| document.addEventListener('keydown', (event) => { | |
| if (event.key in keys) { | |
| keys[event.key] = true; | |
| sendMovementUpdate(); | |
| } | |
| }); | |
| document.addEventListener('keyup', (event) => { | |
| if (event.key in keys) { | |
| keys[event.key] = false; | |
| sendMovementUpdate(); | |
| } | |
| }); | |
| function sendMovementUpdate() { | |
| if (!room || !currentPlayerId) return; | |
| const player = room.state.players.get(currentPlayerId); | |
| if (!player) return; | |
| let newX = player.x; | |
| let newY = player.y; | |
| if (keys.ArrowUp) newY -= PLAYER_SPEED; | |
| if (keys.ArrowDown) newY += PLAYER_SPEED; | |
| if (keys.ArrowLeft) newX -= PLAYER_SPEED; | |
| if (keys.ArrowRight) newX += PLAYER_SPEED; | |
| newX = Math.max(PLAYER_SIZE / 2, Math.min(canvas.width - PLAYER_SIZE / 2, newX)); | |
| newY = Math.max(PLAYER_SIZE / 2, Math.min(canvas.height - PLAYER_SIZE / 2, newY)); | |
| if (newX !== player.x || newY !== player.y) { | |
| room.send("move", { x: newX, y: newY }); | |
| } | |
| } | |
| function addChatMessage(message) { | |
| const p = document.createElement('p'); | |
| p.textContent = message; | |
| chatMessagesDiv.appendChild(p); | |
| chatMessagesDiv.scrollTop = chatMessagesDiv.scrollHeight; | |
| } | |
| sendChatButton.addEventListener('click', () => { | |
| const message = chatInput.value.trim(); | |
| if (message) { | |
| room.send("chat", message); | |
| chatInput.value = ''; | |
| } | |
| }); | |
| chatInput.addEventListener('keypress', (event) => { | |
| if (event.key === 'Enter') { | |
| sendChatButton.click(); | |
| } | |
| }); | |
| function resizeCanvas() { } | |
| resizeCanvas(); | |
| window.addEventListener('resize', resizeCanvas); | |
| let touchStartX = 0; | |
| let touchStartY = 0; | |
| canvas.addEventListener('touchstart', (e) => { | |
| e.preventDefault(); | |
| const touch = e.touches[0]; | |
| touchStartX = touch.clientX; | |
| touchStartY = touch.clientY; | |
| }, { passive: false }); | |
| canvas.addEventListener('touchend', (e) => { | |
| e.preventDefault(); | |
| const touch = e.changedTouches[0]; | |
| const endX = touch.clientX; | |
| const endY = touch.clientY; | |
| const deltaX = endX - touchStartX; | |
| const deltaY = endY - touchStartY; | |
| if (Math.abs(deltaX) > Math.abs(deltaY)) { | |
| if (deltaX > 0) keys.ArrowRight = true; | |
| else keys.ArrowLeft = true; | |
| } else { | |
| if (deltaY > 0) keys.ArrowDown = true; | |
| else keys.ArrowUp = true; | |
| } | |
| sendMovementUpdate(); | |
| setTimeout(() => { | |
| keys.ArrowUp = false; | |
| keys.ArrowDown = false; | |
| keys.ArrowLeft = false; | |
| keys.ArrowRight = false; | |
| }, 100); | |
| }, { passive: false }); | |
| </script> | |
| </body> | |
| </html> | |