Merge branch 'develop' into windows-focus-blur-camera
# Conflicts: # front/src/Phaser/Game/GameScene.ts
This commit is contained in:
commit
1570ef9663
@ -20,9 +20,9 @@ import {parse} from "query-string";
|
|||||||
import {jwtTokenManager} from "../Services/JWTTokenManager";
|
import {jwtTokenManager} from "../Services/JWTTokenManager";
|
||||||
import {adminApi, CharacterTexture, FetchMemberDataByUuidResponse} from "../Services/AdminApi";
|
import {adminApi, CharacterTexture, FetchMemberDataByUuidResponse} from "../Services/AdminApi";
|
||||||
import {SocketManager, socketManager} from "../Services/SocketManager";
|
import {SocketManager, socketManager} from "../Services/SocketManager";
|
||||||
import {emitInBatch, pongMaxInterval, refresLogoutTimerOnPong, resetPing} from "../Services/IoSocketHelpers";
|
import {emitInBatch} from "../Services/IoSocketHelpers";
|
||||||
import {clientEventsEmitter} from "../Services/ClientEventsEmitter";
|
import {clientEventsEmitter} from "../Services/ClientEventsEmitter";
|
||||||
import {ADMIN_API_TOKEN, ADMIN_API_URL} from "../Enum/EnvironmentVariable";
|
import {ADMIN_API_TOKEN, ADMIN_API_URL, SOCKET_IDLE_TIMER} from "../Enum/EnvironmentVariable";
|
||||||
|
|
||||||
export class IoSocketController {
|
export class IoSocketController {
|
||||||
private nextUserId: number = 1;
|
private nextUserId: number = 1;
|
||||||
@ -110,6 +110,7 @@ export class IoSocketController {
|
|||||||
this.app.ws('/room', {
|
this.app.ws('/room', {
|
||||||
/* Options */
|
/* Options */
|
||||||
//compression: uWS.SHARED_COMPRESSOR,
|
//compression: uWS.SHARED_COMPRESSOR,
|
||||||
|
idleTimeout: SOCKET_IDLE_TIMER,
|
||||||
maxPayloadLength: 16 * 1024 * 1024,
|
maxPayloadLength: 16 * 1024 * 1024,
|
||||||
maxBackpressure: 65536, // Maximum 64kB of data in the buffer.
|
maxBackpressure: 65536, // Maximum 64kB of data in the buffer.
|
||||||
//idleTimeout: 10,
|
//idleTimeout: 10,
|
||||||
@ -239,8 +240,6 @@ export class IoSocketController {
|
|||||||
// Let's join the room
|
// Let's join the room
|
||||||
const client = this.initClient(ws); //todo: into the upgrade instead?
|
const client = this.initClient(ws); //todo: into the upgrade instead?
|
||||||
socketManager.handleJoinRoom(client);
|
socketManager.handleJoinRoom(client);
|
||||||
resetPing(client);
|
|
||||||
refresLogoutTimerOnPong(ws as ExSocketInterface);
|
|
||||||
|
|
||||||
//get data information and show messages
|
//get data information and show messages
|
||||||
if (ADMIN_API_URL) {
|
if (ADMIN_API_URL) {
|
||||||
@ -293,9 +292,6 @@ export class IoSocketController {
|
|||||||
drain: (ws) => {
|
drain: (ws) => {
|
||||||
console.log('WebSocket backpressure: ' + ws.getBufferedAmount());
|
console.log('WebSocket backpressure: ' + ws.getBufferedAmount());
|
||||||
},
|
},
|
||||||
pong(ws) {
|
|
||||||
refresLogoutTimerOnPong(ws as ExSocketInterface);
|
|
||||||
},
|
|
||||||
close: (ws, code, message) => {
|
close: (ws, code, message) => {
|
||||||
const Client = (ws as ExSocketInterface);
|
const Client = (ws as ExSocketInterface);
|
||||||
try {
|
try {
|
||||||
|
@ -10,6 +10,7 @@ const CPU_OVERHEAT_THRESHOLD = Number(process.env.CPU_OVERHEAT_THRESHOLD) || 80;
|
|||||||
const JITSI_URL : string|undefined = (process.env.JITSI_URL === '') ? undefined : process.env.JITSI_URL;
|
const JITSI_URL : string|undefined = (process.env.JITSI_URL === '') ? undefined : process.env.JITSI_URL;
|
||||||
const JITSI_ISS = process.env.JITSI_ISS || '';
|
const JITSI_ISS = process.env.JITSI_ISS || '';
|
||||||
const SECRET_JITSI_KEY = process.env.SECRET_JITSI_KEY || '';
|
const SECRET_JITSI_KEY = process.env.SECRET_JITSI_KEY || '';
|
||||||
|
export const SOCKET_IDLE_TIMER = parseInt(process.env.SOCKET_IDLE_TIMER as string) || 30; // maximum time (in second) without activity before a socket is closed
|
||||||
|
|
||||||
export {
|
export {
|
||||||
SECRET_KEY,
|
SECRET_KEY,
|
||||||
|
@ -25,8 +25,6 @@ export interface ExSocketInterface extends WebSocket, Identificable {
|
|||||||
emitInBatch: (payload: SubMessage) => void;
|
emitInBatch: (payload: SubMessage) => void;
|
||||||
batchedMessages: BatchMessage;
|
batchedMessages: BatchMessage;
|
||||||
batchTimeout: NodeJS.Timeout|null;
|
batchTimeout: NodeJS.Timeout|null;
|
||||||
pingTimeout: NodeJS.Timeout|null;
|
|
||||||
pongTimeout: NodeJS.Timeout|null;
|
|
||||||
disconnecting: boolean,
|
disconnecting: boolean,
|
||||||
tags: string[],
|
tags: string[],
|
||||||
textures: CharacterTexture[],
|
textures: CharacterTexture[],
|
||||||
|
@ -6,8 +6,13 @@ class GaugeManager {
|
|||||||
private nbClientsPerRoomGauge: Gauge<string>;
|
private nbClientsPerRoomGauge: Gauge<string>;
|
||||||
private nbGroupsPerRoomGauge: Gauge<string>;
|
private nbGroupsPerRoomGauge: Gauge<string>;
|
||||||
private nbGroupsPerRoomCounter: Counter<string>;
|
private nbGroupsPerRoomCounter: Counter<string>;
|
||||||
|
private nbRoomsGauge: Gauge<string>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
this.nbRoomsGauge = new Gauge({
|
||||||
|
name: 'workadventure_nb_rooms',
|
||||||
|
help: 'Number of active rooms'
|
||||||
|
});
|
||||||
this.nbClientsGauge = new Gauge({
|
this.nbClientsGauge = new Gauge({
|
||||||
name: 'workadventure_nb_sockets',
|
name: 'workadventure_nb_sockets',
|
||||||
help: 'Number of connected sockets',
|
help: 'Number of connected sockets',
|
||||||
@ -31,6 +36,13 @@ class GaugeManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
incNbRoomGauge(): void {
|
||||||
|
this.nbRoomsGauge.inc();
|
||||||
|
}
|
||||||
|
decNbRoomGauge(): void {
|
||||||
|
this.nbRoomsGauge.dec();
|
||||||
|
}
|
||||||
|
|
||||||
incNbClientPerRoomGauge(roomId: string): void {
|
incNbClientPerRoomGauge(roomId: string): void {
|
||||||
this.nbClientsGauge.inc();
|
this.nbClientsGauge.inc();
|
||||||
this.nbClientsPerRoomGauge.inc({ room: roomId });
|
this.nbClientsPerRoomGauge.inc({ room: roomId });
|
||||||
|
@ -18,22 +18,6 @@ export function emitInBatch(socket: ExSocketInterface, payload: SubMessage): voi
|
|||||||
socket.batchTimeout = null;
|
socket.batchTimeout = null;
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we send a message, we don't need to keep the connection alive
|
|
||||||
resetPing(socket);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resetPing(ws: ExSocketInterface): void {
|
|
||||||
if (ws.pingTimeout) {
|
|
||||||
clearTimeout(ws.pingTimeout);
|
|
||||||
}
|
|
||||||
ws.pingTimeout = setTimeout(() => {
|
|
||||||
if (ws.disconnecting) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ws.ping();
|
|
||||||
resetPing(ws);
|
|
||||||
}, 29000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function emitError(Client: ExSocketInterface, message: string): void {
|
export function emitError(Client: ExSocketInterface, message: string): void {
|
||||||
@ -49,11 +33,3 @@ export function emitError(Client: ExSocketInterface, message: string): void {
|
|||||||
console.warn(message);
|
console.warn(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const pongMaxInterval = 30000; // the maximum duration (in ms) between pongs before we shutdown the connexion.
|
|
||||||
|
|
||||||
export function refresLogoutTimerOnPong(ws: ExSocketInterface): void {
|
|
||||||
if(ws.pongTimeout) clearTimeout(ws.pongTimeout);
|
|
||||||
ws.pongTimeout = setTimeout(() => {
|
|
||||||
ws.close();
|
|
||||||
}, pongMaxInterval);
|
|
||||||
}
|
|
||||||
|
@ -351,6 +351,7 @@ export class SocketManager {
|
|||||||
world.leave(Client);
|
world.leave(Client);
|
||||||
if (world.isEmpty()) {
|
if (world.isEmpty()) {
|
||||||
this.Worlds.delete(Client.roomId);
|
this.Worlds.delete(Client.roomId);
|
||||||
|
gaugeManager.decNbRoomGauge();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//user leave previous room
|
//user leave previous room
|
||||||
@ -383,6 +384,7 @@ export class SocketManager {
|
|||||||
world.tags = data.tags
|
world.tags = data.tags
|
||||||
world.policyType = Number(data.policy_type)
|
world.policyType = Number(data.policy_type)
|
||||||
}
|
}
|
||||||
|
gaugeManager.incNbRoomGauge();
|
||||||
this.Worlds.set(roomId, world);
|
this.Worlds.set(roomId, world);
|
||||||
}
|
}
|
||||||
return Promise.resolve(world)
|
return Promise.resolve(world)
|
||||||
|
@ -26,6 +26,7 @@ import {
|
|||||||
QueryJitsiJwtMessage,
|
QueryJitsiJwtMessage,
|
||||||
SendJitsiJwtMessage,
|
SendJitsiJwtMessage,
|
||||||
CharacterLayerMessage,
|
CharacterLayerMessage,
|
||||||
|
PingMessage,
|
||||||
SendUserMessage
|
SendUserMessage
|
||||||
} from "../Messages/generated/messages_pb"
|
} from "../Messages/generated/messages_pb"
|
||||||
|
|
||||||
@ -42,6 +43,8 @@ import {
|
|||||||
} from "./ConnexionModels";
|
} from "./ConnexionModels";
|
||||||
import {BodyResourceDescriptionInterface} from "../Phaser/Entity/body_character";
|
import {BodyResourceDescriptionInterface} from "../Phaser/Entity/body_character";
|
||||||
|
|
||||||
|
const manualPingDelay = 20000;
|
||||||
|
|
||||||
export class RoomConnection implements RoomConnection {
|
export class RoomConnection implements RoomConnection {
|
||||||
private readonly socket: WebSocket;
|
private readonly socket: WebSocket;
|
||||||
private userId: number|null = null;
|
private userId: number|null = null;
|
||||||
@ -84,7 +87,9 @@ export class RoomConnection implements RoomConnection {
|
|||||||
this.socket.binaryType = 'arraybuffer';
|
this.socket.binaryType = 'arraybuffer';
|
||||||
|
|
||||||
this.socket.onopen = (ev) => {
|
this.socket.onopen = (ev) => {
|
||||||
//console.log('WS connected');
|
//we manually ping every 20s to not be logged out by the server, even when the game is in background.
|
||||||
|
const pingMessage = new PingMessage();
|
||||||
|
setInterval(() => this.socket.send(pingMessage.serializeBinary().buffer), manualPingDelay);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.socket.onmessage = (messageEvent) => {
|
this.socket.onmessage = (messageEvent) => {
|
||||||
|
@ -466,35 +466,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
|
|
||||||
// From now, this game scene will be notified of reposition events
|
// From now, this game scene will be notified of reposition events
|
||||||
layoutManager.setListener(this);
|
layoutManager.setListener(this);
|
||||||
|
this.triggerOnMapLayerPropertyChange();
|
||||||
this.gameMap.onPropertyChange('openWebsite', (newValue, oldValue) => {
|
|
||||||
if (newValue === undefined) {
|
|
||||||
coWebsiteManager.closeCoWebsite();
|
|
||||||
} else {
|
|
||||||
coWebsiteManager.loadCoWebsite(newValue as string);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.gameMap.onPropertyChange('jitsiRoom', (newValue, oldValue, allProps) => {
|
|
||||||
if (newValue === undefined) {
|
|
||||||
this.stopJitsi();
|
|
||||||
} else {
|
|
||||||
if (JITSI_PRIVATE_MODE) {
|
|
||||||
const adminTag = allProps.get("jitsiRoomAdminTag") as string | undefined;
|
|
||||||
|
|
||||||
this.connection.emitQueryJitsiJwtMessage(this.instance.replace('/', '-') + "-" + newValue, adminTag);
|
|
||||||
} else {
|
|
||||||
this.startJitsi(newValue as string);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
this.gameMap.onPropertyChange('silent', (newValue, oldValue) => {
|
|
||||||
if (newValue === undefined || newValue === false || newValue === '') {
|
|
||||||
this.connection.setSilent(false);
|
|
||||||
} else {
|
|
||||||
this.connection.setSilent(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const camera = this.cameras.main;
|
const camera = this.cameras.main;
|
||||||
|
|
||||||
@ -630,10 +602,43 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
this.scene.wake();
|
this.scene.wake();
|
||||||
this.scene.sleep(ReconnectingSceneName);
|
this.scene.sleep(ReconnectingSceneName);
|
||||||
|
|
||||||
|
//init user position and play trigger to check layers properties
|
||||||
|
this.gameMap.setPosition(this.CurrentPlayer.x, this.CurrentPlayer.y);
|
||||||
|
|
||||||
return connection;
|
return connection;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private triggerOnMapLayerPropertyChange(){
|
||||||
|
this.gameMap.onPropertyChange('openWebsite', (newValue, oldValue) => {
|
||||||
|
if (newValue === undefined) {
|
||||||
|
coWebsiteManager.closeCoWebsite();
|
||||||
|
} else {
|
||||||
|
coWebsiteManager.loadCoWebsite(newValue as string);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.gameMap.onPropertyChange('jitsiRoom', (newValue, oldValue, allProps) => {
|
||||||
|
if (newValue === undefined) {
|
||||||
|
this.stopJitsi();
|
||||||
|
} else {
|
||||||
|
if (JITSI_PRIVATE_MODE) {
|
||||||
|
const adminTag = allProps.get("jitsiRoomAdminTag") as string|undefined;
|
||||||
|
|
||||||
|
this.connection.emitQueryJitsiJwtMessage(this.instance.replace('/', '-') + "-" + newValue, adminTag);
|
||||||
|
} else {
|
||||||
|
this.startJitsi(newValue as string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.gameMap.onPropertyChange('silent', (newValue, oldValue) => {
|
||||||
|
if (newValue === undefined || newValue === false || newValue === '') {
|
||||||
|
this.connection.setSilent(false);
|
||||||
|
} else {
|
||||||
|
this.connection.setSilent(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private switchLayoutMode(): void {
|
private switchLayoutMode(): void {
|
||||||
const mode = layoutManager.getLayoutMode();
|
const mode = layoutManager.getLayoutMode();
|
||||||
if (mode === LayoutMode.Presentation) {
|
if (mode === LayoutMode.Presentation) {
|
||||||
@ -712,6 +717,7 @@ export class GameScene extends ResizableScene implements CenterListener {
|
|||||||
*/
|
*/
|
||||||
//todo: push that into the gameManager
|
//todo: push that into the gameManager
|
||||||
private loadNextGame(layer: ITiledMapLayer, mapWidth: number, roomId: string){
|
private loadNextGame(layer: ITiledMapLayer, mapWidth: number, roomId: string){
|
||||||
|
|
||||||
const room = new Room(roomId);
|
const room = new Room(roomId);
|
||||||
gameManager.loadMap(room, this.scene);
|
gameManager.loadMap(room, this.scene);
|
||||||
const exitSceneKey = roomId;
|
const exitSceneKey = roomId;
|
||||||
|
@ -40,7 +40,9 @@ export class UserInputManager {
|
|||||||
initKeyBoardEvent(){
|
initKeyBoardEvent(){
|
||||||
this.KeysCode = [
|
this.KeysCode = [
|
||||||
{event: UserInputEvent.MoveUp, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Z, false) },
|
{event: UserInputEvent.MoveUp, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Z, false) },
|
||||||
|
{event: UserInputEvent.MoveUp, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.W, false) },
|
||||||
{event: UserInputEvent.MoveLeft, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Q, false) },
|
{event: UserInputEvent.MoveLeft, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Q, false) },
|
||||||
|
{event: UserInputEvent.MoveLeft, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A, false) },
|
||||||
{event: UserInputEvent.MoveDown, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S, false) },
|
{event: UserInputEvent.MoveDown, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S, false) },
|
||||||
{event: UserInputEvent.MoveRight, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D, false) },
|
{event: UserInputEvent.MoveRight, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D, false) },
|
||||||
|
|
||||||
|
@ -16,6 +16,11 @@ class CoWebsiteManager {
|
|||||||
private opened: iframeStates = iframeStates.closed;
|
private opened: iframeStates = iframeStates.closed;
|
||||||
|
|
||||||
private observers = new Array<CoWebsiteStateChangedCallback>();
|
private observers = new Array<CoWebsiteStateChangedCallback>();
|
||||||
|
/**
|
||||||
|
* Quickly going in and out of an iframe trigger can create conflicts between the iframe states.
|
||||||
|
* So we use this promise to queue up every cowebsite state transition
|
||||||
|
*/
|
||||||
|
private currentOperationPromise: Promise<void> = Promise.resolve();
|
||||||
|
|
||||||
private close(): HTMLDivElement {
|
private close(): HTMLDivElement {
|
||||||
const cowebsiteDiv = HtmlUtils.getElementByIdOrFail<HTMLDivElement>(cowebsiteDivId);
|
const cowebsiteDiv = HtmlUtils.getElementByIdOrFail<HTMLDivElement>(cowebsiteDivId);
|
||||||
@ -52,7 +57,7 @@ class CoWebsiteManager {
|
|||||||
const onTimeoutPromise = new Promise((resolve) => {
|
const onTimeoutPromise = new Promise((resolve) => {
|
||||||
setTimeout(() => resolve(), 2000);
|
setTimeout(() => resolve(), 2000);
|
||||||
});
|
});
|
||||||
Promise.race([onloadPromise, onTimeoutPromise]).then(() => {
|
this.currentOperationPromise = this.currentOperationPromise.then(() =>Promise.race([onloadPromise, onTimeoutPromise])).then(() => {
|
||||||
this.open();
|
this.open();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.fire();
|
this.fire();
|
||||||
@ -65,7 +70,7 @@ class CoWebsiteManager {
|
|||||||
*/
|
*/
|
||||||
public insertCoWebsite(callback: (cowebsite: HTMLDivElement) => Promise<void>): void {
|
public insertCoWebsite(callback: (cowebsite: HTMLDivElement) => Promise<void>): void {
|
||||||
const cowebsiteDiv = this.load();
|
const cowebsiteDiv = this.load();
|
||||||
callback(cowebsiteDiv).then(() => {
|
this.currentOperationPromise = this.currentOperationPromise.then(() => callback(cowebsiteDiv)).then(() => {
|
||||||
this.open();
|
this.open();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.fire();
|
this.fire();
|
||||||
@ -74,14 +79,16 @@ class CoWebsiteManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public closeCoWebsite(): Promise<void> {
|
public closeCoWebsite(): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
this.currentOperationPromise = this.currentOperationPromise.then(() => new Promise((resolve, reject) => {
|
||||||
|
if(this.opened === iframeStates.closed) resolve(); //this method may be called twice, in case of iframe error for example
|
||||||
const cowebsiteDiv = this.close();
|
const cowebsiteDiv = this.close();
|
||||||
this.fire();
|
this.fire();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
resolve();
|
resolve();
|
||||||
setTimeout(() => cowebsiteDiv.innerHTML = '', 500)
|
setTimeout(() => cowebsiteDiv.innerHTML = '', 500)
|
||||||
}, animationTime)
|
}, animationTime)
|
||||||
});
|
}));
|
||||||
|
return this.currentOperationPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getGameSize(): {width: number, height: number} {
|
public getGameSize(): {width: number, height: number} {
|
||||||
|
@ -38,6 +38,10 @@ message CharacterLayerMessage {
|
|||||||
|
|
||||||
/*********** CLIENT TO SERVER MESSAGES *************/
|
/*********** CLIENT TO SERVER MESSAGES *************/
|
||||||
|
|
||||||
|
message PingMessage {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
message SetPlayerDetailsMessage {
|
message SetPlayerDetailsMessage {
|
||||||
string name = 1;
|
string name = 1;
|
||||||
repeated string characterLayers = 2;
|
repeated string characterLayers = 2;
|
||||||
|
Loading…
Reference in New Issue
Block a user