core: split: game and pong

This commit is contained in:
starnakin 2024-04-05 17:47:17 +02:00
parent c49e721e5a
commit f6f59f8ead
34 changed files with 965 additions and 784 deletions

View File

@ -0,0 +1,51 @@
export class AExchangeable
{
/**
* This abstract class implement import and export method useful to export/import data to/from the server
* @param {[String]} fieldNameList
*/
export(fieldNameList = [])
{
let valueList = [];
fieldNameList.forEach(fieldName => {
let value;
if (this[fieldName] instanceof AExchangeable)
value = this[fieldName].export();
else
value = this[fieldName];
});
return valueList;
}
/**
* @param {Object} data
*/
import(data)
{
for (const [key, value] of Object.entries(data)) {
if (Array.isArray(value))
{
for (let i = 0; i < value.length; i++)
{
if (this[key][i] instanceof AExchangeable)
this[key][i].import(value[i]);
else
this[key][i] = value[i];
}
}
else
{
if (this[key] instanceof AExchangeable)
this[key].import(value);
else
this[key] = value;
}
}
}
}

View File

@ -1,13 +1,15 @@
import { AExchangeable } from "./AExchangable.js";
import { Client } from "./Client.js"; import { Client } from "./Client.js";
import { Game } from "./game/Game.js";
class Profile export class Profile extends AExchangeable
{ {
/** /**
* @param {Client} client * @param {Client} client
*/ */
constructor (client, username=undefined, id=undefined, avatar_url=undefined) constructor (client, username, id, avatar)
{ {
super();
/** /**
* @type {Client} client * @type {Client} client
*/ */
@ -26,7 +28,7 @@ class Profile
/** /**
* @type {String} * @type {String}
*/ */
this.avatar_url = avatar_url; this.avatar = avatar;
/** /**
* @type {Boolean} * @type {Boolean}
@ -53,7 +55,7 @@ class Profile
let response_data = await response.json(); let response_data = await response.json();
this.id = response_data.user_id; this.id = response_data.user_id;
this.username = response_data.username; this.username = response_data.username;
this.avatar_url = response_data.avatar; this.avatar = response_data.avatar;
await this.getBlock(); await this.getBlock();
await this.getFriend(); await this.getFriend();
@ -61,7 +63,7 @@ class Profile
} }
/** /**
* @returns {[Game]} * @returns {[Object]}
*/ */
async getGameHistory() async getGameHistory()
{ {
@ -71,20 +73,7 @@ class Profile
let games = []; let games = [];
response_data.forEach(game_data => { response_data.forEach(game_data => {
games.push(new Game(this.client, games.push(game_data);
game_data.id,
null,
null,
null,
game_data.winner_id,
game_data.state,
game_data.started,
game_data.finished,
game_data.players,
game_data.start_timestamp,
game_data.stop_timestamp
)
);
}); });
return games; return games;
@ -126,6 +115,11 @@ class Profile
return this.isFriend; return this.isFriend;
} }
/**
* @param {[String]} additionalFieldList
*/
export(additionalFieldList = [])
{
super.export([...["username", "avatar", "id"], ...additionalFieldList])
}
} }
export {Profile};

View File

@ -0,0 +1,158 @@
import { AExchangeable } from "../AExchangable.js";
import { APlayer } from "./APlayer.js";
import { Client } from "../Client.js"
export class AGame extends AExchangeable
{
/**
* Abstract class to create commununication between client and server
* @param {Client} client
* @param {Number} id
* @param {CallableFunction} receiveHandler
* @param {CallableFunction} disconntectHandler
* @param {"tictactoe" | "pong"} gameType
*/
constructor(client, id, receiveHandler, disconntectHandler, gameType)
{
super();
/**
* @type {Client}
*/
this.client = client;
/**
* @type {Number}
*/
this.id = id;
/**
* ex: Tictactoe, Pong
* @type {String}
*/
this.gameType = gameType;
/**
* @type {CallableFunction}
*/
this._receiveHandler = receiveHandler;
/**
* @type {CallableFunction}
*/
this._disconntectHandler = disconntectHandler;
/**
* @type {Number}
*/
this.winnerId;
/**
* @type {Number}
*/
this.startTimestamp;
/**
* @type {Number}
*/
this.stopTimestamp;
/**
* @type {Boolean}
*/
this.started;
/**
* @type {Boolean}
*/
this.finished;
/**
* @type {[APlayer]}
*/
this.players = [];
}
async init()
{
let response = await this.client._get(`/api/games/${this.id}`);
if (response.status !== 200)
return response.status;
let response_data = await response.json();
this.import(response_data);
}
getState()
{
return ["waiting", "started", "finished"][this.started + this.finished];
}
/**
* Send string to the server, must be excuted after .join()
* @param {String} data
*/
send(data)
{
if (this._socket === undefined || this._socket.readyState === WebSocket.OPEN)
return;
this._socket.send(data);
}
async join()
{
if (this.finished === true)
{
console.error("The Game is not currently ongoing.");
return;
}
const url = `${window.location.protocol[4] === 's' ? 'wss' : 'ws'}://${window.location.host}/ws/games/${this.gameType}/${this.id}`;
this._socket = new WebSocket(url);
this._socket.onmessage = async (event) => {
const data = JSON.parse(event.data);
await this._receiveHandler(data);
};
this._socket.onclose = async () => {
this._socket = undefined;
await this._disconntectHandler();
};
}
leave()
{
if (this._socket)
{
this._socket.close();
this._socket = undefined;
}
}
/**
* Should be redefine using your own APlayer inherited
* @param {Object} data
import(data)
{
super.import(data);
// just an example code
/*
this.players.length = 0;
data.players.forEach(player_data => {
let player = new APlayer(this.client, this);
player.import(player_data);
this.players.push(player);
});
}
*/
}

View File

@ -0,0 +1,39 @@
import { Client } from "../Client.js";
import { Profile } from "../Profile.js";
import { AGame } from "./AGame.js";
export class APlayer extends Profile
{
/**
*
* @param {Client} client
* @param {AGame} game
* @param {Number} id
* @param {String} username
* @param {String} avatar
* @param {Boolean} isConnected
*/
constructor (client, game, id, username, avatar, isConnected)
{
super(client, username, id, avatar);
/**
* @type {AGame}
*/
this.game = game
/**
* @type {Boolean}
*/
this.isConnected = isConnected;
}
/**
* @param {[String]} additionalFieldList
*/
export(additionalFieldList = [])
{
super.export([...additionalFieldList, ...["isConnected"]])
}
}

View File

@ -1,91 +0,0 @@
import { Game } from "./Game.js";
import { Point } from "./Point.js";
import { renderCube } from "../../3D/cube.js"
class Ball
{
/**
*
* @param {Game} game
* @param {Point} position
* @param {Number} angle
* @param {Number} speed
* @param {Number} size
*/
constructor(game, size, position, angle, speed)
{
/**
* @type {Game}
*/
this.game = game;
/**
* @type {Point}
*/
this.position = position === undefined ? new Point() : position;
/**
* @type {Number}
*/
this.size = size;
/**
* @type {Number}
*/
this.angle = angle;
/**
* @type {Number}
*/
this.speed = speed;
}
/**
*
* @param {CanvasRenderingContext2D} ctx
*/
draw(ctx)
{
if(ctx instanceof CanvasRenderingContext2D)
{
ctx.rect(this.position.x - this.size / 2, this.position.y - this.size / 2, this.size, this.size);
}
else if(ctx instanceof WebGLRenderingContext)
{
const size = this.size * 3;
const posx = (this.position.x - this.size / 2) - this.game.config.size_x / 2;
const posy = (this.position.y - this.size / 2) - this.game.config.size_y / 2;
renderCube(ctx, posx, 0, posy, 0, size, size, size);
}
else
{
alert('Unknown rendering context type');
}
}
/**
*
* @param {CanvasRenderingContext2D} ctx
*/
render(ctx)
{
let distance = this.speed * (this.game.time.deltaTime() / 1000);
this.position.x = this.position.x + distance * Math.cos(this.angle);
this.position.y = this.position.y - distance * Math.sin(this.angle);
this.draw(ctx);
}
from_json(data)
{
this.position = this.position.from_json(data.position);
this.size = data.size;
this.angle = data.angle;
this.speed = data.speed;
return this;
}
}
export { Ball };

View File

@ -1,338 +0,0 @@
import { sleep } from "../../utils/sleep.js";
import { Ball } from "./Ball.js";
import { GameConfig } from "./GameConfig.js";
import { Player } from "./Player.js";
import { Time } from "./Time.js";
import { Wall } from "./Wall.js";
import { Client } from "../Client.js";
class Game
{
/**
* @param {Client} client
* @param {CallableFunction} goal_handler
* @param {CallableFunction} finish_handler
* @param {CallableFunction} disconnect_handler
* @param {Boolean} finished
* @param {Number} id
* @param {[Object]} players_data
* @param {Number} start_timestamp
* @param {Number} stop_timestamp
* @param {Boolean} started
* @param {Number} winner_id
* @param {String} state
* @param {String} gamemode
*/
constructor(client, id, disconnect_handler, goal_handler, finish_handler, winner_id, state, started, finished, players_data, start_timestamp, stop_timestamp, gamemode)
{
/**
* @type {Client}
*/
this.client = client;
/**
* @type {Number}
*/
this.id = id;
/**
* @type {CallableFunction}
*/
this.goal_handler = goal_handler;
/**
* @type {CallableFunction}
*/
this.finish_handler = finish_handler;
/**
* @type {CallableFunction}
*/
this.disconnect_handler = disconnect_handler;
/**
* @type {String}
*/
this.state = state;
/**
* @type {Boolean}
*/
this.started = started;
/**
* @type {Boolean}
*/
this.finished = finished;
/**
* @type {Number}
*/
this.winner_id = this.finished ? winner_id : undefined;
/**
* @type {Number}
*/
this.start_timestamp = start_timestamp;
/**
* @type {Number}
*/
this.stop_timestamp = stop_timestamp;
/**
* @type {[Player]}
*/
this.players = [];
players_data = players_data === undefined ? [] : players_data;
players_data.forEach(player_data => {
this.players.push(new Player(this,
player_data.player_id,
player_data.username,
player_data.score
)
);
});
/**
* @type {String}
*/
this.gamemode = gamemode;
}
/**
*
* @returns {Promise<Number>}
*/
async init()
{
let response = await this.client._get(`/api/games/${this.id}`);
if (response.status !== 200)
return response.status;
let response_data = await response.json();
this.players = [];
response_data.players.forEach(player_data => {
this.players.push(new Player(this,
player_data.player_id,
player_data.username,
player_data.score
)
);
});
this.state = response_data.state;
this.started = response_data.started;
this.finished = response_data.finished;
this.winner_id = this.finished ? response_data.winner_id : undefined;
this.start_timestamp = response_data.start_timestamp;
this.stop_timestamp = response_data.stop_timestamp;
if (this.finished === true)
return 0;
/**
* @type {GameConfig}
*/
this.config = new GameConfig(this.client);
let ret = await this.config.init();
if (ret !== 0)
return ret;
/**
* @type {Time}
*/
this.time = new Time();
/**
* @type {Boolean}
*/
this._inited = false;
return 0;
}
/**
*
* @param {CanvasRenderingContext2D} ctx
*/
draw_sides(ctx)
{
this.walls.forEach(wall => {
wall.draw(ctx);
});
this.players.forEach(player => {
player.draw(ctx);
});
}
/**
*
* @param {CanvasRenderingContext2D} ctx
*/
render(ctx)
{
if(ctx instanceof CanvasRenderingContext2D)
ctx.clearRect(0, 0, this.config.size_x, this.config.size_y);
this.draw_sides(ctx);
this.ball.render(ctx);
if(ctx instanceof CanvasRenderingContext2D)
{
ctx.strokeStyle = "#000000";
ctx.lineWidth = this.config.stroke_thickness;
ctx.stroke();
}
}
_send(data)
{
if (this._socket === undefined)
return;
if (this._socket.readyState === WebSocket.OPEN)
{
this._socket.send(JSON.stringify(data));
}
}
/**
* @param {Number} position
* @param {Number} time
*/
_send_paddle_position(position, time)
{
this._send({"detail": "update_my_paddle_pos", ...{"time": time, "position": position}});
}
_receive_update_paddle(data)
{
let player = this.players.find((player) => player.id === data.user_id);
player.from_json(data);
}
_receive_ball(data)
{
this.ball.from_json(data);
}
async _receive_finish(data)
{
await this.finish_handler(data);
}
async _receive_goal(data)
{
/**
* @type { Player }
*/
let player_found;
this.players.forEach(player => {
if (data.player_id === player.id)
{
player_found = player;
return;
}
});
player_found.score.push(data.timestamp);
await this.goal_handler(player_found);
}
async _receive(data)
{
if (data.detail === "update_paddle")
this._receive_update_paddle(data);
else if (data.detail === "update_ball")
this._receive_ball(data);
else if (data.detail === "init_game")
this._init_game(data);
else if (data.detail === "goal")
await this._receive_goal(data);
else if (data.detail === "finish")
await this._receive_finish(data);
}
_init_game(data)
{
/**
* @type {Ball}
*/
this.ball = (new Ball(this)).from_json(data.ball);
/**
* @type {[Wall]}
*/
this.walls = [];
const walls_data = data.walls;
walls_data.forEach((wall_data) => {
this.walls.push(new Wall(this).from_json(wall_data));
});
/**
* @type {[Player]}
*/
const players_data = data.players;
for (let index = 0; index < players_data.length; index++) {
this.players[index].from_json(players_data[index]);
}
this._inited = true;
}
async wait_init()
{
while (this._inited !== true)
await sleep(100);
}
async join()
{
if (this.finished === true)
{
console.error("The Game is not currently ongoing.");
return;
}
let url = `${window.location.protocol[4] === 's' ? 'wss' : 'ws'}://${window.location.host}/ws/games/${this.id}`;
this._socket = new WebSocket(url);
this._socket.onmessage = async (event) => {
const data = JSON.parse(event.data);
await this._receive(data);
};
this._socket.onclose = async () => {
this._socket = undefined;
await this.disconnect_handler();
};
return this.wait_init();
}
leave()
{
if (this._socket)
{
this._socket.close();
this._socket = undefined;
}
}
}
export { Game };

View File

@ -1,113 +0,0 @@
import { Game } from "./Game.js";
import { Point } from "./Point.js";
import { Segment } from "./Segment.js";
class Player
{
/**
* @param {Number} id
* @param {Game} game
* @param {Segment} rail
* @param {[Number]} score
* @param {Number} position
* @param {Boolean} is_connected
* @param {String} username
*/
constructor(game, id, username, score, rail, position, is_connected)
{
/**
* @type {Game}
*/
this.game = game;
/**
* @type {Boolean}
*/
this.is_connected = is_connected;
/**
* @type {Number}
*/
this.id = id;
/**
* @type {Number}
*/
this.position = position;
/**
* @type {[Number]}
*/
this.score = score;
/**
* @type {Segment}
*/
this.rail = rail === undefined ? new Segment(game) : rail;
/**
* @type {String}
*/
this.username = username;
}
/**
*
* @param {Number} new_position
*/
update_pos(new_position, time)
{
this.position = new_position;
}
/**
* @param {CanvasRenderingContext2D} ctx
*/
draw(ctx)
{
if (this.is_connected === false)
{
if(ctx instanceof CanvasRenderingContext2D)
{
ctx.moveTo(this.rail.start.x, this.rail.start.y);
ctx.lineTo(this.rail.stop.x, this.rail.stop.y);
}
return;
}
let diff_x = this.rail.stop.x - this.rail.start.x,
diff_y = this.rail.stop.y - this.rail.start.y;
let rail_length = this.rail.len(),
paddle_length = rail_length * this.game.config.paddle_ratio;
let paddle_center = new Point(this.rail.start.x + diff_x * this.position,
this.rail.start.y + diff_y * this.position);
let paddle_start_x = paddle_center.x - (diff_x * (paddle_length / 2 / rail_length)),
paddle_start_y = paddle_center.y - (diff_y * (paddle_length / 2 / rail_length)),
paddle_stop_x = paddle_center.x + (diff_x * (paddle_length / 2 / rail_length)),
paddle_stop_y = paddle_center.y + (diff_y * (paddle_length / 2 / rail_length));
let paddle_start = new Point(paddle_start_x, paddle_start_y),
paddle_stop = new Point (paddle_stop_x, paddle_stop_y);
let paddle = new Segment(this.game, paddle_start, paddle_stop);
paddle.draw(ctx);
}
from_json(data)
{
this.is_connected = data.is_connected;
this.id = data.user_id;
this.position = data.position.position ;
this.score = data.score;
this.rail = this.rail.from_json(data.rail);
this.username = data.username;
return this;
}
}
export { Player };

View File

@ -1,29 +0,0 @@
import { Segment } from "./Segment.js";
class Wall
{
/**
* @param {Segment} start
*/
constructor (game, rail)
{
/**
* @type {Segment}
*/
this.rail = rail === undefined ? new Segment(game) : rail;
}
draw(ctx)
{
this.rail.draw(ctx);
}
from_json(data)
{
this.rail = this.rail.from_json(data.rail);
return this;
}
}
export { Wall };

View File

@ -1,4 +1,6 @@
class Point import { AExchangeable } from "../../AExchangable.js";
class Point extends AExchangeable
{ {
/** /**
* @param {Number} x * @param {Number} x
@ -6,6 +8,8 @@ class Point
*/ */
constructor(x, y) constructor(x, y)
{ {
super();
/** /**
* @type {Number} * @type {Number}
*/ */
@ -16,12 +20,12 @@ class Point
this.y = y; this.y = y;
} }
from_json(data) /**
* @type {[String]}
*/
export(additionalFieldList)
{ {
this.x = data.x; super.export([...additionalFieldList, "x", "y"])
this.y = data.y;
return this;
} }
} }

View File

@ -0,0 +1,82 @@
import { AExchangeable } from "../../AExchangable.js";
import { PongGame } from "./PongGame.js";
import { renderCube} from "../../../3D/cube.js"
import { Position } from "./Position.js";
export class PongBall extends AExchangeable
{
/**
*
* @param {PongGame} game
* @param {Position} position
* @param {Number} angle
* @param {Number} speed
* @param {Number} size
*/
constructor(game, size, position = new Position(), angle, speed)
{
super();
/**
* @type {PongGame}
*/
this.game = game;
/**
* @type {Position}
*/
this.position = position;
/**
* @type {Number}
*/
this.size = size;
/**
* @type {Number}
*/
this.angle = angle;
/**
* @type {Number}
*/
this.speed = speed;
}
/**
*
* @param {CanvasRenderingContext2D} ctx
*/
draw(ctx)
{
if(ctx instanceof CanvasRenderingContext2D)
{
ctx.rect(this.position.location.x - this.size / 2, this.position.location.y - this.size / 2, this.size, this.size);
}
else if(ctx instanceof WebGLRenderingContext)
{
const size = this.size * 3;
const posx = (this.position.location.x - this.size / 2) - this.game.config.MAP_SIZE_X / 2;
const posy = (this.position.location.y - this.size / 2) - this.game.config.MAP_SIZE_Y / 2;
renderCube(ctx, posx, 0, posy, 0, size, size, size);
}
else
{
alert('Unknown rendering context type');
}
}
/**
*
* @param {CanvasRenderingContext2D} ctx
*/
render(ctx)
{
let distance = this.speed * (this.game.time.deltaTime() / 1000);
this.position.location.x = this.position.location.x + distance * Math.cos(this.angle);
this.position.location.y = this.position.location.y - distance * Math.sin(this.angle);
this.draw(ctx);
}
}

View File

@ -1,14 +1,78 @@
class GameConfig import { AExchangeable } from "../../AExchangable.js";
export class PongConfig extends AExchangeable
{ {
/** /**
* @param {Client} client * @param {Client} client
*/ */
constructor(client) constructor(client)
{ {
super();
/** /**
* @type {Client} * @type {Client}
*/ */
this.client = client; this.client = client;
/**
* @type {Number}
*/
this.MAP_SIZE_X;
/**
* @type {Number}
*/
this.MAP_SIZE_Y;
/**
* @type {Number}
*/
this.WALL_RATIO;
/**
* @type {Number}
*/
this.PADDLE_SPEED_PER_SECOND_MAX;
/**
* @type {Number}
*/
this.PADDLE_RATIO;
/**
* @type {Number}
*/
this.BALL_SIZE;
/**
* @type {Number}
*/
this.BALL_SPEED_INC;
/**
* @type {Number}
*/
this.BALL_SPEED_START;
/**
* @type {Number}
*/
this.STROKE_THICKNESS;
/**
* @type {Number}
*/
this.GAME_MAX_SCORE;
/**
* @type {Number}
*/
this.CENTER_X;
/**
* @type {Number}
*/
this.CENTER_Y;
} }
async init() async init()
@ -20,75 +84,11 @@ class GameConfig
let response_data = await response.json(); let response_data = await response.json();
/** this.import(response_data);
* @type {Number}
*/
this.size_x = response_data.MAP_SIZE_X;
/** this.MAP_CENTER_X = this.MAP_SIZE_X / 2;
* @type {Number} this.MAP_CENTER_Y = this.MAP_SIZE_Y / 2;
*/
this.size_y = response_data.MAP_SIZE_Y;
/**
* @type {Number}
*/
this.center_x = this.size_x / 2;
/**
* @type {Number}
*/
this.center_y = this.size_y / 2;
/**
* @type {Number}
*/
this.paddle_ratio = response_data.PADDLE_RATIO;
/**
* @type {Number}
*/
this.paddle_speed_per_second_max = response_data.PADDLE_SPEED_PER_SECOND_MAX;
/**
* @type {Number}
*/
this.wall_ratio = response_data.WALL_RATIO;
/**
* @type {Number}
*/
this.ball_speed_inc = response_data.BALL_SPEED_INC;
/**
* @type {Number}
*/
this.ball_speed_start = response_data.BALL_SPEED_START;
/**
* @type {Number}
*/
this.ball_size = response_data.BALL_SIZE;
/**
* @type {Number}
*/
this.ball_spawn_x = this.center_x;
/**
* @type {Number}
*/
this.ball_spawn_y = this.center_y;
/**
* @type {Number}
*/
this.stroke_thickness = response_data.STROKE_THICKNESS;
/**
* @type {Number}
*/
this.game_max_score = response_data.GAME_MAX_SCORE;
return 0; return 0;
} }
} }
export { GameConfig };

