matchmaking rework for multiples game modes

This commit is contained in:
LuckyLaszlo
2022-12-01 05:50:31 +01:00
parent e3fd130729
commit 6e572f2fb5
7 changed files with 67 additions and 49 deletions

View File

@@ -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;

View File

@@ -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:

View File

@@ -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);

View File

@@ -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() {

View File

@@ -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) ));
}

View File

@@ -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;
}
}

View File

@@ -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}