p2p-pong-deepsite / index.html
af2022's picture
Update index.html
d49099e verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Classic Pong Game</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/peerjs.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.pulse-animation {
animation: pulse 1.5s infinite;
}
#gameCanvas {
background-color: #111827;
border-radius: 8px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);
}
.glow {
box-shadow: 0 0 10px rgba(59, 130, 246, 0.7);
}
.score-display {
font-family: 'Courier New', monospace;
text-shadow: 0 0 5px rgba(59, 130, 246, 0.7);
}
</style>
</head>
<body class="bg-gray-900 text-white min-h-screen flex flex-col items-center justify-center p-4">
<div class="max-w-4xl w-full">
<h1 class="text-4xl font-bold text-center mb-2 text-blue-400">Classic Pong</h1>
<p class="text-center text-gray-400 mb-8">Relive the arcade classic with modern multiplayer</p>
<div id="menu" class="flex flex-col items-center space-y-6 mb-8">
<button id="singlePlayerBtn" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-6 rounded-lg transition-all duration-300 transform hover:scale-105 w-64 flex items-center justify-center space-x-2">
<i class="fas fa-robot"></i>
<span>Play vs Computer</span>
</button>
<button id="multiplayerBtn" class="bg-purple-600 hover:bg-purple-700 text-white font-bold py-3 px-6 rounded-lg transition-all duration-300 transform hover:scale-105 w-64 flex items-center justify-center space-x-2">
<i class="fas fa-users"></i>
<span>Multiplayer</span>
</button>
<div id="multiplayerControls" class="hidden flex-col items-center space-y-4 w-full max-w-md">
<div class="flex space-x-4 w-full">
<button id="createRoomBtn" class="bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-4 rounded-lg transition-all duration-300 flex-1 flex items-center justify-center space-x-2">
<i class="fas fa-plus"></i>
<span>Create Room</span>
</button>
<button id="joinRoomBtn" class="bg-yellow-600 hover:bg-yellow-700 text-white font-bold py-2 px-4 rounded-lg transition-all duration-300 flex-1 flex items-center justify-center space-x-2">
<i class="fas fa-sign-in-alt"></i>
<span>Join Room</span>
</button>
</div>
<div id="roomControls" class="hidden w-full space-y-4">
<div id="createRoomSection" class="hidden">
<div class="bg-gray-800 p-4 rounded-lg">
<p class="text-sm text-gray-400 mb-2">Share this ID with your friend:</p>
<div class="flex items-center space-x-2">
<input id="hostPeerId" type="text" readonly class="bg-gray-700 text-white p-2 rounded flex-1 font-mono">
<button id="copyHostIdBtn" class="bg-blue-600 hover:bg-blue-700 text-white p-2 rounded">
<i class="fas fa-copy"></i>
</button>
</div>
<p class="text-xs text-gray-500 mt-2">Waiting for player to join...</p>
</div>
</div>
<div id="joinRoomSection" class="hidden">
<div class="bg-gray-800 p-4 rounded-lg">
<p class="text-sm text-gray-400 mb-2">Enter host's ID:</p>
<div class="flex items-center space-x-2">
<input id="guestPeerId" type="text" class="bg-gray-700 text-white p-2 rounded flex-1 font-mono" placeholder="Enter host ID">
<button id="connectBtn" class="bg-green-600 hover:bg-green-700 text-white p-2 rounded">
<i class="fas fa-plug"></i> Connect
</button>
</div>
</div>
</div>
<div id="connectionStatus" class="hidden bg-gray-800 p-4 rounded-lg text-center">
<p class="text-yellow-400 pulse-animation">
<i class="fas fa-circle-notch fa-spin"></i> Connecting...
</p>
</div>
<div id="connectedStatus" class="hidden bg-gray-800 p-4 rounded-lg text-center">
<p class="text-green-400">
<i class="fas fa-check-circle"></i> Connected!
</p>
</div>
</div>
</div>
</div>
<div id="gameContainer" class="hidden flex flex-col items-center">
<div class="flex justify-between w-full mb-4">
<div class="score-display text-2xl">Player: <span id="playerScore">0</span></div>
<div class="score-display text-2xl">Opponent: <span id="opponentScore">0</span></div>
</div>
<canvas id="gameCanvas" width="800" height="500" class="w-full max-w-full"></canvas>
<div id="gameControls" class="mt-4 flex space-x-4">
<button id="pauseBtn" class="bg-yellow-600 hover:bg-yellow-700 text-white font-bold py-2 px-4 rounded-lg">
<i class="fas fa-pause"></i> Pause
</button>
<button id="restartBtn" class="bg-red-600 hover:bg-red-700 text-white font-bold py-2 px-4 rounded-lg">
<i class="fas fa-redo"></i> Restart
</button>
<button id="backToMenuBtn" class="bg-gray-600 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded-lg">
<i class="fas fa-arrow-left"></i> Menu
</button>
</div>
<div id="gameMessage" class="mt-4 text-xl font-bold text-center hidden"></div>
</div>
</div>
<script>
// Game elements
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const playerScoreDisplay = document.getElementById('playerScore');
const opponentScoreDisplay = document.getElementById('opponentScore');
const gameMessage = document.getElementById('gameMessage');
// Menu elements
const menu = document.getElementById('menu');
const gameContainer = document.getElementById('gameContainer');
const singlePlayerBtn = document.getElementById('singlePlayerBtn');
const multiplayerBtn = document.getElementById('multiplayerBtn');
const multiplayerControls = document.getElementById('multiplayerControls');
const roomControls = document.getElementById('roomControls');
const createRoomBtn = document.getElementById('createRoomBtn');
const joinRoomBtn = document.getElementById('joinRoomBtn');
const createRoomSection = document.getElementById('createRoomSection');
const joinRoomSection = document.getElementById('joinRoomSection');
const hostPeerId = document.getElementById('hostPeerId');
const copyHostIdBtn = document.getElementById('copyHostIdBtn');
const guestPeerId = document.getElementById('guestPeerId');
const connectBtn = document.getElementById('connectBtn');
const connectionStatus = document.getElementById('connectionStatus');
const connectedStatus = document.getElementById('connectedStatus');
const pauseBtn = document.getElementById('pauseBtn');
const restartBtn = document.getElementById('restartBtn');
const backToMenuBtn = document.getElementById('backToMenuBtn');
// Game variables
let gameMode = null; // 'single' or 'multiplayer'
let isHost = false;
let isPaused = false;
let gameRunning = false;
let animationId;
let resetInProgress = false; // Flag to prevent reset loops
let paddleUpdateInterval;
// Pong game objects
const paddleWidth = 15;
const paddleHeight = 100;
const ballSize = 10;
const paddleSpeed = 8;
const initialBallSpeed = 5;
const paddleUpdateFrequency = 50; // ms between paddle position updates
let leftPaddle = {
x: 30,
y: canvas.height / 2 - paddleHeight / 2,
width: paddleWidth,
height: paddleHeight,
dy: 0,
score: 0
};
let rightPaddle = {
x: canvas.width - 30 - paddleWidth,
y: canvas.height / 2 - paddleHeight / 2,
width: paddleWidth,
height: paddleHeight,
dy: 0,
score: 0
};
let ball = {
x: canvas.width / 2,
y: canvas.height / 2,
size: ballSize,
dx: initialBallSpeed,
dy: initialBallSpeed
};
// PeerJS variables
let peer;
let conn;
let myPeerId;
let connectionTimeout;
// Event listeners
singlePlayerBtn.addEventListener('click', () => {
gameMode = 'single';
startGame();
});
multiplayerBtn.addEventListener('click', () => {
multiplayerControls.classList.remove('hidden');
multiplayerBtn.classList.add('hidden');
singlePlayerBtn.classList.add('hidden');
});
createRoomBtn.addEventListener('click', () => {
isHost = true;
setupPeerConnection();
createRoomSection.classList.remove('hidden');
joinRoomSection.classList.add('hidden');
roomControls.classList.remove('hidden');
});
joinRoomBtn.addEventListener('click', () => {
isHost = false;
setupPeerConnection();
joinRoomSection.classList.remove('hidden');
createRoomSection.classList.add('hidden');
roomControls.classList.remove('hidden');
});
copyHostIdBtn.addEventListener('click', () => {
hostPeerId.select();
document.execCommand('copy');
showMessage('Copied to clipboard!', 'green-400');
});
connectBtn.addEventListener('click', () => {
const hostId = guestPeerId.value.trim();
if (!hostId) {
showMessage('Please enter a valid host ID', 'red-400');
return;
}
if (!peer) {
showMessage('Peer connection not ready yet', 'red-400');
return;
}
connectionStatus.classList.remove('hidden');
// Set timeout for connection attempt
connectionTimeout = setTimeout(() => {
if (connectionStatus && !connectionStatus.classList.contains('hidden')) {
showMessage('Connection timed out. Try again.', 'red-400');
connectionStatus.classList.add('hidden');
if (conn) conn.close();
}
}, 15000); // 15 seconds timeout
conn = peer.connect(hostId, {
reliable: true,
serialization: 'json',
metadata: {
game: 'pong',
version: '1.0'
}
});
conn.on('open', () => {
clearTimeout(connectionTimeout);
connectionStatus.classList.add('hidden');
connectedStatus.classList.remove('hidden');
gameMode = 'multiplayer';
// Set up data handler
conn.on('data', handleData);
// Handle connection close
conn.on('close', () => {
if (gameRunning) {
showMessage('Player disconnected', 'red-400');
returnToMenu();
}
});
// Start sending paddle updates
if (!isHost) {
startPaddleUpdates(rightPaddle);
} else {
startPaddleUpdates(leftPaddle);
}
startGame();
});
conn.on('error', (err) => {
clearTimeout(connectionTimeout);
console.error('Connection error:', err);
showMessage('Connection failed: ' + (err.message || 'Unknown error'), 'red-400');
connectionStatus.classList.add('hidden');
if (conn) conn.close();
});
});
pauseBtn.addEventListener('click', togglePause);
restartBtn.addEventListener('click', resetGame);
backToMenuBtn.addEventListener('click', returnToMenu);
// Keyboard controls
const keys = {};
document.addEventListener('keydown', (e) => {
keys[e.key] = true;
// Prevent default for arrow keys and space to avoid page scrolling
if (['ArrowUp', 'ArrowDown', ' '].includes(e.key)) {
e.preventDefault();
}
});
document.addEventListener('keyup', (e) => {
keys[e.key] = false;
});
// Game functions
function startGame() {
menu.classList.add('hidden');
gameContainer.classList.remove('hidden');
gameRunning = true;
resetGame();
gameLoop();
// Focus canvas for keyboard controls
canvas.focus();
}
function gameLoop() {
if (isPaused || !gameRunning) return;
update();
draw();
animationId = requestAnimationFrame(gameLoop);
}
function update() {
// Update paddles based on keyboard input
if (gameMode === 'single') {
// Player controls (left paddle)
if (keys['w'] || keys['ArrowUp']) {
leftPaddle.dy = -paddleSpeed;
} else if (keys['s'] || keys['ArrowDown']) {
leftPaddle.dy = paddleSpeed;
} else {
leftPaddle.dy = 0;
}
// Simple AI for right paddle
const paddleCenter = rightPaddle.y + rightPaddle.height / 2;
const ballCenter = ball.y + ball.size / 2;
if (paddleCenter < ballCenter - 10) {
rightPaddle.dy = paddleSpeed * 0.7; // Slightly slower than player
} else if (paddleCenter > ballCenter + 10) {
rightPaddle.dy = -paddleSpeed * 0.7;
} else {
rightPaddle.dy = 0;
}
} else {
// Multiplayer controls
if (isHost) {
// Host controls left paddle
if (keys['w'] || keys['ArrowUp']) {
leftPaddle.dy = -paddleSpeed;
} else if (keys['s'] || keys['ArrowDown']) {
leftPaddle.dy = paddleSpeed;
} else {
leftPaddle.dy = 0;
}
} else {
// Guest controls right paddle
if (keys['ArrowUp']) {
rightPaddle.dy = -paddleSpeed;
} else if (keys['ArrowDown']) {
rightPaddle.dy = paddleSpeed;
} else {
rightPaddle.dy = 0;
}
}
}
// Move paddles
leftPaddle.y += leftPaddle.dy;
rightPaddle.y += rightPaddle.dy;
// Paddle boundaries
if (leftPaddle.y < 0) leftPaddle.y = 0;
if (leftPaddle.y + leftPaddle.height > canvas.height) leftPaddle.y = canvas.height - leftPaddle.height;
if (rightPaddle.y < 0) rightPaddle.y = 0;
if (rightPaddle.y + rightPaddle.height > canvas.height) rightPaddle.y = canvas.height - rightPaddle.height;
// Move ball
ball.x += ball.dx;
ball.y += ball.dy;
// Ball collision with top and bottom
if (ball.y - ball.size / 2 < 0 || ball.y + ball.size / 2 > canvas.height) {
ball.dy = -ball.dy;
}
// Ball collision with paddles
if (
ball.x - ball.size / 2 < leftPaddle.x + leftPaddle.width &&
ball.x + ball.size / 2 > leftPaddle.x &&
ball.y + ball.size / 2 > leftPaddle.y &&
ball.y - ball.size / 2 < leftPaddle.y + leftPaddle.height
) {
const hitPosition = (ball.y - (leftPaddle.y + leftPaddle.height / 2)) / (leftPaddle.height / 2);
ball.dx = Math.abs(ball.dx) * 1.05; // Increase speed slightly
ball.dy = hitPosition * 5; // Change angle based on where ball hits paddle
ball.x = leftPaddle.x + leftPaddle.width + ball.size / 2;
if (gameMode === 'multiplayer' && isHost) {
sendBallUpdate();
}
}
if (
ball.x + ball.size / 2 > rightPaddle.x &&
ball.x - ball.size / 2 < rightPaddle.x + rightPaddle.width &&
ball.y + ball.size / 2 > rightPaddle.y &&
ball.y - ball.size / 2 < rightPaddle.y + rightPaddle.height
) {
const hitPosition = (ball.y - (rightPaddle.y + rightPaddle.height / 2)) / (rightPaddle.height / 2);
ball.dx = -Math.abs(ball.dx) * 1.05;
ball.dy = hitPosition * 5;
ball.x = rightPaddle.x - ball.size / 2;
if (gameMode === 'multiplayer' && !isHost) {
// Guest doesn't send ball updates - only host does
}
}
// Ball out of bounds (score)
if (ball.x - ball.size / 2 < 0) {
rightPaddle.score++;
opponentScoreDisplay.textContent = rightPaddle.score;
resetBall();
if (gameMode === 'multiplayer' && isHost) {
sendScoreUpdate();
}
if (rightPaddle.score >= 5) {
endGame(isHost ? 'You lost!' : 'You won!');
}
}
if (ball.x + ball.size / 2 > canvas.width) {
leftPaddle.score++;
playerScoreDisplay.textContent = leftPaddle.score;
resetBall();
if (gameMode === 'multiplayer' && !isHost) {
sendScoreUpdate();
}
if (leftPaddle.score >= 5) {
endGame(isHost ? 'You won!' : 'You lost!');
}
}
}
function draw() {
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw center line
ctx.strokeStyle = 'rgba(255, 255, 255, 0.2)';
ctx.setLineDash([10, 10]);
ctx.beginPath();
ctx.moveTo(canvas.width / 2, 0);
ctx.lineTo(canvas.width / 2, canvas.height);
ctx.stroke();
ctx.setLineDash([]);
// Draw paddles
ctx.fillStyle = '#3B82F6';
ctx.fillRect(leftPaddle.x, leftPaddle.y, leftPaddle.width, leftPaddle.height);
ctx.fillStyle = '#EC4899';
ctx.fillRect(rightPaddle.x, rightPaddle.y, rightPaddle.width, rightPaddle.height);
// Draw ball
ctx.fillStyle = '#FFFFFF';
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.size / 2, 0, Math.PI * 2);
ctx.fill();
}
function resetBall() {
ball.x = canvas.width / 2;
ball.y = canvas.height / 2;
// Random direction but always towards the scoring player
const direction = Math.random() > 0.5 ? 1 : -1;
ball.dx = initialBallSpeed * direction;
ball.dy = (Math.random() * 4 - 2); // Random angle between -2 and 2
}
function resetGame() {
if (resetInProgress) return;
resetInProgress = true;
leftPaddle.y = canvas.height / 2 - paddleHeight / 2;
rightPaddle.y = canvas.height / 2 - paddleHeight / 2;
leftPaddle.score = 0;
rightPaddle.score = 0;
playerScoreDisplay.textContent = '0';
opponentScoreDisplay.textContent = '0';
resetBall();
// In multiplayer, only host can send reset command
if (gameMode === 'multiplayer' && isHost) {
sendGameState('reset');
}
gameMessage.classList.add('hidden');
isPaused = false;
pauseBtn.innerHTML = '<i class="fas fa-pause"></i> Pause';
// Reset the flag after a small delay
setTimeout(() => {
resetInProgress = false;
}, 100);
}
function endGame(message) {
gameRunning = false;
cancelAnimationFrame(animationId);
clearInterval(paddleUpdateInterval);
gameMessage.textContent = message;
gameMessage.classList.remove('hidden');
gameMessage.className = 'mt-4 text-xl font-bold text-center';
if (message.includes('won')) {
gameMessage.classList.add('text-green-400');
} else {
gameMessage.classList.add('text-red-400');
}
if (gameMode === 'multiplayer') {
sendGameState('end', message);
}
}
function togglePause() {
isPaused = !isPaused;
if (isPaused) {
cancelAnimationFrame(animationId);
clearInterval(paddleUpdateInterval);
pauseBtn.innerHTML = '<i class="fas fa-play"></i> Resume';
gameMessage.textContent = 'Game Paused';
gameMessage.classList.remove('hidden');
gameMessage.className = 'mt-4 text-xl font-bold text-center text-yellow-400';
if (gameMode === 'multiplayer') {
sendGameState('pause');
}
} else {
pauseBtn.innerHTML = '<i class="fas fa-pause"></i> Pause';
gameMessage.classList.add('hidden');
if (gameMode === 'multiplayer') {
// Restart paddle updates
if (isHost) {
startPaddleUpdates(leftPaddle);
} else {
startPaddleUpdates(rightPaddle);
}
sendGameState('resume');
}
gameLoop();
}
}
function startPaddleUpdates(paddle) {
// Clear any existing interval
clearInterval(paddleUpdateInterval);
// Start sending paddle position updates at regular intervals
paddleUpdateInterval = setInterval(() => {
if (gameRunning && !isPaused && conn && conn.open) {
sendPaddlePosition(paddle.y);
}
}, paddleUpdateFrequency);
}
function returnToMenu() {
// Clean up PeerJS connection
if (conn) {
conn.close();
}
if (peer) {
peer.destroy();
}
clearTimeout(connectionTimeout);
clearInterval(paddleUpdateInterval);
// Reset game state
cancelAnimationFrame(animationId);
gameRunning = false;
resetInProgress = false;
// Show menu and hide game
menu.classList.remove('hidden');
gameContainer.classList.add('hidden');
multiplayerControls.classList.add('hidden');
roomControls.classList.add('hidden');
connectionStatus.classList.add('hidden');
connectedStatus.classList.add('hidden');
multiplayerBtn.classList.remove('hidden');
singlePlayerBtn.classList.remove('hidden');
// Reset multiplayer UI
createRoomSection.classList.add('hidden');
joinRoomSection.classList.add('hidden');
}
function showMessage(message, colorClass) {
const messageDiv = document.createElement('div');
messageDiv.className = `fixed top-4 left-1/2 transform -translate-x-1/2 bg-gray-800 text-${colorClass} px-4 py-2 rounded-lg shadow-lg z-50`;
messageDiv.textContent = message;
document.body.appendChild(messageDiv);
setTimeout(() => {
messageDiv.remove();
}, 3000);
}
// PeerJS functions
function setupPeerConnection() {
// Destroy previous peer instance if exists
if (peer) {
peer.destroy();
}
// Create PeerJS instance with TURN servers for better connectivity
peer = new Peer({
config: {
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' }
]
},
debug: 3 // Enable debug logging
});
peer.on('open', (id) => {
console.log('My peer ID is: ' + id);
myPeerId = id;
if (isHost) {
hostPeerId.value = id;
// Listen for incoming connections
peer.on('connection', (connection) => {
console.log('Incoming connection from:', connection.peer);
conn = connection;
conn.on('open', () => {
console.log('Connection established with:', conn.peer);
clearTimeout(connectionTimeout);
connectionStatus.classList.add('hidden');
connectedStatus.classList.remove('hidden');
// Set up data handler
conn.on('data', handleData);
// Handle connection close
conn.on('close', () => {
console.log('Connection closed');
if (gameRunning) {
showMessage('Player disconnected', 'red-400');
returnToMenu();
}
});
gameMode = 'multiplayer';
startGame();
// Host starts sending paddle updates
startPaddleUpdates(leftPaddle);
});
conn.on('error', (err) => {
console.error('Connection error:', err);
showMessage('Connection error: ' + (err.message || 'Unknown error'), 'red-400');
});
});
}
// Reset connection status if we're joining
if (!isHost) {
connectionStatus.classList.add('hidden');
connectedStatus.classList.add('hidden');
}
});
peer.on('error', (err) => {
console.error('PeerJS error:', err);
showMessage('Connection error: ' + (err.message || 'Unknown error'), 'red-400');
connectionStatus.classList.add('hidden');
if (conn) conn.close();
if (isHost) {
returnToMenu();
}
});
}
function handleData(data) {
console.log('Received data:', data);
if (data.type === 'paddle') {
if (isHost) {
// Host receives guest's paddle position (right paddle)
rightPaddle.y = data.y;
} else {
// Guest receives host's paddle position (left paddle)
leftPaddle.y = data.y;
}
} else if (data.type === 'ball') {
if (!isHost) { // Only guests should update ball from host
ball.x = data.x;
ball.y = data.y;
ball.dx = data.dx;
ball.dy = data.dy;
}
} else if (data.type === 'score') {
if (isHost) {
leftPaddle.score = data.playerScore;
playerScoreDisplay.textContent = data.playerScore;
} else {
rightPaddle.score = data.opponentScore;
opponentScoreDisplay.textContent = data.opponentScore;
}
} else if (data.type === 'gameState') {
if (data.state === 'reset' && !isHost) {
// Only guests should respond to reset commands
leftPaddle.y = canvas.height / 2 - paddleHeight / 2;
rightPaddle.y = canvas.height / 2 - paddleHeight / 2;
leftPaddle.score = 0;
rightPaddle.score = 0;
playerScoreDisplay.textContent = '0';
opponentScoreDisplay.textContent = '0';
resetBall();
gameMessage.textContent = 'Game reset by host';
gameMessage.classList.remove('hidden');
gameMessage.className = 'mt-4 text-xl font-bold text-center text-yellow-400';
setTimeout(() => {
gameMessage.classList.add('hidden');
}, 2000);
} else if (data.state === 'pause') {
isPaused = true;
cancelAnimationFrame(animationId);
clearInterval(paddleUpdateInterval);
pauseBtn.innerHTML = '<i class="fas fa-play"></i> Resume';
gameMessage.textContent = 'Game Paused by ' + (isHost ? 'you' : 'host');
gameMessage.classList.remove('hidden');
gameMessage.className = 'mt-4 text-xl font-bold text-center text-yellow-400';
} else if (data.state === 'resume') {
isPaused = false;
pauseBtn.innerHTML = '<i class="fas fa-pause"></i> Pause';
gameMessage.classList.add('hidden');
// Restart paddle updates if we're a guest
if (!isHost) {
startPaddleUpdates(rightPaddle);
}
gameLoop();
} else if (data.state === 'end') {
endGame(data.message);
}
}
}
function sendPaddlePosition(y) {
if (conn && conn.open) {
const data = {
type: 'paddle',
y: y
};
console.log('Sending paddle position:', data);
conn.send(data);
}
}
function sendBallUpdate() {
if (conn && conn.open && isHost) { // Only host should send ball updates
const data = {
type: 'ball',
x: ball.x,
y: ball.y,
dx: ball.dx,
dy: ball.dy
};
console.log('Sending ball update:', data);
conn.send(data);
}
}
function sendScoreUpdate() {
if (conn && conn.open) {
const data = {
type: 'score',
playerScore: leftPaddle.score,
opponentScore: rightPaddle.score
};
console.log('Sending score update:', data);
conn.send(data);
}
}
function sendGameState(state, message = '') {
if (conn && conn.open) {
const data = {
type: 'gameState',
state: state,
message: message
};
console.log('Sending game state:', data);
conn.send(data);
}
}
</script>
</body>
</html>