View File

@ -0,0 +1,216 @@
import { Time } from "./Time.js";
import { AGame } from "../AGame.js";
import { PongConfig } from "./PongConfig.js";
import { PongPlayer } from "./PongPlayer.js";
import { Client } from "../../Client.js";
import { PongBall } from "./PongBall.js";
import { sleep } from "../../../utils/sleep.js";
import { Wall } from "./Wall.js"
import { Point } from "./Point.js";
import { Position } from "./Position.js";
export class PongGame extends AGame
{
/**
* @param {Client} client
* @param {CallableFunction} goal_handler
* @param {CallableFunction} finish_handler
* @param {CallableFunction} disconnect_handler
* @param {Number} id
*/
constructor(client, id, disconnectHandler, goalHandler, finishHandler)
{
super(client, id, undefined, disconnectHandler, "pong");
this._receiveHandler = this._receive;
/**
* @type {CallableFunction}
*/
this._goalHandler = goalHandler;
/**
* @type {CallableFunction}
*/
this._finishHandler = finishHandler
/**
* @type {Time}
*/
this.time;
/**
* @type {Boolean}
*/
this._inited = false;
/**
* @type {PongConfig}
*/
this.config;
/**
* @type {Ball}
*/
this.ball = new PongBall(this, undefined, new Position());
/**
* @type {[Wall]}
*/
this.walls = [];
/**
* @type {[PongPlayer]}
*/
this.players = [];
}
/**
*
* @returns {Promise<Number>}
*/
async init()
{
let response = await this.client._get(`/api/games/${this.id}`);
if (response.status !== 200)
return response.status;
let response_data = await response.json();
console.log(response_data.players[0])
response_data.players.forEach((player_data) => {
this.players.push(new PongPlayer(this.client, this));
});
this.import(response_data);
if (this.finished === true)
return 0;
this.config = new PongConfig(this.client);
let ret = await this.config.init();
if (ret !== 0)
return ret;
this.time = new Time();
return 0;
}
/**
*
* @param {CanvasRenderingContext2D} ctx
*/
drawSides(ctx)
{
this.walls.forEach(wall => {
wall.draw(ctx);
});
this.players.forEach(player => {
player.draw(ctx);
});
}
/**
*
* @param {CanvasRenderingContext2D} ctx
*/
render(ctx)
{
if(ctx instanceof CanvasRenderingContext2D)
ctx.clearRect(0, 0, this.config.MAP_SIZE_Y, this.config.MAP_SIZE_Y);
this.drawSides(ctx);
this.ball.render(ctx);
if(ctx instanceof CanvasRenderingContext2D)
{
ctx.strokeStyle = "#000000";
ctx.lineWidth = this.config.STROKE_THICKNESS;
ctx.stroke();
}
}
/**
* @param {Object} data
*/
send(data)
{
super.send(JSON.stringify(data));
}
/**
* @param {Object} data
*/
async _receive(data)
{
if (this._inited === false && data.detail === "init_game")
{
this._initGame(data);
return;
}
if (data.detail === "update_player")
this._updatePlayer(data);
else if (data.detail === "update_ball")
this._updateBall(data);
else if (data.detail === "goal")
await this._receiveGoal(data);
else if (data.detail === "finish")
await this._finishHandler(data);
}
_updatePlayer(data)
{
let player = this.players.find((player) => player.id === data.user_id);
player.from_json(data);
}
_updateBall(data)
{
this.ball.import(data);
}
async _updateGoal(data)
{
/**
* @type { Player }
*/
let player_found;
this.players.forEach(player => {
if (data.player_id === player.id)
{
player_found = player;
return;
}
});
player_found.score.push(data.timestamp);
await this._goalHandler(player_found);
}
_initGame(data)
{
data.walls.forEach((wall_data) => {
this.walls.push(new Wall(this));
});
this.import(data);
this._inited = true;
}
async waitInit()
{
while (this._inited !== true)
await sleep(100);
}
}

