game: core: recreate classes see other paddle

This commit is contained in:
starnakin 2024-01-17 19:06:36 +01:00
parent 6983983e58
commit 1af55795d9
13 changed files with 295 additions and 191 deletions

View File

@ -56,6 +56,7 @@ class Game
this.walls.forEach(wall => { this.walls.forEach(wall => {
wall.draw(ctx); wall.draw(ctx);
}); });
console.log(this.players)
this.players.forEach(player => { this.players.forEach(player => {
player.draw(ctx); player.draw(ctx);
}); });
@ -82,7 +83,7 @@ class Game
}, 500); }, 500);
} }
_send_paddle(position, time) _send_paddle_position(position, time)
{ {
if (this.last_pos !== null && this.last_pos.time >= time) if (this.last_pos !== null && this.last_pos.time >= time)
return; return;
@ -92,9 +93,38 @@ class Game
this._send({"detail": "update_my_paddle_pos", ...this.last_pos}); this._send({"detail": "update_my_paddle_pos", ...this.last_pos});
} }
_update_paddles(data) _receive_player_join(player_data)
{
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
));
}
_receive_player_leave(player_data)
{
const user_id = player_data.user_id;
const index = this.players.find(player => player.id === user_id);
if (index === -1)
return
this.players.slice(index, 1);
}
_receive_paddle_position(data)
{ {
console.log(data)
data.players.forEach((player_data) => { data.players.forEach((player_data) => {
let player = this.players.find((player) => player.id === player_data.id); let player = this.players.find((player) => player.id === player_data.id);
if (player === null) if (player === null)
@ -103,14 +133,16 @@ class Game
}) })
} }
_update(data) _receive(data)
{ {
if (data.detail === "update_paddles") if (data.detail === "paddle_position")
this._update_paddles(data); this._receive_paddle_position(data);
else if (data.detail === "update_ball") else if (data.detail === "update_ball")
this._update_ball(data); this._update_ball(data);
else if (data.detail === "init_game") else if (data.detail === "init_game")
this._init_game(data) this._init_game(data)
else if (data.detail === "player_join")
this._receive_player_join(data)
} }
_init_game(data) _init_game(data)
@ -130,15 +162,7 @@ class Game
this.players = [] this.players = []
const players_data = data.players; const players_data = data.players;
players_data.forEach((player_data) => { players_data.forEach((player_data) => {
this.players.push(new Player(player_data.user_id, this._receive_player_join(player_data)
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
));
}); });
this._inited = true; this._inited = true;
@ -164,7 +188,7 @@ class Game
this._socket.onmessage = (event) => { this._socket.onmessage = (event) => {
const data = JSON.parse(event.data); const data = JSON.parse(event.data);
this._update(data); this._receive(data);
}; };
return this.wait_init(); return this.wait_init();

View File

@ -25,7 +25,7 @@ class MyPlayer extends Player
this.positon = new_pos; this.positon = new_pos;
this.game._send_paddle(this.positon, this.game.time._current_frame); this.game._send_paddle_position(this.positon, this.game.time._current_frame);
} }
update_pos(new_position, time) update_pos(new_position, time)

View File

@ -1,6 +1,8 @@
PADDLE_SPEED_PER_SECOND_MAX = 0.6 PADDLE_SPEED_PER_SECOND_MAX = 0.6
PADDLE_SPEED_PER_SECOND_TOLERANCE = 1.01 PADDLE_SPEED_PER_SECOND_TOLERANCE = 1.01
PADDLE_RATIO = 0.3 PADDLE_RATIO = 0.3
PADDLE_POSITION_MIN: float = PADDLE_RATIO / 2
PADDLE_POSITION_MAX: float = 1 - PADDLE_POSITION_MIN
MAP_SIZE_X = 700 MAP_SIZE_X = 700
MAP_SIZE_Y = 700 MAP_SIZE_Y = 700

View File

