core: game: split backed django

This commit is contained in:
starnakin 2024-04-08 14:19:53 +02:00
parent c1624cce83
commit 25d86012ba
19 changed files with 192 additions and 117 deletions

View File

@ -11,26 +11,37 @@ from .objects.GameManager import GameManager
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from .objects.Spectator import Spectator from .objects.pong.PongSpectator import PongSpectator
from .objects.Player import Player from .objects.pong.PongPlayer import PongPlayer
from .objects.Game import Game from .objects.pong.PongGame import PongGame
from .objects.tictactoe.TicTacToeGame import TicTacToeGame
from .objects.tictactoe.TicTacToePlayer import TicTacToePlayer
from .objects.tictactoe.TicTacToeSpectator import TicTacToeSpectator
game_manager: GameManager = GameManager() game_manager: GameManager = GameManager()
class TicTacToeWebSocket(WebsocketConsumer): class TicTacToeWebSocket(WebsocketConsumer):
def connect(self): def connect(self):
return super().connect()
self.user: User = self.scope["user"]
if (self.user.pk is None):
self.user.pk = 0
self.accept()
self.game_id = int(self.scope['url_route']['kwargs']['game_id'])
self.game: PongGame = game_manager.get(self.game_id, "pong")
def receive(self, text_data=None, bytes_data=None): def receive(self, text_data=None, bytes_data=None):
return super().receive(text_data, bytes_data) pass
class PongWebSocket(WebsocketConsumer): class PongWebSocket(WebsocketConsumer):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.channel_name = "games"
self.group_name = "games"
self.member = None self.member = None
def connect(self): def connect(self):
@ -43,14 +54,14 @@ class PongWebSocket(WebsocketConsumer):
self.game_id = int(self.scope['url_route']['kwargs']['game_id']) self.game_id = int(self.scope['url_route']['kwargs']['game_id'])
self.game: Game = game_manager.get(self.game_id) self.game: PongGame = game_manager.get(self.game_id, "pong")
if (self.game is None): if (self.game is None):
self.send(text_data=json.dumps({"detail": "Game not found"})) self.send(text_data=json.dumps({"detail": "Game not found"}))
self.disconnect(1404) self.disconnect(1404)
return return
self.member: Player | Spectator = self.game.join(self.user.pk, self) self.member: PongPlayer | PongSpectator = self.game.join(self.user.pk, self)
def disconnect(self, code): def disconnect(self, code):
if (self.member is not None): if (self.member is not None):

View File

@ -15,10 +15,10 @@ class GameModel(models.Model):
winner_id = models.IntegerField(default = -1) winner_id = models.IntegerField(default = -1)
start_timestamp = models.BigIntegerField(null = True, blank = True) start_timestamp = models.BigIntegerField(null = True, blank = True)
stop_timestamp = models.BigIntegerField(null = True, blank = True) stop_timestamp = models.BigIntegerField(null = True, blank = True)
gamemode = models.CharField(max_length = 60, default = "pong") game_type = models.CharField(max_length = 60, default = "pong")
def create(self, gamemode: str, players_id: [int]): def create(self, game_type: str, players_id: list[int]):
self.gamemode = gamemode self.game_type = game_type
self.save() self.save()
for player_id in players_id: for player_id in players_id:
GameMembersModel(game_id = self.pk, player_id = player_id).save() GameMembersModel(game_id = self.pk, player_id = player_id).save()

46
games/objects/AGame.py Normal file
View File

@ -0,0 +1,46 @@
from transcendence.abstract.AbstractRoom import AbstractRoom
from transcendence.abstract.AbstractRoomMember import AbstractRoomMember
from .APlayer import APlayer
from .ASpectator import ASpectator
from ..models import GameModel
class AGame(AbstractRoom):
def __init__(self, game_type: str, game_id: int, game_manager):
super().__init__(game_manager)
self.model: GameModel = GameModel.objects.get(pk = game_id, game_type = game_type)
players_id: list[int] = self.model.get_players_id()
self.players: list[APlayer] = [APlayer(player_id, None, self) for player_id in players_id]
self.spectators: list[ASpectator] = []
self.game_id: int = game_id
def get_players_id(self) -> list[int]:
return [player.user_id for player in self.players]
def get_players_connected(self) -> list[APlayer]:
return [player for player in self.players if player.is_connected()]
def get_player_by_user_id(self, user_id: int) -> APlayer:
for player in self.players:
if (player.user_id == user_id):
return player
return None
def broadcast(self, detail: str, data: dict = {}, excludeds: list[ASpectator | APlayer] = []):
members: list[APlayer | ASpectator] = self.get_players_connected() + self.spectators
for excluded in excludeds:
if (excluded in members):
members.remove(excluded)
for member in members:
member.send(detail, data)