View File

@ -1,28 +1,34 @@
import { Player } from "./Player.js"; import { PongPlayer } from "./PongPlayer.js";
import { Client } from "../Client.js"; import { Client } from "../../Client.js";
import { Game } from "./Game.js";
import { Segment } from "./Segment.js"; import { Segment } from "./Segment.js";
import { PongGame } from "./PongGame.js";
import { Position } from "./Position.js";
class MyPlayer extends Player export class MyPlayer extends PongPlayer
{ {
/** /**
* @param {Client} client * @param {Client} client
* @param {Game} game * @param {PongGame} game
* @param {Segment} rail * @param {Segment} rail
* @param {[Number]} score * @param {[Number]} score
* @param {Number} position * @param {Position} position
*/ */
constructor(client, game, score, rail, position) constructor(client, game, score, rail, position = new Position())
{ {
super(game, client.me.id, client.me.username, score, rail, position, true); super(client, game, client.me.id, client.me.username, client.me.avatar, score, rail, position, true);
/** /**
* @type {Client} * @type {Client}
*/ */
this.client = client; this.client = client;
/**
* @type {PongGame}
*/
this.game;
this.upKeys = []; this.upKeys = [];
this.downKeys = []; this.downKeys = [];
console.log(rail.start.x, rail.stop.x)
if (rail.start.x != rail.stop.x) if (rail.start.x != rail.stop.x)
{ {
if (rail.start.x < rail.stop.x) if (rail.start.x < rail.stop.x)
@ -54,7 +60,7 @@ class MyPlayer extends Player
/** /**
* @param {[string]} keys_pressed * @param {[string]} keys_pressed
*/ */
update_paddle(keys_pressed) updatePaddle(keys_pressed)
{ {
let new_pos = this.position; let new_pos = this.position;
@ -73,30 +79,32 @@ class MyPlayer extends Player
this.position = new_pos; this.position = new_pos;
this.game._send_paddle_position(this.position, this.game.time._current_frame); this._sendPaddlePosition();
}
_sendPaddlePosition()
{
this.game.send({"detail": "update_my_paddle_pos", ...{"time": this.game.time._currentFrame, "position": this.position}});
} }
/** /**
* @param {Number} new_position * @param {Position} newPosition
* @param {Number} time
*/ */
update_pos(new_position, time) updatePos(newPosition)
{ {
let position_verified = new_position; let position_verified = newPosition;
let time_diff = (this.time._current_frame - time) / 1000; let time_diff = (this.time._current_frame - newPosition.time) / 1000;
let sign = this - new_position >= 0 ? 1 : -1; let sign = this.position.location - newPosition.location >= 0 ? 1 : -1;
let distance = Math.abs(this.position - new_position); let distance = Math.abs(this.position.location - newPosition.location);
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.location = distance_max * sign;
this.position = position_verified; this.position = position_verified;
} }
} }
export { MyPlayer };

View File

@ -0,0 +1,99 @@
import { APlayer } from "../APlayer.js";
import { Point } from "./Point.js";
import { Segment } from "./Segment.js";
import { Client } from "../../Client.js";
import { PongGame } from "./PongGame.js";
import { Position } from "./Position.js";
export class PongPlayer extends APlayer
{
/**
* @param {Number} id
* @param {PongGame} game
* @param {Segment} rail
* @param {[Number]} score
* @param {Position} position
* @param {Boolean} isConnected
* @param {String} username
* @param {String} avatar
* @param {Client} client
*/
constructor(client, game, id, username, avatar, score = [], rail = new Segment(game), position = new Position(), isConnected)
{
super(client, game, id, username, avatar, isConnected)
/**
* @type {Position}
*/
this.position = position;
/**
* @type {[Number]}
*/
this.score = score;
/**
* @type {Segment}
*/
this.rail = rail;
/**
* @type {PongPlayer}
*/
this.game = game;
}
/**
*
* @param {Number} new_position
*/
updatePos(new_position, time)
{
this.position = new_position;
}
/**
* @param {CanvasRenderingContext2D} ctx
*/
draw(ctx)
{
if (this.isConnected === false)
{
if(ctx instanceof CanvasRenderingContext2D)
{
ctx.moveTo(this.rail.start.x, this.rail.start.y);
ctx.lineTo(this.rail.stop.x, this.rail.stop.y);
}
return;
}
const diffX = this.rail.stop.x - this.rail.start.x,
diffY = this.rail.stop.y - this.rail.start.y;
const railLength = this.rail.len(),
paddleLength = railLength * this.game.config.PADDLE_RATIO;
const paddleCenter = new Point(this.rail.start.x + diffX * this.position.location,
this.rail.start.y + diffY * this.position.location);
const paddleStartX = paddleCenter.x - (diffX * (paddleLength / 2 / railLength)),
paddleStartY = paddleCenter.y - (diffY * (paddleLength / 2 / railLength)),
paddleStopX = paddleCenter.x + (diffX * (paddleLength / 2 / railLength)),
paddleStopY = paddleCenter.y + (diffY * (paddleLength / 2 / railLength));
let paddleStart = new Point(paddleStartX, paddleStartY),
paddleStop = new Point (paddleStopX, paddleStopY);
let paddle = new Segment(this.game, paddleStart, paddleStop);
paddle.draw(ctx);
}
/**
* @param {[String]} additionalFieldList
*/
export(additionalFieldList = [])
{
super.export([...additionalFieldList, "position", "rail", "score"])
}
}

View File

@ -0,0 +1,31 @@
import { AExchangeable } from "../../AExchangable.js";
import { Point } from "./Point.js";
export class Position extends AExchangeable
{
/**
* @param {Point | Number} location
* @param {Number} time
*/
constructor(location, time)
{
super();
/**
* @type {Point | Number}
*/
this.location = location;
/**
* @type {Number}
*/
this.time = time;
}
/**
* @param {[]} additionalFieldList
*/
export(additionalFieldList)
{
super.export([...additionalFieldList + "location", "time"]);
}
}

View File

@ -1,25 +1,33 @@
import { Point } from "./Point.js"; import { Point } from "./Point.js";
import { renderCube } from "../../3D/cube.js" import { AExchangeable } from "../../AExchangable.js";
import { PongGame } from "./PongGame.js";
import { renderCube } from "../../../3D/cube.js";
class Segment class Segment extends AExchangeable
{ {
/** /**
* @param {Point} start * @param {Point} start
* @param {Point} stop * @param {Point} stop
* @param {PongGame} game
*/ */
constructor(game, start, stop) constructor(game, start = new Point(), stop = new Point())
{ {
/** super();
* @type {Point}
*/
this.start = start === undefined ? new Point() : start;
this.game = game;
/** /**
* @type {Point} * @type {Point}
*/ */
this.stop = stop === undefined ? new Point() : stop; this.start = start;
/**
* @type {Point}
*/
this.stop = stop;
/**
* @type {PongGame}
*/
this.game = game
} }
@ -48,10 +56,10 @@ class Segment
} }
else if(ctx instanceof WebGLRenderingContext) else if(ctx instanceof WebGLRenderingContext)
{ {
const size = this.game.config.ball_size * 2; const size = this.game.config.BALL_SIZE * 2;
const sizex = this.len() / 2; const sizex = this.len() / 2;
const posx = (this.start.x - this.game.config.center_x); const posx = (this.start.x - this.game.config.CENTER_X);
const posy = (this.start.y - this.game.config.center_y); const posy = (this.start.y - this.game.config.CENTER_Y);
renderCube(ctx, posx, 0, posy, -this.angle(), sizex, size, size); renderCube(ctx, posx, 0, posy, -this.angle(), sizex, size, size);
} }
else else
@ -60,12 +68,12 @@ class Segment
} }
} }
from_json(data) /**
* @param {[String]} additionalFieldList
*/
export(additionalFieldList)
{ {
this.start = this.start.from_json(data.start); super.export([...additionalFieldList, "start", "stop"]);
this.stop = this.stop.from_json(data.stop);
return this;
} }
} }

