début de la mise en place du jeu (partie client uniquement).
This commit is contained in:
238
JEU2/src/server/wsServer.ts
Normal file
238
JEU2/src/server/wsServer.ts
Normal file
@@ -0,0 +1,238 @@
|
||||
|
||||
import { WebSocketServer, WebSocket as BaseLibWebSocket } from "ws";
|
||||
|
||||
export class WebSocket extends BaseLibWebSocket {
|
||||
id?: string;
|
||||
}
|
||||
|
||||
import { IncomingMessage } from "http";
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import * as en from "../shared_js/enums.js"
|
||||
import * as ev from "../shared_js/class/Event.js"
|
||||
import { Client, ClientPlayer } from "./class/Client.js"
|
||||
import { GameSession } from "./class/GameSession.js"
|
||||
import { shortId } from "./utils.js";
|
||||
|
||||
// pas indispensable d'avoir un autre port si le WebSocket est relié à un serveur http préexistant ?
|
||||
const wsPort = 8042;
|
||||
export const wsServer = new WebSocketServer<WebSocket>({port: wsPort, path: "/pong"});
|
||||
|
||||
const clientsMap: Map<string, Client> = new Map; // socket.id/Client
|
||||
const matchmakingPlayersMap: Map<string, ClientPlayer> = new Map; // socket.id/ClientPlayer (duplicates with clientsMap)
|
||||
const gameSessionsMap: Map<string, GameSession> = new Map; // GameSession.id(url)/GameSession
|
||||
|
||||
wsServer.on("connection", connectionListener);
|
||||
wsServer.on("error", errorListener);
|
||||
wsServer.on("close", closeListener);
|
||||
|
||||
|
||||
function connectionListener(socket: WebSocket, request: IncomingMessage)
|
||||
{
|
||||
const id = uuidv4();
|
||||
const client = new Client(socket, id);
|
||||
clientsMap.set(id, client);
|
||||
socket.id = id;
|
||||
|
||||
socket.on("pong", function heartbeat() {
|
||||
client.isAlive = true;
|
||||
// console.log(`client ${shortId(client.id)} is alive`);
|
||||
});
|
||||
|
||||
socket.on("message", function log(data: string) {
|
||||
try {
|
||||
const event: ev.ClientEvent = JSON.parse(data);
|
||||
if (event.type === en.EventTypes.clientInput) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (e) {}
|
||||
console.log("data: " + data);
|
||||
});
|
||||
|
||||
socket.once("message", clientAnnounceListener);
|
||||
}
|
||||
|
||||
|
||||
function clientAnnounceListener(this: WebSocket, data: string)
|
||||
{
|
||||
try {
|
||||
const msg : ev.ClientAnnounce = JSON.parse(data);
|
||||
if (msg.type === en.EventTypes.clientAnnounce)
|
||||
{
|
||||
// TODO: reconnection with msg.clientId ?
|
||||
// TODO: spectator/player distinction with msg.role ?
|
||||
// msg.role is probably not a good idea.
|
||||
// something like a different route could be better
|
||||
// "/pong" to play, "/ID_OF_A_GAMESESSION" to spectate
|
||||
|
||||
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(player);
|
||||
}
|
||||
else {
|
||||
console.log("Invalid ClientAnnounce");
|
||||
}
|
||||
return;
|
||||
}
|
||||
catch (e) {
|
||||
console.log("Invalid JSON (clientAnnounceListener)");
|
||||
}
|
||||
this.once("message", clientAnnounceListener);
|
||||
}
|
||||
|
||||
|
||||
function matchmaking(player: ClientPlayer)
|
||||
{
|
||||
const minPlayersNumber = 2;
|
||||
const maxPlayersNumber = 2;
|
||||
const matchOptions = player.matchOptions;
|
||||
matchmakingPlayersMap.set(player.id, player);
|
||||
|
||||
const compatiblePlayers: ClientPlayer[] = [];
|
||||
for (const [id, client] of matchmakingPlayersMap)
|
||||
{
|
||||
if (client.matchOptions === matchOptions)
|
||||
{
|
||||
compatiblePlayers.push(client);
|
||||
if (compatiblePlayers.length === maxPlayersNumber) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (compatiblePlayers.length < minPlayersNumber) {
|
||||
return;
|
||||
}
|
||||
|
||||
const id = uuidv4();
|
||||
const gameSession = new GameSession(id, matchOptions);
|
||||
gameSessionsMap.set(id, gameSession);
|
||||
|
||||
compatiblePlayers.forEach((client) => {
|
||||
matchmakingPlayersMap.delete(client.id);
|
||||
client.gameSession = gameSession;
|
||||
gameSession.playersMap.set(client.id, client);
|
||||
gameSession.unreadyPlayersMap.set(client.id, client);
|
||||
});
|
||||
|
||||
// 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.forEach((client) => {
|
||||
client.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) ));
|
||||
}
|
||||
|
||||
|
||||
function playerReadyConfirmationListener(this: WebSocket, data: string)
|
||||
{
|
||||
try {
|
||||
const msg : ev.ClientEvent = JSON.parse(data);
|
||||
if (msg.type === en.EventTypes.clientPlayerReady)
|
||||
{
|
||||
const client = clientsMap.get(this.id);
|
||||
const gameSession = client.gameSession;
|
||||
gameSession.unreadyPlayersMap.delete(this.id);
|
||||
if (gameSession.unreadyPlayersMap.size === 0) {
|
||||
gameSession.playersMap.forEach( (client) => {
|
||||
client.socket.send(JSON.stringify( new ev.ServerEvent(en.EventTypes.matchStart) ));
|
||||
});
|
||||
gameSession.start();
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.log("Invalid playerReadyConfirmation");
|
||||
}
|
||||
return;
|
||||
}
|
||||
catch (e) {
|
||||
console.log("Invalid JSON (playerReadyConfirmationListener)");
|
||||
}
|
||||
this.once("message", playerReadyConfirmationListener);
|
||||
}
|
||||
|
||||
|
||||
export function clientInputListener(this: WebSocket, data: string)
|
||||
{
|
||||
try {
|
||||
// const input: ev.ClientEvent = JSON.parse(data);
|
||||
const input: ev.EventInput = JSON.parse(data);
|
||||
if (input.type === en.EventTypes.clientInput)
|
||||
{
|
||||
const client = clientsMap.get(this.id) as ClientPlayer;
|
||||
client.inputBuffer = input;
|
||||
client.gameSession.instantInputDebug(client); // wip
|
||||
}
|
||||
else {
|
||||
console.log("Invalid clientInput");
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.log("Invalid JSON (clientInputListener)");
|
||||
}
|
||||
}
|
||||
|
||||
////////////
|
||||
////////////
|
||||
|
||||
const pingInterval = setInterval( () => {
|
||||
let deleteLog = "";
|
||||
clientsMap.forEach( (client, key, map) => {
|
||||
if (!client.isAlive) {
|
||||
clientTerminate(client, key, map);
|
||||
deleteLog += ` ${shortId(key)} |`;
|
||||
}
|
||||
else {
|
||||
client.isAlive = false;
|
||||
client.socket.ping();
|
||||
}
|
||||
});
|
||||
|
||||
if (deleteLog) {
|
||||
console.log(`Disconnected:${deleteLog}`);
|
||||
}
|
||||
console.log("gameSessionMap size: " + gameSessionsMap.size);
|
||||
console.log("clientsMap size: " + clientsMap.size);
|
||||
console.log("matchmakingPlayersMap size: " + matchmakingPlayersMap.size);
|
||||
console.log("");
|
||||
}, 4200);
|
||||
|
||||
|
||||
function clientTerminate(client: Client, key: string, map: Map<string, Client>)
|
||||
{
|
||||
client.socket.terminate();
|
||||
if (client.gameSession)
|
||||
{
|
||||
client.gameSession.playersMap.delete(key);
|
||||
if (client.gameSession.playersMap.size === 0)
|
||||
{
|
||||
clearInterval(client.gameSession.clientsUpdateInterval);
|
||||
clearInterval(client.gameSession.gameLoopInterval);
|
||||
gameSessionsMap.delete(client.gameSession.id);
|
||||
}
|
||||
}
|
||||
map.delete(key);
|
||||
if (matchmakingPlayersMap.has(key)) {
|
||||
matchmakingPlayersMap.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function closeListener()
|
||||
{
|
||||
clearInterval(pingInterval);
|
||||
}
|
||||
|
||||
|
||||
function errorListener(error: Error)
|
||||
{
|
||||
console.log("Error: " + JSON.stringify(error));
|
||||
}
|
||||
Reference in New Issue
Block a user