matchmaking rework for multiples game modes
This commit is contained in:
@@ -17,3 +17,7 @@ export const fixedDeltaTime = gameLoopIntervalMS/1000; // second
|
|||||||
export const soundMutedFlag = true;
|
export const soundMutedFlag = true;
|
||||||
export const soundRobloxVolume = 0.3; // between 0 and 1
|
export const soundRobloxVolume = 0.3; // between 0 and 1
|
||||||
export const soundPongVolume = 0.3; // between 0 and 1
|
export const soundPongVolume = 0.3; // between 0 and 1
|
||||||
|
|
||||||
|
// TODO: replace by a selector on the website
|
||||||
|
import * as en from "../shared_js/enums.js"
|
||||||
|
export const optionsPLACEHOLDER = en.MatchOptions.noOption;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class ClientInfo {
|
|||||||
export const clientInfo = new ClientInfo();
|
export const clientInfo = new ClientInfo();
|
||||||
|
|
||||||
socket.addEventListener("open", (event) => {
|
socket.addEventListener("open", (event) => {
|
||||||
socket.send(JSON.stringify( new ev.ClientAnnounce(en.ClientRole.player, clientInfo.id) ));
|
socket.send(JSON.stringify( new ev.ClientAnnounce(en.ClientRole.player, c.optionsPLACEHOLDER, clientInfo.id) ));
|
||||||
});
|
});
|
||||||
|
|
||||||
// socket.addEventListener("message", logListener); // for testing purpose
|
// socket.addEventListener("message", logListener); // for testing purpose
|
||||||
@@ -58,7 +58,7 @@ function preMatchListener(this: WebSocket, event: MessageEvent) {
|
|||||||
}
|
}
|
||||||
clientInfo.opponentNextPos = new VectorInteger(clientInfo.opponent.pos.x, clientInfo.opponent.pos.y);
|
clientInfo.opponentNextPos = new VectorInteger(clientInfo.opponent.pos.x, clientInfo.opponent.pos.y);
|
||||||
clientInfo.racket.color = "darkgreen"; // for testing purpose
|
clientInfo.racket.color = "darkgreen"; // for testing purpose
|
||||||
socket.send(JSON.stringify( new ev.ClientEvent(en.EventTypes.clientPlayerReady) ));
|
socket.send(JSON.stringify( new ev.ClientEvent(en.EventTypes.clientPlayerReady) )); // TODO: set an interval/timeout to resend until matchStart response (in case of network problem)
|
||||||
matchmakingComplete();
|
matchmakingComplete();
|
||||||
break;
|
break;
|
||||||
case en.EventTypes.matchStart:
|
case en.EventTypes.matchStart:
|
||||||
|
|||||||
@@ -3,16 +3,14 @@ import { WebSocket } from "../wsServer.js";
|
|||||||
import { Racket } from "../../shared_js/class/Rectangle.js";
|
import { Racket } from "../../shared_js/class/Rectangle.js";
|
||||||
import { GameSession } from "./GameSession.js";
|
import { GameSession } from "./GameSession.js";
|
||||||
import * as ev from "../../shared_js/class/Event.js"
|
import * as ev from "../../shared_js/class/Event.js"
|
||||||
|
import * as en from "../../shared_js/enums.js"
|
||||||
|
|
||||||
class Client {
|
class Client {
|
||||||
socket: WebSocket;
|
socket: WebSocket;
|
||||||
id: string; // Pas indispensable si "socket" a une copie de "id"
|
id: string; // Pas indispensable si "socket" a une copie de "id"
|
||||||
isAlive: boolean = true;
|
isAlive: boolean = true;
|
||||||
gameSession: GameSession;
|
gameSession: GameSession = null;
|
||||||
|
matchOptions: en.MatchOptions = 0;
|
||||||
inputBuffer: ev.EventInput = new ev.EventInput();
|
|
||||||
lastInputId: number = 0;
|
|
||||||
|
|
||||||
constructor(socket: WebSocket, id: string) {
|
constructor(socket: WebSocket, id: string) {
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@@ -20,6 +18,8 @@ class Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ClientPlayer extends Client {
|
class ClientPlayer extends Client {
|
||||||
|
inputBuffer: ev.EventInput = new ev.EventInput();
|
||||||
|
lastInputId: number = 0;
|
||||||
racket: Racket;
|
racket: Racket;
|
||||||
constructor(socket: WebSocket, id: string, racket: Racket) {
|
constructor(socket: WebSocket, id: string, racket: Racket) {
|
||||||
super(socket, id);
|
super(socket, id);
|
||||||
|
|||||||
@@ -13,20 +13,20 @@ import { random } from "../utils.js";
|
|||||||
*/
|
*/
|
||||||
class GameSession {
|
class GameSession {
|
||||||
id: string; // url ?
|
id: string; // url ?
|
||||||
playersMap: Map<string, ClientPlayer>;
|
playersMap: Map<string, ClientPlayer> = new Map();
|
||||||
unreadyPlayersMap: Map<string, ClientPlayer>;
|
unreadyPlayersMap: Map<string, ClientPlayer> = new Map();
|
||||||
gameLoopInterval: NodeJS.Timer | number;
|
gameLoopInterval: NodeJS.Timer | number = 0;
|
||||||
clientsUpdateInterval: NodeJS.Timer | number;
|
clientsUpdateInterval: NodeJS.Timer | number = 0;
|
||||||
components: GameComponentsServer;
|
components: GameComponentsServer;
|
||||||
|
matchOptions: en.MatchOptions;
|
||||||
|
|
||||||
actual_time: number;
|
actual_time: number;
|
||||||
last_time: number;
|
last_time: number;
|
||||||
delta_time: number;
|
delta_time: number;
|
||||||
|
|
||||||
constructor(id: string) {
|
constructor(id: string, matchOptions: en.MatchOptions) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.playersMap = new Map();
|
this.matchOptions = matchOptions;
|
||||||
this.unreadyPlayersMap = new Map();
|
|
||||||
this.components = new GameComponentsServer();
|
this.components = new GameComponentsServer();
|
||||||
}
|
}
|
||||||
start() {
|
start() {
|
||||||
|
|||||||
@@ -62,10 +62,12 @@ function clientAnnounceListener(this: WebSocket, data: string)
|
|||||||
{
|
{
|
||||||
// TODO: reconnection with msg.clientId ?
|
// TODO: reconnection with msg.clientId ?
|
||||||
// TODO: spectator/player distinction with msg.role ?
|
// TODO: spectator/player distinction with msg.role ?
|
||||||
|
|
||||||
|
const player = clientsMap.get(this.id) as ClientPlayer;
|
||||||
|
player.matchOptions = msg.matchOptions;
|
||||||
this.send(JSON.stringify( new ev.EventAssignId(this.id) ));
|
this.send(JSON.stringify( new ev.EventAssignId(this.id) ));
|
||||||
this.send(JSON.stringify( new ev.ServerEvent(en.EventTypes.matchmakingInProgress) ));
|
this.send(JSON.stringify( new ev.ServerEvent(en.EventTypes.matchmakingInProgress) ));
|
||||||
matchmaking(this);
|
matchmaking(player);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.log("Invalid ClientAnnounce");
|
console.log("Invalid ClientAnnounce");
|
||||||
@@ -79,43 +81,47 @@ function clientAnnounceListener(this: WebSocket, data: string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function matchmaking(socket: WebSocket)
|
function matchmaking(player: ClientPlayer)
|
||||||
{
|
{
|
||||||
const player: ClientPlayer = clientsMap.get(socket.id) as ClientPlayer;
|
const minPlayersNumber = 2;
|
||||||
if (matchmakingPlayersMap.size < 1)
|
const maxPlayersNumber = 2;
|
||||||
{
|
const matchOptions = player.matchOptions;
|
||||||
matchmakingPlayersMap.set(socket.id, player);
|
matchmakingPlayersMap.set(player.id, player);
|
||||||
|
|
||||||
|
const compatiblePlayers: ClientPlayer[] = [];
|
||||||
|
matchmakingPlayersMap.forEach((client) => {
|
||||||
|
if (compatiblePlayers.length === maxPlayersNumber) {
|
||||||
|
return; // how can we stop forEach entierly and not just this step ???
|
||||||
|
}
|
||||||
|
if (client.matchOptions === matchOptions) {
|
||||||
|
compatiblePlayers.push(client);
|
||||||
|
// PLACE complete forEach stop here
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (compatiblePlayers.length < minPlayersNumber) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
const id = uuidv4();
|
|
||||||
const gameSession = new GameSession(id);
|
|
||||||
gameSessionsMap.set(id, gameSession);
|
|
||||||
|
|
||||||
// for player
|
const id = uuidv4();
|
||||||
gameSession.playersMap.set(player.id, player);
|
const gameSession = new GameSession(id, matchOptions);
|
||||||
player.racket = gameSession.components.playerRight;
|
gameSessionsMap.set(id, gameSession);
|
||||||
socket.send(JSON.stringify( new ev.EventMatchmakingComplete(en.PlayerSide.right) ));
|
|
||||||
|
|
||||||
// for opponent
|
compatiblePlayers.forEach((client) => {
|
||||||
const opponent: ClientPlayer = matchmakingPlayersMap.values().next().value;
|
matchmakingPlayersMap.delete(client.id);
|
||||||
gameSession.playersMap.set(opponent.id, opponent);
|
client.gameSession = gameSession;
|
||||||
matchmakingPlayersMap.delete(opponent.id);
|
gameSession.playersMap.set(client.id, client);
|
||||||
opponent.racket = gameSession.components.playerLeft;
|
gameSession.unreadyPlayersMap.set(client.id, client);
|
||||||
opponent.socket.send(JSON.stringify( new ev.EventMatchmakingComplete(en.PlayerSide.left) ));
|
});
|
||||||
|
|
||||||
// for both
|
// WIP: Not pretty, hardcoded two players.
|
||||||
gameSession.playersMap.forEach( (client) => {
|
// Could be done in gameSession maybe ?
|
||||||
gameSession.unreadyPlayersMap.set(client.id, client);
|
compatiblePlayers[0].racket = gameSession.components.playerRight;
|
||||||
client.gameSession = gameSession;
|
compatiblePlayers[1].racket = gameSession.components.playerLeft;
|
||||||
});
|
compatiblePlayers[0].socket.once("message", playerReadyConfirmationListener);
|
||||||
gameSession.playersMap.forEach( (client) => {
|
compatiblePlayers[1].socket.once("message", playerReadyConfirmationListener);
|
||||||
/* set listener last to be absolutly sure there no early game launch
|
compatiblePlayers[0].socket.send(JSON.stringify( new ev.EventMatchmakingComplete(en.PlayerSide.right) ));
|
||||||
(unlikely, but possible in theory) */
|
compatiblePlayers[1].socket.send(JSON.stringify( new ev.EventMatchmakingComplete(en.PlayerSide.left) ));
|
||||||
client.socket.once("message", playerReadyConfirmationListener);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -75,10 +75,12 @@ class ClientEvent {
|
|||||||
class ClientAnnounce extends ClientEvent {
|
class ClientAnnounce extends ClientEvent {
|
||||||
role: en.ClientRole;
|
role: en.ClientRole;
|
||||||
clientId: string;
|
clientId: string;
|
||||||
constructor(role: en.ClientRole, clientId: string = "") {
|
matchOptions: en.MatchOptions;
|
||||||
|
constructor(role: en.ClientRole, matchOptions: en.MatchOptions, clientId: string = "") {
|
||||||
super(en.EventTypes.clientAnnounce);
|
super(en.EventTypes.clientAnnounce);
|
||||||
this.role = role;
|
this.role = role;
|
||||||
this.clientId = clientId;
|
this.clientId = clientId;
|
||||||
|
this.matchOptions = matchOptions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,4 +37,10 @@ enum ClientRole {
|
|||||||
spectator
|
spectator
|
||||||
}
|
}
|
||||||
|
|
||||||
export {EventTypes, InputEnum, PlayerSide, ClientRole}
|
enum MatchOptions {
|
||||||
|
// binary flags, can be mixed
|
||||||
|
noOption = 0b0,
|
||||||
|
multiBalls = 1 << 0
|
||||||
|
}
|
||||||
|
|
||||||
|
export {EventTypes, InputEnum, PlayerSide, ClientRole, MatchOptions}
|
||||||
|
|||||||
Reference in New Issue
Block a user