diff --git a/back/src/Controller/AuthenticateController.ts b/back/src/Controller/AuthenticateController.ts index 71e538a4..83880f45 100644 --- a/back/src/Controller/AuthenticateController.ts +++ b/back/src/Controller/AuthenticateController.ts @@ -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, }); }); } diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 38950e35..a2732fb8 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -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 = new Map(); - private sockets: Map = new Map(); + private sockets: Map = new Map(); private nbClientsGauge: Gauge; private nbClientsPerRoomGauge: Gauge; + 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 }); } diff --git a/back/src/Model/Group.ts b/back/src/Model/Group.ts index 4909b660..43990ef4 100644 --- a/back/src/Model/Group.ts +++ b/back/src/Model/Group.ts @@ -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; private connectCallback: ConnectCallback; private disconnectCallback: DisconnectCallback; @@ -17,7 +19,8 @@ export class Group implements Movable { this.users = new Set(); 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; } diff --git a/back/src/Model/User.ts b/back/src/Model/User.ts index 160a101c..b147e4be 100644 --- a/back/src/Model/User.ts +++ b/back/src/Model/User.ts @@ -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, diff --git a/back/src/Model/Websocket/ExSocketInterface.ts b/back/src/Model/Websocket/ExSocketInterface.ts index bbe18cbb..648bbe21 100644 --- a/back/src/Model/Websocket/ExSocketInterface.ts +++ b/back/src/Model/Websocket/ExSocketInterface.ts @@ -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; diff --git a/back/src/Model/Websocket/GroupUpdateInterface.ts b/back/src/Model/Websocket/GroupUpdateInterface.ts index 45e64ea4..34a6d8b1 100644 --- a/back/src/Model/Websocket/GroupUpdateInterface.ts +++ b/back/src/Model/Websocket/GroupUpdateInterface.ts @@ -2,5 +2,5 @@ import {PositionInterface} from "_Model/PositionInterface"; export interface GroupUpdateInterface { position: PositionInterface, - groupId: string, + groupId: number, } diff --git a/back/src/Model/Websocket/Identificable.ts b/back/src/Model/Websocket/Identificable.ts index 4e3228ae..424d3a76 100644 --- a/back/src/Model/Websocket/Identificable.ts +++ b/back/src/Model/Websocket/Identificable.ts @@ -1,3 +1,3 @@ export interface Identificable { - userId: string; + userId: number; } diff --git a/back/src/Model/Websocket/MessageUserJoined.ts b/back/src/Model/Websocket/MessageUserJoined.ts index 9e993dd3..9ae7ab2c 100644 --- a/back/src/Model/Websocket/MessageUserJoined.ts +++ b/back/src/Model/Websocket/MessageUserJoined.ts @@ -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) { } } diff --git a/back/src/Model/Websocket/MessageUserMoved.ts b/back/src/Model/Websocket/MessageUserMoved.ts index 283c011d..e08be81b 100644 --- a/back/src/Model/Websocket/MessageUserMoved.ts +++ b/back/src/Model/Websocket/MessageUserMoved.ts @@ -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) { } } diff --git a/back/src/Model/Websocket/MessageUserPosition.ts b/back/src/Model/Websocket/MessageUserPosition.ts index 03fc6f09..08035997 100644 --- a/back/src/Model/Websocket/MessageUserPosition.ts +++ b/back/src/Model/Websocket/MessageUserPosition.ts @@ -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) { } } diff --git a/back/src/Model/Websocket/UserInGroupInterface.ts b/back/src/Model/Websocket/UserInGroupInterface.ts index 26cc5fd4..087f519e 100644 --- a/back/src/Model/Websocket/UserInGroupInterface.ts +++ b/back/src/Model/Websocket/UserInGroupInterface.ts @@ -1,5 +1,5 @@ export interface UserInGroupInterface { - userId: string, + userId: number, name: string, initiator: boolean } diff --git a/back/src/Model/Websocket/UserMovesMessage.ts b/back/src/Model/Websocket/UserMovesMessage.ts deleted file mode 100644 index 2277d4c4..00000000 --- a/back/src/Model/Websocket/UserMovesMessage.ts +++ /dev/null @@ -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; diff --git a/back/src/Model/Websocket/WebRtcSignalMessage.ts b/back/src/Model/Websocket/WebRtcSignalMessage.ts index 5a0dd1af..c0f5f8ab 100644 --- a/back/src/Model/Websocket/WebRtcSignalMessage.ts +++ b/back/src/Model/Websocket/WebRtcSignalMessage.ts @@ -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; diff --git a/back/src/Model/World.ts b/back/src/Model/World.ts index 6e739c02..dc3dcd07 100644 --- a/back/src/Model/World.ts +++ b/back/src/Model/World.ts @@ -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; + private readonly users: Map; private readonly groups: Set; private readonly connectCallback: ConnectCallback; @@ -37,7 +37,7 @@ export class World { onMoves: MovesCallback, onLeaves: LeavesCallback) { - this.users = new Map(); + this.users = new Map(); this.groups = new Set(); this.connectCallback = connectCallback; this.disconnectCallback = disconnectCallback; @@ -51,7 +51,7 @@ export class World { return Array.from(this.groups.values()); } - public getUsers(): Map { + public getUsers(): Map { return this.users; } diff --git a/back/tests/PositionNotifierTest.ts b/back/tests/PositionNotifierTest.ts index dd1e3b4b..643dd938 100644 --- a/back/tests/PositionNotifierTest.ts +++ b/back/tests/PositionNotifierTest.ts @@ -32,14 +32,14 @@ describe("PositionNotifier", () => { leaveTriggered = true; }); - const user1 = new User("1", { + const user1 = new User(1, { x: 500, y: 500, moving: false, direction: 'down' }, false); - const user2 = new User("2", { + const user2 = new User(2, { x: -9999, y: -9999, moving: false, @@ -110,14 +110,14 @@ describe("PositionNotifier", () => { leaveTriggered = true; }); - const user1 = new User("1", { + const user1 = new User(1, { x: 500, y: 500, moving: false, direction: 'down' }, false); - const user2 = new User("2", { + const user2 = new User(2, { x: -9999, y: -9999, moving: false, diff --git a/back/tests/WorldTest.ts b/back/tests/WorldTest.ts index 63e46928..9afef228 100644 --- a/back/tests/WorldTest.ts +++ b/back/tests/WorldTest.ts @@ -6,55 +6,55 @@ import { Group } from "../src/Model/Group"; describe("World", () => { it("should connect user1 and user2", () => { let connectCalledNumber: number = 0; - const connect: ConnectCallback = (user: string, group: Group): void => { + const connect: ConnectCallback = (user: number, group: Group): void => { connectCalledNumber++; } - const disconnect: DisconnectCallback = (user: string, group: Group): void => { + const disconnect: DisconnectCallback = (user: number, group: Group): void => { } const world = new World(connect, disconnect, 160, 160, () => {}, () => {}, () => {}); - world.join({ userId: "foo" }, new Point(100, 100)); + world.join({ userId: 1 }, new Point(100, 100)); - world.join({ userId: "bar" }, new Point(500, 100)); + world.join({ userId: 2 }, new Point(500, 100)); - world.updatePosition({ userId: "bar" }, new Point(261, 100)); + world.updatePosition({ userId: 2 }, new Point(261, 100)); expect(connectCalledNumber).toBe(0); - world.updatePosition({ userId: "bar" }, new Point(101, 100)); + world.updatePosition({ userId: 2 }, new Point(101, 100)); expect(connectCalledNumber).toBe(2); - world.updatePosition({ userId: "bar" }, new Point(102, 100)); + world.updatePosition({ userId: 2 }, new Point(102, 100)); expect(connectCalledNumber).toBe(2); }); it("should connect 3 users", () => { let connectCalled: boolean = false; - const connect: ConnectCallback = (user: string, group: Group): void => { + const connect: ConnectCallback = (user: number, group: Group): void => { connectCalled = true; } - const disconnect: DisconnectCallback = (user: string, group: Group): void => { + const disconnect: DisconnectCallback = (user: number, group: Group): void => { } const world = new World(connect, disconnect, 160, 160, () => {}, () => {}, () => {}); - world.join({ userId: "foo" }, new Point(100, 100)); + world.join({ userId: 1 }, new Point(100, 100)); - world.join({ userId: "bar" }, new Point(200, 100)); + world.join({ userId: 2 }, new Point(200, 100)); expect(connectCalled).toBe(true); connectCalled = false; // baz joins at the outer limit of the group - world.join({ userId: "baz" }, new Point(311, 100)); + world.join({ userId: 3 }, new Point(311, 100)); expect(connectCalled).toBe(false); - world.updatePosition({ userId: "baz" }, new Point(309, 100)); + world.updatePosition({ userId: 3 }, new Point(309, 100)); expect(connectCalled).toBe(true); }); @@ -62,27 +62,27 @@ describe("World", () => { it("should disconnect user1 and user2", () => { let connectCalled: boolean = false; let disconnectCallNumber: number = 0; - const connect: ConnectCallback = (user: string, group: Group): void => { + const connect: ConnectCallback = (user: number, group: Group): void => { connectCalled = true; } - const disconnect: DisconnectCallback = (user: string, group: Group): void => { + const disconnect: DisconnectCallback = (user: number, group: Group): void => { disconnectCallNumber++; } const world = new World(connect, disconnect, 160, 160, () => {}, () => {}, () => {}); - world.join({ userId: "foo" }, new Point(100, 100)); + world.join({ userId: 1 }, new Point(100, 100)); - world.join({ userId: "bar" }, new Point(259, 100)); + world.join({ userId: 2 }, new Point(259, 100)); expect(connectCalled).toBe(true); expect(disconnectCallNumber).toBe(0); - world.updatePosition({ userId: "bar" }, new Point(100+160+160+1, 100)); + world.updatePosition({ userId: 2 }, new Point(100+160+160+1, 100)); expect(disconnectCallNumber).toBe(2); - world.updatePosition({ userId: "bar" }, new Point(262, 100)); + world.updatePosition({ userId: 2 }, new Point(262, 100)); expect(disconnectCallNumber).toBe(2); }); diff --git a/docker-compose.yaml b/docker-compose.yaml index ce16a31b..fce76204 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -67,7 +67,7 @@ services: - "traefik.http.routers.maps-ssl.service=maps" back: - image: thecodingmachine/workadventure-back-base:latest + image: thecodingmachine/nodejs:12 command: yarn dev #command: yarn run profile environment: @@ -103,3 +103,11 @@ services: - "traefik.http.routers.website-ssl.entryPoints=websecure" - "traefik.http.routers.website-ssl.tls=true" - "traefik.http.routers.website-ssl.service=website" + + messages: + image: thecodingmachine/workadventure-back-base:latest + environment: + STARTUP_COMMAND_1: yarn install + STARTUP_COMMAND_2: yarn run proto:watch + volumes: + - ./messages:/usr/src/app diff --git a/front/src/Connection.ts b/front/src/Connection.ts index 7a60cfba..b6d4c6ee 100644 --- a/front/src/Connection.ts +++ b/front/src/Connection.ts @@ -1,13 +1,19 @@ import Axios from "axios"; import {API_URL} from "./Enum/EnvironmentVariable"; import {MessageUI} from "./Logger/MessageUI"; -import {SetPlayerDetailsMessage} from "../../messages/generated/src/proto/messages_pb" +import { + PositionMessage, + SetPlayerDetailsMessage, + UserMovesMessage, + ViewportMessage +} from "../../messages/generated/messages_pb" const SocketIo = require('socket.io-client'); import Socket = SocketIOClient.Socket; import {PlayerAnimationNames} from "./Phaser/Player/Animation"; import {UserSimplePeerInterface} from "./WebRtc/SimplePeer"; import {SignalData} from "simple-peer"; +import Direction = PositionMessage.Direction; enum EventMessage{ WEBRTC_SIGNAL = "webrtc-signal", @@ -46,19 +52,19 @@ export class Point implements PointInterface{ } export interface MessageUserPositionInterface { - userId: string; + userId: number; name: string; characterLayers: string[]; position: PointInterface; } export interface MessageUserMovedInterface { - userId: string; + userId: number; position: PointInterface; } export interface MessageUserJoined { - userId: string; + userId: number; name: string; characterLayers: string[]; position: PointInterface @@ -80,16 +86,16 @@ export interface WebRtcStartMessageInterface { } export interface WebRtcDisconnectMessageInterface { - userId: string + userId: number } export interface WebRtcSignalSentMessageInterface { - receiverId: string, + receiverId: number, signal: SignalData } export interface WebRtcSignalReceivedMessageInterface { - userId: string, + userId: number, signal: SignalData } @@ -105,11 +111,6 @@ export interface ViewportInterface { bottom: number, } -export interface UserMovesInterface { - position: PositionInterface, - viewport: ViewportInterface, -} - export interface BatchedMessageInterface { event: string, payload: unknown @@ -130,7 +131,7 @@ export interface RoomJoinedMessageInterface { export class Connection implements Connection { private readonly socket: Socket; - private userId: string|null = null; + private userId: number|null = null; private constructor(token: string) { @@ -173,7 +174,7 @@ export class Connection implements Connection { const message = new SetPlayerDetailsMessage(); message.setName(name); message.setCharacterlayersList(characterLayersSelected); - connection.socket.emit(EventMessage.SET_PLAYER_DETAILS, message.serializeBinary().buffer, (id: string) => { + connection.socket.emit(EventMessage.SET_PLAYER_DETAILS, message.serializeBinary().buffer, (id: number) => { connection.userId = id; }); @@ -214,7 +215,40 @@ export class Connection implements Connection { return; } const point = new Point(x, y, direction, moving); - this.socket.emit(EventMessage.USER_POSITION, { position: point, viewport } as UserMovesInterface); + const positionMessage = new PositionMessage(); + positionMessage.setX(Math.floor(x)); + positionMessage.setY(Math.floor(y)); + let directionEnum: PositionMessage.DirectionMap[keyof PositionMessage.DirectionMap]; + switch (direction) { + case 'up': + directionEnum = Direction.UP; + break; + case 'down': + directionEnum = Direction.DOWN; + break; + case 'left': + directionEnum = Direction.LEFT; + break; + case 'right': + directionEnum = Direction.RIGHT; + break; + default: + throw new Error("Unexpected direction"); + } + positionMessage.setDirection(directionEnum); + positionMessage.setMoving(moving); + + const viewportMessage = new ViewportMessage(); + viewportMessage.setLeft(Math.floor(viewport.left)); + viewportMessage.setRight(Math.floor(viewport.right)); + viewportMessage.setTop(Math.floor(viewport.top)); + viewportMessage.setBottom(Math.floor(viewport.bottom)); + + const userMovesMessage = new UserMovesMessage(); + userMovesMessage.setPosition(positionMessage); + userMovesMessage.setViewport(viewportMessage); + + this.socket.emit(EventMessage.USER_POSITION, userMovesMessage.serializeBinary().buffer); } public setSilent(silent: boolean): void { @@ -233,7 +267,7 @@ export class Connection implements Connection { this.socket.on(EventMessage.USER_MOVED, callback); } - public onUserLeft(callback: (userId: string) => void): void { + public onUserLeft(callback: (userId: number) => void): void { this.socket.on(EventMessage.USER_LEFT, callback); } @@ -249,14 +283,14 @@ export class Connection implements Connection { this.socket.on(EventMessage.CONNECT_ERROR, callback) } - public sendWebrtcSignal(signal: unknown, receiverId : string) { + public sendWebrtcSignal(signal: unknown, receiverId: number) { return this.socket.emit(EventMessage.WEBRTC_SIGNAL, { receiverId: receiverId, signal: signal } as WebRtcSignalSentMessageInterface); } - public sendWebrtcScreenSharingSignal(signal: unknown, receiverId : string) { + public sendWebrtcScreenSharingSignal(signal: unknown, receiverId: number) { return this.socket.emit(EventMessage.WEBRTC_SCREEN_SHARING_SIGNAL, { receiverId: receiverId, signal: signal @@ -286,7 +320,7 @@ export class Connection implements Connection { } - public getUserId(): string|null { + public getUserId(): number|null { return this.userId; } diff --git a/front/src/Phaser/Entity/RemotePlayer.ts b/front/src/Phaser/Entity/RemotePlayer.ts index 6764ff59..00a3e4c4 100644 --- a/front/src/Phaser/Entity/RemotePlayer.ts +++ b/front/src/Phaser/Entity/RemotePlayer.ts @@ -6,10 +6,10 @@ import {Character} from "../Entity/Character"; * Class representing the sprite of a remote player (a player that plays on another computer) */ export class RemotePlayer extends Character { - userId: string; + userId: number; constructor( - userId: string, + userId: number, Scene: GameScene, x: number, y: number, diff --git a/front/src/Phaser/Game/AddPlayerInterface.ts b/front/src/Phaser/Game/AddPlayerInterface.ts index a3f50de3..d0ed2dad 100644 --- a/front/src/Phaser/Game/AddPlayerInterface.ts +++ b/front/src/Phaser/Game/AddPlayerInterface.ts @@ -1,7 +1,7 @@ import {PointInterface} from "../../Connection"; export interface AddPlayerInterface { - userId: string; + userId: number; name: string; characterLayers: string[]; position: PointInterface; diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index ad378bc3..7695294e 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -63,7 +63,7 @@ interface AddPlayerEventInterface { interface RemovePlayerEventInterface { type: 'RemovePlayerEvent' - userId: string + userId: number } interface UserMovedEventInterface { @@ -86,7 +86,7 @@ export class GameScene extends Phaser.Scene implements CenterListener { Terrains : Array; CurrentPlayer!: CurrentGamerInterface; MapPlayers!: Phaser.Physics.Arcade.Group; - MapPlayersByKey : Map = new Map(); + MapPlayersByKey : Map = new Map(); Map!: Phaser.Tilemaps.Tilemap; Layers!: Array; Objects!: Array; @@ -217,7 +217,7 @@ export class GameScene extends Phaser.Scene implements CenterListener { this.updatePlayerPosition(message); }); - connection.onUserLeft((userId: string) => { + connection.onUserLeft((userId: number) => { this.removePlayer(userId); }); @@ -271,7 +271,7 @@ export class GameScene extends Phaser.Scene implements CenterListener { self.presentationModeSprite.setVisible(true); self.chatModeSprite.setVisible(true); }, - onDisconnect(userId: string) { + onDisconnect(userId: number) { if (self.simplePeer.getNbConnections() === 0) { self.presentationModeSprite.setVisible(false); self.chatModeSprite.setVisible(false); @@ -918,7 +918,7 @@ export class GameScene extends Phaser.Scene implements CenterListener { // Let's move all users const updatedPlayersPositions = this.playersPositionInterpolator.getUpdatedPositions(time); - updatedPlayersPositions.forEach((moveEvent: HasMovedEvent, userId: string) => { + updatedPlayersPositions.forEach((moveEvent: HasMovedEvent, userId: number) => { const player : RemotePlayer | undefined = this.MapPlayersByKey.get(userId); if (player === undefined) { throw new Error('Cannot find player with ID "' + userId +'"'); @@ -973,7 +973,7 @@ export class GameScene extends Phaser.Scene implements CenterListener { player.destroy(); this.MapPlayers.remove(player); }); - this.MapPlayersByKey = new Map(); + this.MapPlayersByKey = new Map(); // load map usersPosition.forEach((userPosition : MessageUserPositionInterface) => { @@ -1030,14 +1030,14 @@ export class GameScene extends Phaser.Scene implements CenterListener { /** * Called by the connexion when a player is removed from the map */ - public removePlayer(userId: string) { + public removePlayer(userId: number) { this.pendingEvents.enqueue({ type: "RemovePlayerEvent", userId }); } - private doRemovePlayer(userId: string) { + private doRemovePlayer(userId: number) { const player = this.MapPlayersByKey.get(userId); if (player === undefined) { console.error('Cannot find user with id ', userId); diff --git a/front/src/Phaser/Game/PlayersPositionInterpolator.ts b/front/src/Phaser/Game/PlayersPositionInterpolator.ts index 080c8a17..3ac87397 100644 --- a/front/src/Phaser/Game/PlayersPositionInterpolator.ts +++ b/front/src/Phaser/Game/PlayersPositionInterpolator.ts @@ -6,19 +6,19 @@ import {PlayerMovement} from "./PlayerMovement"; import {HasMovedEvent} from "./GameManager"; export class PlayersPositionInterpolator { - playerMovements: Map = new Map(); + playerMovements: Map = new Map(); - updatePlayerPosition(userId: string, playerMovement: PlayerMovement) : void { + updatePlayerPosition(userId: number, playerMovement: PlayerMovement) : void { this.playerMovements.set(userId, playerMovement); } - removePlayer(userId: string): void { + removePlayer(userId: number): void { this.playerMovements.delete(userId); } - getUpdatedPositions(tick: number) : Map { - const positions = new Map(); - this.playerMovements.forEach((playerMovement: PlayerMovement, userId: string) => { + getUpdatedPositions(tick: number) : Map { + const positions = new Map(); + this.playerMovements.forEach((playerMovement: PlayerMovement, userId: number) => { if (playerMovement.isOutdated(tick)) { //console.log("outdated") this.playerMovements.delete(userId); diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index 153d660b..6d8e5c3d 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -343,7 +343,7 @@ export class MediaManager { * * @param userId */ - addActiveVideo(userId : string, userName: string = ""){ + addActiveVideo(userId: string, userName: string = ""){ this.webrtcInAudio.play(); userName = userName.toUpperCase(); @@ -368,7 +368,7 @@ export class MediaManager { * * @param userId */ - addScreenSharingActiveVideo(userId : string, divImportance: DivImportance = DivImportance.Important){ + addScreenSharingActiveVideo(userId: string, divImportance: DivImportance = DivImportance.Important){ //this.webrtcInAudio.play(); userId = `screen-sharing-${userId}`; @@ -387,7 +387,7 @@ export class MediaManager { * * @param userId */ - disabledMicrophoneByUserId(userId: string){ + disabledMicrophoneByUserId(userId: number){ const element = document.getElementById(`microphone-${userId}`); if(!element){ return; @@ -399,7 +399,7 @@ export class MediaManager { * * @param userId */ - enabledMicrophoneByUserId(userId: string){ + enabledMicrophoneByUserId(userId: number){ const element = document.getElementById(`microphone-${userId}`); if(!element){ return; @@ -411,7 +411,7 @@ export class MediaManager { * * @param userId */ - disabledVideoByUserId(userId: string) { + disabledVideoByUserId(userId: number) { let element = document.getElementById(`${userId}`); if (element) { element.style.opacity = "0"; @@ -426,7 +426,7 @@ export class MediaManager { * * @param userId */ - enabledVideoByUserId(userId: string){ + enabledVideoByUserId(userId: number){ let element = document.getElementById(`${userId}`); if(element){ element.style.opacity = "1"; @@ -442,7 +442,7 @@ export class MediaManager { * @param userId * @param stream */ - addStreamRemoteVideo(userId : string, stream : MediaStream){ + addStreamRemoteVideo(userId: string, stream : MediaStream){ const remoteVideo = this.remoteVideo.get(userId); if (remoteVideo === undefined) { console.error('Unable to find video for ', userId); @@ -450,7 +450,7 @@ export class MediaManager { } remoteVideo.srcObject = stream; } - addStreamRemoteScreenSharing(userId : string, stream : MediaStream){ + addStreamRemoteScreenSharing(userId: string, stream : MediaStream){ // In the case of screen sharing (going both ways), we may need to create the HTML element if it does not exist yet const remoteVideo = this.remoteVideo.get(`screen-sharing-${userId}`); if (remoteVideo === undefined) { @@ -464,15 +464,15 @@ export class MediaManager { * * @param userId */ - removeActiveVideo(userId : string){ + removeActiveVideo(userId: string){ layoutManager.remove(userId); this.remoteVideo.delete(userId); } - removeActiveScreenSharingVideo(userId : string) { + removeActiveScreenSharingVideo(userId: string) { this.removeActiveVideo(`screen-sharing-${userId}`) } - isConnecting(userId : string): void { + isConnecting(userId: string): void { const connectingSpinnerDiv = this.getSpinner(userId); if (connectingSpinnerDiv === null) { return; @@ -480,7 +480,7 @@ export class MediaManager { connectingSpinnerDiv.style.display = 'block'; } - isConnected(userId : string): void { + isConnected(userId: string): void { const connectingSpinnerDiv = this.getSpinner(userId); if (connectingSpinnerDiv === null) { return; @@ -488,7 +488,7 @@ export class MediaManager { connectingSpinnerDiv.style.display = 'none'; } - isError(userId : string): void { + isError(userId: string): void { console.log("isError", `div-${userId}`); const element = document.getElementById(`div-${userId}`); if(!element){ @@ -500,12 +500,12 @@ export class MediaManager { } errorDiv.style.display = 'block'; } - isErrorScreenSharing(userId : string): void { + isErrorScreenSharing(userId: string): void { this.isError(`screen-sharing-${userId}`); } - private getSpinner(userId : string): HTMLDivElement|null { + private getSpinner(userId: string): HTMLDivElement|null { const element = document.getElementById(`div-${userId}`); if(!element){ return null; diff --git a/front/src/WebRtc/ScreenSharingPeer.ts b/front/src/WebRtc/ScreenSharingPeer.ts index 8857274e..9c2022a6 100644 --- a/front/src/WebRtc/ScreenSharingPeer.ts +++ b/front/src/WebRtc/ScreenSharingPeer.ts @@ -14,7 +14,7 @@ export class ScreenSharingPeer extends Peer { */ private isReceivingStream:boolean = false; - constructor(private userId: string, initiator: boolean, private connection: Connection) { + constructor(private userId: number, initiator: boolean, private connection: Connection) { super({ initiator: initiator ? initiator : false, reconnectTimer: 10000, @@ -52,7 +52,7 @@ export class ScreenSharingPeer extends Peer { if (message.streamEnded !== true) { console.error('Unexpected message on screen sharing peer connection'); } - mediaManager.removeActiveScreenSharingVideo(this.userId); + mediaManager.removeActiveScreenSharingVideo("" + this.userId); }); // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -63,7 +63,7 @@ export class ScreenSharingPeer extends Peer { this.on('connect', () => { // FIXME: we need to put the loader on the screen sharing connection - mediaManager.isConnected(this.userId); + mediaManager.isConnected("" + this.userId); console.info(`connect => ${this.userId}`); }); @@ -86,10 +86,10 @@ export class ScreenSharingPeer extends Peer { //console.log(`ScreenSharingPeer::stream => ${this.userId}`, stream); //console.log(`stream => ${this.userId} => `, stream); if(!stream){ - mediaManager.removeActiveScreenSharingVideo(this.userId); + mediaManager.removeActiveScreenSharingVideo("" + this.userId); this.isReceivingStream = false; } else { - mediaManager.addStreamRemoteScreenSharing(this.userId, stream); + mediaManager.addStreamRemoteScreenSharing("" + this.userId, stream); this.isReceivingStream = true; } } @@ -100,7 +100,7 @@ export class ScreenSharingPeer extends Peer { public destroy(error?: Error): void { try { - mediaManager.removeActiveScreenSharingVideo(this.userId); + mediaManager.removeActiveScreenSharingVideo("" + this.userId); // FIXME: I don't understand why "Closing connection with" message is displayed TWICE before "Nb users in peerConnectionArray" // I do understand the method closeConnection is called twice, but I don't understand how they manage to run in parallel. //console.log('Closing connection with '+userId); diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index f388b2ec..ac603756 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -16,7 +16,7 @@ import {VideoPeer} from "./VideoPeer"; const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer'); export interface UserSimplePeerInterface{ - userId: string; + userId: number; name?: string; initiator?: boolean; } @@ -24,7 +24,7 @@ export interface UserSimplePeerInterface{ export interface PeerConnectionListener { onConnect(user: UserSimplePeerInterface): void; - onDisconnect(userId: string): void; + onDisconnect(userId: number): void; } /** @@ -35,8 +35,8 @@ export class SimplePeer { private WebRtcRoomId: string; private Users: Array = new Array(); - private PeerScreenSharingConnectionArray: Map = new Map(); - private PeerConnectionArray: Map = new Map(); + private PeerScreenSharingConnectionArray: Map = new Map(); + private PeerConnectionArray: Map = new Map(); private readonly sendLocalVideoStreamCallback: UpdatedLocalStreamCallback; private readonly sendLocalScreenSharingStreamCallback: StartScreenSharingCallback; private readonly stopLocalScreenSharingStreamCallback: StopScreenSharingCallback; @@ -140,8 +140,8 @@ export class SimplePeer { } } - mediaManager.removeActiveVideo(user.userId); - mediaManager.addActiveVideo(user.userId, name); + mediaManager.removeActiveVideo("" + user.userId); + mediaManager.addActiveVideo("" + user.userId, name); const peer = new VideoPeer(user.userId, user.initiator ? user.initiator : false, this.Connection); // When a connection is established to a video stream, and if a screen sharing is taking place, @@ -171,8 +171,8 @@ export class SimplePeer { // We should display the screen sharing ONLY if we are not initiator if (!user.initiator) { - mediaManager.removeActiveScreenSharingVideo(user.userId); - mediaManager.addScreenSharingActiveVideo(user.userId); + mediaManager.removeActiveScreenSharingVideo("" + user.userId); + mediaManager.addScreenSharingActiveVideo("" + user.userId); } const peer = new ScreenSharingPeer(user.userId, user.initiator ? user.initiator : false, this.Connection); @@ -189,7 +189,7 @@ export class SimplePeer { * * @param userId */ - private closeConnection(userId : string) { + private closeConnection(userId : number) { try { //mediaManager.removeActiveVideo(userId); const peer = this.PeerConnectionArray.get(userId); @@ -217,9 +217,9 @@ export class SimplePeer { * * @param userId */ - private closeScreenSharingConnection(userId : string) { + private closeScreenSharingConnection(userId : number) { try { - mediaManager.removeActiveScreenSharingVideo(userId); + mediaManager.removeActiveScreenSharingVideo("" + userId); const peer = this.PeerScreenSharingConnectionArray.get(userId); if (peer === undefined) { console.warn("Tried to close connection for user "+userId+" but could not find user") @@ -293,7 +293,7 @@ export class SimplePeer { * * @param userId */ - private pushVideoToRemoteUser(userId : string) { + private pushVideoToRemoteUser(userId : number) { try { const PeerConnection = this.PeerConnectionArray.get(userId); if (!PeerConnection) { @@ -314,7 +314,7 @@ export class SimplePeer { } } - private pushScreenSharingToRemoteUser(userId : string) { + private pushScreenSharingToRemoteUser(userId : number) { const PeerConnection = this.PeerScreenSharingConnectionArray.get(userId); if (!PeerConnection) { throw new Error('While pushing screen sharing, cannot find user with ID ' + userId); @@ -359,7 +359,7 @@ export class SimplePeer { } } - private sendLocalScreenSharingStreamToUser(userId: string): void { + private sendLocalScreenSharingStreamToUser(userId: number): void { // If a connection already exists with user (because it is already sharing a screen with us... let's use this connection) if (this.PeerScreenSharingConnectionArray.has(userId)) { this.pushScreenSharingToRemoteUser(userId); @@ -376,7 +376,7 @@ export class SimplePeer { } } - private stopLocalScreenSharingStreamToUser(userId: string, stream: MediaStream): void { + private stopLocalScreenSharingStreamToUser(userId: number, stream: MediaStream): void { const PeerConnectionScreenSharing = this.PeerScreenSharingConnectionArray.get(userId); if (!PeerConnectionScreenSharing) { throw new Error('Weird, screen sharing connection to user ' + userId + 'not found') diff --git a/front/src/WebRtc/VideoPeer.ts b/front/src/WebRtc/VideoPeer.ts index 33422433..e046ffe2 100644 --- a/front/src/WebRtc/VideoPeer.ts +++ b/front/src/WebRtc/VideoPeer.ts @@ -9,7 +9,7 @@ const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer'); * A peer connection used to transmit video / audio signals between 2 peers. */ export class VideoPeer extends Peer { - constructor(private userId: string, initiator: boolean, private connection: Connection) { + constructor(private userId: number, initiator: boolean, private connection: Connection) { super({ initiator: initiator ? initiator : false, reconnectTimer: 10000, @@ -63,11 +63,11 @@ export class VideoPeer extends Peer { // eslint-disable-next-line @typescript-eslint/no-explicit-any this.on('error', (err: any) => { console.error(`error => ${this.userId} => ${err.code}`, err); - mediaManager.isError(userId); + mediaManager.isError("" + userId); }); this.on('connect', () => { - mediaManager.isConnected(this.userId); + mediaManager.isConnected("" + this.userId); console.info(`connect => ${this.userId}`); }); @@ -108,7 +108,7 @@ export class VideoPeer extends Peer { mediaManager.disabledVideoByUserId(this.userId); mediaManager.disabledMicrophoneByUserId(this.userId); } else { - mediaManager.addStreamRemoteVideo(this.userId, stream); + mediaManager.addStreamRemoteVideo("" + this.userId, stream); } } @@ -117,7 +117,7 @@ export class VideoPeer extends Peer { */ public destroy(error?: Error): void { try { - mediaManager.removeActiveVideo(this.userId); + mediaManager.removeActiveVideo("" + this.userId); // FIXME: I don't understand why "Closing connection with" message is displayed TWICE before "Nb users in peerConnectionArray" // I do understand the method closeConnection is called twice, but I don't understand how they manage to run in parallel. //console.log('Closing connection with '+userId); diff --git a/messages/generated/.gitignore b/messages/generated/.gitignore index 8eba6c8d..d6b7ef32 100644 --- a/messages/generated/.gitignore +++ b/messages/generated/.gitignore @@ -1 +1,2 @@ -src/ +* +!.gitignore diff --git a/messages/generated/src/proto/messages_pb.d.ts b/messages/generated/src/proto/messages_pb.d.ts deleted file mode 100644 index 4c700d90..00000000 --- a/messages/generated/src/proto/messages_pb.d.ts +++ /dev/null @@ -1,31 +0,0 @@ -// package: -// file: src/proto/messages.proto - -import * as jspb from "google-protobuf"; - -export class SetPlayerDetailsMessage extends jspb.Message { - getName(): string; - setName(value: string): void; - - clearCharacterlayersList(): void; - getCharacterlayersList(): Array; - setCharacterlayersList(value: Array): void; - addCharacterlayers(value: string, index?: number): string; - - serializeBinary(): Uint8Array; - toObject(includeInstance?: boolean): SetPlayerDetailsMessage.AsObject; - static toObject(includeInstance: boolean, msg: SetPlayerDetailsMessage): SetPlayerDetailsMessage.AsObject; - static extensions: {[key: number]: jspb.ExtensionFieldInfo}; - static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; - static serializeBinaryToWriter(message: SetPlayerDetailsMessage, writer: jspb.BinaryWriter): void; - static deserializeBinary(bytes: Uint8Array): SetPlayerDetailsMessage; - static deserializeBinaryFromReader(message: SetPlayerDetailsMessage, reader: jspb.BinaryReader): SetPlayerDetailsMessage; -} - -export namespace SetPlayerDetailsMessage { - export type AsObject = { - name: string, - characterlayersList: Array, - } -} - diff --git a/messages/generated/src/proto/messages_pb.js b/messages/generated/src/proto/messages_pb.js deleted file mode 100644 index 27ffc622..00000000 --- a/messages/generated/src/proto/messages_pb.js +++ /dev/null @@ -1,223 +0,0 @@ -// source: src/proto/messages.proto -/** - * @fileoverview - * @enhanceable - * @suppress {messageConventions} JS Compiler reports an error if a variable or - * field starts with 'MSG_' and isn't a translatable message. - * @public - */ -// GENERATED CODE -- DO NOT EDIT! - -var jspb = require('google-protobuf'); -var goog = jspb; -var global = Function('return this')(); - -goog.exportSymbol('proto.SetPlayerDetailsMessage', null, global); -/** - * Generated by JsPbCodeGenerator. - * @param {Array=} opt_data Optional initial data array, typically from a - * server response, or constructed directly in Javascript. The array is used - * in place and becomes part of the constructed object. It is not cloned. - * If no data is provided, the constructed object will be empty, but still - * valid. - * @extends {jspb.Message} - * @constructor - */ -proto.SetPlayerDetailsMessage = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, proto.SetPlayerDetailsMessage.repeatedFields_, null); -}; -goog.inherits(proto.SetPlayerDetailsMessage, jspb.Message); -if (goog.DEBUG && !COMPILED) { - /** - * @public - * @override - */ - proto.SetPlayerDetailsMessage.displayName = 'proto.SetPlayerDetailsMessage'; -} - -/** - * List of repeated fields within this message type. - * @private {!Array} - * @const - */ -proto.SetPlayerDetailsMessage.repeatedFields_ = [2]; - - - -if (jspb.Message.GENERATE_TO_OBJECT) { -/** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} - */ -proto.SetPlayerDetailsMessage.prototype.toObject = function(opt_includeInstance) { - return proto.SetPlayerDetailsMessage.toObject(opt_includeInstance, this); -}; - - -/** - * Static version of the {@see toObject} method. - * @param {boolean|undefined} includeInstance Deprecated. Whether to include - * the JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @param {!proto.SetPlayerDetailsMessage} msg The msg instance to transform. - * @return {!Object} - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.SetPlayerDetailsMessage.toObject = function(includeInstance, msg) { - var f, obj = { - name: jspb.Message.getFieldWithDefault(msg, 1, ""), - characterlayersList: (f = jspb.Message.getRepeatedField(msg, 2)) == null ? undefined : f - }; - - if (includeInstance) { - obj.$jspbMessageInstance = msg; - } - return obj; -}; -} - - -/** - * Deserializes binary data (in protobuf wire format). - * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.SetPlayerDetailsMessage} - */ -proto.SetPlayerDetailsMessage.deserializeBinary = function(bytes) { - var reader = new jspb.BinaryReader(bytes); - var msg = new proto.SetPlayerDetailsMessage; - return proto.SetPlayerDetailsMessage.deserializeBinaryFromReader(msg, reader); -}; - - -/** - * Deserializes binary data (in protobuf wire format) from the - * given reader into the given message object. - * @param {!proto.SetPlayerDetailsMessage} msg The message object to deserialize into. - * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.SetPlayerDetailsMessage} - */ -proto.SetPlayerDetailsMessage.deserializeBinaryFromReader = function(msg, reader) { - while (reader.nextField()) { - if (reader.isEndGroup()) { - break; - } - var field = reader.getFieldNumber(); - switch (field) { - case 1: - var value = /** @type {string} */ (reader.readString()); - msg.setName(value); - break; - case 2: - var value = /** @type {string} */ (reader.readString()); - msg.addCharacterlayers(value); - break; - default: - reader.skipField(); - break; - } - } - return msg; -}; - - -/** - * Serializes the message to binary data (in protobuf wire format). - * @return {!Uint8Array} - */ -proto.SetPlayerDetailsMessage.prototype.serializeBinary = function() { - var writer = new jspb.BinaryWriter(); - proto.SetPlayerDetailsMessage.serializeBinaryToWriter(this, writer); - return writer.getResultBuffer(); -}; - - -/** - * Serializes the given message to binary data (in protobuf wire - * format), writing to the given BinaryWriter. - * @param {!proto.SetPlayerDetailsMessage} message - * @param {!jspb.BinaryWriter} writer - * @suppress {unusedLocalVariables} f is only used for nested messages - */ -proto.SetPlayerDetailsMessage.serializeBinaryToWriter = function(message, writer) { - var f = undefined; - f = message.getName(); - if (f.length > 0) { - writer.writeString( - 1, - f - ); - } - f = message.getCharacterlayersList(); - if (f.length > 0) { - writer.writeRepeatedString( - 2, - f - ); - } -}; - - -/** - * optional string name = 1; - * @return {string} - */ -proto.SetPlayerDetailsMessage.prototype.getName = function() { - return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); -}; - - -/** - * @param {string} value - * @return {!proto.SetPlayerDetailsMessage} returns this - */ -proto.SetPlayerDetailsMessage.prototype.setName = function(value) { - return jspb.Message.setProto3StringField(this, 1, value); -}; - - -/** - * repeated string characterLayers = 2; - * @return {!Array} - */ -proto.SetPlayerDetailsMessage.prototype.getCharacterlayersList = function() { - return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 2)); -}; - - -/** - * @param {!Array} value - * @return {!proto.SetPlayerDetailsMessage} returns this - */ -proto.SetPlayerDetailsMessage.prototype.setCharacterlayersList = function(value) { - return jspb.Message.setField(this, 2, value || []); -}; - - -/** - * @param {string} value - * @param {number=} opt_index - * @return {!proto.SetPlayerDetailsMessage} returns this - */ -proto.SetPlayerDetailsMessage.prototype.addCharacterlayers = function(value, opt_index) { - return jspb.Message.addToRepeatedField(this, 2, value, opt_index); -}; - - -/** - * Clears the list making it empty but non-null. - * @return {!proto.SetPlayerDetailsMessage} returns this - */ -proto.SetPlayerDetailsMessage.prototype.clearCharacterlayersList = function() { - return this.setCharacterlayersList([]); -}; - - -goog.object.extend(exports, proto); diff --git a/messages/messages.proto b/messages/messages.proto index ea9fafe2..cc0449e9 100644 --- a/messages/messages.proto +++ b/messages/messages.proto @@ -1,6 +1,41 @@ syntax = "proto3"; +/*********** CLIENT TO SERVER MESSAGES *************/ + message SetPlayerDetailsMessage { string name = 1; repeated string characterLayers = 2; } + +message PositionMessage { + int32 x = 1; + int32 y = 2; + enum Direction { + UP = 0; + RIGHT = 1; + DOWN = 2; + LEFT = 3; + } + Direction direction = 3; + bool moving = 4; +} + +message ViewportMessage { + int32 left = 1; + int32 top = 2; + int32 right = 3; + int32 bottom = 4; +} + +message UserMovesMessage { + PositionMessage position = 1; + ViewportMessage viewport = 2; +} + + +/*********** SERVER TO CLIENT MESSAGES *************/ + +message UserMovedMessage { + int32 userId = 1; + PositionMessage position = 2; +} diff --git a/messages/package.json b/messages/package.json index e2548499..b70d4900 100644 --- a/messages/package.json +++ b/messages/package.json @@ -4,8 +4,8 @@ "description": "", "main": "generated/src/proto/messages_pb.js", "scripts": { - "proto": "protoc --plugin=\"protoc-gen-ts=./node_modules/.bin/protoc-gen-ts\" --js_out=\"import_style=commonjs,binary:src/messages/generated\" --ts_out=\"src/messages/generated\" src/messages/messages.proto", - "proto:watch": "inotifywait -q -m -e close_write src/messages/messages.proto | while read -r filename event; do yarn run proto; done" + "proto": "protoc --plugin=\"protoc-gen-ts=./node_modules/.bin/protoc-gen-ts\" --js_out=\"import_style=commonjs,binary:generated\" --ts_out=\"generated\" messages.proto", + "proto:watch": "inotifywait -q -m -e close_write messages.proto | while read -r filename event; do yarn run proto; done" }, "repository": { "type": "git",