game: core: use server game calulation form

This commit is contained in:
starnakin 2024-01-17 14:23:23 +01:00
parent b2dc43c1d8
commit 2bd0624100
15 changed files with 274 additions and 110 deletions

View File

@ -2,13 +2,13 @@
class Ball class Ball
{ {
constructor(game) constructor(game, position_x, position_y, velocity_x, velocity_y)
{ {
this.game = game; this.game = game;
this.position_x = game.config.size_x / 2; this.position_x = position_x;
this.position_y = game.config.size_y / 2; this.position_y = position_y;
this.velocity_x = game.config.ball_speed_start; this.velocity_x = velocity_x;
this.velocity_y = game.config.ball_speed_start; this.velocity_y = velocity_y;
} }
_collision(old_pos_x, old_pos_y, new_pos_x, new_pos_y) _collision(old_pos_x, old_pos_y, new_pos_x, new_pos_y)

View File

@ -1,7 +1,10 @@
import { sleep } from "../../utils/sleep.js";
import { Ball } from "./Ball.js"; import { Ball } from "./Ball.js";
import { GameConfig } from "./GameConfig.js" import { GameConfig } from "./GameConfig.js"
import { MyPlayer } from "./MyPlayer.js"; import { MyPlayer } from "./MyPlayer.js";
import { Player } from "./Player.js"; import { Player } from "./Player.js";
import { Time } from "./Time.js";
import { Wall } from "./Wall.js";
class Game class Game
{ {
@ -15,8 +18,6 @@ class Game
*/ */
this.client = client; this.client = client;
this.id = id; this.id = id;
this.keys_pressed = [];
this.last_pos = null
} }
async init() async init()
@ -43,60 +44,33 @@ class Game
if (ret !== 0) if (ret !== 0)
return ret; return ret;
this.players = []; this.time = new Time();
response_data.players_id.forEach(player_id => { this.last_pos = null
let player = new Player(player_id, 0.5, 0, this, 0, 0, 0, 0); this._inited = false;
this.players.push(player); this._wall_drew = false;
});
this.ball = new Ball(this);
return 0; return 0;
} }
draw_wall(ctx, stop_x, stop_y) draw_sides(ctx)
{ {
ctx.lineTo(stop_x, stop_y); if (this._wall_drew !== true)
} {
this.walls.forEach(wall => {
draw_sides(ctx, nb_sides) wall.draw(ctx);
{ });
let start_x,
start_y,
stop_x,
stop_y;
let radius = Math.min(this.config.size_x, this.config.size_y) / 2 - 10;
for (let i = 0; i <= nb_sides; i++)
{
let angle = (i * 2 * Math.PI / nb_sides) + (Math.PI * 3 / 4);
stop_x = this.config.center_x + radius * Math.cos(angle);
stop_y = this.config.center_y + radius * Math.sin(angle);
if (i == 0)
ctx.moveTo(stop_x, start_y);
if (i % 2 == 0)
this.draw_wall(ctx, stop_x, stop_y);
else
this.players[(i - 1) / 2].draw(ctx);
start_x = stop_x;
start_y = stop_y;
} }
this.players.forEach(player => {
player.draw(ctx);
});
this._wall_drew = true;
} }
draw(ctx) draw(ctx)
{ {
ctx.beginPath()
ctx.clearRect(0, 0, this.config.size_x, this.config.size_y); ctx.clearRect(0, 0, this.config.size_x, this.config.size_y);
this.draw_sides(ctx, (this.players_id.length) * 2); this.draw_sides(ctx);
this.ball.draw(ctx); this.ball.draw(ctx);
ctx.strokeStyle = "#000000";
ctx.lineWidth = 10;
ctx.stroke();
} }
_send(data) _send(data)
@ -134,24 +108,54 @@ class Game
}) })
} }
_update_ball(data)
{
this.ball.position_x = data.position_x;
this.ball.position_y = data.position_y;
this.ball.velocity_x = data.velocity_x;
this.ball.velocity_y = data.velocity_y;
}
_update(data) _update(data)
{ {
console.log("bozo2", data)
if (data.detail === "update_paddles") if (data.detail === "update_paddles")
this._update_paddles(data); this._update_paddles(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")
this._init_game(data)
} }
join() _init_game(data)
{
const ball_data = data.ball;
this.ball = new Ball(this, ball_data.position_x, ball_data.position_y, ball_data.velocity_x, ball_data.velocity_y);
this.walls = [];
const walls_data = data.walls;
walls_data.forEach((wall_data) => {
this.walls.push(new Wall(wall_data.rail_start_x,
wall_data.rail_start_y,
wall_data.rail_stop_x,
wall_data.rail_stop_y));
});
this.players = []
const players_data = data.players;
players_data.forEach((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
));
});
this._inited = true;
}
async wait_init()
{
while (this._inited !== true)
await sleep(100);
}
async join()
{ {
if (this.started !== true || this.finished === true) if (this.started !== true || this.finished === true)
{ {
@ -167,6 +171,8 @@ class Game
const data = JSON.parse(event.data); const data = JSON.parse(event.data);
this._update(data); this._update(data);
}; };
return this.wait_init();
} }
leave() leave()