27
games/objects/APlayer.py Normal file
View File

@ -0,0 +1,27 @@
from __future__ import annotations
from channels.generic.websocket import WebsocketConsumer
from .ASpectator import ASpectator
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from games.objects.AGame import AGame
class APlayer(ASpectator):
def __init__(self, user_id: int, socket: WebsocketConsumer, game: AGame):
super().__init__(user_id, socket, game)
def is_connected(self) -> bool:
return self.socket != None
def send_error(self, error_message: str, error_data = {}):
data: dict = {
"error_message": error_message
}
data.update(error_data)
self.send("error", data)

View File

@ -0,0 +1,17 @@
from channels.generic.websocket import WebsocketConsumer
from transcendence.abstract.AbstractRoomMember import AbstractRoomMember
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from .AGame import AGame
class ASpectator(AbstractRoomMember):
def __init__(self, user_id: int, socket: WebsocketConsumer, game):
super().__init__(user_id, socket)
self.game: AGame = game

View File

@ -1,28 +1,33 @@
from ..models import GameModel from ..models import GameModel
from .Game import Game from .pong.PongGame import PongGame
from .tictactoe.TicTacToeGame import TicTacToeGame
class GameManager(): class GameManager():
def __init__(self) -> None: def __init__(self) -> None:
self._game_list: list[Game] = [] self._game_list: list[PongGame | TicTacToeGame] = []
def remove(self, game: Game): def remove(self, game: PongGame | TicTacToeGame) -> None:
if (game not in self._game_list): if (game not in self._game_list):
return return
self._game_list.remove(game) self._game_list.remove(game)
def get(self, game_id: int) -> Game: def get(self, game_id: int, game_type: str) -> TicTacToeGame | PongGame:
if (not GameModel.objects.filter(pk = game_id, finished = False).exists()): if (not GameModel.objects.filter(pk=game_id, finished=False, game_type=game_type).exists()):
return None return None
for game in self._game_list: for game in self._game_list:
game: Game game: PongGame | TicTacToeGame
if (game.game_id == game_id): if (game.game_id == game_id):
return game return game
game: Game = Game(game_id, self) game: PongGame | TicTacToeGame
if (game_type == "pong"):
game = PongGame(game_id, self)
elif (game_type == "tictactoe"):
game = PongGame(game_id, self)
self._game_list.append(game) self._game_list.append(game)

View File

@ -1,6 +1,6 @@
from __future__ import annotations from __future__ import annotations
from .. import config from ... import config
from .Position import Position from .Position import Position
from .Point import Point from .Point import Point

View File

