From 7cf13640a1a9cc209c0e105666b29c45fd8f3934 Mon Sep 17 00:00:00 2001 From: starnakin Date: Fri, 19 Jan 2024 15:38:04 +0100 Subject: [PATCH] game: core: change player to player.isconnected befort: if a player is disconnected he doesn't have an object after: he have un object with a socket == None --- frontend/static/js/api/game/Game.js | 46 +++++++------- frontend/static/js/api/game/MyPlayer.js | 2 +- frontend/static/js/api/game/Player.js | 11 +++- games/config.py | 2 + games/consumers.py | 9 ++- games/models.py | 5 +- games/objects/Game.py | 84 ++++++++++++++++--------- games/objects/GameManager.py | 4 +- games/objects/Player.py | 29 ++++++--- games/objects/Position.py | 7 ++- games/objects/Spectator.py | 12 +++- 11 files changed, 132 insertions(+), 79 deletions(-) diff --git a/frontend/static/js/api/game/Game.js b/frontend/static/js/api/game/Game.js index d1dac07..e8a3e5f 100644 --- a/frontend/static/js/api/game/Game.js +++ b/frontend/static/js/api/game/Game.js @@ -96,35 +96,26 @@ class Game { let index = this.players.indexOf((player) => player.id === player_data.user_id); - if (index !== -1) - this.players.slice(index, 1); - - this.players.push(new Player(player_data.user_id, - this, - player_data.rail_start_x, - player_data.rail_start_y, - player_data.rail_stop_x, - player_data.rail_stop_y, - player_data.nb_goal, - player_data.position.position - )); + this.players[index].is_connected = true; } _receive_player_leave(player_data) { - const user_id = player_data.user_id; - - const index = this.players.indexOf(this.players.find(player => player.id === user_id)); + let index = this.players.indexOf((player) => player.id === player_data.user_id); - if (index === -1) - return + this.players[index].is_connected = false; + } - this.players.splice(index, 1); + _receive_update_ball(data) + { + this.ball.position_x = data.position_x + this.ball.position_y = data.position_y + this.ball.position_x = data.position_x + this.ball.position_x = data.position_x } _receive_update_paddle(data) { - console.log(data) let player = this.players.find((player) => player.id === data.user_id); if (player === null) @@ -132,16 +123,17 @@ class Game this._receive_player_join(data); return; } - + player.is_connected = data.is_connected; player.update_pos(data.position.position, data.position.time); } _receive(data) { + console.log(data) if (data.detail === "update_paddle") this._receive_update_paddle(data); else if (data.detail === "update_ball") - this._update_ball(data); + this._receive_update_ball(data); else if (data.detail === "init_game") this._init_game(data) else if (data.detail === "player_join") @@ -167,7 +159,17 @@ class Game this.players = [] const players_data = data.players; players_data.forEach((player_data) => { - this._receive_player_join(player_data) + this.players.push(new Player(player_data.user_id, + this, + player_data.rail_start_x, + player_data.rail_start_y, + player_data.rail_stop_x, + player_data.rail_stop_y, + player_data.nb_goal, + player_data.position.position, + player_data.is_connected, + )); + }); this._inited = true; diff --git a/frontend/static/js/api/game/MyPlayer.js b/frontend/static/js/api/game/MyPlayer.js index 4c28fb0..a75c19b 100644 --- a/frontend/static/js/api/game/MyPlayer.js +++ b/frontend/static/js/api/game/MyPlayer.js @@ -5,7 +5,7 @@ class MyPlayer extends Player { constructor(client, game, rail_start_x, rail_start_y, rail_stop_x, rail_stop_y, nb_goal, positon) { - super(client.me.id, game, rail_start_x, rail_start_y, rail_stop_x, rail_stop_y, nb_goal, positon); + super(client.me.id, game, rail_start_x, rail_start_y, rail_stop_x, rail_stop_y, nb_goal, positon, true); this.client = client; } diff --git a/frontend/static/js/api/game/Player.js b/frontend/static/js/api/game/Player.js index 4d4dc0d..cc838ea 100644 --- a/frontend/static/js/api/game/Player.js +++ b/frontend/static/js/api/game/Player.js @@ -1,8 +1,9 @@ class Player { - constructor(id, game, rail_start_x, rail_start_y, rail_stop_x, rail_stop_y, nb_goal, positon) + constructor(id, game, rail_start_x, rail_start_y, rail_stop_x, rail_stop_y, nb_goal, positon, is_connected) { + this.is_connected = is_connected; this.id = id; this.positon = positon; this.nb_goal = nb_goal; @@ -27,7 +28,13 @@ class Player } draw(ctx) - { + { + if (this.is_connected === false) + { + ctx.moveTo(this.rail_start_x, this.rail_start_y); + ctx.lineTo(this.rail_stop_x, this.rail_stop_y); + return; + } let paddle_pos_x = this.rail_start_x + this.diff_x * this.positon, paddle_pos_y = this.rail_start_y + this.diff_y * this.positon; diff --git a/games/config.py b/games/config.py index f1209dc..40b72ef 100644 --- a/games/config.py +++ b/games/config.py @@ -16,3 +16,5 @@ BALL_SPEED_START = 1 BALL_SIZE = 4 BALL_SPAWN_POS_X = MAP_SIZE_X / 2 BALL_SPAWN_POS_Y = MAP_SIZE_Y / 2 + +SERVER_TPS = 20 \ No newline at end of file diff --git a/games/consumers.py b/games/consumers.py index 6ba0b6b..837e02c 100644 --- a/games/consumers.py +++ b/games/consumers.py @@ -41,6 +41,9 @@ class GameWebSocket(WebsocketConsumer): self.disconnect(1017) self.member: Player | Spectator = self.game.join(self.user.pk, self) + + def disconnect(self, code): + self.member.disconnect() def receive(self, text_data: str = None, bytes_data: bytes = None): @@ -49,8 +52,4 @@ class GameWebSocket(WebsocketConsumer): data: dict = json.loads(text_data) - self.member.receive(data) - - def disconnect(self, close_code): - if (self.user.pk in self.game.players_id): - self.game.remove(self.member) \ No newline at end of file + self.member.receive(data) \ No newline at end of file diff --git a/games/models.py b/games/models.py index c08f6ea..9641af5 100644 --- a/games/models.py +++ b/games/models.py @@ -1,7 +1,5 @@ from django.db import models -from channels.generic.websocket import AsyncWebsocketConsumer - # Create your models here. class GameModel(models.Model): @@ -14,6 +12,9 @@ class GameModel(models.Model): for player_id in players_id: GameMembersModel(game_id = self.pk, player_id = player_id).save() return self.pk + + def start(self): + self.started = True def get_players_id(self): return [game_player.player_id for game_player in GameMembersModel.objects.filter(game_id = self.pk)] diff --git a/games/objects/Game.py b/games/objects/Game.py index 465f9c9..7db38c7 100644 --- a/games/objects/Game.py +++ b/games/objects/Game.py @@ -3,6 +3,8 @@ from transcendence.abstract.AbstractRoomMember import AbstractRoomMember from channels.generic.websocket import WebsocketConsumer +from asgiref.sync import async_to_sync + from .Ball import Ball from .Player import Player from .Spectator import Spectator @@ -14,20 +16,25 @@ from .. import config from ..models import GameModel +from ..routine import routine + +import threading + class Game(AbstractRoom): def __init__(self, game_id): super().__init__(None) self.ball: Ball = Ball() - game_model: GameModel = GameModel.objects.get(pk = game_id) - self.players_id: list[int] = game_model.get_players_id() + self.model: GameModel = GameModel.objects.get(pk = game_id) self.started = False radius: float = min(config.MAP_SIZE_X, config.MAP_SIZE_Y) / 2 - 10 - self.nb_sides = len(self.players_id) * 2 + players_id: list[int] = self.model.get_players_id() + + self.nb_sides = len(players_id) * 2 self.polygon: list[tuple[float, float]] = [] @@ -41,6 +48,10 @@ class Game(AbstractRoom): self.polygon.append((x, y)) self.players: list[Player] = [] + for i, player_id in enumerate(players_id): + player = Player(self, player_id, None, *self.polygon[i * 2], *self.polygon[(i * 2 + 1) % self.nb_sides]) + self.players.append(player) + self.spectators: list[Spectator] = [] self.walls: list[Wall] = [] @@ -48,14 +59,27 @@ class Game(AbstractRoom): for i in range(1, self.nb_sides, 2): self.walls.append(Wall(*self.polygon[i], *self.polygon[(i + 1) % self.nb_sides])) + self._updated_players: list[Player] = [] + self.game_id: int = game_id + + self.thread = threading.Thread(target = routine, args=(self,)) + + self.thread.start() + + def get_players_id(self): + return [player.user_id for player in self.players] + + def get_players_connected(self) -> list[Player]: + return [player for player in self.players if player.is_connected()] def broadcast(self, detail: str, data: dict = {}, excludeds: list[Spectator | Player] = []): - members: list[Player | Spectator] = self.players + self.spectators + members: list[Player | Spectator] = self.get_players_connected() + self.spectators for excluded in excludeds: - members.remove(excluded) + if (excluded in members): + members.remove(excluded) for member in members: member.send(detail, data) @@ -70,40 +94,39 @@ class Game(AbstractRoom): def _send_game_data(self, member: AbstractRoomMember): member.send("init_game", self.to_dict()) - def append(self, member: AbstractRoomMember): - super().append(member) - member.send("init_game", self.to_dict()) + def everbody_is_here(self): + for player in self.players: + if not player.is_connected(): + return False + return True def _player_join(self, user_id: int, socket: WebsocketConsumer): - #check if member is a player - if (user_id not in self.players_id): - return None - # check if player is already connected player = self.get_player_by_user_id(user_id) - if (player is not None): + if (player is None): + return None + + if (player.is_connected()): player.disconnect(1001) - index: int = self.players_id.index(user_id) * 2 - - player = Player(self, user_id, socket, *self.polygon[index], *self.polygon[(index + 1) % self.nb_sides]) - - self.players.append(player) + player.connect(socket) - player.accept() - - self.broadcast("player_join", player.to_dict(), [player]) + if (self.everbody_is_here()): + print("start") + self.start() return player + def _update_player(self, player: Player): + self._updated_players.append(player) + def _player_leave(self, player: Player): - self.broadcast("player_leave", player.to_dict(), [player]) - self.players.remove(player) + self._updated_players.append(player) def _spectator_join(self, user_id: int, socket: WebsocketConsumer): - spectator: Spectator = Spectator(user_id, socket) + spectator: Spectator = Spectator(user_id, socket, self) self.spectators.append(spectator) @@ -115,19 +138,20 @@ class Game(AbstractRoom): self.spectators.remove(spectator) def join(self, user_id: int, socket: WebsocketConsumer) -> Spectator | Player: - if (user_id in self.players_id): - member: Player = self._player_join(user_id, socket) - print("yo") - else: + member: Player = self._player_join(user_id, socket) + if (member is None): member: Spectator = self._spectator_join(user_id, socket) self._send_game_data(member) return member def start(self): + if (self.started == True): + return self.started = True + self.model.start() - def remove(self, member: AbstractRoomMember): + def leave(self, member: AbstractRoomMember): if (isinstance(member, Player)): self._player_leave(member) elif (isinstance(member, Spectator)): @@ -140,6 +164,4 @@ class Game(AbstractRoom): "walls": [wall.to_dict() for wall in self.walls], } - print(self.players ) - return data \ No newline at end of file diff --git a/games/objects/GameManager.py b/games/objects/GameManager.py index e8367e8..d953690 100644 --- a/games/objects/GameManager.py +++ b/games/objects/GameManager.py @@ -21,6 +21,4 @@ class GameManager(): self._game_list.append(game) - return game - - \ No newline at end of file + return game \ No newline at end of file diff --git a/games/objects/Player.py b/games/objects/Player.py index 7dac516..f9a10ea 100644 --- a/games/objects/Player.py +++ b/games/objects/Player.py @@ -17,9 +17,7 @@ class Player(Spectator): def __init__(self, game: Game, user_id: int, socket: WebsocketConsumer, rail_start_x: float, rail_start_y: float, rail_stop_x: float, rail_stop_y: float) -> None: - super().__init__(user_id, socket) - - self.game: Game = game + super().__init__(user_id, socket, game) self.position: Position = Position(0.5, 0) @@ -29,7 +27,7 @@ class Player(Spectator): self.rail_start_y: float = rail_start_y self.rail_stop_x: float = rail_stop_x self.rail_stop_y: float = rail_stop_y - + def receive(self, data: dict): detail: str = data.get("detail") @@ -52,8 +50,6 @@ class Player(Spectator): def update_position(self, data: dict): - print(data) - new_position: Position = Position() new_position.position: float = data.get("position") @@ -83,7 +79,6 @@ class Player(Spectator): new_position_verified: Position = new_position.copy() if (distance > max_distance): - print(max_distance, distance, time_difference, self.position.position, new_position.position) new_position_verified.position = self.position.position + max_distance * sign if (not config.PADDLE_POSITION_MIN <= new_position_verified.position <= config.PADDLE_POSITION_MAX): @@ -92,15 +87,27 @@ class Player(Spectator): new_position_verified.position = min(new_position_verified.position, config.PADDLE_POSITION_MAX) invalid_pos: bool = new_position.position != new_position_verified.position + + if (new_position != self.position): + self.game._update_player(self) self.position = new_position if (invalid_pos): self.send("update_paddle", self.to_dict()) - self.game.broadcast("update_paddle", self.to_dict(), [self]) - - + def connect(self, socket: WebsocketConsumer): + self.socket = socket + self.accept() + self.game._update_player(self) + + def is_connected(self): + return self.socket != None + + def disconnect(self, code: int = 1000): + self.socket = None + self.game.leave(self) + def to_dict(self): data = { @@ -112,6 +119,8 @@ class Player(Spectator): "rail_start_y": self.rail_start_y, "rail_stop_x": self.rail_stop_x, "rail_stop_y": self.rail_stop_y, + + "is_connected": self.is_connected(), } return data \ No newline at end of file diff --git a/games/objects/Position.py b/games/objects/Position.py index aa13099..b2cfe66 100644 --- a/games/objects/Position.py +++ b/games/objects/Position.py @@ -1,4 +1,4 @@ -from typing import Union +from __future__ import annotations class Position: @@ -16,4 +16,7 @@ class Position: "time": self.time, } - return data \ No newline at end of file + return data + + def __eq__(self, __value: Position) -> bool: + return (self.position == __value.position) \ No newline at end of file diff --git a/games/objects/Spectator.py b/games/objects/Spectator.py index 444875c..deb008e 100644 --- a/games/objects/Spectator.py +++ b/games/objects/Spectator.py @@ -1,18 +1,28 @@ from __future__ import annotations +from channels.generic.websocket import WebsocketConsumer + from transcendence.abstract.AbstractRoomMember import AbstractRoomMember from typing import TYPE_CHECKING if TYPE_CHECKING: from .Player import Player + from .Game import Game from .Ball import Ball class Spectator(AbstractRoomMember): + def __init__(self, user_id: int, socket: WebsocketConsumer, game: Game): + super().__init__(user_id, socket) + self.game: Game = game + def send_paddle(self, player: Player): self.send("update_paddle", player.to_dict()) def send_ball(self, ball: Ball): - self.send("update_ball", ball.to_dict()) \ No newline at end of file + self.send("update_ball", ball.to_dict()) + + def disconnect(self, code: int = 1000): + self.game.leave(self) \ No newline at end of file