View File

@ -3,30 +3,29 @@ import { Player } from "./Player.js";
class MyPlayer extends Player class MyPlayer extends Player
{ {
constructor(client, pos, nb_goal, game, time, rail_start_x, rail_start_y, rail_stop_x, rail_stop_y) constructor(client, game, rail_start_x, rail_start_y, rail_stop_x, rail_stop_y, nb_goal, positon)
{ {
super(client.me.id, pos, nb_goal, game, rail_start_x, rail_start_y, rail_stop_x, rail_stop_y); super(client.me.id, game, rail_start_x, rail_start_y, rail_stop_x, rail_stop_y, nb_goal, positon);
this.time = time;
this.client = client; this.client = client;
} }
update_paddle(keys_pressed) update_paddle(keys_pressed)
{ {
let new_pos = this.pos; let new_pos = this.positon;
if (keys_pressed.includes("s")) if (keys_pressed.includes("s"))
new_pos -= this.game.config.paddle_speed_per_second_max * this.time.deltaTimeSecond() * 1.0; new_pos -= this.game.config.paddle_speed_per_second_max * this.game.time.deltaTimeSecond() * 1.0;
if (keys_pressed.includes("w")) if (keys_pressed.includes("w"))
new_pos += this.game.config.paddle_speed_per_second_max * this.time.deltaTimeSecond() * 1.0; new_pos += this.game.config.paddle_speed_per_second_max * this.game.time.deltaTimeSecond() * 1.0;
new_pos = Math.max(0 + this.game.config.paddle_ratio / 2, new_pos); new_pos = Math.max(0 + this.game.config.paddle_ratio / 2, new_pos);
new_pos = Math.min(1 - this.game.config.paddle_ratio / 2, new_pos); new_pos = Math.min(1 - this.game.config.paddle_ratio / 2, new_pos);
if (this.pos === new_pos) if (this.positon === new_pos)
return; return;
console.log(this.client.me.id)
this.pos = new_pos;
this.game._send_paddle(this.pos, this.time._current_frame); this.positon = new_pos;
this.game._send_paddle(this.positon, this.game.time._current_frame);
} }
update_pos(new_position, time) update_pos(new_position, time)
@ -37,14 +36,14 @@ class MyPlayer extends Player
let sign = this - new_position >= 0 ? 1 : -1; let sign = this - new_position >= 0 ? 1 : -1;
let distance = Math.abs(this.pos - new_position); let distance = Math.abs(this.positon - new_position);
let distance_max = time_diff * this.game.config.paddle_speed_per_second_max; let distance_max = time_diff * this.game.config.paddle_speed_per_second_max;
if (distance > distance_max) if (distance > distance_max)
position_verified = distance_max * sign; position_verified = distance_max * sign;
this.pos = position_verified; this.positon = position_verified;
} }
} }

