From bf070c33b5421cfbbdefa5f26306fb2f55bb3c80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Mon, 3 Jan 2022 15:23:28 +0100 Subject: [PATCH] Migrating front protobuf encode/decode to ts-proto lib The ts-proto lib has the huge advantage of producing code the "Typescript" way and not the "Java" way. In particular, for "oneof" types in protobuf, it is generating "ADT" that can be used to check if we forgot or not to deal with a type. --- .github/workflows/continuous_integration.yml | 2 +- .github/workflows/push-to-npm.yml | 2 +- front/Dockerfile | 5 +- front/package.json | 1 + front/src/Connexion/AdminMessagesService.ts | 6 +- front/src/Connexion/ConnectionManager.ts | 4 +- front/src/Connexion/ConnexionModels.ts | 36 +- front/src/Connexion/EmoteEventStream.ts | 17 - front/src/Connexion/RoomConnection.ts | 1068 +++++++++-------- front/src/Connexion/WorldFullMessageStream.ts | 12 - front/src/Messages/.gitignore | 1 - .../Messages/ts-proto-generated/.gitignore | 1 + front/src/Network/ProtobufClientUtils.ts | 20 +- front/src/Phaser/Game/EmoteManager.ts | 8 +- front/src/Phaser/Game/GameScene.ts | 56 +- .../src/Phaser/Game/SharedVariablesManager.ts | 2 +- front/src/Stores/PlayersStore.ts | 8 +- front/src/WebRtc/SimplePeer.ts | 14 +- front/yarn.lock | 143 ++- messages/package.json | 8 +- messages/ts-proto-generated/.gitignore | 2 + messages/yarn.lock | 75 +- 22 files changed, 865 insertions(+), 626 deletions(-) delete mode 100644 front/src/Connexion/EmoteEventStream.ts delete mode 100644 front/src/Connexion/WorldFullMessageStream.ts delete mode 100644 front/src/Messages/.gitignore create mode 100644 front/src/Messages/ts-proto-generated/.gitignore create mode 100644 messages/ts-proto-generated/.gitignore diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 1ad73027..ea67c3c1 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -39,7 +39,7 @@ jobs: working-directory: "messages" - name: "Build proto messages" - run: yarn run proto && yarn run copy-to-front && yarn run json-copy-to-front + run: yarn run ts-proto && yarn run copy-to-front-ts-proto && yarn run json-copy-to-front working-directory: "messages" - name: "Create index.html" diff --git a/.github/workflows/push-to-npm.yml b/.github/workflows/push-to-npm.yml index 71f2824f..571a16e6 100644 --- a/.github/workflows/push-to-npm.yml +++ b/.github/workflows/push-to-npm.yml @@ -36,7 +36,7 @@ jobs: working-directory: "messages" - name: "Build proto messages" - run: yarn run proto && yarn run copy-to-front && yarn run json-copy-to-front + run: yarn run ts-proto && yarn run copy-to-front-ts-proto && yarn run json-copy-to-front working-directory: "messages" - name: "Create index.html" diff --git a/front/Dockerfile b/front/Dockerfile index f781a37c..49cf6046 100644 --- a/front/Dockerfile +++ b/front/Dockerfile @@ -1,13 +1,14 @@ FROM node:14.15.4-buster-slim@sha256:cbae886186467bbfd274b82a234a1cdfbbd31201c2a6ee63a6893eefcf3c6e76 as builder WORKDIR /usr/src COPY messages . -RUN yarn install && yarn proto +RUN yarn install && yarn ts-proto # we are rebuilding on each deploy to cope with the PUSHER_URL environment URL FROM thecodingmachine/nodejs:14-apache COPY --chown=docker:docker front . -COPY --from=builder --chown=docker:docker /usr/src/generated /var/www/html/src/Messages/generated +COPY --from=builder --chown=docker:docker /usr/src/ts-proto-generated/protos /var/www/html/src/Messages/ts-proto-generated +RUN sed -i 's/import { Observable } from "rxjs";/import type { Observable } from "rxjs";/g' /var/www/html/src/Messages/ts-proto-generated/messages.ts COPY --from=builder --chown=docker:docker /usr/src/JsonMessages /var/www/html/src/Messages/JsonMessages # Removing the iframe.html file from the final image as this adds a XSS attack. diff --git a/front/package.json b/front/package.json index eae92cd2..935c254f 100644 --- a/front/package.json +++ b/front/package.json @@ -62,6 +62,7 @@ "simple-peer": "^9.11.0", "socket.io-client": "^2.3.0", "standardized-audio-context": "^25.2.4", + "ts-proto": "^1.96.0", "uuidv4": "^6.2.10" }, "scripts": { diff --git a/front/src/Connexion/AdminMessagesService.ts b/front/src/Connexion/AdminMessagesService.ts index 0b217760..4b7030ed 100644 --- a/front/src/Connexion/AdminMessagesService.ts +++ b/front/src/Connexion/AdminMessagesService.ts @@ -1,5 +1,5 @@ import { Subject } from "rxjs"; -import type { BanUserMessage, SendUserMessage } from "../Messages/generated/messages_pb"; +import type { BanUserMessage, SendUserMessage } from "../Messages/ts-proto-generated/messages"; export enum AdminMessageEventTypes { admin = "message", @@ -26,8 +26,8 @@ class AdminMessagesService { onSendusermessage(message: SendUserMessage | BanUserMessage) { this._messageStream.next({ - type: message.getType() as unknown as AdminMessageEventTypes, - text: message.getMessage(), + type: message.type as unknown as AdminMessageEventTypes, + text: message.message, }); } } diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 026cc20a..19750ee8 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -280,7 +280,7 @@ class ConnectionManager { reject(error); }); - connection.onConnectingError((event: CloseEvent) => { + connection.connectionErrorStream.subscribe((event: CloseEvent) => { console.log("An error occurred while connecting to socket server. Retrying"); reject( new Error( @@ -292,7 +292,7 @@ class ConnectionManager { ); }); - connection.onConnect((connect: OnConnectInterface) => { + connection.roomJoinedMessageStream.subscribe((connect: OnConnectInterface) => { resolve(connect); }); }).catch((err) => { diff --git a/front/src/Connexion/ConnexionModels.ts b/front/src/Connexion/ConnexionModels.ts index 6200e0c9..bf834a02 100644 --- a/front/src/Connexion/ConnexionModels.ts +++ b/front/src/Connexion/ConnexionModels.ts @@ -1,44 +1,12 @@ import type { SignalData } from "simple-peer"; import type { RoomConnection } from "./RoomConnection"; import type { BodyResourceDescriptionInterface } from "../Phaser/Entity/PlayerTextures"; - -export enum EventMessage { - CONNECT = "connect", - WEBRTC_SIGNAL = "webrtc-signal", - WEBRTC_SCREEN_SHARING_SIGNAL = "webrtc-screen-sharing-signal", - WEBRTC_START = "webrtc-start", - //START_ROOM = "start-room", // From server to client: list of all room users/groups/items - JOIN_ROOM = "join-room", // bi-directional - USER_POSITION = "user-position", // From client to server - USER_MOVED = "user-moved", // From server to client - USER_LEFT = "user-left", // From server to client - MESSAGE_ERROR = "message-error", - WEBRTC_DISCONNECT = "webrtc-disconect", - GROUP_CREATE_UPDATE = "group-create-update", - GROUP_DELETE = "group-delete", - SET_PLAYER_DETAILS = "set-player-details", // Send the name and character to the server (on connect), receive back the id. - ITEM_EVENT = "item-event", - USER_DETAILS_UPDATED = "user-details-updated", - - CONNECT_ERROR = "connect_error", - CONNECTING_ERROR = "connecting_error", - SET_SILENT = "set_silent", // Set or unset the silent mode for this user. - SET_VIEWPORT = "set-viewport", - BATCH = "batch", - - PLAY_GLOBAL_MESSAGE = "play-global-message", - STOP_GLOBAL_MESSAGE = "stop-global-message", - - TELEPORT = "teleport", - USER_MESSAGE = "user-message", - START_JITSI_ROOM = "start-jitsi-room", - SET_VARIABLE = "set-variable", -} +import { PositionMessage_Direction } from "../Messages/ts-proto-generated/messages"; export interface PointInterface { x: number; y: number; - direction: string; + direction: string; // TODO: modify this to the enum from ts-proto moving: boolean; } diff --git a/front/src/Connexion/EmoteEventStream.ts b/front/src/Connexion/EmoteEventStream.ts deleted file mode 100644 index 32f1daa0..00000000 --- a/front/src/Connexion/EmoteEventStream.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Subject } from "rxjs"; - -interface EmoteEvent { - userId: number; - emote: string; -} - -class EmoteEventStream { - private _stream: Subject = new Subject(); - public stream = this._stream.asObservable(); - - fire(userId: number, emote: string) { - this._stream.next({ userId, emote }); - } -} - -export const emoteEventStream = new EmoteEventStream(); diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 328f1aec..b7aa30ce 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -1,50 +1,9 @@ import { PUSHER_URL, UPLOADER_URL } from "../Enum/EnvironmentVariable"; import Axios from "axios"; -import { - BatchMessage, - ClientToServerMessage, - GroupDeleteMessage, - GroupUpdateMessage, - ItemEventMessage, - PlayGlobalMessage, - PositionMessage, - RoomJoinedMessage, - ServerToClientMessage, - SetPlayerDetailsMessage, - SilentMessage, - StopGlobalMessage, - UserJoinedMessage, - UserLeftMessage, - UserMovedMessage, - UserMovesMessage, - ViewportMessage, - WebRtcDisconnectMessage, - WebRtcSignalToClientMessage, - WebRtcSignalToServerMessage, - WebRtcStartMessage, - ReportPlayerMessage, - TeleportMessageMessage, - QueryJitsiJwtMessage, - SendJitsiJwtMessage, - CharacterLayerMessage, - PingMessage, - EmoteEventMessage, - EmotePromptMessage, - FollowRequestMessage, - FollowConfirmationMessage, - FollowAbortMessage, - SendUserMessage, - BanUserMessage, - VariableMessage, - ErrorMessage, - PlayerDetailsUpdatedMessage, -} from "../Messages/generated/messages_pb"; import type { UserSimplePeerInterface } from "../WebRtc/SimplePeer"; -import Direction = PositionMessage.Direction; import { ProtobufClientUtils } from "../Network/ProtobufClientUtils"; -import { - EventMessage, +import type { GroupCreatedUpdatedMessageInterface, ItemEventMessageInterface, MessageUserJoined, @@ -59,13 +18,44 @@ import { } from "./ConnexionModels"; import type { BodyResourceDescriptionInterface } from "../Phaser/Entity/PlayerTextures"; import { adminMessagesService } from "./AdminMessagesService"; -import { worldFullMessageStream } from "./WorldFullMessageStream"; import { connectionManager } from "./ConnectionManager"; -import { emoteEventStream } from "./EmoteEventStream"; import { get } from "svelte/store"; import { warningContainerStore } from "../Stores/MenuStore"; import { followStateStore, followRoleStore, followUsersStore } from "../Stores/FollowStore"; import { localUserStore } from "./LocalUserStore"; +import { + RefreshRoomMessage, + ServerToClientMessage as ServerToClientMessageTsProto, + TokenExpiredMessage, + WorldConnexionMessage, + WorldFullMessage, + ErrorMessage as ErrorMessageTsProto, + UserMovedMessage as UserMovedMessageTsProto, + GroupUpdateMessage as GroupUpdateMessageTsProto, + GroupDeleteMessage as GroupDeleteMessageTsProto, + UserJoinedMessage as UserJoinedMessageTsProto, + UserLeftMessage as UserLeftMessageTsProto, + ItemEventMessage as ItemEventMessageTsProto, + EmoteEventMessage as EmoteEventMessageTsProto, + VariableMessage as VariableMessageTsProto, + PlayerDetailsUpdatedMessage as PlayerDetailsUpdatedMessageTsProto, + WorldFullWarningMessage, + WebRtcDisconnectMessage as WebRtcDisconnectMessageTsProto, + PlayGlobalMessage as PlayGlobalMessageTsProto, + StopGlobalMessage as StopGlobalMessageTsProto, + SendJitsiJwtMessage as SendJitsiJwtMessageTsProto, + SendUserMessage as SendUserMessageTsProto, + BanUserMessage as BanUserMessageTsProto, + ClientToServerMessage as ClientToServerMessageTsProto, + PositionMessage as PositionMessageTsProto, + ViewportMessage as ViewportMessageTsProto, + PositionMessage_Direction, + SetPlayerDetailsMessage as SetPlayerDetailsMessageTsProto, + PingMessage as PingMessageTsProto, +} from "../Messages/ts-proto-generated/messages"; +import { Subject } from "rxjs"; +import { OpenPopupEvent } from "../Api/Events/OpenPopupEvent"; +import { match } from "assert"; const manualPingDelay = 20000; @@ -78,6 +68,85 @@ export class RoomConnection implements RoomConnection { private tags: string[] = []; private _userRoomToken: string | undefined; + private readonly _errorMessageStream = new Subject(); + public readonly errorMessageStream = this._errorMessageStream.asObservable(); + + private readonly _roomJoinedMessageStream = new Subject<{ + connection: RoomConnection; + room: RoomJoinedMessageInterface; + }>(); + public readonly roomJoinedMessageStream = this._roomJoinedMessageStream.asObservable(); + + private readonly _webRtcStartMessageStream = new Subject(); + public readonly webRtcStartMessageStream = this._webRtcStartMessageStream.asObservable(); + + private readonly _webRtcSignalToClientMessageStream = new Subject(); + public readonly webRtcSignalToClientMessageStream = this._webRtcSignalToClientMessageStream.asObservable(); + + private readonly _webRtcScreenSharingSignalToClientMessageStream = + new Subject(); + public readonly webRtcScreenSharingSignalToClientMessageStream = + this._webRtcScreenSharingSignalToClientMessageStream.asObservable(); + + private readonly _webRtcDisconnectMessageStream = new Subject(); + public readonly webRtcDisconnectMessageStream = this._webRtcDisconnectMessageStream.asObservable(); + + private readonly _playGlobalMessageStream = new Subject(); + public readonly playGlobalMessageStream = this._playGlobalMessageStream.asObservable(); + + private readonly _stopGlobalMessageStream = new Subject(); + public readonly stopGlobalMessageStream = this._stopGlobalMessageStream.asObservable(); + + private readonly _teleportMessageMessageStream = new Subject(); + public readonly teleportMessageMessageStream = this._teleportMessageMessageStream.asObservable(); + + private readonly _sendJitsiJwtMessageStream = new Subject(); + public readonly sendJitsiJwtMessageStream = this._sendJitsiJwtMessageStream.asObservable(); + + private readonly _worldFullMessageStream = new Subject(); + public readonly worldFullMessageStream = this._worldFullMessageStream.asObservable(); + + private readonly _worldConnexionMessageStream = new Subject(); + public readonly worldConnexionMessageStream = this._worldConnexionMessageStream.asObservable(); + + private readonly _tokenExpiredMessageStream = new Subject(); + public readonly tokenExpiredMessageStream = this._tokenExpiredMessageStream.asObservable(); + + private readonly _userMovedMessageStream = new Subject(); + public readonly userMovedMessageStream = this._userMovedMessageStream.asObservable(); + + private readonly _groupUpdateMessageStream = new Subject(); + public readonly groupUpdateMessageStream = this._groupUpdateMessageStream.asObservable(); + + private readonly _groupDeleteMessageStream = new Subject(); + public readonly groupDeleteMessageStream = this._groupDeleteMessageStream.asObservable(); + + private readonly _userJoinedMessageStream = new Subject(); + public readonly userJoinedMessageStream = this._userJoinedMessageStream.asObservable(); + + private readonly _userLeftMessageStream = new Subject(); + public readonly userLeftMessageStream = this._userLeftMessageStream.asObservable(); + + private readonly _itemEventMessageStream = new Subject<{ + itemId: number; + event: string; + parameters: unknown; + state: unknown; + }>(); + public readonly itemEventMessageStream = this._itemEventMessageStream.asObservable(); + + private readonly _emoteEventMessageStream = new Subject(); + public readonly emoteEventMessageStream = this._emoteEventMessageStream.asObservable(); + + private readonly _variableMessageStream = new Subject<{ name: string; value: unknown }>(); + public readonly variableMessageStream = this._variableMessageStream.asObservable(); + + private readonly _playerDetailsUpdatedMessageStream = new Subject(); + public readonly playerDetailsUpdatedMessageStream = this._playerDetailsUpdatedMessageStream.asObservable(); + + private readonly _connectionErrorStream = new Subject(); + public readonly connectionErrorStream = this._connectionErrorStream.asObservable(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any public static setWebsocketFactory(websocketFactory: (url: string) => any): void { RoomConnection.websocketFactory = websocketFactory; @@ -136,8 +205,8 @@ export class RoomConnection implements RoomConnection { this.socket.onopen = (ev) => { //we manually ping every 20s to not be logged out by the server, even when the game is in background. - const pingMessage = new PingMessage(); - interval = setInterval(() => this.socket.send(pingMessage.serializeBinary().buffer), manualPingDelay); + const pingMessage = PingMessageTsProto.encode({}).finish(); + interval = setInterval(() => this.socket.send(pingMessage), manualPingDelay); }; this.socket.addEventListener("close", (event) => { @@ -147,147 +216,264 @@ export class RoomConnection implements RoomConnection { // If we are not connected yet (if a JoinRoomMessage was not sent), we need to retry. if (this.userId === null && !this.closed) { - this.dispatch(EventMessage.CONNECTING_ERROR, event); + this._connectionErrorStream.next(event); } }); this.socket.onmessage = (messageEvent) => { const arrayBuffer: ArrayBuffer = messageEvent.data; - const message = ServerToClientMessage.deserializeBinary(new Uint8Array(arrayBuffer)); - if (message.hasBatchmessage()) { - for (const subMessage of (message.getBatchmessage() as BatchMessage).getPayloadList()) { - let event: string | null = null; - let payload; - if (subMessage.hasUsermovedmessage()) { - event = EventMessage.USER_MOVED; - payload = subMessage.getUsermovedmessage(); - } else if (subMessage.hasGroupupdatemessage()) { - event = EventMessage.GROUP_CREATE_UPDATE; - payload = subMessage.getGroupupdatemessage(); - } else if (subMessage.hasGroupdeletemessage()) { - event = EventMessage.GROUP_DELETE; - payload = subMessage.getGroupdeletemessage(); - } else if (subMessage.hasUserjoinedmessage()) { - event = EventMessage.JOIN_ROOM; - payload = subMessage.getUserjoinedmessage(); - } else if (subMessage.hasUserleftmessage()) { - event = EventMessage.USER_LEFT; - payload = subMessage.getUserleftmessage(); - } else if (subMessage.hasItemeventmessage()) { - event = EventMessage.ITEM_EVENT; - payload = subMessage.getItemeventmessage(); - } else if (subMessage.hasEmoteeventmessage()) { - const emoteMessage = subMessage.getEmoteeventmessage() as EmoteEventMessage; - emoteEventStream.fire(emoteMessage.getActoruserid(), emoteMessage.getEmote()); - } else if (subMessage.hasPlayerdetailsupdatedmessage()) { - event = EventMessage.USER_DETAILS_UPDATED; - payload = subMessage.getPlayerdetailsupdatedmessage(); - } else if (subMessage.hasErrormessage()) { - const errorMessage = subMessage.getErrormessage() as ErrorMessage; - console.error("An error occurred server side: " + errorMessage.getMessage()); - } else if (subMessage.hasVariablemessage()) { - event = EventMessage.SET_VARIABLE; - payload = subMessage.getVariablemessage(); + const serverToClientMessage = ServerToClientMessageTsProto.decode(new Uint8Array(arrayBuffer)); + //const message = ServerToClientMessage.deserializeBinary(new Uint8Array(arrayBuffer)); + + const message = serverToClientMessage.message; + if (message === undefined) { + return; + } + + switch (message.$case) { + case "batchMessage": { + for (const subMessageWrapper of message.batchMessage.payload) { + const subMessage = subMessageWrapper.message; + if (subMessage === undefined) { + return; + } + switch (subMessage.$case) { + case "errorMessage": { + this._errorMessageStream.next(subMessage.errorMessage); + console.error("An error occurred server side: " + subMessage.errorMessage.message); + break; + } + case "userJoinedMessage": { + this._userJoinedMessageStream.next( + this.toMessageUserJoined(subMessage.userJoinedMessage) + ); + break; + } + case "userLeftMessage": { + this._userLeftMessageStream.next(subMessage.userLeftMessage); + break; + } + case "userMovedMessage": { + this._userMovedMessageStream.next(subMessage.userMovedMessage); + break; + } + case "groupUpdateMessage": { + this._groupUpdateMessageStream.next( + this.toGroupCreatedUpdatedMessage(subMessage.groupUpdateMessage) + ); + break; + } + case "groupDeleteMessage": { + this._groupDeleteMessageStream.next(subMessage.groupDeleteMessage); + break; + } + case "itemEventMessage": { + this._itemEventMessageStream.next({ + itemId: subMessage.itemEventMessage.itemId, + event: subMessage.itemEventMessage.event, + parameters: JSON.parse(subMessage.itemEventMessage.parametersJson), + state: JSON.parse(subMessage.itemEventMessage.stateJson), + }); + break; + } + case "emoteEventMessage": { + this._emoteEventMessageStream.next(subMessage.emoteEventMessage); + break; + } + case "playerDetailsUpdatedMessage": { + this._playerDetailsUpdatedMessageStream.next(subMessage.playerDetailsUpdatedMessage); + break; + } + case "variableMessage": { + const name = subMessage.variableMessage.name; + const serializedValue = subMessage.variableMessage.value; + let value: unknown = undefined; + if (serializedValue) { + try { + value = JSON.parse(serializedValue); + } catch (e) { + console.error( + 'Unable to unserialize value received from server for variable "' + + name + + '". Value received: "' + + serializedValue + + '". Error: ', + e + ); + } + } + + this._variableMessageStream.next({ name, value }); + break; + } + default: { + // Security check: if we forget a "case", the line below will catch the error at compile-time. + const tmp: never = subMessage; + } + } + } + break; + } + case "roomJoinedMessage": { + const roomJoinedMessage = message.roomJoinedMessage; + + const items: { [itemId: number]: unknown } = {}; + for (const item of roomJoinedMessage.item) { + items[item.itemId] = JSON.parse(item.stateJson); + } + + const variables = new Map(); + for (const variable of roomJoinedMessage.variable) { + try { + variables.set(variable.name, JSON.parse(variable.value)); + } catch (e) { + console.error( + 'Unable to unserialize value received from server for variable "' + + variable.name + + '". Value received: "' + + variable.value + + '". Error: ', + e + ); + } + } + + this.userId = roomJoinedMessage.currentUserId; + this.tags = roomJoinedMessage.tag; + this._userRoomToken = roomJoinedMessage.userRoomToken; + + this._roomJoinedMessageStream.next({ + connection: this, + room: { + items, + variables, + } as RoomJoinedMessageInterface, + }); + break; + } + case "worldFullMessage": { + this._worldFullMessageStream.next(null); + this.closed = true; + break; + } + case "tokenExpiredMessage": { + connectionManager.logout(); + this.closed = true; //technically, this isn't needed since loadOpenIDScreen() will do window.location.assign() but I prefer to leave it for consistency + break; + } + case "worldConnexionMessage": { + this._worldFullMessageStream.next(message.worldConnexionMessage.message); + this.closed = true; + break; + } + case "webRtcSignalToClientMessage": { + this._webRtcSignalToClientMessageStream.next({ + userId: message.webRtcSignalToClientMessage.userId, + signal: JSON.parse(message.webRtcSignalToClientMessage.signal), + webRtcUser: message.webRtcSignalToClientMessage.webrtcUserName + ? message.webRtcSignalToClientMessage.webrtcUserName + : undefined, + webRtcPassword: message.webRtcSignalToClientMessage.webrtcPassword + ? message.webRtcSignalToClientMessage.webrtcPassword + : undefined, + }); + break; + } + case "webRtcScreenSharingSignalToClientMessage": { + this._webRtcScreenSharingSignalToClientMessageStream.next({ + userId: message.webRtcScreenSharingSignalToClientMessage.userId, + signal: JSON.parse(message.webRtcScreenSharingSignalToClientMessage.signal), + webRtcUser: message.webRtcScreenSharingSignalToClientMessage.webrtcUserName + ? message.webRtcScreenSharingSignalToClientMessage.webrtcUserName + : undefined, + webRtcPassword: message.webRtcScreenSharingSignalToClientMessage.webrtcPassword + ? message.webRtcScreenSharingSignalToClientMessage.webrtcPassword + : undefined, + }); + break; + } + case "webRtcStartMessage": { + this._webRtcStartMessageStream.next({ + userId: message.webRtcStartMessage.userId, + initiator: message.webRtcStartMessage.initiator, + webRtcUser: message.webRtcStartMessage.webrtcUserName + ? message.webRtcStartMessage.webrtcUserName + : undefined, + webRtcPassword: message.webRtcStartMessage.webrtcPassword + ? message.webRtcStartMessage.webrtcPassword + : undefined, + }); + break; + } + case "webRtcDisconnectMessage": { + this._webRtcDisconnectMessageStream.next(message.webRtcDisconnectMessage); + break; + } + case "playGlobalMessage": { + // FIXME: WHY IS THIS UNUSED? CAN WE REMOVE THIS??? + // FIXME: WHY IS THIS UNUSED? CAN WE REMOVE THIS??? + this._playGlobalMessageStream.next(message.playGlobalMessage); + break; + } + case "stopGlobalMessage": { + // FIXME: WHY IS THIS UNUSED? CAN WE REMOVE THIS??? + // FIXME: WHY IS THIS UNUSED? CAN WE REMOVE THIS??? + this._stopGlobalMessageStream.next(message.stopGlobalMessage); + break; + } + case "teleportMessageMessage": { + // FIXME: WHY IS THIS UNUSED? CAN WE REMOVE THIS??? + this._teleportMessageMessageStream.next(message.teleportMessageMessage.map); + break; + } + case "sendJitsiJwtMessage": { + this._sendJitsiJwtMessageStream.next(message.sendJitsiJwtMessage); + break; + } + case "sendUserMessage": { + adminMessagesService.onSendusermessage(message.sendUserMessage); + break; + } + case "banUserMessage": { + adminMessagesService.onSendusermessage(message.banUserMessage); + break; + } + case "worldFullWarningMessage": { + warningContainerStore.activateWarningContainer(); + break; + } + case "refreshRoomMessage": { + //todo: implement a way to notify the user the room was refreshed. + break; + } + case "followRequestMessage": { + if (!localUserStore.getIgnoreFollowRequests()) { + followUsersStore.addFollowRequest(message.followRequestMessage.leader); + } + break; + } + case "followConfirmationMessage": { + followUsersStore.addFollower(message.followConfirmationMessage.follower); + break; + } + case "followAbortMessage": { + if (get(followRoleStore) === "follower") { + followUsersStore.stopFollowing(); } else { - throw new Error("Unexpected batch message type"); - } - - if (event) { - this.dispatch(event, payload); + followUsersStore.removeFollower(message.followAbortMessage.follower); } + break; } - } else if (message.hasRoomjoinedmessage()) { - const roomJoinedMessage = message.getRoomjoinedmessage() as RoomJoinedMessage; - - const items: { [itemId: number]: unknown } = {}; - for (const item of roomJoinedMessage.getItemList()) { - items[item.getItemid()] = JSON.parse(item.getStatejson()); + case "errorMessage": { + this._errorMessageStream.next(message.errorMessage); + console.error("An error occurred server side: " + message.errorMessage.message); + break; } - - const variables = new Map(); - for (const variable of roomJoinedMessage.getVariableList()) { - try { - variables.set(variable.getName(), JSON.parse(variable.getValue())); - } catch (e) { - console.error( - 'Unable to unserialize value received from server for variable "' + - variable.getName() + - '". Value received: "' + - variable.getValue() + - '". Error: ', - e - ); - } + default: { + // Security check: if we forget a "case", the line below will catch the error at compile-time. + const tmp: never = message; } - - this.userId = roomJoinedMessage.getCurrentuserid(); - this.tags = roomJoinedMessage.getTagList(); - this._userRoomToken = roomJoinedMessage.getUserroomtoken(); - - this.dispatch(EventMessage.CONNECT, { - connection: this, - room: { - items, - variables, - } as RoomJoinedMessageInterface, - }); - } else if (message.hasWorldfullmessage()) { - worldFullMessageStream.onMessage(); - this.closed = true; - } else if (message.hasTokenexpiredmessage()) { - connectionManager.logout(); - this.closed = true; //technically, this isn't needed since loadOpenIDScreen() will do window.location.assign() but I prefer to leave it for consistency - } else if (message.hasWorldconnexionmessage()) { - worldFullMessageStream.onMessage(message.getWorldconnexionmessage()?.getMessage()); - this.closed = true; - } else if (message.hasWebrtcsignaltoclientmessage()) { - this.dispatch(EventMessage.WEBRTC_SIGNAL, message.getWebrtcsignaltoclientmessage()); - } else if (message.hasWebrtcscreensharingsignaltoclientmessage()) { - this.dispatch( - EventMessage.WEBRTC_SCREEN_SHARING_SIGNAL, - message.getWebrtcscreensharingsignaltoclientmessage() - ); - } else if (message.hasWebrtcstartmessage()) { - this.dispatch(EventMessage.WEBRTC_START, message.getWebrtcstartmessage()); - } else if (message.hasWebrtcdisconnectmessage()) { - this.dispatch(EventMessage.WEBRTC_DISCONNECT, message.getWebrtcdisconnectmessage()); - } else if (message.hasPlayglobalmessage()) { - this.dispatch(EventMessage.PLAY_GLOBAL_MESSAGE, message.getPlayglobalmessage()); - } else if (message.hasStopglobalmessage()) { - this.dispatch(EventMessage.STOP_GLOBAL_MESSAGE, message.getStopglobalmessage()); - } else if (message.hasTeleportmessagemessage()) { - this.dispatch(EventMessage.TELEPORT, message.getTeleportmessagemessage()); - } else if (message.hasSendjitsijwtmessage()) { - this.dispatch(EventMessage.START_JITSI_ROOM, message.getSendjitsijwtmessage()); - } else if (message.hasSendusermessage()) { - adminMessagesService.onSendusermessage(message.getSendusermessage() as SendUserMessage); - } else if (message.hasBanusermessage()) { - adminMessagesService.onSendusermessage(message.getBanusermessage() as BanUserMessage); - } else if (message.hasWorldfullwarningmessage()) { - warningContainerStore.activateWarningContainer(); - } else if (message.hasRefreshroommessage()) { - //todo: implement a way to notify the user the room was refreshed. - } else if (message.hasFollowrequestmessage()) { - const requestMessage = message.getFollowrequestmessage() as FollowRequestMessage; - if (!localUserStore.getIgnoreFollowRequests()) { - followUsersStore.addFollowRequest(requestMessage.getLeader()); - } - } else if (message.hasFollowconfirmationmessage()) { - const responseMessage = message.getFollowconfirmationmessage() as FollowConfirmationMessage; - followUsersStore.addFollower(responseMessage.getFollower()); - } else if (message.hasFollowabortmessage()) { - const abortMessage = message.getFollowabortmessage() as FollowAbortMessage; - if (get(followRoleStore) === "follower") { - followUsersStore.stopFollowing(); - } else { - followUsersStore.removeFollower(abortMessage.getFollower()); - } - } else if (message.hasErrormessage()) { - const errorMessage = message.getErrormessage() as ErrorMessage; - console.error("An error occurred server side: " + errorMessage.getMessage()); - } else { - throw new Error("Unknown message received"); } }; } @@ -314,17 +500,25 @@ export class RoomConnection implements RoomConnection { }*/ public emitPlayerOutlineColor(color: number | null) { - const message = new SetPlayerDetailsMessage(); + let message: SetPlayerDetailsMessageTsProto; if (color === null) { - message.setRemoveoutlinecolor(true); + message = SetPlayerDetailsMessageTsProto.fromPartial({ + removeOutlineColor: true, + }); } else { - message.setOutlinecolor(color); + message = SetPlayerDetailsMessageTsProto.fromPartial({ + outlineColor: color, + }); } - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setSetplayerdetailsmessage(message); + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "setPlayerDetailsMessage", + setPlayerDetailsMessage: message, + }, + }).finish(); - this.socket.send(clientToServerMessage.serializeBinary().buffer); + this.socket.send(bytes); } public closeConnection(): void { @@ -332,41 +526,35 @@ export class RoomConnection implements RoomConnection { this.closed = true; } - private toPositionMessage(x: number, y: number, direction: string, moving: boolean): PositionMessage { - const positionMessage = new PositionMessage(); - positionMessage.setX(Math.floor(x)); - positionMessage.setY(Math.floor(y)); - let directionEnum: Direction; - 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); - - return positionMessage; + private toPositionMessage(x: number, y: number, direction: string, moving: boolean): PositionMessageTsProto { + return { + x: Math.floor(x), + y: Math.floor(y), + moving, + direction: (() => { + switch (direction) { + case "up": + return PositionMessage_Direction.UP; + case "down": + return PositionMessage_Direction.DOWN; + case "left": + return PositionMessage_Direction.LEFT; + case "right": + return PositionMessage_Direction.RIGHT; + default: + throw new Error("Unexpected direction"); + } + })(), + }; } - private toViewportMessage(viewport: ViewportInterface): ViewportMessage { - 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)); - - return viewportMessage; + private toViewportMessage(viewport: ViewportInterface): ViewportMessageTsProto { + return { + left: Math.floor(viewport.left), + right: Math.floor(viewport.right), + top: Math.floor(viewport.top), + bottom: Math.floor(viewport.bottom), + }; } public sharePosition(x: number, y: number, direction: string, moving: boolean, viewport: ViewportInterface): void { @@ -378,81 +566,77 @@ export class RoomConnection implements RoomConnection { const viewportMessage = this.toViewportMessage(viewport); - const userMovesMessage = new UserMovesMessage(); - userMovesMessage.setPosition(positionMessage); - userMovesMessage.setViewport(viewportMessage); + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "userMovesMessage", + userMovesMessage: { + position: positionMessage, + viewport: viewportMessage, + }, + }, + }).finish(); - //console.log('Sending position ', positionMessage.getX(), positionMessage.getY()); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setUsermovesmessage(userMovesMessage); - - this.socket.send(clientToServerMessage.serializeBinary().buffer); + this.socket.send(bytes); } public setSilent(silent: boolean): void { - const silentMessage = new SilentMessage(); - silentMessage.setSilent(silent); + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "silentMessage", + silentMessage: { + silent, + }, + }, + }).finish(); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setSilentmessage(silentMessage); - - this.socket.send(clientToServerMessage.serializeBinary().buffer); + this.socket.send(bytes); } public setViewport(viewport: ViewportInterface): void { - const viewportMessage = new ViewportMessage(); - viewportMessage.setTop(Math.round(viewport.top)); - viewportMessage.setBottom(Math.round(viewport.bottom)); - viewportMessage.setLeft(Math.round(viewport.left)); - viewportMessage.setRight(Math.round(viewport.right)); + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "viewportMessage", + viewportMessage: this.toViewportMessage(viewport), + }, + }).finish(); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setViewportmessage(viewportMessage); - - this.socket.send(clientToServerMessage.serializeBinary().buffer); + this.socket.send(bytes); } - public onUserJoins(callback: (message: MessageUserJoined) => void): void { + /* public onUserJoins(callback: (message: MessageUserJoined) => void): void { this.onMessage(EventMessage.JOIN_ROOM, (message: UserJoinedMessage) => { callback(this.toMessageUserJoined(message)); }); - } + }*/ // TODO: move this to protobuf utils - private toMessageUserJoined(message: UserJoinedMessage): MessageUserJoined { - const position = message.getPosition(); + private toMessageUserJoined(message: UserJoinedMessageTsProto): MessageUserJoined { + const position = message.position; if (position === undefined) { throw new Error("Invalid JOIN_ROOM message"); } - const characterLayers = message - .getCharacterlayersList() - .map((characterLayer: CharacterLayerMessage): BodyResourceDescriptionInterface => { - return { - name: characterLayer.getName(), - img: characterLayer.getUrl(), - }; - }); + const characterLayers = message.characterLayers.map((characterLayer): BodyResourceDescriptionInterface => { + return { + name: characterLayer.name, + img: characterLayer.url, + }; + }); - const companion = message.getCompanion(); + const companion = message.companion; return { - userId: message.getUserid(), - name: message.getName(), + userId: message.userId, + name: message.name, characterLayers, - visitCardUrl: message.getVisitcardurl(), + visitCardUrl: message.visitCardUrl, position: ProtobufClientUtils.toPointInterface(position), - companion: companion ? companion.getName() : null, - userUuid: message.getUseruuid(), - outlineColor: message.getHasoutline() ? message.getOutlinecolor() : undefined, + companion: companion ? companion.name : null, + userUuid: message.userUuid, + outlineColor: message.hasOutline ? message.outlineColor : undefined, }; } - public onUserMoved(callback: (message: UserMovedMessage) => void): void { - this.onMessage(EventMessage.USER_MOVED, callback); - //this.socket.on(EventMessage.USER_MOVED, callback); - } - /** * Registers a listener on a message that is part of a batch */ @@ -465,114 +649,49 @@ export class RoomConnection implements RoomConnection { callbacks.push(callback); } - public onUserLeft(callback: (userId: number) => void): void { - this.onMessage(EventMessage.USER_LEFT, (message: UserLeftMessage) => { - callback(message.getUserid()); - }); - } - - public onGroupUpdatedOrCreated( - callback: (groupCreateUpdateMessage: GroupCreatedUpdatedMessageInterface) => void - ): void { - this.onMessage(EventMessage.GROUP_CREATE_UPDATE, (message: GroupUpdateMessage) => { - callback(this.toGroupCreatedUpdatedMessage(message)); - }); - } - - private toGroupCreatedUpdatedMessage(message: GroupUpdateMessage): GroupCreatedUpdatedMessageInterface { - const position = message.getPosition(); + private toGroupCreatedUpdatedMessage(message: GroupUpdateMessageTsProto): GroupCreatedUpdatedMessageInterface { + const position = message.position; if (position === undefined) { throw new Error("Missing position in GROUP_CREATE_UPDATE"); } return { - groupId: message.getGroupid(), - position: position.toObject(), - groupSize: message.getGroupsize(), + groupId: message.groupId, + position: position, + groupSize: message.groupSize, }; } - public onGroupDeleted(callback: (groupId: number) => void): void { - this.onMessage(EventMessage.GROUP_DELETE, (message: GroupDeleteMessage) => { - callback(message.getGroupid()); - }); - } - - public onConnectingError(callback: (event: CloseEvent) => void): void { - this.onMessage(EventMessage.CONNECTING_ERROR, (event: CloseEvent) => { - callback(event); - }); - } - public onConnectError(callback: (error: Event) => void): void { this.socket.addEventListener("error", callback); } - public onConnect(callback: (roomConnection: OnConnectInterface) => void): void { - //this.socket.addEventListener('open', callback) - this.onMessage(EventMessage.CONNECT, callback); - } - - /** - * Triggered when we receive all the details of a room (users, groups, ...) - */ - /*public onStartRoom(callback: (event: RoomJoinedMessageInterface) => void): void { - this.onMessage(EventMessage.START_ROOM, callback); - }*/ - public sendWebrtcSignal(signal: unknown, receiverId: number) { - const webRtcSignal = new WebRtcSignalToServerMessage(); - webRtcSignal.setReceiverid(receiverId); - webRtcSignal.setSignal(JSON.stringify(signal)); + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "webRtcSignalToServerMessage", + webRtcSignalToServerMessage: { + receiverId, + signal: JSON.stringify(signal), + }, + }, + }).finish(); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setWebrtcsignaltoservermessage(webRtcSignal); - - this.socket.send(clientToServerMessage.serializeBinary().buffer); + this.socket.send(bytes); } public sendWebrtcScreenSharingSignal(signal: unknown, receiverId: number) { - const webRtcSignal = new WebRtcSignalToServerMessage(); - webRtcSignal.setReceiverid(receiverId); - webRtcSignal.setSignal(JSON.stringify(signal)); + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "webRtcScreenSharingSignalToServerMessage", + webRtcScreenSharingSignalToServerMessage: { + receiverId, + signal: JSON.stringify(signal), + }, + }, + }).finish(); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setWebrtcscreensharingsignaltoservermessage(webRtcSignal); - - this.socket.send(clientToServerMessage.serializeBinary().buffer); - } - - public receiveWebrtcStart(callback: (message: UserSimplePeerInterface) => void) { - this.onMessage(EventMessage.WEBRTC_START, (message: WebRtcStartMessage) => { - callback({ - userId: message.getUserid(), - initiator: message.getInitiator(), - webRtcUser: message.getWebrtcusername() ?? undefined, - webRtcPassword: message.getWebrtcpassword() ?? undefined, - }); - }); - } - - public receiveWebrtcSignal(callback: (message: WebRtcSignalReceivedMessageInterface) => void) { - this.onMessage(EventMessage.WEBRTC_SIGNAL, (message: WebRtcSignalToClientMessage) => { - callback({ - userId: message.getUserid(), - signal: JSON.parse(message.getSignal()), - webRtcUser: message.getWebrtcusername() ?? undefined, - webRtcPassword: message.getWebrtcpassword() ?? undefined, - }); - }); - } - - public receiveWebrtcScreenSharingSignal(callback: (message: WebRtcSignalReceivedMessageInterface) => void) { - this.onMessage(EventMessage.WEBRTC_SCREEN_SHARING_SIGNAL, (message: WebRtcSignalToClientMessage) => { - callback({ - userId: message.getUserid(), - signal: JSON.parse(message.getSignal()), - webRtcUser: message.getWebrtcusername() ?? undefined, - webRtcPassword: message.getWebrtcpassword() ?? undefined, - }); - }); + this.socket.send(bytes); } public onServerDisconnected(callback: () => void): void { @@ -594,61 +713,34 @@ export class RoomConnection implements RoomConnection { return this.userId; } - disconnectMessage(callback: (message: WebRtcDisconnectMessageInterface) => void): void { - this.onMessage(EventMessage.WEBRTC_DISCONNECT, (message: WebRtcDisconnectMessage) => { - callback({ - userId: message.getUserid(), - }); - }); - } - emitActionableEvent(itemId: number, event: string, state: unknown, parameters: unknown): void { - const itemEventMessage = new ItemEventMessage(); - itemEventMessage.setItemid(itemId); - itemEventMessage.setEvent(event); - itemEventMessage.setStatejson(JSON.stringify(state)); - itemEventMessage.setParametersjson(JSON.stringify(parameters)); + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "itemEventMessage", + itemEventMessage: { + itemId, + event, + stateJson: JSON.stringify(state), + parametersJson: JSON.stringify(parameters), + }, + }, + }).finish(); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setItemeventmessage(itemEventMessage); - - this.socket.send(clientToServerMessage.serializeBinary().buffer); + this.socket.send(bytes); } emitSetVariableEvent(name: string, value: unknown): void { - const variableMessage = new VariableMessage(); - variableMessage.setName(name); - variableMessage.setValue(JSON.stringify(value)); + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "variableMessage", + variableMessage: { + name, + value: JSON.stringify(value), + }, + }, + }).finish(); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setVariablemessage(variableMessage); - - this.socket.send(clientToServerMessage.serializeBinary().buffer); - } - - onActionableEvent(callback: (message: ItemEventMessageInterface) => void): void { - this.onMessage(EventMessage.ITEM_EVENT, (message: ItemEventMessage) => { - callback({ - itemId: message.getItemid(), - event: message.getEvent(), - parameters: JSON.parse(message.getParametersjson()), - state: JSON.parse(message.getStatejson()), - }); - }); - } - - onPlayerDetailsUpdated(callback: (message: PlayerDetailsUpdatedMessageInterface) => void): void { - this.onMessage(EventMessage.USER_DETAILS_UPDATED, (message: PlayerDetailsUpdatedMessage) => { - const details = message.getDetails(); - if (details === undefined) { - throw new Error("Malformed message. Missing details in PlayerDetailsUpdatedMessage"); - } - callback({ - userId: message.getUserid(), - outlineColor: details.getOutlinecolor(), - removeOutlineColor: details.getRemoveoutlinecolor(), - }); - }); + this.socket.send(bytes); } public uploadAudio(file: FormData) { @@ -662,91 +754,48 @@ export class RoomConnection implements RoomConnection { }); } - /* public receivePlayGlobalMessage(callback: (message: PlayGlobalMessageInterface) => void) { - return this.onMessage(EventMessage.PLAY_GLOBAL_MESSAGE, (message: PlayGlobalMessage) => { - callback({ - id: message.getId(), - type: message.getType(), - message: message.getMessage(), - }); - }); - }*/ - - public receiveStopGlobalMessage(callback: (messageId: string) => void) { - return this.onMessage(EventMessage.STOP_GLOBAL_MESSAGE, (message: StopGlobalMessage) => { - callback(message.getId()); - }); - } - - public receiveTeleportMessage(callback: (messageId: string) => void) { - return this.onMessage(EventMessage.TELEPORT, (message: TeleportMessageMessage) => { - callback(message.getMap()); - }); - } - public emitGlobalMessage(message: PlayGlobalMessageInterface): void { - const playGlobalMessage = new PlayGlobalMessage(); - playGlobalMessage.setType(message.type); - playGlobalMessage.setContent(message.content); - playGlobalMessage.setBroadcasttoworld(message.broadcastToWorld); + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "playGlobalMessage", + playGlobalMessage: { + type: message.type, + content: message.content, + broadcastToWorld: message.broadcastToWorld, + }, + }, + }).finish(); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setPlayglobalmessage(playGlobalMessage); - - this.socket.send(clientToServerMessage.serializeBinary().buffer); + this.socket.send(bytes); } public emitReportPlayerMessage(reportedUserUuid: string, reportComment: string): void { - const reportPlayerMessage = new ReportPlayerMessage(); - reportPlayerMessage.setReporteduseruuid(reportedUserUuid); - reportPlayerMessage.setReportcomment(reportComment); + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "reportPlayerMessage", + reportPlayerMessage: { + reportedUserUuid, + reportComment, + }, + }, + }).finish(); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setReportplayermessage(reportPlayerMessage); - - this.socket.send(clientToServerMessage.serializeBinary().buffer); + this.socket.send(bytes); } public emitQueryJitsiJwtMessage(jitsiRoom: string, tag: string | undefined): void { - const queryJitsiJwtMessage = new QueryJitsiJwtMessage(); - queryJitsiJwtMessage.setJitsiroom(jitsiRoom); - if (tag !== undefined) { - queryJitsiJwtMessage.setTag(tag); - } + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "queryJitsiJwtMessage", + queryJitsiJwtMessage: { + jitsiRoom, + tag: tag ?? "", // empty string is sent as "undefined" by ts-proto + // TODO: when we migrated "pusher" to ts-proto, migrate this to a StringValue + }, + }, + }).finish(); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setQueryjitsijwtmessage(queryJitsiJwtMessage); - - this.socket.send(clientToServerMessage.serializeBinary().buffer); - } - - public onStartJitsiRoom(callback: (jwt: string, room: string) => void): void { - this.onMessage(EventMessage.START_JITSI_ROOM, (message: SendJitsiJwtMessage) => { - callback(message.getJwt(), message.getJitsiroom()); - }); - } - - public onSetVariable(callback: (name: string, value: unknown) => void): void { - this.onMessage(EventMessage.SET_VARIABLE, (message: VariableMessage) => { - const name = message.getName(); - const serializedValue = message.getValue(); - let value: unknown = undefined; - if (serializedValue) { - try { - value = JSON.parse(serializedValue); - } catch (e) { - console.error( - 'Unable to unserialize value received from server for variable "' + - name + - '". Value received: "' + - serializedValue + - '". Error: ', - e - ); - } - } - callback(name, value); - }); + this.socket.send(bytes); } public hasTag(tag: string): boolean { @@ -758,36 +807,51 @@ export class RoomConnection implements RoomConnection { } public emitEmoteEvent(emoteName: string): void { - const emoteMessage = new EmotePromptMessage(); - emoteMessage.setEmote(emoteName); + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "emotePromptMessage", + emotePromptMessage: { + emote: emoteName, + }, + }, + }).finish(); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setEmotepromptmessage(emoteMessage); - - this.socket.send(clientToServerMessage.serializeBinary().buffer); + this.socket.send(bytes); } public emitFollowRequest(): void { if (!this.userId) { return; } - const message = new FollowRequestMessage(); - message.setLeader(this.userId); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setFollowrequestmessage(message); - this.socket.send(clientToServerMessage.serializeBinary().buffer); + + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "followRequestMessage", + followRequestMessage: { + leader: this.userId, + }, + }, + }).finish(); + + this.socket.send(bytes); } public emitFollowConfirmation(): void { if (!this.userId) { return; } - const message = new FollowConfirmationMessage(); - message.setLeader(get(followUsersStore)[0]); - message.setFollower(this.userId); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setFollowconfirmationmessage(message); - this.socket.send(clientToServerMessage.serializeBinary().buffer); + + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "followConfirmationMessage", + followConfirmationMessage: { + leader: get(followUsersStore)[0], + follower: this.userId, + }, + }, + }).finish(); + + this.socket.send(bytes); } public emitFollowAbort(): void { @@ -796,12 +860,18 @@ export class RoomConnection implements RoomConnection { if (!this.userId || (isLeader && !hasFollowers)) { return; } - const message = new FollowAbortMessage(); - message.setLeader(isLeader ? this.userId : get(followUsersStore)[0]); - message.setFollower(isLeader ? 0 : this.userId); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setFollowabortmessage(message); - this.socket.send(clientToServerMessage.serializeBinary().buffer); + + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "followAbortMessage", + followAbortMessage: { + leader: isLeader ? this.userId : get(followUsersStore)[0], + follower: isLeader ? 0 : this.userId, + }, + }, + }).finish(); + + this.socket.send(bytes); } public getAllTags(): string[] { diff --git a/front/src/Connexion/WorldFullMessageStream.ts b/front/src/Connexion/WorldFullMessageStream.ts deleted file mode 100644 index 01ce6f20..00000000 --- a/front/src/Connexion/WorldFullMessageStream.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Subject } from "rxjs"; - -class WorldFullMessageStream { - private _stream: Subject = new Subject(); - public stream = this._stream.asObservable(); - - onMessage(message?: string) { - this._stream.next(message); - } -} - -export const worldFullMessageStream = new WorldFullMessageStream(); diff --git a/front/src/Messages/.gitignore b/front/src/Messages/.gitignore deleted file mode 100644 index 9e0adcc1..00000000 --- a/front/src/Messages/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/generated/ diff --git a/front/src/Messages/ts-proto-generated/.gitignore b/front/src/Messages/ts-proto-generated/.gitignore new file mode 100644 index 00000000..72e8ffc0 --- /dev/null +++ b/front/src/Messages/ts-proto-generated/.gitignore @@ -0,0 +1 @@ +* diff --git a/front/src/Network/ProtobufClientUtils.ts b/front/src/Network/ProtobufClientUtils.ts index 9ba0f40b..3e172d0f 100644 --- a/front/src/Network/ProtobufClientUtils.ts +++ b/front/src/Network/ProtobufClientUtils.ts @@ -1,21 +1,21 @@ -import { PositionMessage } from "../Messages/generated/messages_pb"; -import Direction = PositionMessage.Direction; +import { PositionMessage, PositionMessage_Direction } from "../Messages/ts-proto-generated/messages"; + import type { PointInterface } from "../Connexion/ConnexionModels"; export class ProtobufClientUtils { public static toPointInterface(position: PositionMessage): PointInterface { let direction: string; - switch (position.getDirection()) { - case Direction.UP: + switch (position.direction) { + case PositionMessage_Direction.UP: direction = "up"; break; - case Direction.DOWN: + case PositionMessage_Direction.DOWN: direction = "down"; break; - case Direction.LEFT: + case PositionMessage_Direction.LEFT: direction = "left"; break; - case Direction.RIGHT: + case PositionMessage_Direction.RIGHT: direction = "right"; break; default: @@ -24,10 +24,10 @@ export class ProtobufClientUtils { // sending to all clients in room except sender return { - x: position.getX(), - y: position.getY(), + x: position.x, + y: position.y, direction, - moving: position.getMoving(), + moving: position.moving, }; } } diff --git a/front/src/Phaser/Game/EmoteManager.ts b/front/src/Phaser/Game/EmoteManager.ts index 06e8b099..097ebf45 100644 --- a/front/src/Phaser/Game/EmoteManager.ts +++ b/front/src/Phaser/Game/EmoteManager.ts @@ -1,13 +1,13 @@ -import { emoteEventStream } from "../../Connexion/EmoteEventStream"; import type { GameScene } from "./GameScene"; import type { Subscription } from "rxjs"; +import type { RoomConnection } from "../../Connexion/RoomConnection"; export class EmoteManager { private subscription: Subscription; - constructor(private scene: GameScene) { - this.subscription = emoteEventStream.stream.subscribe((event) => { - const actor = this.scene.MapPlayersByKey.get(event.userId); + constructor(private scene: GameScene, private connection: RoomConnection) { + this.subscription = connection.emoteEventMessageStream.subscribe((event) => { + const actor = this.scene.MapPlayersByKey.get(event.actorUserId); if (actor) { actor.playEmote(event.emote); } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 4800e259..bab589e1 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -40,7 +40,6 @@ import { ReconnectingSceneName } from "../Reconnecting/ReconnectingScene"; import { GameMap } from "./GameMap"; import { PlayerMovement } from "./PlayerMovement"; import { PlayersPositionInterpolator } from "./PlayersPositionInterpolator"; -import { worldFullMessageStream } from "../../Connexion/WorldFullMessageStream"; import { DirtyScene } from "./DirtyScene"; import { TextUtils } from "../Components/TextUtils"; import { joystickBaseImg, joystickBaseKey, joystickThumbImg, joystickThumbKey } from "../Components/MobileJoystick"; @@ -60,7 +59,6 @@ import type { PositionInterface, RoomJoinedMessageInterface, } from "../../Connexion/ConnexionModels"; -import type { UserMovedMessage } from "../../Messages/generated/messages_pb"; import type { RoomConnection } from "../../Connexion/RoomConnection"; import type { ActionableItem } from "../Items/ActionableItem"; import type { ItemFactoryInterface } from "../Items/ItemFactoryInterface"; @@ -90,7 +88,6 @@ import SpriteSheetFile = Phaser.Loader.FileTypes.SpriteSheetFile; import { deepCopy } from "deep-copy-ts"; import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR; import { MapStore } from "../../Stores/Utils/MapStore"; -import { SetPlayerDetailsMessage } from "../../Messages/generated/messages_pb"; import { followUsersColorStore, followUsersStore } from "../../Stores/FollowStore"; import { getColorRgbFromHue } from "../../WebRtc/ColorGenerator"; @@ -448,10 +445,6 @@ export class GameScene extends DirtyScene { this.pinchManager = new PinchManager(this); } - this.messageSubscription = worldFullMessageStream.stream.subscribe((message) => - this.showWorldFullError(message) - ); - const playerName = gameManager.getPlayerName(); if (!playerName) { throw "playerName is not set"; @@ -617,8 +610,6 @@ export class GameScene extends DirtyScene { this.connect(); } - this.emoteManager = new EmoteManager(this); - let oldPeerNumber = 0; this.peerStoreUnsubscribe = peerStore.subscribe((peers) => { const newPeerNumber = peers.size; @@ -693,7 +684,7 @@ export class GameScene extends DirtyScene { playersStore.connectToRoomConnection(this.connection); userIsAdminStore.set(this.connection.hasTag("admin")); - this.connection.onUserJoins((message: MessageUserJoined) => { + this.connection.userJoinedMessageStream.subscribe((message) => { const userMessage: AddPlayerInterface = { userId: message.userId, characterLayers: message.characterLayers, @@ -707,31 +698,33 @@ export class GameScene extends DirtyScene { this.addPlayer(userMessage); }); - this.connection.onUserMoved((message: UserMovedMessage) => { - const position = message.getPosition(); + this.connection.userMovedMessageStream.subscribe((message) => { + const position = message.position; if (position === undefined) { throw new Error("Position missing from UserMovedMessage"); } const messageUserMoved: MessageUserMovedInterface = { - userId: message.getUserid(), + userId: message.userId, position: ProtobufClientUtils.toPointInterface(position), }; this.updatePlayerPosition(messageUserMoved); }); - this.connection.onUserLeft((userId: number) => { - this.removePlayer(userId); + this.connection.userLeftMessageStream.subscribe((message) => { + this.removePlayer(message.userId); }); - this.connection.onGroupUpdatedOrCreated((groupPositionMessage: GroupCreatedUpdatedMessageInterface) => { - this.shareGroupPosition(groupPositionMessage); - }); + this.connection.groupUpdateMessageStream.subscribe( + (groupPositionMessage: GroupCreatedUpdatedMessageInterface) => { + this.shareGroupPosition(groupPositionMessage); + } + ); - this.connection.onGroupDeleted((groupId: number) => { + this.connection.groupDeleteMessageStream.subscribe((message) => { try { - this.deleteGroup(groupId); + this.deleteGroup(message.groupId); } catch (e) { console.error(e); } @@ -743,7 +736,7 @@ export class GameScene extends DirtyScene { this.createSuccessorGameScene(true, true); }); - this.connection.onActionableEvent((message) => { + this.connection.itemEventMessageStream.subscribe((message) => { const item = this.actionableItems.get(message.itemId); if (item === undefined) { console.warn( @@ -756,18 +749,29 @@ export class GameScene extends DirtyScene { item.fire(message.event, message.state, message.parameters); }); - this.connection.onPlayerDetailsUpdated((message) => { + this.connection.playerDetailsUpdatedMessageStream.subscribe((message) => { + if (message.details === undefined) { + throw new Error("Malformed message. Missing details in PlayerDetailsUpdatedMessage"); + } this.pendingEvents.enqueue({ type: "PlayerDetailsUpdated", - details: message, + details: { + userId: message.userId, + outlineColor: message.details.outlineColor, + removeOutlineColor: message.details.removeOutlineColor, + }, }); }); /** * Triggered when we receive the JWT token to connect to Jitsi */ - this.connection.onStartJitsiRoom((jwt, room) => { - this.startJitsi(room, jwt); + this.connection.sendJitsiJwtMessageStream.subscribe((message) => { + this.startJitsi(message.jitsiRoom, message.jwt); + }); + + this.messageSubscription = this.connection.worldFullMessageStream.subscribe((message) => { + this.showWorldFullError(message); }); // When connection is performed, let's connect SimplePeer @@ -842,6 +846,8 @@ export class GameScene extends DirtyScene { }); }); + this.emoteManager = new EmoteManager(this, this.connection); + // this.gameMap.onLeaveLayer((layers) => { // layers.forEach((layer) => { // iframeListener.sendLeaveLayerEvent(layer.name); diff --git a/front/src/Phaser/Game/SharedVariablesManager.ts b/front/src/Phaser/Game/SharedVariablesManager.ts index 5b5867dc..62d39d1a 100644 --- a/front/src/Phaser/Game/SharedVariablesManager.ts +++ b/front/src/Phaser/Game/SharedVariablesManager.ts @@ -41,7 +41,7 @@ export class SharedVariablesManager { this._variables.set(name, value); } - roomConnection.onSetVariable((name, value) => { + roomConnection.variableMessageStream.subscribe(({ name, value }) => { this._variables.set(name, value); // On server change, let's notify the iframes diff --git a/front/src/Stores/PlayersStore.ts b/front/src/Stores/PlayersStore.ts index 07c18b96..0676235a 100644 --- a/front/src/Stores/PlayersStore.ts +++ b/front/src/Stores/PlayersStore.ts @@ -3,6 +3,7 @@ import type { PlayerInterface } from "../Phaser/Game/PlayerInterface"; import type { RoomConnection } from "../Connexion/RoomConnection"; import { getRandomColor } from "../WebRtc/ColorGenerator"; import { localUserStore } from "../Connexion/LocalUserStore"; +import room from "../Api/iframe/room"; let idCount = 0; @@ -19,7 +20,8 @@ function createPlayersStore() { connectToRoomConnection: (roomConnection: RoomConnection) => { players = new Map(); set(players); - roomConnection.onUserJoins((message) => { + // TODO: it would be cool to unsubscribe properly here + roomConnection.userJoinedMessageStream.subscribe((message) => { update((users) => { users.set(message.userId, { userId: message.userId, @@ -33,9 +35,9 @@ function createPlayersStore() { return users; }); }); - roomConnection.onUserLeft((userId) => { + roomConnection.userLeftMessageStream.subscribe((message) => { update((users) => { - users.delete(userId); + users.delete(message.userId); return users; }); }); diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index ccbd0012..f4016015 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -75,23 +75,25 @@ export class SimplePeer { */ private initialise() { //receive signal by gemer - this.Connection.receiveWebrtcSignal((message: WebRtcSignalReceivedMessageInterface) => { + this.Connection.webRtcSignalToClientMessageStream.subscribe((message: WebRtcSignalReceivedMessageInterface) => { this.receiveWebrtcSignal(message); }); //receive signal by gemer - this.Connection.receiveWebrtcScreenSharingSignal((message: WebRtcSignalReceivedMessageInterface) => { - this.receiveWebrtcScreenSharingSignal(message); - }); + this.Connection.webRtcScreenSharingSignalToClientMessageStream.subscribe( + (message: WebRtcSignalReceivedMessageInterface) => { + this.receiveWebrtcScreenSharingSignal(message); + } + ); mediaManager.showGameOverlay(); //receive message start - this.Connection.receiveWebrtcStart((message: UserSimplePeerInterface) => { + this.Connection.webRtcStartMessageStream.subscribe((message: UserSimplePeerInterface) => { this.receiveWebrtcStart(message); }); - this.Connection.disconnectMessage((data: WebRtcDisconnectMessageInterface): void => { + this.Connection.webRtcDisconnectMessageStream.subscribe((data: WebRtcDisconnectMessageInterface): void => { this.closeConnection(data.userId); }); } diff --git a/front/yarn.lock b/front/yarn.lock index 1557a08e..faf5e681 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -150,6 +150,59 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.9.2.tgz#adea7b6953cbb34651766b0548468e743c6a2353" integrity sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q== +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + integrity sha1-m4sMxmPWaafY9vXQiToU00jzD78= + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== + +"@protobufjs/eventemitter@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + integrity sha1-NVy8mLr61ZePntCV85diHx0Ga3A= + +"@protobufjs/fetch@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + integrity sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU= + dependencies: + "@protobufjs/aspromise" "^1.1.1" + "@protobufjs/inquire" "^1.1.0" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + integrity sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E= + +"@protobufjs/inquire@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + integrity sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik= + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + integrity sha1-bMKyDFya1q0NzP0hynZz2Nf79o0= + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + integrity sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q= + +"@protobufjs/utf8@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= + "@sentry/types@^6.11.0": version "6.12.0" resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.12.0.tgz#b7395688a79403c6df8d8bb8d81deb8222519853" @@ -293,6 +346,11 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/long@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" + integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w== + "@types/mime@^1": version "1.3.2" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" @@ -317,11 +375,26 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-15.3.0.tgz#d6fed7d6bc6854306da3dea1af9f874b00783e26" integrity sha512-8/bnjSZD86ZfpBsDlCIkNXIvm+h6wi9g7IqL+kmFkQ+Wvu3JrasgLElfiPgoo8V8vVfnEi0QVS12gbl94h9YsQ== +"@types/node@>=13.7.0": + version "17.0.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.5.tgz#57ca67ec4e57ad9e4ef5a6bab48a15387a1c83e0" + integrity sha512-w3mrvNXLeDYV1GKTZorGJQivK6XLCoGwpnyJFbJVK/aTBQUxOCaa/GlFAAN3OTDFcb7h5tiFG+YXCO2By+riZw== + +"@types/object-hash@^1.3.0": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@types/object-hash/-/object-hash-1.3.4.tgz#079ba142be65833293673254831b5e3e847fe58b" + integrity sha512-xFdpkAkikBgqBdG9vIlsqffDV8GpvnPEzs0IUtr1v3BEB97ijsFQ4RXVbUZwjFThhB4MDSTUfvmxUD5PGx0wXA== + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/prettier@^1.19.0": + version "1.19.1" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f" + integrity sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ== + "@types/pug@^2.0.4": version "2.0.4" resolved "https://registry.yarnpkg.com/@types/pug/-/pug-2.0.4.tgz#8772fcd0418e3cd2cc171555d73007415051f4b2" @@ -1656,6 +1729,11 @@ cssesc@^3.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== +dataloader@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-1.4.0.tgz#bca11d867f5d3f1b9ed9f737bd15970c65dff5c8" + integrity sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw== + debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -3662,7 +3740,7 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.20: +lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -3695,6 +3773,11 @@ lokijs@^1.5.12: resolved "https://registry.yarnpkg.com/lokijs/-/lokijs-1.5.12.tgz#cb55b37009bdf09ee7952a6adddd555b893653a0" integrity sha512-Q5ALD6JiS6xAUWCwX3taQmgwxyveCtIIuL08+ml0nHwT3k0S/GIFJN+Hd38b1qYIMaE5X++iqsqWVksz7SYW+Q== +long@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== + lower-case@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" @@ -4098,6 +4181,11 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" +object-hash@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" + integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA== + object-inspect@^1.9.0: version "1.10.3" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369" @@ -4590,6 +4678,11 @@ prettier-plugin-svelte@^2.5.0: resolved "https://registry.yarnpkg.com/prettier-plugin-svelte/-/prettier-plugin-svelte-2.5.0.tgz#7922534729f7febe59b4c56c3f5360539f0d8ab1" integrity sha512-+iHY2uGChOngrgKielJUnqo74gIL/EO5oeWm8MftFWjEi213lq9QYTOwm1pv4lI1nA61tdgf80CF2i5zMcu1kw== +prettier@^2.0.2: + version "2.5.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" + integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== + prettier@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.1.tgz#76903c3f8c4449bc9ac597acefa24dc5ad4cbea6" @@ -4618,6 +4711,25 @@ progress@^2.0.0: resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== +protobufjs@^6.8.8: + version "6.11.2" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.2.tgz#de39fabd4ed32beaa08e9bb1e30d08544c1edf8b" + integrity sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/long" "^4.0.1" + "@types/node" ">=13.7.0" + long "^4.0.0" + proxy-addr@~2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" @@ -5819,6 +5931,35 @@ ts-node@^10.4.0: make-error "^1.1.1" yn "3.1.1" +ts-poet@^4.5.0: + version "4.6.1" + resolved "https://registry.yarnpkg.com/ts-poet/-/ts-poet-4.6.1.tgz#015dc823d726655af9f095c900f84ed7c60e2dd3" + integrity sha512-DXJ+mBJIDp+jiaUgB4N5I/sczHHDU2FWacdbDNVAVS4Mh4hb7ckpvUWVW7m7/nAOcjR0r4Wt+7AoO7FeJKExfA== + dependencies: + "@types/prettier" "^1.19.0" + lodash "^4.17.15" + prettier "^2.0.2" + +ts-proto-descriptors@^1.2.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/ts-proto-descriptors/-/ts-proto-descriptors-1.3.1.tgz#760ebaaa19475b03662f7b358ffea45b9c5348f5" + integrity sha512-Cybb3fqceMwA6JzHdC32dIo8eVGVmXrM6TWhdk1XQVVHT/6OQqk0ioyX1dIdu3rCIBhRmWUhUE4HsyK+olmgMw== + dependencies: + long "^4.0.0" + protobufjs "^6.8.8" + +ts-proto@^1.96.0: + version "1.96.0" + resolved "https://registry.yarnpkg.com/ts-proto/-/ts-proto-1.96.0.tgz#63768d7da533b337aee84db065dd66773bd4cac9" + integrity sha512-fKwaGzi8EOCU9xwmcXK917jj1WhFdLbFkPRawQ+5CAZM9eSXr/mpkz/yEctXCiuei364z6jAB2Odb64KCDFTPQ== + dependencies: + "@types/object-hash" "^1.3.0" + dataloader "^1.4.0" + object-hash "^1.3.1" + protobufjs "^6.8.8" + ts-poet "^4.5.0" + ts-proto-descriptors "^1.2.1" + tsconfig-paths@^3.9.0: version "3.9.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" diff --git a/messages/package.json b/messages/package.json index 6095a626..e6f04ba5 100644 --- a/messages/package.json +++ b/messages/package.json @@ -4,13 +4,14 @@ "description": "", "scripts": { "proto": "grpc_tools_node_protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts --grpc_out=generated --js_out=\"import_style=commonjs,binary:generated\" --ts_out=generated -I ./protos protos/*.proto", + "ts-proto": "grpc_tools_node_protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=ts-proto-generated --ts_proto_opt=oneof=unions --ts_proto_opt=esModuleInterop=true protos/*.proto", "copy-to-back": "rm -rf ../back/src/Messages/generated && cp -rf generated/ ../back/src/Messages/generated", - "copy-to-front": "rm -rf ../front/src/Messages/generated && cp -rf generated/ ../front/src/Messages/generated", + "copy-to-front-ts-proto": "sed 's/import { Observable } from \"rxjs\";/import type { Observable } from \"rxjs\";/g' ts-proto-generated/protos/messages.ts > ../front/src/Messages/ts-proto-generated/messages.ts", "copy-to-pusher": "rm -rf ../pusher/src/Messages/generated && cp -rf generated/ ../pusher/src/Messages/generated", "json-copy-to-pusher": "rm -rf ../pusher/src/Messages/JsonMessages/* && cp -rf JsonMessages/* ../pusher/src/Messages/JsonMessages/", "json-copy-to-front": "rm -rf ../front/src/Messages/JsonMessages/* && cp -rf JsonMessages/* ../front/src/Messages/JsonMessages/", "precommit": "lint-staged", - "proto-all": "yarn run proto && yarn run copy-to-back && yarn run copy-to-front && yarn run copy-to-pusher && yarn run json-copy-to-pusher && yarn run json-copy-to-front", + "proto-all": "yarn run proto && yarn run ts-proto && yarn run copy-to-back && yarn run copy-to-front-ts-proto && yarn run copy-to-pusher && yarn run json-copy-to-pusher && yarn run json-copy-to-front", "proto:watch": "yarn run proto-all; inotifywait -q -m -e close_write protos/messages.proto JsonMessages/ | while read -r filename event; do yarn run proto-all; done", "pretty": "yarn prettier --write 'JsonMessages/**/*.ts'", "pretty-check": "yarn prettier --check 'JsonMessages/**/*.ts'" @@ -18,7 +19,8 @@ "dependencies": { "generic-type-guard": "^3.5.0", "google-protobuf": "^3.13.0", - "grpc": "^1.24.4" + "grpc": "^1.24.4", + "ts-proto": "^1.96.0" }, "devDependencies": { "@types/google-protobuf": "^3.7.4", diff --git a/messages/ts-proto-generated/.gitignore b/messages/ts-proto-generated/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/messages/ts-proto-generated/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/messages/yarn.lock b/messages/yarn.lock index 5d040c6f..8ba2fe90 100644 --- a/messages/yarn.lock +++ b/messages/yarn.lock @@ -174,6 +174,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.7.tgz#8ea1e8f8eae2430cf440564b98c6dfce1ec5945d" integrity sha512-Zw1vhUSQZYw+7u5dAwNbIA9TuTotpzY/OF7sJM9FqPOF3SPjKnxrjoTktXDZgUjybf4cWVBP7O8wvKdSaGHweg== +"@types/node@>=13.7.0": + version "17.0.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.5.tgz#57ca67ec4e57ad9e4ef5a6bab48a15387a1c83e0" + integrity sha512-w3mrvNXLeDYV1GKTZorGJQivK6XLCoGwpnyJFbJVK/aTBQUxOCaa/GlFAAN3OTDFcb7h5tiFG+YXCO2By+riZw== + "@types/node@^12.12.29": version "12.19.4" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.4.tgz#cdfbb62e26c7435ed9aab9c941393cc3598e9b46" @@ -184,6 +189,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.30.tgz#1ed6e01e4ca576d5aec9cc802cc3bcf94c274192" integrity sha512-HmqFpNzp3TSELxU/bUuRK+xzarVOAsR00hzcvM0TXrMlt/+wcSLa5q6YhTb6/cA6wqDCZLDcfd8fSL95x5h7AA== +"@types/object-hash@^1.3.0": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@types/object-hash/-/object-hash-1.3.4.tgz#079ba142be65833293673254831b5e3e847fe58b" + integrity sha512-xFdpkAkikBgqBdG9vIlsqffDV8GpvnPEzs0IUtr1v3BEB97ijsFQ4RXVbUZwjFThhB4MDSTUfvmxUD5PGx0wXA== + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" @@ -194,6 +204,11 @@ resolved "https://registry.yarnpkg.com/@types/parsimmon/-/parsimmon-1.10.4.tgz#7639e16015440d9baf622f83c12dae47787226b7" integrity sha512-M56NfQHfaWuaj6daSgCVs7jh8fXLI3LmxjRoQxmOvYesgIkI+9HPsDLO0vd7wX7cwA0D0ZWFEJdp0VPwLdS+bQ== +"@types/prettier@^1.19.0": + version "1.19.1" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f" + integrity sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ== + "@typescript-eslint/eslint-plugin@^4.7.0": version "4.7.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.7.0.tgz#85c9bbda00c0cb604d3c241f7bc7fb171a2d3479" @@ -1156,6 +1171,11 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +dataloader@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-1.4.0.tgz#bca11d867f5d3f1b9ed9f737bd15970c65dff5c8" + integrity sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw== + date.js@^0.3.1: version "0.3.3" resolved "https://registry.yarnpkg.com/date.js/-/date.js-0.3.3.tgz#ef1e92332f507a638795dbb985e951882e50bbda" @@ -3154,6 +3174,11 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" +object-hash@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" + integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA== + object-inspect@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" @@ -3423,7 +3448,7 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prettier@^2.3.1: +prettier@^2.0.2, prettier@^2.3.1: version "2.5.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== @@ -3467,6 +3492,25 @@ protobufjs@^6.10.1: "@types/node" "^13.7.0" long "^4.0.0" +protobufjs@^6.8.8: + version "6.11.2" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.2.tgz#de39fabd4ed32beaa08e9bb1e30d08544c1edf8b" + integrity sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/long" "^4.0.1" + "@types/node" ">=13.7.0" + long "^4.0.0" + psl@^1.1.28: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" @@ -4245,6 +4289,35 @@ tough-cookie@~2.5.0: psl "^1.1.28" punycode "^2.1.1" +ts-poet@^4.5.0: + version "4.6.1" + resolved "https://registry.yarnpkg.com/ts-poet/-/ts-poet-4.6.1.tgz#015dc823d726655af9f095c900f84ed7c60e2dd3" + integrity sha512-DXJ+mBJIDp+jiaUgB4N5I/sczHHDU2FWacdbDNVAVS4Mh4hb7ckpvUWVW7m7/nAOcjR0r4Wt+7AoO7FeJKExfA== + dependencies: + "@types/prettier" "^1.19.0" + lodash "^4.17.15" + prettier "^2.0.2" + +ts-proto-descriptors@^1.2.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/ts-proto-descriptors/-/ts-proto-descriptors-1.3.1.tgz#760ebaaa19475b03662f7b358ffea45b9c5348f5" + integrity sha512-Cybb3fqceMwA6JzHdC32dIo8eVGVmXrM6TWhdk1XQVVHT/6OQqk0ioyX1dIdu3rCIBhRmWUhUE4HsyK+olmgMw== + dependencies: + long "^4.0.0" + protobufjs "^6.8.8" + +ts-proto@^1.96.0: + version "1.96.0" + resolved "https://registry.yarnpkg.com/ts-proto/-/ts-proto-1.96.0.tgz#63768d7da533b337aee84db065dd66773bd4cac9" + integrity sha512-fKwaGzi8EOCU9xwmcXK917jj1WhFdLbFkPRawQ+5CAZM9eSXr/mpkz/yEctXCiuei364z6jAB2Odb64KCDFTPQ== + dependencies: + "@types/object-hash" "^1.3.0" + dataloader "^1.4.0" + object-hash "^1.3.1" + protobufjs "^6.8.8" + ts-poet "^4.5.0" + ts-proto-descriptors "^1.2.1" + tsconfig-paths@^3.9.0: version "3.9.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b"