Fixing reconnection to server on back failure

This commit is contained in:
David Négrier 2020-12-03 16:39:44 +01:00
parent 2fba6956a6
commit a19edd4dc1
6 changed files with 87 additions and 47 deletions

View file

@ -96,7 +96,6 @@ export class GameRoom {
} }
const position = ProtobufUtils.toPointInterface(positionMessage); const position = ProtobufUtils.toPointInterface(positionMessage);
const user = new User(this.nextUserId, joinRoomMessage.getUseruuid(), position, false, this.positionNotifier, socket, joinRoomMessage.getTagList(), joinRoomMessage.getName(), ProtobufUtils.toCharacterLayerObjects(joinRoomMessage.getCharacterlayerList())); const user = new User(this.nextUserId, joinRoomMessage.getUseruuid(), position, false, this.positionNotifier, socket, joinRoomMessage.getTagList(), joinRoomMessage.getName(), ProtobufUtils.toCharacterLayerObjects(joinRoomMessage.getCharacterlayerList()));
this.nextUserId++; this.nextUserId++;
this.users.set(user.id, user); this.users.set(user.id, user);

View file

@ -1,7 +1,7 @@
import Axios from "axios"; import Axios from "axios";
import {API_URL} from "../Enum/EnvironmentVariable"; import {API_URL} from "../Enum/EnvironmentVariable";
import {RoomConnection} from "./RoomConnection"; import {RoomConnection} from "./RoomConnection";
import {PositionInterface, ViewportInterface} from "./ConnexionModels"; import {OnConnectInterface, PositionInterface, ViewportInterface} from "./ConnexionModels";
import {GameConnexionTypes, urlManager} from "../Url/UrlManager"; import {GameConnexionTypes, urlManager} from "../Url/UrlManager";
import {localUserStore} from "./LocalUserStore"; import {localUserStore} from "./LocalUserStore";
import {LocalUser} from "./LocalUser"; import {LocalUser} from "./LocalUser";
@ -88,24 +88,29 @@ class ConnectionManager {
this.localUser = new LocalUser('', 'test', []); this.localUser = new LocalUser('', 'test', []);
} }
public connectToRoomSocket(roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface): Promise<RoomConnection> { public connectToRoomSocket(roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface): Promise<OnConnectInterface> {
return new Promise<RoomConnection>((resolve, reject) => { return new Promise<OnConnectInterface>((resolve, reject) => {
const connection = new RoomConnection(this.localUser.jwtToken, roomId, name, characterLayers, position, viewport); const connection = new RoomConnection(this.localUser.jwtToken, roomId, name, characterLayers, position, viewport);
connection.onConnectError((error: object) => { connection.onConnectError((error: object) => {
console.log('An error occurred while connecting to socket server. Retrying'); console.log('An error occurred while connecting to socket server. Retrying');
reject(error); reject(error);
}); });
// FIXME: onConnect should be triggered by the first JoinRoomEvent (instead of the connection) connection.onConnectingError((event: CloseEvent) => {
connection.onConnect(() => { console.log('An error occurred while connecting to socket server. Retrying');
console.warn('CONNECT RECEIVED'); reject(new Error('An error occurred while connecting to socket server. Retrying. Code: '+event.code+', Reason: '+event.reason));
resolve(connection); });
})
connection.onConnect((connect: OnConnectInterface) => {
resolve(connect);
});
}).catch((err) => { }).catch((err) => {
// Let's retry in 4-6 seconds // Let's retry in 4-6 seconds
return new Promise<RoomConnection>((resolve, reject) => { return new Promise<OnConnectInterface>((resolve, reject) => {
setTimeout(() => { setTimeout(() => {
//todo: allow a way to break recurrsion? //todo: allow a way to break recursion?
//todo: find a way to avoid recursive function. Otherwise, the call stack will grow indefinitely.
this.connectToRoomSocket(roomId, name, characterLayers, position, viewport).then((connection) => resolve(connection)); this.connectToRoomSocket(roomId, name, characterLayers, position, viewport).then((connection) => resolve(connection));
}, 4000 + Math.floor(Math.random() * 2000) ); }, 4000 + Math.floor(Math.random() * 2000) );
}); });

View file

@ -2,13 +2,14 @@ import {PlayerAnimationNames} from "../Phaser/Player/Animation";
import {UserSimplePeerInterface} from "../WebRtc/SimplePeer"; import {UserSimplePeerInterface} from "../WebRtc/SimplePeer";
import {SignalData} from "simple-peer"; import {SignalData} from "simple-peer";
import {BodyResourceDescriptionInterface} from "../Phaser/Entity/body_character"; import {BodyResourceDescriptionInterface} from "../Phaser/Entity/body_character";
import {RoomConnection} from "./RoomConnection";
export enum EventMessage{ export enum EventMessage{
CONNECT = "connect", CONNECT = "connect",
WEBRTC_SIGNAL = "webrtc-signal", WEBRTC_SIGNAL = "webrtc-signal",
WEBRTC_SCREEN_SHARING_SIGNAL = "webrtc-screen-sharing-signal", WEBRTC_SCREEN_SHARING_SIGNAL = "webrtc-screen-sharing-signal",
WEBRTC_START = "webrtc-start", WEBRTC_START = "webrtc-start",
START_ROOM = "start-room", // From server to client: list of all room users/groups/items //START_ROOM = "start-room", // From server to client: list of all room users/groups/items
JOIN_ROOM = "join-room", // bi-directional JOIN_ROOM = "join-room", // bi-directional
USER_POSITION = "user-position", // From client to server USER_POSITION = "user-position", // From client to server
USER_MOVED = "user-moved", // From server to client USER_MOVED = "user-moved", // From server to client
@ -21,6 +22,7 @@ export enum EventMessage{
ITEM_EVENT = 'item-event', ITEM_EVENT = 'item-event',
CONNECT_ERROR = "connect_error", CONNECT_ERROR = "connect_error",
CONNECTING_ERROR = "connecting_error",
SET_SILENT = "set_silent", // Set or unset the silent mode for this user. SET_SILENT = "set_silent", // Set or unset the silent mode for this user.
SET_VIEWPORT = "set-viewport", SET_VIEWPORT = "set-viewport",
BATCH = "batch", BATCH = "batch",
@ -132,3 +134,8 @@ export interface PlayGlobalMessageInterface {
type: string type: string
message: string message: string
} }
export interface OnConnectInterface {
connection: RoomConnection,
room: RoomJoinedMessageInterface
}

View file

@ -36,7 +36,7 @@ import {ProtobufClientUtils} from "../Network/ProtobufClientUtils";
import { import {
EventMessage, EventMessage,
GroupCreatedUpdatedMessageInterface, ItemEventMessageInterface, GroupCreatedUpdatedMessageInterface, ItemEventMessageInterface,
MessageUserJoined, PlayGlobalMessageInterface, PositionInterface, MessageUserJoined, OnConnectInterface, PlayGlobalMessageInterface, PositionInterface,
RoomJoinedMessageInterface, RoomJoinedMessageInterface,
ViewportInterface, WebRtcDisconnectMessageInterface, ViewportInterface, WebRtcDisconnectMessageInterface,
WebRtcSignalReceivedMessageInterface, WebRtcSignalReceivedMessageInterface,
@ -86,14 +86,26 @@ export class RoomConnection implements RoomConnection {
this.socket.binaryType = 'arraybuffer'; this.socket.binaryType = 'arraybuffer';
let interval: ReturnType<typeof setInterval>|undefined = undefined;
this.socket.onopen = (ev) => { this.socket.onopen = (ev) => {
//we manually ping every 20s to not be logged out by the server, even when the game is in background. //we manually ping every 20s to not be logged out by the server, even when the game is in background.
const pingMessage = new PingMessage(); const pingMessage = new PingMessage();
setInterval(() => this.socket.send(pingMessage.serializeBinary().buffer), manualPingDelay); interval = setInterval(() => this.socket.send(pingMessage.serializeBinary().buffer), manualPingDelay);
}; };
this.socket.addEventListener('close', (event) => {
if (interval) {
clearInterval(interval);
}
// If we are not connected yet (if a JoinRoomMessage was not sent), we need to retry.
if (this.userId === null) {
this.dispatch(EventMessage.CONNECTING_ERROR, event);
}
});
this.socket.onmessage = (messageEvent) => { this.socket.onmessage = (messageEvent) => {
console.warn('message received');
const arrayBuffer: ArrayBuffer = messageEvent.data; const arrayBuffer: ArrayBuffer = messageEvent.data;
const message = ServerToClientMessage.deserializeBinary(new Uint8Array(arrayBuffer)); const message = ServerToClientMessage.deserializeBinary(new Uint8Array(arrayBuffer));
@ -138,13 +150,22 @@ export class RoomConnection implements RoomConnection {
this.userId = roomJoinedMessage.getCurrentuserid(); this.userId = roomJoinedMessage.getCurrentuserid();
this.tags = roomJoinedMessage.getTagList(); this.tags = roomJoinedMessage.getTagList();
this.dispatch(EventMessage.CONNECT, this); //console.log('Dispatching CONNECT')
this.dispatch(EventMessage.CONNECT, {
connection: this,
room: {
//users,
//groups,
items
} as RoomJoinedMessageInterface
});
/*console.log('Dispatching START_ROOM')
this.dispatch(EventMessage.START_ROOM, { this.dispatch(EventMessage.START_ROOM, {
//users, //users,
//groups, //groups,
items items
}); });*/
} else if (message.hasErrormessage()) { } else if (message.hasErrormessage()) {
console.error(EventMessage.MESSAGE_ERROR, message.getErrormessage()?.getMessage()); console.error(EventMessage.MESSAGE_ERROR, message.getErrormessage()?.getMessage());
} else if (message.hasWebrtcsignaltoclientmessage()) { } else if (message.hasWebrtcsignaltoclientmessage()) {
@ -354,6 +375,12 @@ export class RoomConnection implements RoomConnection {
}); });
} }
public onConnectingError(callback: (event: CloseEvent) => void): void {
this.onMessage(EventMessage.CONNECTING_ERROR, (event: CloseEvent) => {
callback(event);
});
}
public onConnectError(callback: (error: Event) => void): void { public onConnectError(callback: (error: Event) => void): void {
this.socket.addEventListener('error', callback) this.socket.addEventListener('error', callback)
} }
@ -361,7 +388,7 @@ export class RoomConnection implements RoomConnection {
/*public onConnect(callback: (e: Event) => void): void { /*public onConnect(callback: (e: Event) => void): void {
this.socket.addEventListener('open', callback) this.socket.addEventListener('open', callback)
}*/ }*/
public onConnect(callback: (roomConnection: RoomConnection) => void): void { public onConnect(callback: (roomConnection: OnConnectInterface) => void): void {
//this.socket.addEventListener('open', callback) //this.socket.addEventListener('open', callback)
this.onMessage(EventMessage.CONNECT, callback); this.onMessage(EventMessage.CONNECT, callback);
} }
@ -369,9 +396,9 @@ export class RoomConnection implements RoomConnection {
/** /**
* Triggered when we receive all the details of a room (users, groups, ...) * Triggered when we receive all the details of a room (users, groups, ...)
*/ */
public onStartRoom(callback: (event: RoomJoinedMessageInterface) => void): void { /*public onStartRoom(callback: (event: RoomJoinedMessageInterface) => void): void {
this.onMessage(EventMessage.START_ROOM, callback); this.onMessage(EventMessage.START_ROOM, callback);
} }*/
public sendWebrtcSignal(signal: unknown, receiverId: number) { public sendWebrtcSignal(signal: unknown, receiverId: number) {
const webRtcSignal = new WebRtcSignalToServerMessage(); const webRtcSignal = new WebRtcSignalToServerMessage();

View file

@ -3,7 +3,7 @@ import {
GroupCreatedUpdatedMessageInterface, GroupCreatedUpdatedMessageInterface,
MessageUserJoined, MessageUserJoined,
MessageUserMovedInterface, MessageUserMovedInterface,
MessageUserPositionInterface, MessageUserPositionInterface, OnConnectInterface,
PointInterface, PointInterface,
PositionInterface, PositionInterface,
RoomJoinedMessageInterface RoomJoinedMessageInterface
@ -521,23 +521,14 @@ export class GameScene extends ResizableScene implements CenterListener {
top: camera.scrollY, top: camera.scrollY,
right: camera.scrollX + camera.width, right: camera.scrollX + camera.width,
bottom: camera.scrollY + camera.height, bottom: camera.scrollY + camera.height,
}).then((connection: RoomConnection) => { }).then((onConnect: OnConnectInterface) => {
this.connection = connection; this.connection = onConnect.connection;
//this.connection.emitPlayerDetailsMessage(gameManager.getPlayerName(), gameManager.getCharacterSelected()) //this.connection.emitPlayerDetailsMessage(gameManager.getPlayerName(), gameManager.getCharacterSelected())
connection.onStartRoom((roomJoinedMessage: RoomJoinedMessageInterface) => { /*this.connection.onStartRoom((roomJoinedMessage: RoomJoinedMessageInterface) => {
//this.initUsersPosition(roomJoinedMessage.users);
this.connectionAnswerPromiseResolve(roomJoinedMessage);
// Analyze tags to find if we are admin. If yes, show console.
if (this.connection.hasTag('admin')) {
this.ConsoleGlobalMessageManager = new ConsoleGlobalMessageManager(this.connection, this.userInputManager);
}
this.scene.wake(); });*/
this.scene.sleep(ReconnectingSceneName); this.connection.onUserJoins((message: MessageUserJoined) => {
});
connection.onUserJoins((message: MessageUserJoined) => {
const userMessage: AddPlayerInterface = { const userMessage: AddPlayerInterface = {
userId: message.userId, userId: message.userId,
characterLayers: message.characterLayers, characterLayers: message.characterLayers,
@ -547,7 +538,7 @@ export class GameScene extends ResizableScene implements CenterListener {
this.addPlayer(userMessage); this.addPlayer(userMessage);
}); });
connection.onUserMoved((message: UserMovedMessage) => { this.connection.onUserMoved((message: UserMovedMessage) => {
const position = message.getPosition(); const position = message.getPosition();
if (position === undefined) { if (position === undefined) {
throw new Error('Position missing from UserMovedMessage'); throw new Error('Position missing from UserMovedMessage');
@ -562,15 +553,15 @@ export class GameScene extends ResizableScene implements CenterListener {
this.updatePlayerPosition(messageUserMoved); this.updatePlayerPosition(messageUserMoved);
}); });
connection.onUserLeft((userId: number) => { this.connection.onUserLeft((userId: number) => {
this.removePlayer(userId); this.removePlayer(userId);
}); });
connection.onGroupUpdatedOrCreated((groupPositionMessage: GroupCreatedUpdatedMessageInterface) => { this.connection.onGroupUpdatedOrCreated((groupPositionMessage: GroupCreatedUpdatedMessageInterface) => {
this.shareGroupPosition(groupPositionMessage); this.shareGroupPosition(groupPositionMessage);
}) })
connection.onGroupDeleted((groupId: number) => { this.connection.onGroupDeleted((groupId: number) => {
try { try {
this.deleteGroup(groupId); this.deleteGroup(groupId);
} catch (e) { } catch (e) {
@ -578,7 +569,7 @@ export class GameScene extends ResizableScene implements CenterListener {
} }
}) })
connection.onServerDisconnected(() => { this.connection.onServerDisconnected(() => {
console.log('Player disconnected from server. Reloading scene.'); console.log('Player disconnected from server. Reloading scene.');
this.simplePeer.closeAllConnections(); this.simplePeer.closeAllConnections();
@ -599,7 +590,7 @@ export class GameScene extends ResizableScene implements CenterListener {
this.scene.remove(this.scene.key); this.scene.remove(this.scene.key);
}) })
connection.onActionableEvent((message => { this.connection.onActionableEvent((message => {
const item = this.actionableItems.get(message.itemId); const item = this.actionableItems.get(message.itemId);
if (item === undefined) { if (item === undefined) {
console.warn('Received an event about object "' + message.itemId + '" but cannot find this item on the map.'); console.warn('Received an event about object "' + message.itemId + '" but cannot find this item on the map.');
@ -611,7 +602,7 @@ export class GameScene extends ResizableScene implements CenterListener {
/** /**
* Triggered when we receive the JWT token to connect to Jitsi * Triggered when we receive the JWT token to connect to Jitsi
*/ */
connection.onStartJitsiRoom((jwt, room) => { this.connection.onStartJitsiRoom((jwt, room) => {
this.startJitsi(room, jwt); this.startJitsi(room, jwt);
}); });
@ -641,7 +632,17 @@ export class GameScene extends ResizableScene implements CenterListener {
this.gameMap.setPosition(event.x, event.y); this.gameMap.setPosition(event.x, event.y);
}) })
return connection; //this.initUsersPosition(roomJoinedMessage.users);
this.connectionAnswerPromiseResolve(onConnect.room);
// Analyze tags to find if we are admin. If yes, show console.
if (this.connection.hasTag('admin')) {
this.ConsoleGlobalMessageManager = new ConsoleGlobalMessageManager(this.connection, this.userInputManager);
}
console.log('wakingup');
this.scene.wake();
this.scene.sleep(ReconnectingSceneName);
return this.connection;
}); });
} }

View file

@ -136,13 +136,13 @@ export class SocketManager implements ZoneEventListener {
console.warn('Connection lost to back server'); console.warn('Connection lost to back server');
// Let's close the front connection if the back connection is closed. This way, we can retry connecting from the start. // Let's close the front connection if the back connection is closed. This way, we can retry connecting from the start.
if (!client.disconnecting) { if (!client.disconnecting) {
this.closeWebsocketConnection(client); this.closeWebsocketConnection(client, 1011, 'Connection lost to back server');
} }
console.log('A user left'); console.log('A user left');
}).on('error', (err: Error) => { }).on('error', (err: Error) => {
console.error('Error in connection to back server:', err); console.error('Error in connection to back server:', err);
if (!client.disconnecting) { if (!client.disconnecting) {
this.closeWebsocketConnection(client); this.closeWebsocketConnection(client, 1011, 'Error while connecting to back server');
} }
}); });
@ -208,10 +208,11 @@ export class SocketManager implements ZoneEventListener {
} }
} }
closeWebsocketConnection(client: ExSocketInterface) { private closeWebsocketConnection(client: ExSocketInterface, code: number, reason: string) {
client.disconnecting = true; client.disconnecting = true;
//this.leaveRoom(client); //this.leaveRoom(client);
client.close(); //client.close();
client.end(code, reason);
} }
handleViewport(client: ExSocketInterface, viewport: ViewportMessage.AsObject) { handleViewport(client: ExSocketInterface, viewport: ViewportMessage.AsObject) {