added frontend, backend game logic v1

This commit is contained in:
CHIBOUB Chakib
2024-07-22 17:21:58 +02:00
parent dafa62612e
commit c4a73078ab
24 changed files with 765 additions and 129 deletions

0
pong/game/__init__.py Normal file
View File

47
pong/game/consumers.py Normal file
View File

@@ -0,0 +1,47 @@
# /pong/game/consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer
from django.contrib.auth.models import User
from channels.db import database_sync_to_async
from .matchmaking import match_maker # Import the match_maker instance
class GameConsumer(AsyncWebsocketConsumer):
async def connect(self):
await self.accept()
print("User connected")
async def receive(self, text_data):
data = json.loads(text_data)
#print(f"MESSAGE RECEIVED: {data['type']}")
if data['type'] == 'authenticate':
await self.authenticate(data['token'])
elif data['type'] == 'key_press':
await match_maker.handle_key_press(self, data['key'])
async def authenticate(self, token):
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")
await self.join_waiting_room()
else:
await self.send(text_data=json.dumps({'type': 'error', 'message': 'Authentication failed'}))
print("Authentication failed")
@database_sync_to_async
def get_user_from_token(self, token):
try:
user = User.objects.filter(auth_token=token).first()
return user
except User.DoesNotExist:
return None
async def join_waiting_room(self):
await self.send(text_data=json.dumps({'type': 'waiting_room'}))
await match_maker.add_player(self)
async def disconnect(self, close_code):
await match_maker.remove_player(self)
print(f"User {self.user} disconnected")

86
pong/game/game.py Normal file
View File

@@ -0,0 +1,86 @@
# /pong/game/game.py
import json
import asyncio
import random
class Game:
def __init__(self, game_id, player1, player2):
self.game_id = game_id
self.player1 = player1
self.player2 = player2
self.game_state = {
'player1_name': player1.user.username,
'player2_name': player2.user.username,
'player1_position': 200, # middle of the game field
'player2_position': 200,
'ball_position': {'x': 400, 'y': 300}, # middle of the game field
'ball_velocity': {'x': random.choice([-5, 5]), 'y': random.choice([-5, 5])},
'player1_score': 0,
'player2_score': 0
}
self.game_loop_task = None
async def start_game(self):
print(f"- Game {self.game_id} START")
self.game_loop_task = asyncio.create_task(self.game_loop())
async def game_loop(self):
while True:
self.update_game_state()
await self.send_game_state()
await asyncio.sleep(1/60) # 60 FPS
def update_game_state(self):
# Update ball position
self.game_state['ball_position']['x'] += self.game_state['ball_velocity']['x']
self.game_state['ball_position']['y'] += self.game_state['ball_velocity']['y']
# Check for collisions with top and bottom walls
if self.game_state['ball_position']['y'] <= 0 or self.game_state['ball_position']['y'] >= 600:
self.game_state['ball_velocity']['y'] *= -1
# Check for scoring
if self.game_state['ball_position']['x'] <= 0:
self.game_state['player2_score'] += 1
self.reset_ball()
elif self.game_state['ball_position']['x'] >= 800:
self.game_state['player1_score'] += 1
self.reset_ball()
# Check for collisions with paddles
if self.game_state['ball_position']['x'] <= 20 and \
self.game_state['player1_position'] - 50 <= self.game_state['ball_position']['y'] <= self.game_state['player1_position'] + 50:
self.game_state['ball_velocity']['x'] *= -1
elif self.game_state['ball_position']['x'] >= 780 and \
self.game_state['player2_position'] - 50 <= self.game_state['ball_position']['y'] <= self.game_state['player2_position'] + 50:
self.game_state['ball_velocity']['x'] *= -1
def reset_ball(self):
self.game_state['ball_position'] = {'x': 400, 'y': 300}
self.game_state['ball_velocity'] = {'x': random.choice([-5, 5]), 'y': random.choice([-5, 5])}
async def send_game_state(self):
message = json.dumps({
'type': 'game_state_update',
'game_state': self.game_state
})
await self.player1.send(message)
await self.player2.send(message)
async def handle_key_press(self, player, key):
if player == self.player1:
if key == 'arrowup' and self.game_state['player1_position'] > 0:
self.game_state['player1_position'] -= 10
elif key == 'arrowdown' and self.game_state['player1_position'] < 550:
self.game_state['player1_position'] += 10
elif player == self.player2:
if key == 'arrowup' and self.game_state['player2_position'] > 0:
self.game_state['player2_position'] -= 10
elif key == 'arrowdown' and self.game_state['player2_position'] < 550:
self.game_state['player2_position'] += 10
async def end_game(self):
if self.game_loop_task:
self.game_loop_task.cancel()
# Add any cleanup code here