@ -1,15 +1,20 @@
from __future__ import annotations
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
import json import json
from .objects.GameRoomManager import GameRoomManager
from .objects.GameMember import GameMember
from .objects.Game import Game
from .objects.GameManager import GameManager from .objects.GameManager import GameManager
game_room_manager: GameRoomManager = GameRoomManager() from typing import TYPE_CHECKING
if TYPE_CHECKING:
from .objects.Spectator import Spectator
from .objects.Player import Player
from .objects.Game import Game
game_manager: GameManager = GameManager() game_manager: GameManager = GameManager()
class GameWebSocket(WebsocketConsumer): class GameWebSocket(WebsocketConsumer):
@ -19,9 +24,6 @@ class GameWebSocket(WebsocketConsumer):
self.channel_name = "games" self.channel_name = "games"
self.group_name = "games" self.group_name = "games"
def send_game_data(self):
self.member.send("init_game", self.game.to_dict())
def connect(self): def connect(self):
self.user: User = self.scope["user"] self.user: User = self.scope["user"]
@ -32,19 +34,13 @@ class GameWebSocket(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_manager.get(self.game_id) self.game: Game = game_manager.get(self.game_id)
self.room = game_room_manager.get(self.game_id) if (self.game is None):
if (self.room is None):
self.send("Game not found.") self.send("Game not found.")
self.disconnect(1017) self.disconnect(1017)
self.member = GameMember(self.user.pk, self, self.room) self.member: Player | Spectator = self.game.join(self.user.pk, self)
self.room.append(self.member)
self.send_game_data()
def receive(self, text_data: str = None, bytes_data: bytes = None): def receive(self, text_data: str = None, bytes_data: bytes = None):
@ -56,6 +52,4 @@ class GameWebSocket(WebsocketConsumer):
self.member.receive(data) self.member.receive(data)
def disconnect(self, close_code): def disconnect(self, close_code):
member = self.room.get_member_by_socket(self) self.game.remove(self.member)
if (member is not None):
self.room.remove(self.member, close_code)

View File

@ -1,5 +1,11 @@
from transcendence.abstract.AbstractRoom import AbstractRoom
from transcendence.abstract.AbstractRoomMember import AbstractRoomMember
from channels.generic.websocket import WebsocketConsumer
from .Ball import Ball from .Ball import Ball
from .Player import Player from .Player import Player
from .Spectator import Spectator
from .Wall import Wall from .Wall import Wall
import math import math
@ -8,39 +14,125 @@ from .. import config
from ..models import GameModel from ..models import GameModel
class Game: class Game(AbstractRoom):
def __init__(self, game_id):
super().__init__(None)
def __init__(self, game_id: int) -> None:
self.ball: Ball = Ball() self.ball: Ball = Ball()
game: GameModel = GameModel.objects.get(pk = game_id) game_model: GameModel = GameModel.objects.get(pk = game_id)
players_id: [int] = game.get_players_id() self.players_id: list[int] = game_model.get_players_id()
self.started = False
radius: float = min(config.MAP_SIZE_X, config.MAP_SIZE_Y) / 2 - 10 radius: float = min(config.MAP_SIZE_X, config.MAP_SIZE_Y) / 2 - 10
nb_sides = len(players_id) * 2 self.nb_sides = len(self.players_id) * 2
self.polygon = [] self.polygon: list[tuple[float, float]] = []
for i in range(nb_sides): for i in range(self.nb_sides):
angle: float = (i * 2 * math.pi / nb_sides) + (math.pi * 3 / 4) angle: float = (i * 2 * math.pi / self.nb_sides) + (math.pi * 3 / 4)
x: float = config.MAP_CENTER_X + radius * math.cos(angle) x: float = config.MAP_CENTER_X + radius * math.cos(angle)
y: float = config.MAP_CENTER_Y + radius * math.sin(angle) y: float = config.MAP_CENTER_Y + radius * math.sin(angle)
self.polygon.append((x, y)) self.polygon.append((x, y))
self.players: list(Player) = [] self.players: list[Player] = []
self.walls: list(Wall) = [] self.spectators: list[Spectator] = []
for i in range(nb_sides): self.walls: list[Wall] = []
if (i % 2 == 0):
self.players.append(Player(players_id[ i // 2], *self.polygon[i], *self.polygon[(i + 1) % nb_sides])) for i in range(1, self.nb_sides, 2):
else: self.walls.append(Wall(*self.polygon[i], *self.polygon[(i + 1) % self.nb_sides]))
self.walls.append(Wall(*self.polygon[i], *self.polygon[(i + 1) % nb_sides]))
self.game_id: int = game_id self.game_id: int = game_id
def broadcast(self, detail: str, data: dict = {}, excludeds: list[Spectator | Player] = []):
members: list[Player | Spectator] = self.players + self.spectators
for excluded in excludeds:
members.remove(excluded)
for member in members:
member.send(detail, data)
def get_player_by_user_id(self, user_id: int) -> Player:
for player in self.players:
player: Player
if (player.user_id == user_id):
return player
return None
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 _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):
player.disconnect(1001)
index: int = self.players_id.index(user_id) * 2
player = Player(user_id, socket, *self.polygon[index], *self.polygon[(index + 1) % self.nb_sides])
self.players.append(player)
player.accept()
self.broadcast("player_join", player.to_dict(), [player])
return player
def _player_leave(self, player: Player):
# TODO send data to all players
self.players.remove(player)
def _spectator_join(self, user_id: int, socket: WebsocketConsumer):
spectator: Spectator = Spectator(user_id, socket)
self.spectators.append(spectator)
spectator.accept()
return spectator
def _spectator_leave(self, spectator: Spectator):
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: Spectator = self._spectator_join(user_id, socket)
self._send_game_data(member)
return member
def start(self):
self.started = True
def remove(self, member: AbstractRoomMember):
if (isinstance(member, Player)):
self._player_leave(member)
elif (isinstance(member, Spectator)):
self._spectator_leave(member)
def to_dict(self): def to_dict(self):
data: dict = {"ball": self.ball.to_dict(), data: dict = {"ball": self.ball.to_dict(),
@ -48,6 +140,6 @@ class Game:
"walls": [wall.to_dict() for wall in self.walls], "walls": [wall.to_dict() for wall in self.walls],
} }
print(data) print(self.players )
return data return data

View File

@ -5,9 +5,9 @@ from ..models import GameModel
class GameManager(): class GameManager():
def __init__(self) -> None: def __init__(self) -> None:
self._game_list: [Game] = [] self._game_list: list[Game] = []
def get(self, game_id: int): def get(self, game_id: int) -> Game:
if (not GameModel.objects.filter(pk = game_id, started = True, finished = False).exists()): if (not GameModel.objects.filter(pk = game_id, started = True, finished = False).exists()):
return None return None
@ -17,6 +17,10 @@ class GameManager():
if (game.game_id == game_id): if (game.game_id == game_id):
return game return game
return Game(game_id) game: Game = Game(game_id)
self._game_list.append(game)
return game

View File

@ -1,97 +0,0 @@
from channels.generic.websocket import AsyncWebsocketConsumer
from transcendence.abstract.AbstractRoomMember import AbstractRoomMember
from .Position import Position
import time
from .. import config
from ..models import GameModel
MIN_POSITION: float = config.PADDLE_RATIO / 2
MAX_POSITION: float = 1 - MIN_POSITION
class GameMember(AbstractRoomMember):
def __init__(self, user_id: int, socket: AsyncWebsocketConsumer, room):
super().__init__(user_id, socket)
self.position: Position = Position(0.5, int(time.time() * 1000.0))
self.room = room
self.is_a_player = user_id in GameModel.objects.get(pk = self.room.game_id).get_players_id()
def receive(self, data: dict):
detail: str = data.get("detail")
if (detail is None):
return
if (detail == "update_my_paddle_pos"):
self.update_position(data)
def send_error(self, error_message: str):
self.send("error", {"error_message": error_message})
def update_position(self, data: dict):
if (not self.is_a_player):
return
new_position: Position = Position()
new_position.position = data.get("position")
if (new_position.position is None):
self.send_error("missing new_position")
return
new_position.time = data.get("time")
if (new_position.time is None):
self.send_error("missing time")
return
if (self.position.time > new_position.time):
self.send_error("time error")
return
distance: float = abs(self.position.position - new_position.position)
sign = 1 if self.position.position >= new_position.position else -1
time_difference = (new_position.time - self.position.time) / 1000
max_distance = config.PADDLE_SPEED_PER_SECOND_MAX * (time_difference) * config.PADDLE_SPEED_PER_SECOND_TOLERANCE
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 MIN_POSITION <= new_position_verified.position <= MAX_POSITION):
new_position_verified.position = max(new_position_verified.position, MIN_POSITION)
new_position_verified.position = min(new_position_verified.position, MAX_POSITION)
if (new_position.position != new_position_verified.position):
self.send("update_paddles", {
"players": [
{
"position": new_position_verified.position,
"time": new_position_verified.time,
"id": self.user_id,
},
],
})
print("trop vite")
self.position = new_position
def send_ball(self, ball):
self.send("update_ball_pos", {"x": ball.x,
"y": ball.y})

View File

@ -1,7 +0,0 @@
from transcendence.abstract.AbstractRoom import AbstractRoom
class GameRoom(AbstractRoom):
def __init__(self, game_room_manager, game_id: int):
super().__init__(game_room_manager)
self.game_id = game_id

View File

@ -1,19 +0,0 @@
from transcendence.abstract.AbstractRoomManager import AbstractRoomManager
from ..models import GameModel
from .GameRoom import GameRoom
class GameRoomManager(AbstractRoomManager):
def get(self, game_id: int):
for room in self._room_list:
if (room.game_id == game_id):
return room
if (GameModel.objects.filter(pk = game_id).exists()):
room = GameRoom(self, game_id)
self.append(room)
return room
return None

View File

@ -1,22 +1,106 @@
from __future__ import annotations
from .. import config
class Player: from channels.generic.websocket import WebsocketConsumer
def __init__(self, user_id, rail_start_x, rail_start_y, rail_stop_x, rail_stop_y) -> None: from .Position import Position
self.user_id = user_id from .Spectator import Spectator
self.position = 0.5
self.nb_goal = 0 from typing import TYPE_CHECKING
if TYPE_CHECKING:
from ...transcendence.abstract.AbstractRoomMember import AbstractRoomMember
class Player(Spectator):
def __init__(self, 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.position: Position = Position(0.5, 0)
self.nb_goal: int = 0
self.rail_start_x: float = rail_start_x
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")
if (detail is None):
return
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):
print(data)
new_position: Position = Position()
new_position.position: float = data.get("position")
if (new_position.position is None):
self.send_error("missing new_position")
return
new_position.time: float = data.get("time")
if (new_position.time is None):
self.game_member.send_error("missing time")
return
if (self.position.time > new_position.time):
self.game_member.send_error("time error")
return
distance: float = abs(self.position.position - new_position.position)
sign: int = 1 if self.position.position >= new_position.position else -1
time_difference: float = (new_position.time - self.position.time) / 1000
max_distance: float = config.PADDLE_SPEED_PER_SECOND_MAX * (time_difference) * config.PADDLE_SPEED_PER_SECOND_TOLERANCE
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):
new_position_verified.position = max(new_position_verified.position, config.PADDLE_POSITION_MIN)
new_position_verified.position = min(new_position_verified.position, config.PADDLE_POSITION_MAX)
invalid_pos: bool = new_position.position != new_position_verified.position
self.position = new_position
if (invalid_pos):
self.game_member.send("update_paddle", self.to_dict())
self.rail_start_x = rail_start_x
self.rail_start_y = rail_start_y
self.rail_stop_x = rail_stop_x
self.rail_stop_y = rail_stop_y
def to_dict(self): def to_dict(self):
data = { data = {
"user_id": self.user_id, "user_id": self.user_id,
"position": self.position, "position": self.position.to_dict(),
"nb_goal": self.nb_goal, "nb_goal": self.nb_goal,
"rail_start_x": self.rail_start_x, "rail_start_x": self.rail_start_x,

View File

@ -8,3 +8,12 @@ class Position:
def copy(self): def copy(self):
return Position(self.position, self.time) return Position(self.position, self.time)
def to_dict(self):
data: dict = {
"position": self.position,
"time": self.time,
}
return data

View File

@ -0,0 +1,18 @@
from __future__ import annotations
from transcendence.abstract.AbstractRoomMember import AbstractRoomMember
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from .Player import Player
from .Ball import Ball
class Spectator(AbstractRoomMember):
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())

0
games/routine.py Normal file
View File