diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index eed2dfaf..543b2f54 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -163,8 +163,8 @@ export class IoSocketController { let memberTags: string[] = []; let memberTextures: CharacterTexture[] = []; const room = await socketManager.getOrCreateRoom(roomId); - if (room.getUsers().size > MAX_USERS_PER_ROOM) { - res.writeStatus("302").end('Too many users'); + if (room.isFull()) { + res.writeStatus("503").end('Too many users'); return; } try { diff --git a/back/src/Controller/MapController.ts b/back/src/Controller/MapController.ts index abe34886..e667f005 100644 --- a/back/src/Controller/MapController.ts +++ b/back/src/Controller/MapController.ts @@ -4,6 +4,7 @@ import {HttpRequest, HttpResponse, TemplatedApp} from "uWebSockets.js"; import {BaseController} from "./BaseController"; import {parse} from "query-string"; import {adminApi} from "../Services/AdminApi"; +import {socketManager} from "../Services/SocketManager"; //todo: delete this export class MapController extends BaseController{ @@ -12,6 +13,7 @@ export class MapController extends BaseController{ super(); this.App = App; this.getMapUrl(); + this.canConnectToMap(); } @@ -67,4 +69,23 @@ export class MapController extends BaseController{ }); } + + canConnectToMap() { + this.App.options("/canjoin", (res: HttpResponse, req: HttpRequest) => { + this.addCorsHeaders(res); + + res.end(); + }); + + this.App.get("/canjoin", (res: HttpResponse, req: HttpRequest) => { + const query = parse(req.getQuery()); + const roomId = query.roomId as string; + const world = socketManager.getWorlds().get(roomId); + if (!world) { + res.writeStatus('404').end('No room found'); + return; + } + res.writeStatus("200").end(world.isFull() ? '1':'0'); + }); + } } diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 5efde159..718c5832 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -10,6 +10,7 @@ import {ViewportInterface} from "_Model/Websocket/ViewportMessage"; import {Movable} from "_Model/Movable"; import {extractDataFromPrivateRoomId, extractRoomSlugPublicRoomId, isRoomAnonymous} from "./RoomIdentifier"; import {arrayIntersect} from "../Services/ArrayHelper"; +import {MAX_USERS_PER_ROOM} from "_Enum/EnvironmentVariable"; export type ConnectCallback = (user: User, group: Group) => void; export type DisconnectCallback = (user: User, group: Group) => void; @@ -179,6 +180,10 @@ export class GameRoom { } } + isFull() : boolean { + return this.getUsers().size > MAX_USERS_PER_ROOM; + } + /** * Makes a user leave a group and closes and destroy the group if the group contains only one remaining person. * diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 971903f1..f10f9788 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -9,6 +9,11 @@ import {Room} from "./Room"; const URL_ROOM_STARTED = '/Floor0/floor0.json'; +export enum connexionErrorTypes { + serverError = 1, + tooManyUsers, +} + class ConnectionManager { private localUser!:LocalUser; @@ -90,8 +95,12 @@ class ConnectionManager { return new Promise((resolve, reject) => { const connection = new RoomConnection(this.localUser.jwtToken, roomId, name, characterLayers, position, viewport); connection.onConnectError((error: object) => { - console.log('An error occurred while connecting to socket server. Retrying'); - reject(error); + console.log(error); + if (false) { //todo: how to check error type? + reject(connexionErrorTypes.tooManyUsers); + } else { + reject(connexionErrorTypes.serverError); + } }); connection.onConnect(() => { resolve(connection); @@ -99,6 +108,8 @@ class ConnectionManager { }).catch((err) => { // Let's retry in 4-6 seconds return new Promise((resolve, reject) => { + if (err === connexionErrorTypes.tooManyUsers) return reject(err); + console.log('An error occurred while connecting to socket server. Retrying'); setTimeout(() => { //todo: allow a way to break recurrsion? this.connectToRoomSocket(roomId, name, characterLayers, position, viewport).then((connection) => resolve(connection)); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index c35b6d31..bf9a3b9f 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -48,13 +48,14 @@ import {ActionableItem} from "../Items/ActionableItem"; import {UserInputManager} from "../UserInput/UserInputManager"; import {UserMovedMessage} from "../../Messages/generated/messages_pb"; import {ProtobufClientUtils} from "../../Network/ProtobufClientUtils"; -import {connectionManager} from "../../Connexion/ConnectionManager"; +import {connectionManager, connexionErrorTypes} from "../../Connexion/ConnectionManager"; import {RoomConnection} from "../../Connexion/RoomConnection"; import {GlobalMessageManager} from "../../Administration/GlobalMessageManager"; import {UserMessageManager} from "../../Administration/UserMessageManager"; import {ConsoleGlobalMessageManager} from "../../Administration/ConsoleGlobalMessageManager"; import {ResizableScene} from "../Login/ResizableScene"; import {Room} from "../../Connexion/Room"; +import {MessageUI} from "../../Logger/MessageUI"; export enum Textures { @@ -122,6 +123,7 @@ export class GameScene extends ResizableScene implements CenterListener { // A promise that will resolve when the "create" method is called (signaling loading is ended) private createPromise: Promise; private createPromiseResolve!: (value?: void | PromiseLike) => void; + private offlineMode: boolean = false; MapKey: string; MapUrlFile: string; @@ -426,7 +428,7 @@ export class GameScene extends ResizableScene implements CenterListener { if (this.connection === undefined) { // Let's wait 0.5 seconds before printing the "connecting" screen to avoid blinking setTimeout(() => { - if (this.connection === undefined) { + if (this.connection === undefined && !this.offlineMode) { this.scene.sleep(); this.scene.launch(ReconnectingSceneName); } @@ -629,6 +631,11 @@ export class GameScene extends ResizableScene implements CenterListener { this.scene.sleep(ReconnectingSceneName); return connection; + }).catch(error => { + if (error === connexionErrorTypes.tooManyUsers) { + this.offlineMode = true; + MessageUI.warningMessage('Too many users. You switched to offline mode. Please try to connect again later.'); + } }); } @@ -956,14 +963,19 @@ export class GameScene extends ResizableScene implements CenterListener { const nextSceneKey = this.checkToExit(); if (nextSceneKey) { - // We are completely destroying the current scene to avoid using a half-backed instance when coming back to the same map. - this.connection.closeConnection(); - this.simplePeer.unregister(); - this.scene.stop(); - this.scene.remove(this.scene.key); - this.scene.start(nextSceneKey.key); + this.goToNextScene(nextSceneKey.key); } } + + private goToNextScene(nextSceneKey: string): void { + + // We are completely destroying the current scene to avoid using a half-backed instance when coming back to the same map. + this.connection.closeConnection(); + this.simplePeer.unregister(); + this.scene.stop(); + this.scene.remove(this.scene.key); + this.scene.start(nextSceneKey); + } private checkToExit(): {key: string, hash: string} | null { const x = Math.floor(this.CurrentPlayer.x / 32);