View File

@ -1,10 +1,10 @@
class Player class Player
{ {
constructor(id, pos, nb_goal, game, rail_start_x, rail_start_y, rail_stop_x, rail_stop_y) constructor(id, game, rail_start_x, rail_start_y, rail_stop_x, rail_stop_y, nb_goal, positon)
{ {
this.id = id; this.id = id;
this.pos = pos; this.positon = positon;
this.nb_goal = nb_goal; this.nb_goal = nb_goal;
this.game = game; this.game = game;
@ -23,28 +23,22 @@ class Player
update_pos(new_position, time) update_pos(new_position, time)
{ {
this.pos = new_position; this.positon = new_position;
} }
draw(ctx) draw(ctx)
{ {
let paddle_pos_x = this.rail_start_x + this.diff_x * this.pos, let paddle_pos_x = this.rail_start_x + this.diff_x * this.positon,
paddle_pos_y = this.rail_start_y + this.diff_y * this.pos; paddle_pos_y = this.rail_start_y + this.diff_y * this.positon;
console.log(this)
let start_x = paddle_pos_x - (this.diff_x * (this.paddle_size / 2 / this.rail_size)), let start_x = paddle_pos_x - (this.diff_x * (this.paddle_size / 2 / this.rail_size)),
start_y = paddle_pos_y - (this.diff_y * (this.paddle_size / 2 / this.rail_size)), start_y = paddle_pos_y - (this.diff_y * (this.paddle_size / 2 / this.rail_size)),
stop_x = paddle_pos_x + (this.diff_x * (this.paddle_size / 2 / this.rail_size)), stop_x = paddle_pos_x + (this.diff_x * (this.paddle_size / 2 / this.rail_size)),
stop_y = paddle_pos_y + (this.diff_y * (this.paddle_size / 2 / this.rail_size)); stop_y = paddle_pos_y + (this.diff_y * (this.paddle_size / 2 / this.rail_size));
console.log(start_x, stop_x);
console.log(start_y, stop_y)
ctx.moveTo(start_x, start_y); ctx.moveTo(start_x, start_y);
ctx.lineTo(stop_x, stop_y); ctx.lineTo(stop_x, stop_y);
ctx.moveTo(this.rail_stop_x, this.rail_stop_y);
} }
} }

View File

@ -0,0 +1,20 @@
class Wall
{
constructor (rail_start_x, rail_start_y, rail_stop_x, rail_stop_y)
{
this.rail_start_x = rail_start_x;
this.rail_start_y = rail_start_y;
this.rail_stop_x = rail_stop_x;
this.rail_stop_y = rail_stop_y;
}
draw(ctx)
{
ctx.moveTo(this.rail_start_x, this.rail_start_y);
ctx.lineTo(this.rail_stop_x, this.rail_stop_y);
}
}
export { Wall }

View File

@ -0,0 +1,6 @@
function sleep(ms)
{
return new Promise(resolve => setTimeout(resolve, ms));
}
export {sleep}

View File

@ -12,7 +12,6 @@ export default class extends AbstractView
super(params, "Game"); super(params, "Game");
this.game = new Game(client, params.id); this.game = new Game(client, params.id);
this.keys_pressed = []; this.keys_pressed = [];
this.time = new Time();
this.my_player = undefined; this.my_player = undefined;
} }
@ -33,7 +32,13 @@ export default class extends AbstractView
{ {
let ctx = document.getElementById('canva').getContext('2d'); let ctx = document.getElementById('canva').getContext('2d');
ctx.beginPath();
this.game.draw(ctx); this.game.draw(ctx);
ctx.strokeStyle = "#000000";
ctx.lineWidth = 10;
ctx.stroke();
} }
render_game() render_game()
@ -42,8 +47,8 @@ export default class extends AbstractView
if (this.my_player) if (this.my_player)
this.my_player.update_paddle(this.keys_pressed); this.my_player.update_paddle(this.keys_pressed);
this.draw(); this.draw();
this.time.new_frame(); this.game.time.new_frame();
//clearInterval(loop_id); clearInterval(loop_id);
// 1 sec fps // 1 sec fps
}, 1000 / 60); }, 1000 / 60);
} }
@ -60,15 +65,9 @@ export default class extends AbstractView
document.removeEventListener('keyup', this.keyStretchHandler); document.removeEventListener('keyup', this.keyStretchHandler);
} }
start_game() async start_game()
{ {
let index = this.game.players.findIndex((player) => player.id); await this.game.join()
if (index !== -1)
{
let my_player = this.game.players[index];
this.my_player = new MyPlayer(client, my_player.pos, my_player.nb_goal, my_player.game, this.time);
this.game.players[index] = this.my_player;
}
let canva = document.createElement("canvas"); let canva = document.createElement("canvas");
@ -78,19 +77,33 @@ export default class extends AbstractView
document.getElementById("app").appendChild(canva); document.getElementById("app").appendChild(canva);
this.game.join()
this.register_key() this.register_key()
this.render_game(); this.render_game();
let index = this.game.players.findIndex((player) => player.id);
if (index !== -1)
{
let my_player = this.game.players[index];
this.my_player = new MyPlayer(client,
this.game,
my_player.rail_start_x,
my_player.rail_start_y,
my_player.rail_stop_y,
my_player.rail_stop_y,
my_player.nb_goal,
my_player.positon,
);
this.game.players[index] = this.my_player;
}
} }
update_game_state() async update_game_state()
{ {
document.getElementById("game-state").innerText = this.game.state; document.getElementById("game-state").innerText = this.game.state;
if (this.game.started === true && this.game.finished === false) if (this.game.started === true && this.game.finished === false)
this.start_game(); await this.start_game();
} }
async postInit() async postInit()
@ -100,7 +113,7 @@ export default class extends AbstractView
if (error_code) if (error_code)
return error_code; return error_code;
this.update_game_state(); await this.update_game_state();
} }
async leavePage() async leavePage()