View File

@ -7,19 +7,19 @@ class Time
/** /**
* @type {Number} * @type {Number}
*/ */
this._last_frame = undefined; this._lastFrame = undefined;
/** /**
* @type {Number} * @type {Number}
*/ */
this._current_frame = undefined; this._currentFrame = undefined;
} }
deltaTime() deltaTime()
{ {
if (this._last_frame === undefined) if (this._lastFrame === undefined)
return 0; return 0;
return (this._current_frame - this._last_frame); return (this._currentFrame - this._lastFrame);
} }
deltaTimeSecond() deltaTimeSecond()
@ -34,8 +34,8 @@ class Time
new_frame() new_frame()
{ {
this._last_frame = this._current_frame; this._lastFrame = this._currentFrame;
this._current_frame = Date.now(); this._currentFrame = Date.now();
} }
} }

View File

@ -0,0 +1,44 @@
import { Point } from "./Point.js";
import { PongGame } from "./PongGame.js";
import { Segment } from "./Segment.js";
import { renderCube} from "../../../3D/cube.js"
export class Wall extends Segment
{
/**
* @param {PongGame} game
* @param {Point} start
* @param {Point} stop
*/
constructor(game, start, stop)
{
super(game, start, stop)
/**
* @type {PongGame}
*/
this.game = game
}
draw(ctx)
{
if(ctx instanceof CanvasRenderingContext2D)
{
ctx.moveTo(this.start.x, this.start.y);
ctx.lineTo(this.stop.x, this.stop.y);
}
else if(ctx instanceof WebGLRenderingContext)
{
const size = this.game.config.BALL_SIZE * 2;
const sizeX = this.len() / 2;
const posX = (this.start.x - this.game.config.CENTER_X);
const posY = (this.start.y - this.game.config.CENTER_Y);
renderCube(ctx, posX, 0, posY, -this.angle(), sizeX, size, size);
}
else
{
alert('Unknown rendering context type');
}
}
}

