diff --git a/docker-compose.yml b/docker-compose.yml index d11b2d0..21e272e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,11 +10,11 @@ services: venv/bin/python manage.py makemigrations --noinput && venv/bin/python manage.py migrate --noinput && venv/bin/python manage.py collectstatic --noinput && - venv/bin/daphne -b 0.0.0.0 -p 8080 pong.asgi:application" + venv/bin/daphne -b 0.0.0.0 -p 80 pong.asgi:application" volumes: - pong:/transcendence/pong ports: - - "8080:8080" + - "80:80" networks: - app-network environment: diff --git a/makefile b/makefile index 92b9d50..1a44697 100644 --- a/makefile +++ b/makefile @@ -2,7 +2,7 @@ COMPOSE_FILE=docker-compose.yml COMPOSE=docker compose -f $(COMPOSE_FILE) CONTAINER=$(c) -up: +up: down $(COMPOSE) build $(COMPOSE) up diff --git a/pong/game/consumers.py b/pong/game/consumers.py index b473ece..e3d68a1 100644 --- a/pong/game/consumers.py +++ b/pong/game/consumers.py @@ -16,6 +16,8 @@ class GameConsumer(AsyncWebsocketConsumer): data = json.loads(text_data) if data['type'] == 'authenticate': await self.authenticate(data['token']) + elif data['type'] == 'authenticate2': + await self.authenticate2(data['token_1'], data['token_2']) elif data['type'] == 'key_press': if self.game: await self.game.handle_key_press(self, data['key']) @@ -45,6 +47,33 @@ class GameConsumer(AsyncWebsocketConsumer): await self.send(text_data=json.dumps({'type': 'waiting_room'})) await match_maker.add_player(self) + async def authenticate2(self, token, token2): + user = await self.get_user_from_token(token) + if user: + self.user = user + await self.send(text_data=json.dumps({'type': 'authenticated'})) + print(f"User {self.user} authenticated") + user2 = await self.get_user_from_token2(token2) + if user2: + self.user2 = user2 + await self.send(text_data=json.dumps({'type': 'authenticated'})) + print(f"User {self.user2} authenticated") + await match_maker.create_game(self, None, True) + else: + await self.send(text_data=json.dumps({'type': 'error', 'message': 'Authentication failed'})) + print("Authentication failed") + else: + await self.send(text_data=json.dumps({'type': 'error', 'message': 'Authentication failed'})) + print("Authentication failed") + + @database_sync_to_async + def get_user_from_token2(self, token): + try: + user2 = User.objects.filter(auth_token=token).first() + return user2 + except User.DoesNotExist: + return None + async def disconnect(self, close_code): if self.game: await self.game.end_game(disconnected_player=self) diff --git a/pong/game/game.py b/pong/game/game.py index 7ff81f3..23f9719 100644 --- a/pong/game/game.py +++ b/pong/game/game.py @@ -6,23 +6,36 @@ import random from datetime import datetime from .utils import endfortheouche - class Game: - def __init__(self, game_id, player1, player2): + def __init__(self, game_id, player1, player2, localgame): self.game_id = game_id self.player1 = player1 self.player2 = player2 - self.botgame = player2 is None - self.game_state = { - 'player1_name': player1.user.username, - 'player2_name': player2.user.username if player2 else 'BOT', - 'player1_position': 150, - 'player2_position': 150, - 'ball_position': {'x': 390, 'y': 190}, - 'ball_velocity': {'x': random.choice([-5, 5]), 'y': random.choice([-5, 5])}, - 'player1_score': 0, - 'player2_score': 0 - } + self.localgame = localgame + if self.localgame: + self.botgame = False + self.game_state = { + 'player1_name': player1.user.username, + 'player2_name': player1.user2.username, + 'player1_position': 150, + 'player2_position': 150, + 'ball_position': {'x': 390, 'y': 190}, + 'ball_velocity': {'x': random.choice([-5, 5]), 'y': random.choice([-5, 5])}, + 'player1_score': 0, + 'player2_score': 0 + } + else: + self.botgame = player2 is None + self.game_state = { + 'player1_name': player1.user.username, + 'player2_name': player2.user.username if player2 else 'BOT', + 'player1_position': 150, + 'player2_position': 150, + 'ball_position': {'x': 390, 'y': 190}, + 'ball_velocity': {'x': random.choice([-5, 5]), 'y': random.choice([-5, 5])}, + 'player1_score': 0, + 'player2_score': 0 + } self.speed = 1 self.game_loop_task = None self.ended = False @@ -31,21 +44,18 @@ class Game: self.bt1 = 0 self.bt2 = 0 self.start_time = None - - async def start_game(self): print(f"- Game #{self.game_id} STARTED") self.game_loop_task = asyncio.create_task(self.game_loop()) - print(" begin MATCH at : ") self.start_time = datetime.now() - print(f" begin MATCH : {self.start_time} ") + print(f" Begin MATCH at: {self.start_time}") async def game_loop(self): + print(" In the game loop..") while not self.ended: if self.botgame: - await self.update_bot_position() - + await self.update_bot_position() await self.handle_pad_movement() await self.update_game_state() await self.send_game_state() @@ -60,7 +70,6 @@ class Game: elif self.game_state['player2_position'] + 80 > target_y: self.game_state['player2_position'] = max(self.game_state['player2_position'] - (5 * self.speed), 0) - async def update_game_state(self): if self.ended: return @@ -84,18 +93,19 @@ class Game: self.bt2 += 1 self.update_ball_velocity() # Check for scoring - print(f"########### score user 1 {self.game_state['player1_score']} ###########") - print(f"§§§§§§§§§§§ score user 2 {self.game_state['player2_score']} §§§§§§§§§§§") + #print(f"########### score user 1 {self.game_state['player1_score']} ###########") + #print(f"§§§§§§§§§§§ score user 2 {self.game_state['player2_score']} §§§§§§§§§§§") if self.game_state['ball_position']['x'] <= 10: self.game_state['player2_score'] += 1 - if self.game_state['player2_score'] >= 2: + if self.game_state['player2_score'] > 2: print("Here !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") await self.end_game() self.reset_ball() elif self.game_state['ball_position']['x'] >= 790: self.game_state['player1_score'] += 1 - if self.game_state['player1_score'] >= 2: + if self.game_state['player1_score'] > 2: + print("Here !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") await self.end_game() self.reset_ball() @@ -128,30 +138,33 @@ class Game: }) await self.player1.send(message) if not self.botgame: - await self.player2.send(message) + if not self.localgame: + await self.player2.send(message) async def handle_key_press(self, player, key): if self.ended: return - if player == self.player1: - print(f"Key press: {key}") + if self.localgame: if key == 'arrowup': self.p1_mov = -1 - #self.game_state['player1_position'] = max(self.game_state['player1_position'] - 25, 0) elif key == 'arrowdown': self.p1_mov = 1 - #self.game_state['player1_position'] = min(self.game_state['player1_position'] + 25, 300) - elif not self.botgame and player == self.player2: + elif key == 'w': + self.p2_mov = -1 + elif key == 's': + self.p2_mov = 1 + elif player == self.player1: + if key == 'arrowup': + self.p1_mov = -1 + elif key == 'arrowdown': + self.p1_mov = 1 + elif player == self.player2: if key == 'arrowup': self.p2_mov = -1 - #self.game_state['player2_position'] = max(self.game_state['player2_position'] - 25, 0) elif key == 'arrowdown': self.p2_mov = 1 - #self.game_state['player2_position'] = min(self.game_state['player2_position'] + 25, 300) async def handle_pad_movement(self): - #print(f"P1 mov: {self.p1_mov}") - #print(f"P2 mov: {self.p2_mov}") if self.ended: return if self.p1_mov == -1: @@ -197,4 +210,3 @@ class Game: await endfortheouche(self.game_state['player1_name'], self.game_state['player2_name'], self.game_state['player1_score'], self.game_state['player2_score'], self.bt1, self.bt2, duration, False, None) - diff --git a/pong/game/matchmaking.py b/pong/game/matchmaking.py index 32f9981..9d20580 100644 --- a/pong/game/matchmaking.py +++ b/pong/game/matchmaking.py @@ -35,12 +35,12 @@ class MatchMaker: player1 = self.waiting_players.pop(0) player2 = self.waiting_players.pop(0) print(f"*** MATCH FOUND: {player1.user.username} vs {player2.user.username}") - await self.create_game(player1, player2) + await self.create_game(player1, player2, False) else: await asyncio.sleep(1) self.timer += 1 # Waiting for more than 30s -> BOT game - if self.timer >= 5 and self.waiting_players: + if self.timer >= 30 and self.waiting_players: player1 = self.waiting_players.pop(0) print(f"*** MATCH FOUND: {player1.user.username} vs BOT") self.botgame = True @@ -49,26 +49,30 @@ class MatchMaker: if not self.waiting_players: break - async def create_game(self, player1, player2): + async def create_game(self, player1, player2, localgame): game_id = len(self.active_games) + 1 - print(f"- Creating game: #{game_id}") - new_game = Game(game_id, player1, player2) + if localgame: + print(f"- Creating LOCAL game: #{game_id}") + else: + print(f"- Creating MATCH game: #{game_id}") + new_game = Game(game_id, player1, player2, localgame) self.active_games[game_id] = new_game await player1.set_game(new_game) - await player2.set_game(new_game) - await self.notify_players(player1, player2, game_id) + if not localgame: + await player2.set_game(new_game) + await self.notify_players(player1, player2, game_id, localgame) asyncio.create_task(new_game.start_game()) async def create_bot_game(self, player1): game_id = len(self.active_games) + 1 print(f"- Creating BOT game: #{game_id}") - new_game = Game(game_id, player1, None) + new_game = Game(game_id, player1, None, False) self.active_games[game_id] = new_game await player1.set_game(new_game) - await self.notify_players(player1, None, game_id) + await self.notify_players(player1, None, game_id, False) asyncio.create_task(new_game.start_game()) - async def notify_players(self, player1, player2, game_id): + async def notify_players(self, player1, player2, game_id, localgame): if player2: await player1.send(json.dumps({ 'type': 'game_start', @@ -83,12 +87,20 @@ class MatchMaker: 'player2': player2.user.username })) else: - await player1.send(json.dumps({ - 'type': 'game_start', - 'game_id': game_id, - 'player1': player1.user.username, - 'player2': 'BOT' - })) + if localgame: + await player1.send(json.dumps({ + 'type': 'game_start', + 'game_id': game_id, + 'player1': player1.user.username, + 'player2': player1.user2.username + })) + else: + await player1.send(json.dumps({ + 'type': 'game_start', + 'game_id': game_id, + 'player1': player1.user.username, + 'player2': 'BOT' + })) async def handle_key_press(self, player, key): for game in self.active_games.values(): diff --git a/pong/game/views.py b/pong/game/views.py index e810b78..e9b0151 100644 --- a/pong/game/views.py +++ b/pong/game/views.py @@ -18,6 +18,16 @@ from rest_framework import viewsets import json import uuid +@csrf_exempt +def check_user_exists(request): + if request.method == 'POST': + data = json.loads(request.body) + username = data.get('username') + if User.objects.filter(username=username).exists(): + return JsonResponse({'exists': True}) + return JsonResponse({'exists': False}) + return JsonResponse({'error': 'Invalid request method'}, status=400) + @csrf_exempt def register_user(request): if request.method == 'POST': @@ -31,16 +41,6 @@ def register_user(request): return JsonResponse({'registered': False, 'error': 'User already exists'}) return JsonResponse({'error': 'Invalid request method'}, status=400) -@csrf_exempt -def check_user_exists(request): - if request.method == 'POST': - data = json.loads(request.body) - username = data.get('username') - if User.objects.filter(username=username).exists(): - return JsonResponse({'exists': True}) - return JsonResponse({'exists': False}) - return JsonResponse({'error': 'Invalid request method'}, status=400) - @csrf_exempt def authenticate_user(request): if request.method == 'POST': diff --git a/pong/static/game.js b/pong/static/game.js index 0698013..f081244 100644 --- a/pong/static/game.js +++ b/pong/static/game.js @@ -1,26 +1,45 @@ document.addEventListener('DOMContentLoaded', () => { - console.log('DOMContentLoaded event fired'); - const checkNicknameButton = document.getElementById('check-nickname'); - const registerButton = document.getElementById('register'); - const loginButton = document.getElementById('login'); + const formBlock = document.getElementById('block-form'); + const authForm = document.getElementById('auth-form'); - const gameContainer = document.getElementById('game1'); const nicknameInput = document.getElementById('nickname'); + const checkNicknameButton = document.getElementById('check-nickname'); + + const registerForm = document.getElementById('register-form'); const passwordInput = document.getElementById('password'); const confirmPasswordInput = document.getElementById('confirm-password'); - const loginPasswordInput = document.getElementById('login-password'); + const registerButton = document.getElementById('register'); + const loginForm = document.getElementById('login-form'); - const registerForm = document.getElementById('register-form'); - const formBlock = document.getElementById('block-form'); + const loginPasswordInput = document.getElementById('login-password'); + const loginButton = document.getElementById('login'); + + const authForm2 = document.getElementById('auth-form2'); + const nicknameInput2 = document.getElementById('nickname2'); + const checkNicknameButton2 = document.getElementById('check-nickname2'); + + const registerForm2 = document.getElementById('register-form2'); + const passwordInput2 = document.getElementById('password2'); + const confirmPasswordInput2 = document.getElementById('confirm-password2'); + const registerButton2 = document.getElementById('register2'); + + const loginForm2 = document.getElementById('login-form2'); + const loginPasswordInput2 = document.getElementById('login-password2'); + const loginButton2 = document.getElementById('login2'); + + const gameContainer = document.getElementById('game1'); + const menuButton = document.querySelector('.burger-menu'); + const dropdownMenu = document.getElementById('dropdown-menu'); + const playerList = document.getElementById('player-list'); const matchList = document.getElementById('match-list'); const tournoiList = document.getElementById('tournoi-list'); - const dropdownMenu = document.getElementById('dropdown-menu'); const pongElements = document.getElementById('pong-elements'); const logo = document.querySelector('.logo'); + const localGameButton = document.getElementById('local-game'); const quickMatchButton = document.getElementById('quick-match'); const tournamentButton = document.getElementById('tournament'); @@ -41,11 +60,15 @@ document.addEventListener('DOMContentLoaded', () => { registerButton.addEventListener('click', handleRegister); loginButton.addEventListener('click', handleLogin); + checkNicknameButton2.addEventListener('click', handleCheckNickname2); + registerButton2.addEventListener('click', handleRegister2); + loginButton2.addEventListener('click', handleLogin2); + + localGameButton.addEventListener('click', startLocalGame); quickMatchButton.addEventListener('click', startQuickMatch); tournamentButton.addEventListener('click', startTournament); - async function handleCheckNickname() { const nickname = nicknameInput.value.trim(); if (nickname) { @@ -109,13 +132,7 @@ document.addEventListener('DOMContentLoaded', () => { const result = await registerUser(nickname, password); if (result) { registerForm.style.display = 'none'; - //gameContainer.style.display = 'flex'; - //formBlock.style.display = 'none'; - //logo.style.display = 'none'; - pongElements.style.display = 'none'; - console.log("new button must appear !"); - document.getElementById("post-form-buttons").style.display = 'inline-block'; - //startWebSocketConnection(token); + document.getElementById("post-form-buttons").style.display = 'block'; } else { alert('Registration failed. Please try again.'); } @@ -149,13 +166,7 @@ document.addEventListener('DOMContentLoaded', () => { const result = await authenticateUser(nickname, password); if (result) { loginForm.style.display = 'none'; - //gameContainer.style.display = 'flex'; - //formBlock.style.display = 'none'; - //logo.style.display = 'none'; - //pongElements.style.display = 'none'; - console.log("new button must appear !"); - document.getElementById("post-form-buttons").style.display = 'inline-block'; - //startWebSocketConnection(token); + document.getElementById("post-form-buttons").style.display = 'block'; } else { alert('Authentication failed. Please try again.'); } @@ -164,18 +175,6 @@ document.addEventListener('DOMContentLoaded', () => { } } - function startQuickMatch() { - gameContainer.style.display = 'flex'; - logo.style.display = 'none'; - menuButton.style.display = 'none'; - formBlock.style.display = 'none'; - startWebSocketConnection(token); - } - - function startTournament() { - console.log("For now, do nothing, hurry up and work Senor chaku !!!!") - } - async function authenticateUser(username, password) { const response = await fetch('/authenticate_user/', { method: 'POST', @@ -191,12 +190,176 @@ document.addEventListener('DOMContentLoaded', () => { return data.authenticated; } - function startWebSocketConnection(token) { + // functions to handle the second player + + async function handleCheckNickname2() { + const nickname2 = nicknameInput2.value.trim(); + if (nickname2) { + try { + const exists = await checkUserExists2(nickname2); + if (exists) { + authForm2.style.display = 'none'; + loginForm2.style.display = 'block'; + // Auto-focus and key handling for LOGIN-FORM2 + loginPasswordInput2.focus(); + loginPasswordInput2.addEventListener('keypress', function (event) { + if (event.key === 'Enter') { + event.preventDefault(); + loginButton2.click(); + } + }); + } else { + authForm2.style.display = 'none'; + registerForm2.style.display = 'block'; + // Auto-focus and key handling for REGISTER-FORM2 + passwordInput2.focus(); + passwordInput2.addEventListener('keypress', function (event) { + if (event.key === 'Enter') { + confirmPasswordInput2.focus(); + confirmPasswordInput2.addEventListener('keypress', function (event) { + if (event.key === 'Enter') { + event.preventDefault(); + registerButton2.click(); + } + }); + } + }); + } + } catch (error) { + console.error('Error checking user existence:', error); + } + } else { + alert('Please enter a nickname.'); + } + } + + async function checkUserExists2(username) { + const response = await fetch('/check_user_exists/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ username }) + }); + const data = await response.json(); + return data.exists; + } + + async function handleRegister2() { + const nickname2 = nicknameInput2.value.trim(); + const password2 = passwordInput2.value.trim(); + const confirmPassword2 = confirmPasswordInput2.value.trim(); + + if (password2 === confirmPassword2) { + try { + const result = await registerUser2(nickname2, password2); + if (result) { + registerForm2.style.display = 'none'; + startLocalGame2(); + } else { + alert('Registration failed. Please try again.'); + } + } catch (error) { + console.error('Error registering user:', error); + } + } else { + alert('Passwords do not match.'); + } + } + + async function registerUser2(username, password) { + const response = await fetch('/register_user/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ username, password }) + }); + const data = await response.json(); + if (data.registered) { + token2 = data.token; + } + return data.registered; + } + + async function handleLogin2() { + const nickname2 = nicknameInput2.value.trim(); + const password2 = loginPasswordInput2.value.trim(); + try { + const result = await authenticateUser2(nickname2, password2); + if (result) { + loginForm2.style.display = 'none'; + startLocalGame2(); + } else { + alert('Authentication failed. Please try again.'); + } + } catch (error) { + console.error('Error authenticating user:', error); + } + } + + async function authenticateUser2(username, password) { + const response = await fetch('/authenticate_user/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ username, password }) + }); + const data = await response.json(); + if (data.authenticated) { + token2 = data.token; + } + return data.authenticated; + } + + function startLocalGame() { + console.log("starting a Local Game.."); + document.getElementById("post-form-buttons").style.display = 'none'; + authForm2.style.display = 'block'; + nicknameInput2.focus(); + nicknameInput2.addEventListener('keypress', function (event) { + if (event.key === 'Enter') { + event.preventDefault(); + checkNicknameButton2.click(); + } + }); + } + + function startLocalGame2() { + gameContainer.style.display = 'flex'; + logo.style.display = 'none'; + pongElements.style.display = 'none'; + menuButton.style.display = 'none'; + formBlock.style.display = 'none'; + startWebSocketConnection(token, 2); + } + + function startQuickMatch() { + gameContainer.style.display = 'flex'; + logo.style.display = 'none'; + pongElements.style.display = 'none'; + menuButton.style.display = 'none'; + formBlock.style.display = 'none'; + startWebSocketConnection(token, 1); + } + + function startTournament() { + console.log("For now, do nothing, hurry up and work Senor chaku !!!!") + } + + function startWebSocketConnection(token, players) { socket = new WebSocket(`ws://${window.location.host}/ws/game/`); socket.onopen = function (event) { console.log('WebSocket connection established'); - socket.send(JSON.stringify({ type: 'authenticate', token: token })); + if (players === 1) { + console.log("Sending token for 1 player game"); + socket.send(JSON.stringify({ type: 'authenticate', token: token })); + } else { + console.log("Sending tokens for 2 player game"); + socket.send(JSON.stringify({ type: 'authenticate2', token_1: token, token_2: token2 })); + } }; socket.onmessage = function (event) { @@ -239,15 +402,15 @@ document.addEventListener('DOMContentLoaded', () => { } function handleKeyDown(event) { - if (event.key === 'ArrowUp' || event.key === 'ArrowDown') { - console.log('Key press: ', event.key); + if (event.key === 'ArrowUp' || event.key === 'ArrowDown' || event.key === 'w' || event.key === 's') { + //console.log('Key press: ', event.key); sendKeyPress(event.key.toLowerCase()); } } function sendKeyPress(key) { if (socket.readyState === WebSocket.OPEN) { - console.log('Key sent: ', key); + //console.log('Key sent: ', key); socket.send(JSON.stringify({ type: 'key_press', key })); } } @@ -403,7 +566,6 @@ document.addEventListener('DOMContentLoaded', () => { console.log('No players to display'); } - players.forEach(player => { const row = document.createElement('tr'); row.innerHTML = ` diff --git a/pong/static/index.html b/pong/static/index.html index 48d0691..4270897 100644 --- a/pong/static/index.html +++ b/pong/static/index.html @@ -110,9 +110,27 @@ + + +