65
pong/game/matchmaking.py Normal file
View File

@@ -0,0 +1,65 @@
# /pong/game/matchmaking.py
import json
import asyncio
from .game import Game
class MatchMaker:
def __init__(self):
self.waiting_players = []
self.active_games = {}
self.matching_task = None
async def add_player(self, player):
if player not in self.waiting_players:
self.waiting_players.append(player)
print(f"User {player.user.username} joins the WAITING ROOM")
if not self.matching_task or self.matching_task.done():
self.matching_task = asyncio.create_task(self.match_loop())
async def remove_player(self, player):
if player in self.waiting_players:
self.waiting_players.remove(player)
async def match_loop(self):
while True:
if len(self.waiting_players) >= 2:
player1 = self.waiting_players.pop(0)
player2 = self.waiting_players.pop(0)
print(f"MATCH FOUND: {player1.user.username} vs {player2.user.username}")
game_id = await self.create_game(player1, player2)
else:
# No players to match, wait for a short time before checking again
await asyncio.sleep(1)
async def create_game(self, player1, player2):
game_id = len(self.active_games) + 1
print(f"- Creating game: {game_id}")
new_game = Game(game_id, player1, player2)
self.active_games[game_id] = new_game
await self.notify_players(player1, player2, game_id)
asyncio.create_task(new_game.start_game())
return game_id
async def notify_players(self, player1, player2, game_id):
await player1.send(json.dumps({
'type': 'game_start',
'game_id': game_id,
'player1': player1.user.username,
'player2': player2.user.username
}))
await player2.send(json.dumps({
'type': 'game_start',
'game_id': game_id,
'player1': player1.user.username,
'player2': player2.user.username
}))
async def handle_key_press(self, player, key):
for game in self.active_games.values():
if player in [game.player1, game.player2]:
await game.handle_key_press(player, key)
break
# Instance of the class
match_maker = MatchMaker()

6
pong/game/models.py Normal file
View File

@@ -0,0 +1,6 @@
# /pong/game/models.py
from django.db import models
from django.contrib.auth.models import User
User.add_to_class('auth_token', models.CharField(max_length=100, null=True, blank=True, unique=True))

8
pong/game/routing.py Normal file
View File

@@ -0,0 +1,8 @@
# /pong/game/routing.py
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r'ws/game/$', consumers.GameConsumer.as_asgi()),
]

11
pong/game/urls.py Normal file
View File

@@ -0,0 +1,11 @@
# /pong/game/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('check_user_exists/', views.check_user_exists, name='check_user_exists'),
path('register_user/', views.register_user, name='register_user'),
path('authenticate_user/', views.authenticate_user, name='authenticate_user'),
]

64
pong/game/views.py Normal file
View File

@@ -0,0 +1,64 @@
# /pong/game/views.py
from django.shortcuts import render
def index(request):
return render(request, 'index.html')
from django.http import JsonResponse
from django.contrib.auth.models import User
from django.contrib.auth import authenticate
from django.views.decorators.csrf import csrf_exempt
import json
import uuid
@csrf_exempt
def register_user(request):
if request.method == 'POST':
data = json.loads(request.body)
username = data.get('username')
password = data.get('password')
if not User.objects.filter(username=username).exists():
user = User.objects.create_user(username=username, password=password)
token = get_or_create_token(user)
return JsonResponse({'registered': True, 'token': token})
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':
try:
data = json.loads(request.body)
username = data.get('username', '')
password = data.get('password', '')
user = authenticate(username=username, password=password)
if user is not None:
token = get_or_create_token(user)
return JsonResponse({'authenticated': True, 'token': token, 'user_id': user.id})
else:
return JsonResponse({'authenticated': False}, status=401)
except Exception as e:
return JsonResponse({'error': str(e)}, status=500)
else:
return JsonResponse({'error': 'Method not allowed'}, status=405)
def get_or_create_token(user):
if not user.auth_token:
while True:
token = str(uuid.uuid4())
if not User.objects.filter(auth_token=token).exists():
user.auth_token = token
user.save()
break
return user.auth_token