@ -3,39 +3,34 @@ from transcendence.abstract.AbstractRoomMember import AbstractRoomMember
from channels.generic.websocket import WebsocketConsumer from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync from ..AGame import AGame
from .Ball import Ball from .Ball import Ball
from .Player import Player from .PongPlayer import PongPlayer
from .Spectator import Spectator from .PongSpectator import PongSpectator
from .Wall import Wall from .Wall import Wall
from .Point import Point from .Point import Point
from .Segment import Segment from .Segment import Segment
import math import math
from .. import config from ... import config
from ..models import GameModel from ...routine import routine
from ..routine import routine
import threading import threading
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from .GameManager import GameManager pass
class Game(AbstractRoom): class PongGame(AGame):
def __init__(self, game_id: int, game_manager): def __init__(self, game_id: int, game_manager):
super().__init__(None)
self.game_manager: GameManager = game_manager super().__init__("pong", game_id, game_manager)
self.ball: Ball = Ball() self.ball: Ball = Ball()
self.model: GameModel = GameModel.objects.get(pk = game_id)
self.stopped: bool = False self.stopped: bool = False
@ -61,24 +56,24 @@ class Game(AbstractRoom):
segments.append(Segment(polygon[i], polygon[(i + 1) % nb_sides])) segments.append(Segment(polygon[i], polygon[(i + 1) % nb_sides]))
self.walls: list[Wall] self.walls: list[Wall]
self.players: list[Player] self.players: list[PongPlayer]
nb_players: int = len(players_id) nb_players: int = len(players_id)
if (nb_players == 2): if (nb_players == 2):
self.players = [Player(self, players_id[0], None, segments[0]), Player(self, players_id[1], None, segments[2])] self.players = [PongPlayer(self, players_id[0], None, segments[0]), PongPlayer(self, players_id[1], None, segments[2])]
self.walls = [Wall(segments[1].start, segments[1].stop), Wall(segments[3].start, segments[3].stop)] self.walls = [Wall(segments[1].start, segments[1].stop), Wall(segments[3].start, segments[3].stop)]
else: else:
self.players = [] self.players = []
self.walls = [] self.walls = []
for i, side in enumerate(range(4)): for i in range(4):
if (i < nb_players): if (i < nb_players):
self.players.append(Player(self, players_id[i], None, segments[i])) self.players.append(PongPlayer(self, players_id[i], None, segments[i]))
else: else:
self.walls.append(Wall(segments[i])) self.walls.append(Wall(segments[i]))
self.spectators: list[Spectator] = [] self.spectators: list[PongSpectator]
self._updated_players: list[Player] = [] self._updated_players: list[PongPlayer] = []
self.game_id: int = game_id self.game_id: int = game_id
@ -86,43 +81,20 @@ class Game(AbstractRoom):
self.thread.start() self.thread.start()
def get_players_id(self): def goal(self, goal_taker: PongPlayer):
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.get_players_connected() + self.spectators
for excluded in excludeds:
if (excluded in members):
members.remove(excluded)
for member in members:
member.send(detail, data)
def goal(self, goal_taker: Player):
timestamp = goal_taker.add_goal() timestamp = goal_taker.add_goal()
self.broadcast("goal", {"player_id": goal_taker.user_id, self.broadcast("goal", {"player_id": goal_taker.user_id,
"timestamp": timestamp}) "timestamp": timestamp})
if (len(goal_taker.score) >= config.GAME_MAX_SCORE): if (len(goal_taker.score) >= config.GAME_MAX_SCORE):
connected_players: list[Player] = self.get_players_connected() connected_players: list[PongPlayer] = self.get_players_connected()
if (len(connected_players) == 2): if (len(connected_players) == 2):
self.finish(connected_players[not connected_players.index(goal_taker)]) self.finish(connected_players[not connected_players.index(goal_taker)])
else: else:
goal_taker.eliminate() goal_taker.eliminate()
def get_player_by_user_id(self, user_id: int) -> Player: def _send_game_data(self, member: PongSpectator | PongPlayer):
for player in self.players:
if (player.user_id == user_id):
return player
return None
def _send_game_data(self, member: Spectator | Player):
member.send("init_game", self.to_dict()) member.send("init_game", self.to_dict())
def _everbody_is_here(self): def _everbody_is_here(self):
@ -153,21 +125,21 @@ class Game(AbstractRoom):
return player return player
def update_player(self, player: Player): def update_player(self, player: PongPlayer):
self._updated_players.append(player) self._updated_players.append(player)
def finish(self, winner: Player): def finish(self, winner: PongPlayer):
self.broadcast("finish", {"winner": winner.to_dict()}) self.broadcast("finish", {"winner": winner.to_dict()})
self.model.finish(winner.user_id) self.model.finish(winner.user_id)
def _player_leave(self, player: Player): def _player_leave(self, player: PongPlayer):
connected_players: list[Player] = self.get_players_connected() connected_players: list[PongPlayer] = self.get_players_connected()
if (self.model.started): if (self.model.started):
if (len(connected_players) == 1): if (len(connected_players) == 1):
print([player.username for player in connected_players]) print([player.username for player in connected_players])
last_player: Player = connected_players[0] last_player: PongPlayer = connected_players[0]
self.finish(last_player) self.finish(last_player)
return return
@ -175,19 +147,19 @@ class Game(AbstractRoom):
def _spectator_join(self, user_id: int, socket: WebsocketConsumer): def _spectator_join(self, user_id: int, socket: WebsocketConsumer):
spectator: Spectator = Spectator(user_id, socket, self) spectator: PongSpectator = PongSpectator(user_id, socket, self)
self.spectators.append(spectator) self.spectators.append(spectator)
return spectator return spectator
def _spectator_leave(self, spectator: Spectator): def _spectator_leave(self, spectator: PongSpectator):
self.spectators.remove(spectator) self.spectators.remove(spectator)
def join(self, user_id: int, socket: WebsocketConsumer) -> Spectator | Player: def join(self, user_id: int, socket: WebsocketConsumer) -> PongSpectator | PongPlayer:
member: Player = self._player_join(user_id, socket) member: PongPlayer = self._player_join(user_id, socket)
if (member is None): if (member is None):
member: Spectator = self._spectator_join(user_id, socket) member: PongSpectator = self._spectator_join(user_id, socket)
self._send_game_data(member) self._send_game_data(member)
return member return member
@ -197,9 +169,9 @@ class Game(AbstractRoom):
self.model.start() self.model.start()
def leave(self, member: AbstractRoomMember): def leave(self, member: AbstractRoomMember):
if (isinstance(member, Player)): if (isinstance(member, PongPlayer)):
self._player_leave(member) self._player_leave(member)
elif (isinstance(member, Spectator)): elif (isinstance(member, PongSpectator)):
self._spectator_leave(member) self._spectator_leave(member)
if (self._nobody_is_here()): if (self._nobody_is_here()):
self.stopped = True self.stopped = True