View File

@ -3,8 +3,12 @@ import { Client } from "./api/Client.js";
import Search from "./views/Search.js"; import Search from "./views/Search.js";
import HomeView from "./views/HomeView.js"; import HomeView from "./views/HomeView.js";
import LogoutView from "./views/accounts/LogoutView.js"; import LogoutView from "./views/accounts/LogoutView.js";
import GameOfflineView from "./views/GameOfflineView.js";
import GameView from "./views/GameView.js"; import { PongOnlineView } from "./views/PongOnlineView.js"
import { PongOfflineView } from "./views/PongOfflineView.js"
import { TicTacToeOnlineView } from "./views/TicTacToeOnlineView.js"
import { TicTacToeOfflineView } from "./views/TicTacToeOfflineView.js"
import PageNotFoundView from './views/PageNotFoundView.js' ; import PageNotFoundView from './views/PageNotFoundView.js' ;
@ -16,9 +20,7 @@ import TournamentPageView from "./views/tournament/TournamentPageView.js";
import TournamentsListView from "./views/tournament/TournamentsListView.js"; import TournamentsListView from "./views/tournament/TournamentsListView.js";
import TournamentCreateView from "./views/tournament/TournamentCreateView.js"; import TournamentCreateView from "./views/tournament/TournamentCreateView.js";
import AuthenticationView from "./views/accounts/AuthenticationView.js"; import AuthenticationView from "./views/accounts/AuthenticationView.js";
import TicTacToeView from "./views/TicTacToeView.js";
import GameHistoryView from "./views/GameHistoryView.js"; import GameHistoryView from "./views/GameHistoryView.js";
import TicTacToeOnlineView from "./views/TicTacToeOnlineView.js";
let client = new Client(location.origin); let client = new Client(location.origin);
let lang = client.lang; let lang = client.lang;
@ -89,9 +91,9 @@ const router = async(uri) => {
{ path: "/home", view: HomeView }, { path: "/home", view: HomeView },
{ path: "/settings", view: SettingsView }, { path: "/settings", view: SettingsView },
{ path: "/matchmaking", view: MatchMakingView }, { path: "/matchmaking", view: MatchMakingView },
{ path: "/games/offline", view: GameOfflineView }, { path: "/games/pong/offline", view: PongOfflineView },
{ path: "/tictactoe", view: TicTacToeView }, { path: "/games/pong/:id", view: PongOnlineView },
{ path: "/games/pong/:id", view: GameView }, { path: "/games/tictactoe", view: TicTacToeOfflineView },
{ path: "/games/tictactoe/:id", view: TicTacToeOnlineView }, { path: "/games/tictactoe/:id", view: TicTacToeOnlineView },
]; ];