View File

@ -4,6 +4,8 @@ PADDLE_RATIO = 0.3
MAP_SIZE_X = 700 MAP_SIZE_X = 700
MAP_SIZE_Y = 700 MAP_SIZE_Y = 700
MAP_CENTER_X = MAP_SIZE_X / 2
MAP_CENTER_Y = MAP_SIZE_Y / 2
WALL_RATIO = 1 WALL_RATIO = 1

View File

@ -6,8 +6,11 @@ import json
from .objects.GameRoomManager import GameRoomManager from .objects.GameRoomManager import GameRoomManager
from .objects.GameMember import GameMember from .objects.GameMember import GameMember
from .objects.Game import Game
from .objects.GameManager import GameManager
game_room_manager: GameRoomManager = GameRoomManager() game_room_manager: GameRoomManager = GameRoomManager()
game_manager: GameManager = GameManager()
class GameWebSocket(WebsocketConsumer): class GameWebSocket(WebsocketConsumer):
@ -16,6 +19,9 @@ 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"]
@ -26,6 +32,8 @@ 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.room = game_room_manager.get(self.game_id) self.room = game_room_manager.get(self.game_id)
if (self.room is None): if (self.room is None):
@ -36,6 +44,8 @@ class GameWebSocket(WebsocketConsumer):
self.room.append(self.member) 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):
if (text_data is None): if (text_data is None):

View File

@ -3,7 +3,19 @@ from .. import config
class Ball: class Ball:
def __init__(self) -> None: def __init__(self) -> None:
self.x: float = config.BALL_SPAWN_POS_X self.postion_x: float = config.BALL_SPAWN_POS_X
self.y: float = config.BALL_SPAWN_POS_Y self.postion_y: float = config.BALL_SPAWN_POS_Y
self.speed: float = config.BALL_SPEED_START self.velocity_x: float = config.BALL_SPEED_START
self.velocity_y: float = config.BALL_SPEED_START
def to_dict(self):
data: dict = {
"position_x": self.postion_x,
"position_y": self.postion_y,
"velocity_x": self.velocity_x,
"velocity_y": self.velocity_y,
}
return data

