Migrating userId to "int32" to save some space and adding userMoves message in protobuf

This commit is contained in:
David Négrier 2020-09-18 13:57:38 +02:00
parent 4b55b54a07
commit e9ca8721a6
31 changed files with 295 additions and 445 deletions

View file

@ -6,7 +6,7 @@ import { uuid } from 'uuidv4';
export interface TokenInterface {
name: string,
userId: string
userUuid: string
}
export class AuthenticateController {
@ -28,12 +28,12 @@ export class AuthenticateController {
});
}*/
//TODO check user email for The Coding Machine game
const userId = uuid();
const token = Jwt.sign({name: param.name, userId: userId} as TokenInterface, SECRET_KEY, {expiresIn: '24h'});
const userUuid = uuid();
const token = Jwt.sign({name: param.name, userUuid: userUuid} as TokenInterface, SECRET_KEY, {expiresIn: '24h'});
return res.status(OK).send({
token: token,
mapUrlStart: URL_ROOM_STARTED,
userId: userId,
userId: userUuid,
});
});
}

View file

@ -20,13 +20,14 @@ import {isWebRtcSignalMessageInterface} from "../Model/Websocket/WebRtcSignalMes
import {UserInGroupInterface} from "../Model/Websocket/UserInGroupInterface";
import {isItemEventMessageInterface} from "../Model/Websocket/ItemEventMessage";
import {uuid} from 'uuidv4';
import {isUserMovesInterface} from "../Model/Websocket/UserMovesMessage";
import {isViewport} from "../Model/Websocket/ViewportMessage";
import {GroupUpdateInterface} from "_Model/Websocket/GroupUpdateInterface";
import {Movable} from "../Model/Movable";
import {SetPlayerDetailsMessage} from "../../../messages/generated/src/proto/messages_pb";
import {PositionMessage, SetPlayerDetailsMessage} from "../../../messages/generated/messages_pb";
import {UserMovesMessage} from "../../../messages/generated/messages_pb";
import Direction = PositionMessage.Direction;
enum SockerIoEvent {
enum SocketIoEvent {
CONNECTION = "connection",
DISCONNECT = "disconnect",
JOIN_ROOM = "join-room", // bi-directional
@ -52,7 +53,7 @@ function emitInBatch(socket: ExSocketInterface, event: string | symbol, payload:
if (socket.batchTimeout === null) {
socket.batchTimeout = setTimeout(() => {
socket.emit(SockerIoEvent.BATCH, socket.batchedMessages);
socket.emit(SocketIoEvent.BATCH, socket.batchedMessages);
socket.batchedMessages = [];
socket.batchTimeout = null;
}, 100);
@ -62,9 +63,10 @@ function emitInBatch(socket: ExSocketInterface, event: string | symbol, payload:
export class IoSocketController {
public readonly Io: socketIO.Server;
private Worlds: Map<string, World> = new Map<string, World>();
private sockets: Map<string, ExSocketInterface> = new Map<string, ExSocketInterface>();
private sockets: Map<number, ExSocketInterface> = new Map<number, ExSocketInterface>();
private nbClientsGauge: Gauge<string>;
private nbClientsPerRoomGauge: Gauge<string>;
private nextUserId: number = 1;
constructor(server: http.Server) {
this.Io = socketIO(server);
@ -90,7 +92,9 @@ export class IoSocketController {
if(socket.handshake.query.token === 'test'){
if (ALLOW_ARTILLERY) {
(socket as ExSocketInterface).token = socket.handshake.query.token;
(socket as ExSocketInterface).userId = uuid();
(socket as ExSocketInterface).userId = this.nextUserId;
(socket as ExSocketInterface).userUuid = uuid();
this.nextUserId++;
(socket as ExSocketInterface).isArtillery = true;
console.log((socket as ExSocketInterface).userId);
next();
@ -116,7 +120,9 @@ export class IoSocketController {
}
(socket as ExSocketInterface).token = socket.handshake.query.token;
(socket as ExSocketInterface).userId = tokenDecoded.userId;
(socket as ExSocketInterface).userId = this.nextUserId;
(socket as ExSocketInterface).userUuid = tokenDecoded.userUuid;
this.nextUserId++;
next();
});
});
@ -125,7 +131,7 @@ export class IoSocketController {
}
private isValidToken(token: object): token is TokenInterface {
if (typeof((token as TokenInterface).userId) !== 'string') {
if (typeof((token as TokenInterface).userUuid) !== 'string') {
return false;
}
if (typeof((token as TokenInterface).name) !== 'string') {
@ -151,7 +157,7 @@ export class IoSocketController {
}
ioConnection() {
this.Io.on(SockerIoEvent.CONNECTION, (socket: Socket) => {
this.Io.on(SocketIoEvent.CONNECTION, (socket: Socket) => {
const client : ExSocketInterface = socket as ExSocketInterface;
client.batchedMessages = [];
client.batchTimeout = null;
@ -176,11 +182,11 @@ export class IoSocketController {
x: user x position on map
y: user y position on map
*/
socket.on(SockerIoEvent.JOIN_ROOM, (message: unknown, answerFn): void => {
console.log(SockerIoEvent.JOIN_ROOM, message);
socket.on(SocketIoEvent.JOIN_ROOM, (message: unknown, answerFn): void => {
console.log(SocketIoEvent.JOIN_ROOM, message);
try {
if (!isJoinRoomMessageInterface(message)) {
socket.emit(SockerIoEvent.MESSAGE_ERROR, {message: 'Invalid JOIN_ROOM message.'});
socket.emit(SocketIoEvent.MESSAGE_ERROR, {message: 'Invalid JOIN_ROOM message.'});
console.warn('Invalid JOIN_ROOM message received: ', message);
return;
}
@ -244,11 +250,11 @@ export class IoSocketController {
}
});
socket.on(SockerIoEvent.SET_VIEWPORT, (message: unknown): void => {
socket.on(SocketIoEvent.SET_VIEWPORT, (message: unknown): void => {
try {
//console.log('SET_VIEWPORT')
if (!isViewport(message)) {
socket.emit(SockerIoEvent.MESSAGE_ERROR, {message: 'Invalid SET_VIEWPORT message.'});
socket.emit(SocketIoEvent.MESSAGE_ERROR, {message: 'Invalid SET_VIEWPORT message.'});
console.warn('Invalid SET_VIEWPORT message received: ', message);
return;
}
@ -268,20 +274,47 @@ export class IoSocketController {
}
});
socket.on(SockerIoEvent.USER_POSITION, (userMovesMessage: unknown): void => {
socket.on(SocketIoEvent.USER_POSITION, (message: unknown): void => {
//console.log(SockerIoEvent.USER_POSITION, userMovesMessage);
try {
if (!isUserMovesInterface(userMovesMessage)) {
socket.emit(SockerIoEvent.MESSAGE_ERROR, {message: 'Invalid USER_POSITION message.'});
console.warn('Invalid USER_POSITION message received: ', userMovesMessage);
return;
const userMovesMessage = UserMovesMessage.deserializeBinary(new Uint8Array(message as ArrayBuffer));
const userMoves = userMovesMessage.toObject();
const position = userMoves.position;
if (position === undefined) {
throw new Error('Position not found in message');
}
const viewport = userMoves.viewport;
if (viewport === undefined) {
throw new Error('Viewport not found in message');
}
let direction: string;
switch (position.direction) {
case Direction.UP:
direction = 'up';
break;
case Direction.DOWN:
direction = 'down';
break;
case Direction.LEFT:
direction = 'left';
break;
case Direction.RIGHT:
direction = 'right';
break;
}
const Client = (socket as ExSocketInterface);
// sending to all clients in room except sender
Client.position = userMovesMessage.position;
Client.viewport = userMovesMessage.viewport;
Client.position = {
x: position.x,
y: position.y,
direction,
moving: position.moving,
};
Client.viewport = viewport;
// update position in the world
const world = this.Worlds.get(Client.roomId);
@ -297,15 +330,15 @@ export class IoSocketController {
}
});
socket.on(SockerIoEvent.WEBRTC_SIGNAL, (data: unknown) => {
socket.on(SocketIoEvent.WEBRTC_SIGNAL, (data: unknown) => {
this.emitVideo((socket as ExSocketInterface), data);
});
socket.on(SockerIoEvent.WEBRTC_SCREEN_SHARING_SIGNAL, (data: unknown) => {
socket.on(SocketIoEvent.WEBRTC_SCREEN_SHARING_SIGNAL, (data: unknown) => {
this.emitScreenSharing((socket as ExSocketInterface), data);
});
socket.on(SockerIoEvent.DISCONNECT, () => {
socket.on(SocketIoEvent.DISCONNECT, () => {
const Client = (socket as ExSocketInterface);
try {
//leave room
@ -335,16 +368,16 @@ export class IoSocketController {
});
// Let's send the user id to the user
socket.on(SockerIoEvent.SET_PLAYER_DETAILS, (message: any, answerFn) => {
console.log(SockerIoEvent.SET_PLAYER_DETAILS, message);
socket.on(SocketIoEvent.SET_PLAYER_DETAILS, (message: any, answerFn) => {
console.log(SocketIoEvent.SET_PLAYER_DETAILS, message);
const playerDetailsMessage = SetPlayerDetailsMessage.deserializeBinary(new Uint8Array(message));
const playerDetails = {
name: playerDetailsMessage.getName(),
characterLayers: playerDetailsMessage.getCharacterlayersList()
};
console.log(SockerIoEvent.SET_PLAYER_DETAILS, playerDetails);
console.log(SocketIoEvent.SET_PLAYER_DETAILS, playerDetails);
if (!isSetPlayerDetailsMessage(playerDetails)) {
socket.emit(SockerIoEvent.MESSAGE_ERROR, {message: 'Invalid SET_PLAYER_DETAILS message.'});
socket.emit(SocketIoEvent.MESSAGE_ERROR, {message: 'Invalid SET_PLAYER_DETAILS message.'});
console.warn('Invalid SET_PLAYER_DETAILS message received: ', playerDetails);
return;
}
@ -357,10 +390,10 @@ export class IoSocketController {
}
});
socket.on(SockerIoEvent.SET_SILENT, (silent: unknown) => {
console.log(SockerIoEvent.SET_SILENT, silent);
socket.on(SocketIoEvent.SET_SILENT, (silent: unknown) => {
console.log(SocketIoEvent.SET_SILENT, silent);
if (typeof silent !== "boolean") {
socket.emit(SockerIoEvent.MESSAGE_ERROR, {message: 'Invalid SET_SILENT message.'});
socket.emit(SocketIoEvent.MESSAGE_ERROR, {message: 'Invalid SET_SILENT message.'});
console.warn('Invalid SET_SILENT message received: ', silent);
return;
}
@ -381,16 +414,16 @@ export class IoSocketController {
}
});
socket.on(SockerIoEvent.ITEM_EVENT, (itemEvent: unknown) => {
socket.on(SocketIoEvent.ITEM_EVENT, (itemEvent: unknown) => {
if (!isItemEventMessageInterface(itemEvent)) {
socket.emit(SockerIoEvent.MESSAGE_ERROR, {message: 'Invalid ITEM_EVENT message.'});
socket.emit(SocketIoEvent.MESSAGE_ERROR, {message: 'Invalid ITEM_EVENT message.'});
console.warn('Invalid ITEM_EVENT message received: ', itemEvent);
return;
}
try {
const Client = (socket as ExSocketInterface);
socket.to(Client.roomId).emit(SockerIoEvent.ITEM_EVENT, itemEvent);
socket.to(Client.roomId).emit(SocketIoEvent.ITEM_EVENT, itemEvent);
const world = this.Worlds.get(Client.roomId);
if (!world) {
@ -408,7 +441,7 @@ export class IoSocketController {
emitVideo(socket: ExSocketInterface, data: unknown){
if (!isWebRtcSignalMessageInterface(data)) {
socket.emit(SockerIoEvent.MESSAGE_ERROR, {message: 'Invalid WEBRTC_SIGNAL message.'});
socket.emit(SocketIoEvent.MESSAGE_ERROR, {message: 'Invalid WEBRTC_SIGNAL message.'});
console.warn('Invalid WEBRTC_SIGNAL message received: ', data);
return;
}
@ -418,7 +451,7 @@ export class IoSocketController {
console.warn("While exchanging a WebRTC signal: client with id ", data.receiverId, " does not exist. This might be a race condition.");
return;
}
return client.emit(SockerIoEvent.WEBRTC_SIGNAL, {
return client.emit(SocketIoEvent.WEBRTC_SIGNAL, {
userId: socket.userId,
signal: data.signal
});
@ -426,7 +459,7 @@ export class IoSocketController {
emitScreenSharing(socket: ExSocketInterface, data: unknown){
if (!isWebRtcSignalMessageInterface(data)) {
socket.emit(SockerIoEvent.MESSAGE_ERROR, {message: 'Invalid WEBRTC_SCREEN_SHARING message.'});
socket.emit(SocketIoEvent.MESSAGE_ERROR, {message: 'Invalid WEBRTC_SCREEN_SHARING message.'});
console.warn('Invalid WEBRTC_SCREEN_SHARING message received: ', data);
return;
}
@ -436,13 +469,13 @@ export class IoSocketController {
console.warn("While exchanging a WEBRTC_SCREEN_SHARING signal: client with id ", data.receiverId, " does not exist. This might be a race condition.");
return;
}
return client.emit(SockerIoEvent.WEBRTC_SCREEN_SHARING_SIGNAL, {
return client.emit(SocketIoEvent.WEBRTC_SCREEN_SHARING_SIGNAL, {
userId: socket.userId,
signal: data.signal
});
}
searchClientByIdOrFail(userId: string): ExSocketInterface {
searchClientByIdOrFail(userId: number): ExSocketInterface {
const client: ExSocketInterface|undefined = this.sockets.get(userId);
if (client === undefined) {
throw new Error("Could not find user with id " + userId);
@ -481,9 +514,9 @@ export class IoSocketController {
//check and create new world for a room
let world = this.Worlds.get(roomId)
if(world === undefined){
world = new World((user1: string, group: Group) => {
world = new World((user1: number, group: Group) => {
this.connectedUser(user1, group);
}, (user1: string, group: Group) => {
}, (user1: number, group: Group) => {
this.disConnectedUser(user1, group);
}, MINIMUM_DISTANCE, GROUP_RADIUS, (thing: Movable, listener: User) => {
const clientListener = this.searchClientByIdOrFail(listener.id);
@ -491,9 +524,9 @@ export class IoSocketController {
const clientUser = this.searchClientByIdOrFail(thing.id);
const messageUserJoined = new MessageUserJoined(clientUser.userId, clientUser.name, clientUser.characterLayers, clientUser.position);
clientListener.emit(SockerIoEvent.JOIN_ROOM, messageUserJoined);
clientListener.emit(SocketIoEvent.JOIN_ROOM, messageUserJoined);
} else if (thing instanceof Group) {
clientListener.emit(SockerIoEvent.GROUP_CREATE_UPDATE, {
clientListener.emit(SocketIoEvent.GROUP_CREATE_UPDATE, {
position: thing.getPosition(),
groupId: thing.getId()
} as GroupUpdateInterface);
@ -505,10 +538,10 @@ export class IoSocketController {
if (thing instanceof User) {
const clientUser = this.searchClientByIdOrFail(thing.id);
clientListener.emitInBatch(SockerIoEvent.USER_MOVED, new MessageUserMoved(clientUser.userId, clientUser.position));
clientListener.emitInBatch(SocketIoEvent.USER_MOVED, new MessageUserMoved(clientUser.userId, clientUser.position));
//console.log("Sending USER_MOVED event");
} else if (thing instanceof Group) {
clientListener.emit(SockerIoEvent.GROUP_CREATE_UPDATE, {
clientListener.emit(SocketIoEvent.GROUP_CREATE_UPDATE, {
position: thing.getPosition(),
groupId: thing.getId()
} as GroupUpdateInterface);
@ -519,10 +552,10 @@ export class IoSocketController {
const clientListener = this.searchClientByIdOrFail(listener.id);
if (thing instanceof User) {
const clientUser = this.searchClientByIdOrFail(thing.id);
clientListener.emit(SockerIoEvent.USER_LEFT, clientUser.userId);
clientListener.emit(SocketIoEvent.USER_LEFT, clientUser.userId);
//console.log("Sending USER_LEFT event");
} else if (thing instanceof Group) {
clientListener.emit(SockerIoEvent.GROUP_DELETE, thing.getId());
clientListener.emit(SocketIoEvent.GROUP_DELETE, thing.getId());
} else {
console.error('Unexpected type for Movable.');
}
@ -533,7 +566,7 @@ export class IoSocketController {
// Dispatch groups position to newly connected user
world.getGroups().forEach((group: Group) => {
Client.emit(SockerIoEvent.GROUP_CREATE_UPDATE, {
Client.emit(SocketIoEvent.GROUP_CREATE_UPDATE, {
position: group.getPosition(),
groupId: group.getId()
} as GroupUpdateInterface);
@ -578,7 +611,7 @@ export class IoSocketController {
return tabs;
}, []);
client.emit(SockerIoEvent.WEBRTC_START, {clients: peerClients, roomId: roomId});
client.emit(SocketIoEvent.WEBRTC_START, {clients: peerClients, roomId: roomId});
});
}
@ -600,19 +633,19 @@ export class IoSocketController {
**/
//connected user
connectedUser(userId: string, group: Group) {
connectedUser(userId: number, group: Group) {
/*let Client = this.sockets.get(userId);
if (Client === undefined) {
return;
}*/
const Client = this.searchClientByIdOrFail(userId);
this.joinWebRtcRoom(Client, group.getId());
this.joinWebRtcRoom(Client, "webrtcroom"+group.getId());
}
//disconnect user
disConnectedUser(userId: string, group: Group) {
disConnectedUser(userId: number, group: Group) {
const Client = this.searchClientByIdOrFail(userId);
Client.to(group.getId()).emit(SockerIoEvent.WEBRTC_DISCONNECT, {
Client.to("webrtcroom"+group.getId()).emit(SocketIoEvent.WEBRTC_DISCONNECT, {
userId: userId
});
@ -622,7 +655,7 @@ export class IoSocketController {
// the other player will try connecting until a timeout happens (during this time, the connection icon will be displayed for nothing).
// So we also send the disconnect event to the other player.
for (const user of group.getUsers()) {
Client.emit(SockerIoEvent.WEBRTC_DISCONNECT, {
Client.emit(SocketIoEvent.WEBRTC_DISCONNECT, {
userId: user.id
});
}

View file

@ -7,7 +7,9 @@ import {Movable} from "_Model/Movable";
export class Group implements Movable {
static readonly MAX_PER_GROUP = 4;
private id: string;
private static nextId: number = 1;
private id: number;
private users: Set<User>;
private connectCallback: ConnectCallback;
private disconnectCallback: DisconnectCallback;
@ -17,7 +19,8 @@ export class Group implements Movable {
this.users = new Set<User>();
this.connectCallback = connectCallback;
this.disconnectCallback = disconnectCallback;
this.id = uuid();
this.id = Group.nextId;
Group.nextId++;
users.forEach((user: User) => {
this.join(user);
@ -28,7 +31,7 @@ export class Group implements Movable {
return Array.from(this.users.values());
}
getId() : string{
getId() : number {
return this.id;
}

View file

@ -9,7 +9,7 @@ export class User implements Movable {
public group?: Group;
public constructor(
public id: string,
public id: number,
public position: PointInterface,
public silent: boolean,

View file

@ -8,7 +8,8 @@ export interface ExSocketInterface extends Socket, Identificable {
token: string;
roomId: string;
webRtcRoomId: string;
userId: string;
userId: number; // A temporary (autoincremented) identifier for this user
userUuid: string; // A unique identifier for this user
name: string;
characterLayers: string[];
position: PointInterface;

View file

@ -2,5 +2,5 @@ import {PositionInterface} from "_Model/PositionInterface";
export interface GroupUpdateInterface {
position: PositionInterface,
groupId: string,
groupId: number,
}

View file

@ -1,3 +1,3 @@
export interface Identificable {
userId: string;
userId: number;
}

View file

@ -1,6 +1,6 @@
import {PointInterface} from "_Model/Websocket/PointInterface";
export class MessageUserJoined {
constructor(public userId: string, public name: string, public characterLayers: string[], public position: PointInterface) {
constructor(public userId: number, public name: string, public characterLayers: string[], public position: PointInterface) {
}
}

View file

@ -1,6 +1,6 @@
import {PointInterface} from "./PointInterface";
export class MessageUserMoved {
constructor(public userId: string, public position: PointInterface) {
constructor(public userId: number, public position: PointInterface) {
}
}

View file

@ -6,6 +6,6 @@ export class Point implements PointInterface{
}
export class MessageUserPosition {
constructor(public userId: string, public name: string, public characterLayers: string[], public position: PointInterface) {
constructor(public userId: number, public name: string, public characterLayers: string[], public position: PointInterface) {
}
}

View file

@ -1,5 +1,5 @@
export interface UserInGroupInterface {
userId: string,
userId: number,
name: string,
initiator: boolean
}

View file

@ -1,11 +0,0 @@
import * as tg from "generic-type-guard";
import {isPointInterface} from "./PointInterface";
import {isViewport} from "./ViewportMessage";
export const isUserMovesInterface =
new tg.IsInterface().withProperties({
position: isPointInterface,
viewport: isViewport,
}).get();
export type UserMovesInterface = tg.GuardedType<typeof isUserMovesInterface>;

View file

@ -7,12 +7,12 @@ export const isSignalData =
export const isWebRtcSignalMessageInterface =
new tg.IsInterface().withProperties({
receiverId: tg.isString,
receiverId: tg.isNumber,
signal: isSignalData
}).get();
export const isWebRtcScreenSharingStartMessageInterface =
new tg.IsInterface().withProperties({
userId: tg.isString,
userId: tg.isNumber,
roomId: tg.isString
}).get();
export type WebRtcSignalMessageInterface = tg.GuardedType<typeof isWebRtcSignalMessageInterface>;

View file

@ -11,15 +11,15 @@ import {PositionNotifier} from "./PositionNotifier";
import {ViewportInterface} from "_Model/Websocket/ViewportMessage";
import {Movable} from "_Model/Movable";
export type ConnectCallback = (user: string, group: Group) => void;
export type DisconnectCallback = (user: string, group: Group) => void;
export type ConnectCallback = (user: number, group: Group) => void;
export type DisconnectCallback = (user: number, group: Group) => void;
export class World {
private readonly minDistance: number;
private readonly groupRadius: number;
// Users, sorted by ID
private readonly users: Map<string, User>;
private readonly users: Map<number, User>;
private readonly groups: Set<Group>;
private readonly connectCallback: ConnectCallback;
@ -37,7 +37,7 @@ export class World {
onMoves: MovesCallback,
onLeaves: LeavesCallback)
{
this.users = new Map<string, User>();
this.users = new Map<number, User>();
this.groups = new Set<Group>();
this.connectCallback = connectCallback;
this.disconnectCallback = disconnectCallback;
@ -51,7 +51,7 @@ export class World {
return Array.from(this.groups.values());
}
public getUsers(): Map<string, User> {
public getUsers(): Map<number, User> {
return this.users;
}