View File

@ -1,6 +1,6 @@
import AbstractAuthenticatedView from "./abstracts/AbstractAuthenticatedView.js"; import AbstractAuthenticatedView from "./abstracts/AbstractAuthenticatedView.js";
export default class extends AbstractAuthenticatedView { export class PongOfflineView extends AbstractAuthenticatedView {
constructor(params) { constructor(params) {
super(params, 'Game'); super(params, 'Game');
this.game = null; this.game = null;

View File

@ -1,7 +1,4 @@
import { client, reloadView, lang } from "../index.js"; import { client, reloadView, lang } from "../index.js";
import { Game } from "../api/game/Game.js";
import { MyPlayer } from "../api/game/MyPlayer.js";
import { Player } from "../api/game/Player.js";
import { initShaderProgram, shaderInfos } from "../3D/shaders.js" import { initShaderProgram, shaderInfos } from "../3D/shaders.js"
import { initBuffers } from "../3D/buffers.js" import { initBuffers } from "../3D/buffers.js"
import "../3D/maths/gl-matrix-min.js" import "../3D/maths/gl-matrix-min.js"
@ -9,12 +6,16 @@ import "../chartjs/chart.umd.min.js";
import { get_labels, transformData } from "../utils/graph.js"; import { get_labels, transformData } from "../utils/graph.js";
import { sleep } from "../utils/sleep.js"; import { sleep } from "../utils/sleep.js";
import AbstractAuthenticatedView from "./abstracts/AbstractAuthenticatedView.js"; import AbstractAuthenticatedView from "./abstracts/AbstractAuthenticatedView.js";
import { PongGame } from "../api/game/pong/PongGame.js";
import { MyPlayer } from "../api/game/pong/PongMyPlayer.js";
import { PongPlayer } from "../api/game/pong/PongPlayer.js";
export default class extends AbstractAuthenticatedView export class PongOnlineView extends AbstractAuthenticatedView
{ {
constructor(params) constructor(params)
{ {
super(params, "Game"); super(params, "Game");
this.game_id = params.id; this.game_id = params.id;
this.ctx = null; this.ctx = null;
this.shader_prog = null; this.shader_prog = null;
@ -23,13 +24,18 @@ export default class extends AbstractAuthenticatedView
this.cam_target = [0, 0, 0]; this.cam_target = [0, 0, 0];
this.cam_up = [0, 0, -1]; this.cam_up = [0, 0, -1];
this.game_mode = 1; // 1 is 2D, 2 is 3D this.game_mode = 1; // 1 is 2D, 2 is 3D
/**
* @type {MyPlayer}
*/
this.myPlayer;
} }
initWebGL() initWebGL()
{ {
let canva = document.createElement("canvas"); let canva = document.createElement("canvas");
canva.height = this.game.config.size_x; canva.height = this.game.config.MAP_SIZE_X;
canva.width = this.game.config.size_y; canva.width = this.game.config.MAP_SIZE_Y;
canva.id = "canva"; canva.id = "canva";
document.getElementById("app").appendChild(canva); document.getElementById("app").appendChild(canva);
@ -51,8 +57,8 @@ export default class extends AbstractAuthenticatedView
init2D() init2D()
{ {
let canva = document.createElement("canvas"); let canva = document.createElement("canvas");
canva.height = this.game.config.size_x; canva.height = this.game.config.MAP_SIZE_X;
canva.width = this.game.config.size_y; canva.width = this.game.config.MAP_SIZE_Y;
canva.id = "canva"; canva.id = "canva";
document.getElementById("app").appendChild(canva); document.getElementById("app").appendChild(canva);
@ -111,7 +117,7 @@ export default class extends AbstractAuthenticatedView
this.ctx.enableVertexAttribArray(shaderInfos.attribLocations.vertexPosition); this.ctx.enableVertexAttribArray(shaderInfos.attribLocations.vertexPosition);
} }
render_game() renderGame()
{ {
const canva = document.getElementById('canva'); const canva = document.getElementById('canva');
if (canva === null) if (canva === null)
@ -156,10 +162,10 @@ export default class extends AbstractAuthenticatedView
} }
/** /**
* @param {Player} player * @param {PongPlayer} player
* @returns { Promise } * @returns { Promise }
*/ */
async on_goal(player) async onGoal(player)
{ {
document.getElementById(`goal-${player.id}`).innerText = player.score.length; document.getElementById(`goal-${player.id}`).innerText = player.score.length;
} }
@ -168,7 +174,7 @@ export default class extends AbstractAuthenticatedView
* @param {*} data * @param {*} data
* @returns { Promise } * @returns { Promise }
*/ */
async on_finish(data /* unused */) async onFinish(data /* unused */)
{ {
await reloadView(); await reloadView();
} }
@ -178,8 +184,8 @@ export default class extends AbstractAuthenticatedView
let loop_id = setInterval(() => { let loop_id = setInterval(() => {
if (this.game === undefined) if (this.game === undefined)
clearInterval(loop_id); clearInterval(loop_id);
this.my_player?.update_paddle(this.keys_pressed); this.myPlayer?.updatePaddle(this.keys_pressed);
this.render_game(); this.renderGame();
this.game?.time?.new_frame(); this.game?.time?.new_frame();
//clearInterval(loop_id); //clearInterval(loop_id);
// 1 sec fps // 1 sec fps
@ -205,7 +211,7 @@ export default class extends AbstractAuthenticatedView
this.createGameBoard(this.game_mode); this.createGameBoard(this.game_mode);
} }
register_key() registerKey()
{ {
this.keyPressHandler = this.keyPressHandler.bind(this); this.keyPressHandler = this.keyPressHandler.bind(this);
this.keyReleaseHandler = this.keyReleaseHandler.bind(this); this.keyReleaseHandler = this.keyReleaseHandler.bind(this);
@ -213,12 +219,13 @@ export default class extends AbstractAuthenticatedView
document.addEventListener('keyup', this.keyReleaseHandler); document.addEventListener('keyup', this.keyReleaseHandler);
} }
unregister_key() unregisterKey()
{ {
document.removeEventListener('keydown', this.keyPressHandler); document.removeEventListener('keydown', this.keyPressHandler);
document.removeEventListener('keyup', this.keyReleaseHandler); document.removeEventListener('keyup', this.keyReleaseHandler);
} }
/** /**
* @param {int} game_mode * @param {int} game_mode
* @returns { Cramptex } * @returns { Cramptex }
@ -237,34 +244,39 @@ export default class extends AbstractAuthenticatedView
if (index !== -1) if (index !== -1)
{ {
let my_player = this.game.players[index]; let myPlayer = this.game.players[index];
this.my_player = new MyPlayer(client,
this.myPlayer = new MyPlayer(client,
this.game, this.game,
my_player.score, myPlayer.score,
my_player.rail, myPlayer.rail,
my_player.position, myPlayer.position,
); );
this.game.players[index] = this.my_player;
this.game.players[index] = this.myPlayer;
} }
} }
async join_game() async joinGame()
{ {
document.getElementById("game-mode").onclick = this.toggleGameMode.bind(this); document.getElementById("game-mode").onclick = this.toggleGameMode.bind(this);
await this.game.join(); await this.game.join();
await this.game.waitInit();
this.createGameBoard(1); // create the board for 2D game by default. Can switch to 3D with a toggle this.createGameBoard(1); // create the board for 2D game by default. Can switch to 3D with a toggle
this.createMyPlayer(); this.createMyPlayer();
this.displayPlayersList(); this.displayPlayersList();
this.register_key(); this.registerKey();
this.render(); this.render();
} }
async update_game_state() async updateGameState()
{ {
document.getElementById("game-state").innerText = this.game.state; document.getElementById("game-state").innerText = this.game.state;
if (this.game.finished === false) if (this.game.finished === false)
await this.join_game(); await this.joinGame();
else else
{ {
this.createGraph(); this.createGraph();
@ -360,7 +372,7 @@ export default class extends AbstractAuthenticatedView
}); });
} }
async on_disconnect() async onDisconnect()
{ {
sleep(500); sleep(500);
await reloadView(); await reloadView();
@ -368,16 +380,15 @@ export default class extends AbstractAuthenticatedView
async postInit() async postInit()
{ {
this.game = new Game(client, this.game_id, this.on_disconnect, this.on_goal, this.on_finish); this.game = new PongGame(client, this.game_id, this.onDisconnect, this.onGoal, this.onFinish);
this.keys_pressed = []; this.keys_pressed = [];
this.my_player = undefined;
let error_code = await this.game.init(); let error_code = await this.game.init();
if (error_code) if (error_code)
return error_code; return error_code;
await this.update_game_state(); await this.updateGameState();
} }
async leavePage() async leavePage()
@ -388,7 +399,7 @@ export default class extends AbstractAuthenticatedView
this.game = undefined; this.game = undefined;
} }
this.game = undefined; this.game = undefined;
this.unregister_key(); this.unregisterKey();
} }
async getHtml() async getHtml()

View File

@ -1,8 +1,7 @@
import { lang } from "../index.js"; import { lang } from "../index.js";
import AbstractView from "./abstracts/AbstractView.js"; import AbstractView from "./abstracts/AbstractView.js";
export class TicTacToeOfflineView extends AbstractView
export default class extends AbstractView
{ {
constructor(params) constructor(params)
{ {

View File

@ -1,8 +1,8 @@
import AbstractView from "./abstracts/AbstractView.js"; import AbstractView from "./abstracts/AbstractView.js";
import { lang } from "../index.js"; import { lang } from "../index.js";
import { TicTacToe } from "../api/game/TicTacToeGame.js" import { TicTacToe } from "../api/game/tictactoe/TicTacToeGame.js"
export default class extends AbstractView export class TicTacToeOnlineView extends AbstractView
{ {
constructor(params, titleKey) constructor(params, titleKey)
{ {

View File

@ -17,7 +17,15 @@ if TYPE_CHECKING:
game_manager: GameManager = GameManager() game_manager: GameManager = GameManager()
class GameWebSocket(WebsocketConsumer): class TicTacToeWebSocket(WebsocketConsumer):
def connect(self):
return super().connect()
def receive(self, text_data=None, bytes_data=None):
return super().receive(text_data, bytes_data)
class PongWebSocket(WebsocketConsumer):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)

View File

@ -2,14 +2,16 @@ from __future__ import annotations
from .. import config from .. import config
from .Position import Position
from .Point import Point from .Point import Point
import time
import math import math
class Ball: class Ball:
def __init__(self) -> None: def __init__(self) -> None:
self.size: float self.size: float
self.position: Point self.position: Position
self.angle: float self.angle: float
self.speed: float self.speed: float
@ -17,7 +19,7 @@ class Ball:
def reset(self) -> None: def reset(self) -> None:
self.size = config.BALL_SIZE self.size = config.BALL_SIZE
self.position = Point(config.BALL_SPAWN_POS_X + self.size / 2, config.BALL_SPAWN_POS_Y + self.size / 2) self.position = Position(Point(config.BALL_SPAWN_POS_X + self.size / 2, config.BALL_SPAWN_POS_Y + self.size / 2), time.time())
self.angle = math.pi * 0.3 self.angle = math.pi * 0.3
self.speed = config.BALL_SPEED_START self.speed = config.BALL_SPEED_START

View File

@ -56,7 +56,7 @@ class Game(AbstractRoom):
polygon.append(Point(x, y)) polygon.append(Point(x, y))
segments: list[Point] = [] segments: list[Segment] = []
for i in range(nb_sides): for i in range(nb_sides):
segments.append(Segment(polygon[i], polygon[(i + 1) % nb_sides])) segments.append(Segment(polygon[i], polygon[(i + 1) % nb_sides]))
@ -66,7 +66,7 @@ class Game(AbstractRoom):
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 = [Player(self, players_id[0], None, segments[0]), Player(self, players_id[1], None, segments[2])]
self.walls = [Wall(segments[1]), Wall(segments[3])] 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 = []

View File

@ -103,7 +103,7 @@ class Player(Spectator):
self.position = new_position self.position = new_position
if (invalid_pos): if (invalid_pos):
self.send("update_paddle", self.to_dict()) self.send("update_player", self.to_dict())
def connect(self, socket: WebsocketConsumer): def connect(self, socket: WebsocketConsumer):
self.socket = socket self.socket = socket
@ -129,13 +129,13 @@ class Player(Spectator):
data = { data = {
"username": self.username, "username": self.username,
"user_id": self.user_id, "id": self.user_id,
"position": self.position.to_dict(), "position": self.position.to_dict(),
"score": [*self.score], "score": [*self.score],
"rail": self.rail.to_dict(), "rail": self.rail.to_dict(),
"is_connected": self.is_connected(), "isConnected": self.is_connected(),
} }
return data return data

View File

@ -1,22 +1,27 @@
from __future__ import annotations from __future__ import annotations
from .Point import Point
class Position: class Position:
def __init__(self, position = 0, time: int = 0) -> None: def __init__(self, location: int | Point = 0, time: int = 0) -> None:
self.time = time self.time = time
self.position = position self.location = location
def copy(self): def copy(self):
return Position(self.position, self.time) return Position(self.location, self.time)
def to_dict(self): def to_dict(self):
data: dict = { data: dict = {
"position": self.position,
"time": self.time, "time": self.time,
} }
try:
data.update({"location": self.location.to_dict()})
except:
data.update({"location": self.location})
return data return data
def __eq__(self, __value: Position) -> bool: def __eq__(self, __value: Position) -> bool:
return (self.position == __value.position) return (self.location == __value.location)

View File

@ -19,7 +19,7 @@ class Spectator(AbstractRoomMember):
self.game: Game = game self.game: Game = game
def send_paddle(self, player: Player): def send_paddle(self, player: Player):
self.send("update_paddle", player.to_dict()) self.send("update_player", player.to_dict())
def send_ball(self, ball: Ball): def send_ball(self, ball: Ball):
self.send("update_ball", ball.to_dict()) self.send("update_ball", ball.to_dict())

View File

@ -1,15 +1,5 @@
from .Segment import Segment from .Segment import Segment
class Wall: class Wall(Segment):
pass
def __init__(self, rail: Segment) -> None:
self.rail: Segment = rail
def to_dict(self) -> dict[str: dict]:
data = {
"rail": self.rail.to_dict(),
}
return data

View File

@ -266,7 +266,7 @@ async def render_ball(game: Game):
while True: while True:
segments: list[Segment] = [player.rail for player in game.players] + [wall.rail for wall in game.walls] segments: list[Segment] = [player.rail for player in game.players] + game.walls
impact_data: dict = get_impact_data(segments, game.ball) impact_data: dict = get_impact_data(segments, game.ball)

View File

@ -2,5 +2,6 @@ from django.urls import re_path
from . import consumers from . import consumers
websocket_urlpatterns = [ websocket_urlpatterns = [
re_path(r'ws/games/(?P<game_id>\d+)$', consumers.GameWebSocket.as_asgi()) re_path(r'ws/games/pong/(?P<game_id>\d+)$', consumers.PongWebSocket.as_asgi()),
re_path(r'ws/games/tictactoe/(?P<game_id>\d+)$', consumers.TicTacToeWebSocket.as_asgi()),
] ]