View File

@ -1,25 +1,23 @@
from __future__ import annotations from __future__ import annotations
from .. import config from ... import config
from channels.generic.websocket import WebsocketConsumer from channels.generic.websocket import WebsocketConsumer
from django.contrib.auth.models import User from django.contrib.auth.models import User
from .Position import Position from .Position import Position
from .Spectator import Spectator from ..APlayer import APlayer
from .Point import Point
from .Segment import Segment from .Segment import Segment
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from ...transcendence.abstract.AbstractRoomMember import AbstractRoomMember from .PongGame import PongGame
from .Game import Game
class Player(Spectator): class PongPlayer(APlayer):
def __init__(self, game: Game, user_id: int, socket: WebsocketConsumer, rail: Segment) -> None: def __init__(self, game: PongGame, user_id: int, socket: WebsocketConsumer, rail: Segment) -> None:
super().__init__(user_id, socket, game) super().__init__(user_id, socket, game)
@ -47,30 +45,20 @@ class Player(Spectator):
if (detail == "update_my_paddle_pos"): if (detail == "update_my_paddle_pos"):
self.update_position(data) self.update_position(data)
def send_error(self, error_message: str, error_data = {}):
data: dict = {
"error_message": error_message
}
data.update(error_data)
self.send("error", data)
def update_position(self, data: dict): def update_position(self, data: dict):
new_position: Position = Position() new_position: Position = Position()
new_position.position: float = data.get("position") new_position.location = data.get("position")
if (new_position.position is None): if (new_position.position is None):
self.send_error("missing new_position") self.send_error("missing new_position")
return return
new_position.time: float = data.get("time") new_position.time = data.get("time")
if (new_position.time is None): if (new_position.time is None):
self.game_member.send_error("missing time") self.send_error("missing time")
return return
if (self.position.time > new_position.time): if (self.position.time > new_position.time):
@ -110,9 +98,6 @@ class Player(Spectator):
self.accept() self.accept()
self.game.update_player(self) self.game.update_player(self)
def is_connected(self):
return self.socket != None
def disconnect(self, code: int = 1000): def disconnect(self, code: int = 1000):
self.socket = None self.socket = None
self.game.leave(self) self.game.leave(self)

View File

@ -7,18 +7,19 @@ from transcendence.abstract.AbstractRoomMember import AbstractRoomMember
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from .Player import Player from .PongPlayer import PongPlayer
from .Game import Game from .PongGame import PongGame
from ..ASpectator import ASpectator
from .Ball import Ball from .Ball import Ball
class Spectator(AbstractRoomMember): class PongSpectator(ASpectator):
def __init__(self, user_id: int, socket: WebsocketConsumer, game: Game): def __init__(self, user_id: int, socket: WebsocketConsumer, game: PongGame):
super().__init__(user_id, socket) super().__init__(user_id, socket)
self.game: Game = game self.game: PongGame = game
def send_paddle(self, player: Player): def send_paddle(self, player: PongPlayer):
self.send("update_player", player.to_dict()) self.send("update_player", player.to_dict())
def send_ball(self, ball: Ball): def send_ball(self, ball: Ball):

View File

@ -4,8 +4,8 @@ from .Point import Point
class Position: class Position:
def __init__(self, location: int | Point = 0, time: int = 0) -> None: def __init__(self, location: int | Point = 0, time: int = 0) -> None:
self.time = time self.time: float = time
self.location = location self.location: float = location
def copy(self): def copy(self):
return Position(self.location, self.time) return Position(self.location, self.time)

View File

@ -0,0 +1,5 @@
from ..AGame import AGame
class TicTacToeGame(AGame):
pass

View File

@ -0,0 +1,4 @@
from ..APlayer import APlayer
class TicTacToePlayer(APlayer):
pass

View File

@ -0,0 +1,4 @@
from ..ASpectator import ASpectator
class TicTacToeSpectator(ASpectator):
pass

View File

@ -3,15 +3,13 @@ from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from .objects.Spectator import Spectator from .objects.pong.PongSpectator import PongSpectator
from .objects.Player import Player from .objects.pong.PongPlayer import PongPlayer
from .objects.Game import Game from .objects.pong.PongGame import PongGame
from .objects.Ball import Ball from .objects.pong.Ball import Ball
from .objects.Point import Point from .objects.pong.Point import Point
from .objects.Vector import Point from .objects.pong.Segment import Segment
from .objects.Segment import Segment
from .objects.Vector import Vector
from . import config from . import config