mirror of
https://github.com/AudebertAdrien/ft_transcendence.git
synced 2025-12-16 14:07:49 +01:00
Server now can handle multiple games at once, and closes game routine when someone disconnects
This commit is contained in:
parent
e7797eac2c
commit
18c6580ae3
@ -4,20 +4,23 @@ 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
|
||||
from .matchmaking import match_maker
|
||||
|
||||
class GameConsumer(AsyncWebsocketConsumer):
|
||||
async def connect(self):
|
||||
await self.accept()
|
||||
self.game = None
|
||||
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'])
|
||||
if self.game:
|
||||
await self.game.handle_key_press(self, data['key'])
|
||||
else:
|
||||
await match_maker.handle_key_press(self, data['key'])
|
||||
|
||||
async def authenticate(self, token):
|
||||
user = await self.get_user_from_token(token)
|
||||
@ -43,5 +46,10 @@ class GameConsumer(AsyncWebsocketConsumer):
|
||||
await match_maker.add_player(self)
|
||||
|
||||
async def disconnect(self, close_code):
|
||||
if self.game:
|
||||
await self.game.end_game(disconnected_player=self)
|
||||
await match_maker.remove_player(self)
|
||||
print(f"User {self.user} disconnected")
|
||||
print(f"User {self.user.username if hasattr(self, 'user') else 'Unknown'} disconnected")
|
||||
|
||||
async def set_game(self, game):
|
||||
self.game = game
|
||||
|
||||
@ -22,9 +22,10 @@ class Game:
|
||||
}
|
||||
self.speed = 1
|
||||
self.game_loop_task = None
|
||||
self.ended = False
|
||||
|
||||
async def start_game(self):
|
||||
print(f"- Game {self.game_id} START")
|
||||
print(f"- Game #{self.game_id} STARTED")
|
||||
self.game_loop_task = asyncio.create_task(self.game_loop())
|
||||
|
||||
async def game_loop(self):
|
||||
@ -35,7 +36,6 @@ class Game:
|
||||
await self.send_game_state()
|
||||
await asyncio.sleep(1/60) # Around 60 FPS
|
||||
|
||||
# The amazing AI BOT player
|
||||
async def update_bot_position(self):
|
||||
target_y = self.game_state['ball_position']['y']
|
||||
if self.game_state['player2_position'] < target_y < self.game_state['player2_position'] + 80:
|
||||
@ -69,7 +69,6 @@ class Game:
|
||||
self.reset_ball()
|
||||
elif self.game_state['ball_position']['x'] >= 790:
|
||||
self.game_state['player1_score'] += 1
|
||||
print(f"*** Ball in position ({self.game_state['ball_position']['x']},{self.game_state['ball_position']['y']}) with a speed ratio of {self.speed}")
|
||||
self.reset_ball()
|
||||
|
||||
def reset_ball(self):
|
||||
@ -93,6 +92,8 @@ class Game:
|
||||
self.game_state['ball_velocity']['y'] = 10
|
||||
|
||||
async def send_game_state(self):
|
||||
if self.ended:
|
||||
return
|
||||
message = json.dumps({
|
||||
'type': 'game_state_update',
|
||||
'game_state': self.game_state
|
||||
@ -102,26 +103,40 @@ class Game:
|
||||
await self.player2.send(message)
|
||||
|
||||
async def handle_key_press(self, player, key):
|
||||
if self.ended:
|
||||
return
|
||||
if player == self.player1:
|
||||
if key == 'arrowup':
|
||||
self.game_state['player1_position'] -= 25
|
||||
if self.game_state['player1_position'] < 0:
|
||||
self.game_state['player1_position'] = 0
|
||||
self.game_state['player1_position'] = max(self.game_state['player1_position'] - 25, 0)
|
||||
elif key == 'arrowdown':
|
||||
self.game_state['player1_position'] += 25
|
||||
if self.game_state['player1_position'] > 300:
|
||||
self.game_state['player1_position'] = 300
|
||||
self.game_state['player1_position'] = min(self.game_state['player1_position'] + 25, 300)
|
||||
elif not self.botgame and player == self.player2:
|
||||
if key == 'arrowup':
|
||||
self.game_state['player2_position'] -= 25
|
||||
if self.game_state['player2_position'] < 0:
|
||||
self.game_state['player2_position'] = 0
|
||||
self.game_state['player2_position'] = max(self.game_state['player2_position'] - 25, 0)
|
||||
elif key == 'arrowdown':
|
||||
self.game_state['player2_position'] += 25
|
||||
if self.game_state['player2_position'] > 300:
|
||||
self.game_state['player2_position'] = 300
|
||||
self.game_state['player2_position'] = min(self.game_state['player2_position'] + 25, 300)
|
||||
|
||||
async def end_game(self):
|
||||
if self.game_loop_task:
|
||||
self.game_loop_task.cancel()
|
||||
# Add any cleanup code here
|
||||
async def end_game(self, disconnected_player=None):
|
||||
if not self.ended:
|
||||
self.ended = True
|
||||
if self.game_loop_task:
|
||||
self.game_loop_task.cancel()
|
||||
print(f"- Game #{self.game_id} ENDED")
|
||||
# Notify that one player left the game
|
||||
if disconnected_player:
|
||||
remaining_player = self.player2 if disconnected_player == self.player1 else self.player1
|
||||
disconnected_name = disconnected_player.user.username
|
||||
message = json.dumps({
|
||||
'type': 'player_disconnected',
|
||||
'player': disconnected_name
|
||||
})
|
||||
if not self.botgame:
|
||||
await remaining_player.send(message)
|
||||
# Notify both players that the game has ended
|
||||
end_message = json.dumps({
|
||||
'type': 'game_ended',
|
||||
'game_id': self.game_id
|
||||
})
|
||||
await self.player1.send(end_message)
|
||||
if not self.botgame:
|
||||
await self.player2.send(end_message)
|
||||
|
||||
@ -22,42 +22,52 @@ class MatchMaker:
|
||||
async def remove_player(self, player):
|
||||
if player in self.waiting_players:
|
||||
self.waiting_players.remove(player)
|
||||
|
||||
for game in self.active_games.values():
|
||||
if player in [game.player1, game.player2]:
|
||||
await game.end_game(disconnected_player=player)
|
||||
del self.active_games[game.game_id]
|
||||
break
|
||||
|
||||
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}")
|
||||
print(f"*** MATCH FOUND: {player1.user.username} vs {player2.user.username}")
|
||||
await self.create_game(player1, player2)
|
||||
else:
|
||||
await asyncio.sleep(1)
|
||||
self.timer += 1
|
||||
# Waiting for more than 30s -> BOT game
|
||||
if self.timer >= 30:
|
||||
if self.timer >= 30 and self.waiting_players:
|
||||
player1 = self.waiting_players.pop(0)
|
||||
print(f"MATCH FOUND: {player1.user.username} vs BOT")
|
||||
print(f"*** MATCH FOUND: {player1.user.username} vs BOT")
|
||||
self.botgame = True
|
||||
self.timer = 0
|
||||
await self.create_bot_game(player1)
|
||||
if not self.waiting_players:
|
||||
break
|
||||
|
||||
async def create_game(self, player1, player2):
|
||||
game_id = len(self.active_games) + 1
|
||||
print(f"- Creating game: {game_id}")
|
||||
print(f"- Creating game: #{game_id}")
|
||||
new_game = Game(game_id, player1, player2)
|
||||
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)
|
||||
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}")
|
||||
print(f"- Creating BOT game: #{game_id}")
|
||||
new_game = Game(game_id, player1, None)
|
||||
self.active_games[game_id] = new_game
|
||||
await player1.set_game(new_game)
|
||||
await self.notify_players(player1, None, game_id)
|
||||
asyncio.create_task(new_game.start_game())
|
||||
|
||||
|
||||
async def notify_players(self, player1, player2, game_id):
|
||||
if player2:
|
||||
await player1.send(json.dumps({
|
||||
@ -82,11 +92,7 @@ class MatchMaker:
|
||||
|
||||
async def handle_key_press(self, player, key):
|
||||
for game in self.active_games.values():
|
||||
if not self.botgame:
|
||||
if player in [game.player1, game.player2]:
|
||||
await game.handle_key_press(player, key)
|
||||
break
|
||||
else:
|
||||
if player in [game.player1, game.player2]:
|
||||
await game.handle_key_press(player, key)
|
||||
break
|
||||
|
||||
|
||||
@ -170,6 +170,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
startGame(data.game_id, data.player1, data.player2);
|
||||
} else if (data.type === 'game_state_update') {
|
||||
updateGameState(data.game_state);
|
||||
} else if (data.type === 'player_disconnected') {
|
||||
console.log("Player disconnected:", data.player);
|
||||
} else if (data.type === 'game_ended') {
|
||||
console.log("Game ended:", data.game_id);
|
||||
} else if (data.type === 'error') {
|
||||
console.error(data.message);
|
||||
} else {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user