View File

@ -1,8 +1,53 @@
from .Ball import Ball from .Ball import Ball
from .Paddle import Paddle from .Player import Player
from .Wall import Wall
import math
from .. import config
from ..models import GameModel
class Game: class Game:
def __init__(self, paddles: [Paddle], ball: Ball) -> None: def __init__(self, game_id: int) -> None:
self.ball: Ball = ball self.ball: Ball = Ball()
self.paddles: [paddles] = paddles game: GameModel = GameModel.objects.get(pk = game_id)
players_id: [int] = game.get_players_id()
radius: float = min(config.MAP_SIZE_X, config.MAP_SIZE_Y) / 2 - 10
nb_sides = len(players_id) * 2
self.polygon = []
for i in range(nb_sides):
angle: float = (i * 2 * math.pi / nb_sides) + (math.pi * 3 / 4)
x: float = config.MAP_CENTER_X + radius * math.cos(angle)
y: float = config.MAP_CENTER_Y + radius * math.sin(angle)
self.polygon.append((x, y))
self.players: list(Player) = []
self.walls: list(Wall) = []
for i in range(nb_sides):
if (i % 2 == 0):
self.players.append(Player(players_id[ i // 2], *self.polygon[i], *self.polygon[(i + 1) % nb_sides]))
else:
self.walls.append(Wall(*self.polygon[i], *self.polygon[(i + 1) % nb_sides]))
self.game_id: int = game_id
def to_dict(self):
data: dict = {"ball": self.ball.to_dict(),
"players": [player.to_dict() for player in self.players],
"walls": [wall.to_dict() for wall in self.walls],
}
print(data)
return data

View File

@ -0,0 +1,22 @@
from .Game import Game
from ..models import GameModel
class GameManager():
def __init__(self) -> None:
self._game_list: [Game] = []
def get(self, game_id: int):
if (not GameModel.objects.filter(pk = game_id, started = True, finished = False).exists()):
return None
for game in self._game_list:
game: Game
if (game.game_id == game_id):
return game
return Game(game_id)

View File

@ -44,7 +44,7 @@ class GameMember(AbstractRoomMember):
new_position.position = data.get("position") new_position.position = data.get("position")
if (new_position is None): if (new_position.position is None):
self.send_error("missing new_position") self.send_error("missing new_position")
return return

View File

@ -2,12 +2,27 @@
class Player: class Player:
def __init__(self, user_id, pos, nb_goal, rail_start_x, rail_start_y, rail_stop_x, rail_stop_y) -> None: def __init__(self, user_id, rail_start_x, rail_start_y, rail_stop_x, rail_stop_y) -> None:
self.user_id = user_id self.user_id = user_id
self.pos = pos self.position = 0.5
self.nb_goal = nb_goal self.nb_goal = 0
self.rail_start_x = rail_start_x self.rail_start_x = rail_start_x
self.rail_start_y = rail_start_y self.rail_start_y = rail_start_y
self.rail_stop_x = rail_stop_x self.rail_stop_x = rail_stop_x
self.rail_stop_y = rail_stop_y self.rail_stop_y = rail_stop_y
def to_dict(self):
data = {
"user_id": self.user_id,
"position": self.position,
"nb_goal": self.nb_goal,
"rail_start_x": self.rail_start_x,
"rail_start_y": self.rail_start_y,
"rail_stop_x": self.rail_stop_x,
"rail_stop_y": self.rail_stop_y,
}
return data

20
games/objects/Wall.py Normal file
View File

@ -0,0 +1,20 @@
class Wall:
def __init__(self, rail_start_x, rail_start_y, rail_stop_x, rail_stop_y) -> None:
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):
data = {
"rail_start_x": self.rail_start_x,
"rail_start_y": self.rail_start_y,
"rail_stop_x": self.rail_stop_x,
"rail_stop_y": self.rail_stop_y,
}
return data