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 soundRobloxVolume = 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();
|
||||
|
||||
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
|
||||
@@ -58,7 +58,7 @@ function preMatchListener(this: WebSocket, event: MessageEvent) {
|
||||
}
|
||||
clientInfo.opponentNextPos = new VectorInteger(clientInfo.opponent.pos.x, clientInfo.opponent.pos.y);
|
||||
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();
|
||||
break;
|
||||
case en.EventTypes.matchStart:
|
||||
|
||||
@@ -3,16 +3,14 @@ import { WebSocket } from "../wsServer.js";
|
||||
import { Racket } from "../../shared_js/class/Rectangle.js";
|
||||
import { GameSession } from "./GameSession.js";
|
||||
import * as ev from "../../shared_js/class/Event.js"
|
||||
import * as en from "../../shared_js/enums.js"
|
||||
|
||||
class Client {
|
||||
socket: WebSocket;
|
||||
id: string; // Pas indispensable si "socket" a une copie de "id"
|
||||
isAlive: boolean = true;
|
||||
gameSession: GameSession;
|
||||
|
||||
inputBuffer: ev.EventInput = new ev.EventInput();
|
||||
lastInputId: number = 0;
|
||||
|
||||
gameSession: GameSession = null;
|
||||
matchOptions: en.MatchOptions = 0;
|
||||
constructor(socket: WebSocket, id: string) {
|
||||
this.socket = socket;
|
||||
this.id = id;
|
||||
@@ -20,6 +18,8 @@ class Client {
|
||||
}
|
||||
|
||||
class ClientPlayer extends Client {
|
||||
inputBuffer: ev.EventInput = new ev.EventInput();
|
||||
lastInputId: number = 0;
|
||||
racket: Racket;
|
||||
constructor(socket: WebSocket, id: string, racket: Racket) {
|
||||
super(socket, id);
|
||||
|
||||
@@ -13,20 +13,20 @@ import { random } from "../utils.js";
|
||||
*/
|
||||
class GameSession {
|
||||
id: string; // url ?
|
||||
playersMap: Map<string, ClientPlayer>;
|
||||
unreadyPlayersMap: Map<string, ClientPlayer>;
|
||||
gameLoopInterval: NodeJS.Timer | number;
|
||||
clientsUpdateInterval: NodeJS.Timer | number;
|
||||
playersMap: Map<string, ClientPlayer> = new Map();
|
||||
unreadyPlayersMap: Map<string, ClientPlayer> = new Map();
|
||||
gameLoopInterval: NodeJS.Timer | number = 0;
|
||||
clientsUpdateInterval: NodeJS.Timer | number = 0;
|
||||
components: GameComponentsServer;
|
||||
matchOptions: en.MatchOptions;
|
||||
|
||||
actual_time: number;
|
||||
last_time: number;
|
||||
delta_time: number;
|
||||
|
||||
constructor(id: string) {
|
||||
constructor(id: string, matchOptions: en.MatchOptions) {
|
||||
this.id = id;
|
||||
this.playersMap = new Map();
|
||||
this.unreadyPlayersMap = new Map();
|
||||
this.matchOptions = matchOptions;
|
||||
this.components = new GameComponentsServer();
|
||||
}
|
||||
start() {
|
||||
|
||||
@@ -62,10 +62,12 @@ function clientAnnounceListener(this: WebSocket, data: string)
|
||||
{
|
||||
// TODO: reconnection with msg.clientId ?
|
||||
// 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.ServerEvent(en.EventTypes.matchmakingInProgress) ));
|
||||
matchmaking(this);
|
||||
matchmaking(player);
|
||||
}
|
||||
else {
|
||||
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;
|
||||
if (matchmakingPlayersMap.size < 1)
|
||||
{
|
||||
matchmakingPlayersMap.set(socket.id, player);
|
||||
const minPlayersNumber = 2;
|
||||
const maxPlayersNumber = 2;
|
||||
const matchOptions = player.matchOptions;
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
const id = uuidv4();
|
||||
const gameSession = new GameSession(id);
|
||||
gameSessionsMap.set(id, gameSession);
|
||||
|
||||
// for player
|
||||
gameSession.playersMap.set(player.id, player);
|
||||
player.racket = gameSession.components.playerRight;
|
||||
socket.send(JSON.stringify( new ev.EventMatchmakingComplete(en.PlayerSide.right) ));
|
||||
const id = uuidv4();
|
||||
const gameSession = new GameSession(id, matchOptions);
|
||||
gameSessionsMap.set(id, gameSession);
|
||||
|
||||
// for opponent
|
||||
const opponent: ClientPlayer = matchmakingPlayersMap.values().next().value;
|
||||
gameSession.playersMap.set(opponent.id, opponent);
|
||||
matchmakingPlayersMap.delete(opponent.id);
|
||||
opponent.racket = gameSession.components.playerLeft;
|
||||
opponent.socket.send(JSON.stringify( new ev.EventMatchmakingComplete(en.PlayerSide.left) ));
|
||||
compatiblePlayers.forEach((client) => {
|
||||
matchmakingPlayersMap.delete(client.id);
|
||||
client.gameSession = gameSession;
|
||||
gameSession.playersMap.set(client.id, client);
|
||||
gameSession.unreadyPlayersMap.set(client.id, client);
|
||||
});
|
||||
|
||||
// for both
|
||||
gameSession.playersMap.forEach( (client) => {
|
||||
gameSession.unreadyPlayersMap.set(client.id, client);
|
||||
client.gameSession = gameSession;
|
||||
});
|
||||
gameSession.playersMap.forEach( (client) => {
|
||||
/* set listener last to be absolutly sure there no early game launch
|
||||
(unlikely, but possible in theory) */
|
||||
client.socket.once("message", playerReadyConfirmationListener);
|
||||
});
|
||||
}
|
||||
// WIP: Not pretty, hardcoded two players.
|
||||
// Could be done in gameSession maybe ?
|
||||
compatiblePlayers[0].racket = gameSession.components.playerRight;
|
||||
compatiblePlayers[1].racket = gameSession.components.playerLeft;
|
||||
compatiblePlayers[0].socket.once("message", playerReadyConfirmationListener);
|
||||
compatiblePlayers[1].socket.once("message", playerReadyConfirmationListener);
|
||||
compatiblePlayers[0].socket.send(JSON.stringify( new ev.EventMatchmakingComplete(en.PlayerSide.right) ));
|
||||
compatiblePlayers[1].socket.send(JSON.stringify( new ev.EventMatchmakingComplete(en.PlayerSide.left) ));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -75,10 +75,12 @@ class ClientEvent {
|
||||
class ClientAnnounce extends ClientEvent {
|
||||
role: en.ClientRole;
|
||||
clientId: string;
|
||||
constructor(role: en.ClientRole, clientId: string = "") {
|
||||
matchOptions: en.MatchOptions;
|
||||
constructor(role: en.ClientRole, matchOptions: en.MatchOptions, clientId: string = "") {
|
||||
super(en.EventTypes.clientAnnounce);
|
||||
this.role = role;
|
||||
this.clientId = clientId;
|
||||
this.matchOptions = matchOptions;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,4 +37,10 @@ enum ClientRole {
|
||||
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