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
if TYPE_CHECKING:
from .objects.Spectator import Spectator
from .objects.Player import Player
from .objects.Game import Game
from .objects.pong.PongSpectator import PongSpectator
from .objects.pong.PongPlayer import PongPlayer
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()
class TicTacToeWebSocket(WebsocketConsumer):
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):
return super().receive(text_data, bytes_data)
pass
class PongWebSocket(WebsocketConsumer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.channel_name = "games"
self.group_name = "games"
self.member = None
def connect(self):
@ -43,14 +54,14 @@ class PongWebSocket(WebsocketConsumer):
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):
self.send(text_data=json.dumps({"detail": "Game not found"}))
self.disconnect(1404)
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):
if (self.member is not None):

View File

@ -15,10 +15,10 @@ class GameModel(models.Model):
winner_id = models.IntegerField(default = -1)
start_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]):
self.gamemode = gamemode
def create(self, game_type: str, players_id: list[int]):
self.game_type = game_type
self.save()
for player_id in players_id:
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 .Game import Game
from .pong.PongGame import PongGame
from .tictactoe.TicTacToeGame import TicTacToeGame
class GameManager():
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):
return
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
for game in self._game_list:
game: Game
game: PongGame | TicTacToeGame
if (game.game_id == game_id):
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)

View File

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

View File

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

View File

@ -1,25 +1,23 @@
from __future__ import annotations
from .. import config
from ... import config
from channels.generic.websocket import WebsocketConsumer
from django.contrib.auth.models import User
from .Position import Position
from .Spectator import Spectator
from .Point import Point
from ..APlayer import APlayer
from .Segment import Segment
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from ...transcendence.abstract.AbstractRoomMember import AbstractRoomMember
from .Game import Game
from .PongGame import PongGame
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)
@ -47,30 +45,20 @@ class Player(Spectator):
if (detail == "update_my_paddle_pos"):
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):
new_position: Position = Position()
new_position.position: float = data.get("position")
new_position.location = data.get("position")
if (new_position.position is None):
self.send_error("missing new_position")
return
new_position.time: float = data.get("time")
new_position.time = data.get("time")
if (new_position.time is None):
self.game_member.send_error("missing time")
self.send_error("missing time")
return
if (self.position.time > new_position.time):
@ -110,9 +98,6 @@ class Player(Spectator):
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)

View File

@ -7,18 +7,19 @@ from transcendence.abstract.AbstractRoomMember import AbstractRoomMember
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from .Player import Player
from .Game import Game
from .PongPlayer import PongPlayer
from .PongGame import PongGame
from ..ASpectator import ASpectator
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)
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())
def send_ball(self, ball: Ball):

View File

@ -4,8 +4,8 @@ from .Point import Point
class Position:
def __init__(self, location: int | Point = 0, time: int = 0) -> None:
self.time = time
self.location = location
self.time: float = time
self.location: float = location
def copy(self):
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
if TYPE_CHECKING:
from .objects.Spectator import Spectator
from .objects.Player import Player
from .objects.Game import Game
from .objects.Ball import Ball
from .objects.pong.PongSpectator import PongSpectator
from .objects.pong.PongPlayer import PongPlayer
from .objects.pong.PongGame import PongGame
from .objects.pong.Ball import Ball
from .objects.Point import Point
from .objects.Vector import Point
from .objects.Segment import Segment
from .objects.Vector import Vector
from .objects.pong.Point import Point
from .objects.pong.Segment import Segment
from . import config