Merge branch 'develop' of github.com:thecodingmachine/workadventure into feat/follow-woka

This commit is contained in:
David Négrier 2021-12-23 16:04:59 +01:00
commit 368a115b4c
93 changed files with 4017 additions and 2405 deletions

View file

@ -36,9 +36,11 @@ export class DebugController {
return "BatchedMessages";
}
if (value instanceof Map) {
const obj: any = {}; // eslint-disable-line @typescript-eslint/no-explicit-any
const obj: { [key: string | number]: unknown } = {};
for (const [mapKey, mapValue] of value.entries()) {
obj[mapKey] = mapValue;
if (typeof mapKey === "number" || typeof mapKey === "string") {
obj[mapKey] = mapValue;
}
}
return obj;
} else if (value instanceof Set) {

View file

@ -1,12 +1,10 @@
import { App } from "../Server/sifrr.server";
import { HttpRequest, HttpResponse } from "uWebSockets.js";
const register = require("prom-client").register;
const collectDefaultMetrics = require("prom-client").collectDefaultMetrics;
import { register, collectDefaultMetrics } from "prom-client";
export class PrometheusController {
constructor(private App: App) {
collectDefaultMetrics({
timeout: 10000,
gcDurationBuckets: [0.001, 0.01, 0.1, 1, 2, 5], // These are the default buckets.
});

View file

@ -2,7 +2,13 @@ import { PointInterface } from "./Websocket/PointInterface";
import { Group } from "./Group";
import { User, UserSocket } from "./User";
import { PositionInterface } from "_Model/PositionInterface";
import { EmoteCallback, EntersCallback, LeavesCallback, MovesCallback } from "_Model/Zone";
import {
EmoteCallback,
EntersCallback,
LeavesCallback,
MovesCallback,
PlayerDetailsUpdatedCallback,
} from "_Model/Zone";
import { PositionNotifier } from "./PositionNotifier";
import { Movable } from "_Model/Movable";
import {
@ -11,6 +17,7 @@ import {
EmoteEventMessage,
ErrorMessage,
JoinRoomMessage,
SetPlayerDetailsMessage,
SubToPusherRoomMessage,
VariableMessage,
VariableWithTagMessage,
@ -28,6 +35,7 @@ import { ADMIN_API_URL } from "../Enum/EnvironmentVariable";
import { LocalUrlError } from "../Services/LocalUrlError";
import { emitErrorOnRoomSocket } from "../Services/MessageHelpers";
import { VariableError } from "../Services/VariableError";
import { isRoomRedirect } from "../Services/AdminApi/RoomRedirect";
export type ConnectCallback = (user: User, group: Group) => void;
export type DisconnectCallback = (user: User, group: Group) => void;
@ -57,10 +65,19 @@ export class GameRoom {
onEnters: EntersCallback,
onMoves: MovesCallback,
onLeaves: LeavesCallback,
onEmote: EmoteCallback
onEmote: EmoteCallback,
onPlayerDetailsUpdated: PlayerDetailsUpdatedCallback
) {
// A zone is 10 sprites wide.
this.positionNotifier = new PositionNotifier(320, 320, onEnters, onMoves, onLeaves, onEmote);
this.positionNotifier = new PositionNotifier(
320,
320,
onEnters,
onMoves,
onLeaves,
onEmote,
onPlayerDetailsUpdated
);
}
public static async create(
@ -72,7 +89,8 @@ export class GameRoom {
onEnters: EntersCallback,
onMoves: MovesCallback,
onLeaves: LeavesCallback,
onEmote: EmoteCallback
onEmote: EmoteCallback,
onPlayerDetailsUpdated: PlayerDetailsUpdatedCallback
): Promise<GameRoom> {
const mapDetails = await GameRoom.getMapDetails(roomUrl);
@ -86,7 +104,8 @@ export class GameRoom {
onEnters,
onMoves,
onLeaves,
onEmote
onEmote,
onPlayerDetailsUpdated
);
return gameRoom;
@ -185,6 +204,14 @@ export class GameRoom {
this.updateUserGroup(user);
}
updatePlayerDetails(user: User, playerDetailsMessage: SetPlayerDetailsMessage) {
if (playerDetailsMessage.getRemoveoutlinecolor()) {
user.outlineColor = undefined;
} else {
user.outlineColor = playerDetailsMessage.getOutlinecolor();
}
}
private updateUserGroup(user: User): void {
user.group?.updatePosition();
user.group?.searchForNearbyUsers();
@ -277,9 +304,7 @@ export class GameRoom {
this.positionNotifier.leave(group);
group.destroy();
if (!this.groups.has(group)) {
throw new Error(
"Could not find group " + group.getId() + " referenced by user " + user.id + " in World."
);
throw new Error(`Could not find group ${group.getId()} referenced by user ${user.id} in World.`);
}
this.groups.delete(group);
//todo: is the group garbage collected?
@ -480,9 +505,9 @@ export class GameRoom {
}
const result = await adminApi.fetchMapDetails(roomUrl);
if (!isMapDetailsData(result)) {
console.error("Unexpected room details received from server", result);
throw new Error("Unexpected room details received from server");
if (isRoomRedirect(result)) {
console.error("Unexpected room redirect received while querying map details", result);
throw new Error("Unexpected room redirect received while querying map details");
}
return result;
}

View file

@ -116,7 +116,7 @@ export class Group implements Movable {
leave(user: User): void {
const success = this.users.delete(user);
if (success === false) {
throw new Error("Could not find user " + user.id + " in the group " + this.id);
throw new Error(`Could not find user ${user.id} in the group ${this.id}`);
}
user.group = undefined;

View file

@ -8,12 +8,19 @@
* The PositionNotifier is important for performance. It allows us to send the position of players only to a restricted
* number of players around the current player.
*/
import { EmoteCallback, EntersCallback, LeavesCallback, MovesCallback, Zone } from "./Zone";
import {
EmoteCallback,
EntersCallback,
LeavesCallback,
MovesCallback,
PlayerDetailsUpdatedCallback,
Zone,
} from "./Zone";
import { Movable } from "_Model/Movable";
import { PositionInterface } from "_Model/PositionInterface";
import { ZoneSocket } from "../RoomManager";
import { User } from "../Model/User";
import { EmoteEventMessage } from "../Messages/generated/messages_pb";
import { EmoteEventMessage, SetPlayerDetailsMessage } from "../Messages/generated/messages_pb";
interface ZoneDescriptor {
i: number;
@ -42,7 +49,8 @@ export class PositionNotifier {
private onUserEnters: EntersCallback,
private onUserMoves: MovesCallback,
private onUserLeaves: LeavesCallback,
private onEmote: EmoteCallback
private onEmote: EmoteCallback,
private onPlayerDetailsUpdated: PlayerDetailsUpdatedCallback
) {}
private getZoneDescriptorFromCoordinates(x: number, y: number): ZoneDescriptor {
@ -98,7 +106,15 @@ export class PositionNotifier {
let zone = this.zones[j][i];
if (zone === undefined) {
zone = new Zone(this.onUserEnters, this.onUserMoves, this.onUserLeaves, this.onEmote, i, j);
zone = new Zone(
this.onUserEnters,
this.onUserMoves,
this.onUserLeaves,
this.onEmote,
this.onPlayerDetailsUpdated,
i,
j
);
this.zones[j][i] = zone;
}
return zone;
@ -132,4 +148,11 @@ export class PositionNotifier {
}
}
}
public updatePlayerDetails(user: User, playerDetails: SetPlayerDetailsMessage) {
const position = user.getPosition();
const zoneDesc = this.getZoneDescriptorFromCoordinates(position.x, position.y);
const zone = this.getZone(zoneDesc.i, zoneDesc.j);
zone.updatePlayerDetails(user, playerDetails);
}
}

View file

@ -11,6 +11,7 @@ import {
FollowConfirmationMessage,
PusherToBackMessage,
ServerToClientMessage,
SetPlayerDetailsMessage,
SubMessage,
} from "../Messages/generated/messages_pb";
import { CharacterLayer } from "_Model/Websocket/CharacterLayer";
@ -35,7 +36,8 @@ export class User implements Movable {
public readonly visitCardUrl: string | null,
public readonly name: string,
public readonly characterLayers: CharacterLayer[],
public readonly companion?: CompanionMessage
public readonly companion?: CompanionMessage,
private _outlineColor?: number | undefined
) {
this.listenedZones = new Set<Zone>();
@ -112,4 +114,17 @@ export class User implements Movable {
}, 100);
}
}
public set outlineColor(value: number | undefined) {
this._outlineColor = value;
const playerDetails = new SetPlayerDetailsMessage();
if (value === undefined) {
playerDetails.setRemoveoutlinecolor(true);
} else {
playerDetails.setOutlinecolor(value);
}
this.positionNotifier.updatePlayerDetails(this, playerDetails);
}
}

View file

@ -3,12 +3,20 @@ import { PositionInterface } from "_Model/PositionInterface";
import { Movable } from "./Movable";
import { Group } from "./Group";
import { ZoneSocket } from "../RoomManager";
import { EmoteEventMessage } from "../Messages/generated/messages_pb";
import {
EmoteEventMessage,
SetPlayerDetailsMessage,
PlayerDetailsUpdatedMessage,
} from "../Messages/generated/messages_pb";
export type EntersCallback = (thing: Movable, fromZone: Zone | null, listener: ZoneSocket) => void;
export type MovesCallback = (thing: Movable, position: PositionInterface, listener: ZoneSocket) => void;
export type LeavesCallback = (thing: Movable, newZone: Zone | null, listener: ZoneSocket) => void;
export type EmoteCallback = (emoteEventMessage: EmoteEventMessage, listener: ZoneSocket) => void;
export type PlayerDetailsUpdatedCallback = (
playerDetailsUpdatedMessage: PlayerDetailsUpdatedMessage,
listener: ZoneSocket
) => void;
export class Zone {
private things: Set<Movable> = new Set<Movable>();
@ -19,6 +27,7 @@ export class Zone {
private onMoves: MovesCallback,
private onLeaves: LeavesCallback,
private onEmote: EmoteCallback,
private onPlayerDetailsUpdated: PlayerDetailsUpdatedCallback,
public readonly x: number,
public readonly y: number
) {}
@ -30,21 +39,13 @@ export class Zone {
const result = this.things.delete(thing);
if (!result) {
if (thing instanceof User) {
throw new Error("Could not find user in zone " + thing.id);
throw new Error(`Could not find user in zone ${thing.id}`);
}
if (thing instanceof Group) {
throw new Error(
"Could not find group " +
thing.getId() +
" in zone (" +
this.x +
"," +
this.y +
"). Position of group: (" +
thing.getPosition().x +
"," +
thing.getPosition().y +
")"
`Could not find group ${thing.getId()} in zone (${this.x},${this.y}). Position of group: (${
thing.getPosition().x
},${thing.getPosition().y})`
);
}
}
@ -106,4 +107,14 @@ export class Zone {
this.onEmote(emoteEventMessage, listener);
}
}
public updatePlayerDetails(user: User, playerDetails: SetPlayerDetailsMessage) {
const playerDetailsUpdatedMessage = new PlayerDetailsUpdatedMessage();
playerDetailsUpdatedMessage.setUserid(user.id);
playerDetailsUpdatedMessage.setDetails(playerDetails);
for (const listener of this.listeners) {
this.onPlayerDetailsUpdated(playerDetailsUpdatedMessage, listener);
}
}
}

View file

@ -5,6 +5,7 @@ import {
AdminPusherToBackMessage,
AdminRoomMessage,
BanMessage,
BanUserMessage,
BatchToPusherMessage,
BatchToPusherRoomMessage,
EmotePromptMessage,
@ -19,7 +20,9 @@ import {
QueryJitsiJwtMessage,
RefreshRoomPromptMessage,
RoomMessage,
SendUserMessage,
ServerToAdminClientMessage,
SetPlayerDetailsMessage,
SilentMessage,
UserMovesMessage,
VariableMessage,
@ -139,14 +142,17 @@ const roomManager: IRoomManagerServer = {
);
} else if (message.hasSendusermessage()) {
const sendUserMessage = message.getSendusermessage();
if (sendUserMessage !== undefined) {
socketManager.handlerSendUserMessage(user, sendUserMessage);
}
socketManager.handleSendUserMessage(user, sendUserMessage as SendUserMessage);
} else if (message.hasBanusermessage()) {
const banUserMessage = message.getBanusermessage();
if (banUserMessage !== undefined) {
socketManager.handlerBanUserMessage(room, user, banUserMessage);
}
socketManager.handlerBanUserMessage(room, user, banUserMessage as BanUserMessage);
} else if (message.hasSetplayerdetailsmessage()) {
const setPlayerDetailsMessage = message.getSetplayerdetailsmessage();
socketManager.handleSetPlayerDetails(
room,
user,
setPlayerDetailsMessage as SetPlayerDetailsMessage
);
} else {
throw new Error("Unhandled message type");
}
@ -181,7 +187,7 @@ const roomManager: IRoomManagerServer = {
socketManager
.addZoneListener(call, zoneMessage.getRoomid(), zoneMessage.getX(), zoneMessage.getY())
.catch((e) => {
emitErrorOnZoneSocket(call, e.toString());
emitErrorOnZoneSocket(call, e);
});
call.on("cancelled", () => {
@ -211,7 +217,7 @@ const roomManager: IRoomManagerServer = {
const roomMessage = call.request;
socketManager.addRoomListener(call, roomMessage.getRoomid()).catch((e) => {
emitErrorOnRoomSocket(call, e.toString());
emitErrorOnRoomSocket(call, e);
});
call.on("cancelled", () => {
@ -272,7 +278,12 @@ const roomManager: IRoomManagerServer = {
},
sendAdminMessage(call: ServerUnaryCall<AdminMessage>, callback: sendUnaryData<EmptyMessage>): void {
socketManager
.sendAdminMessage(call.request.getRoomid(), call.request.getRecipientuuid(), call.request.getMessage())
.sendAdminMessage(
call.request.getRoomid(),
call.request.getRecipientuuid(),
call.request.getMessage(),
call.request.getType()
)
.catch((e) => console.error(e));
callback(null, new EmptyMessage());

View file

@ -1,3 +1,5 @@
/* eslint-disable */
import { Readable } from "stream";
import { us_listen_socket_close, TemplatedApp, HttpResponse, HttpRequest } from "uWebSockets.js";

View file

@ -1,3 +1,5 @@
/* eslint-disable */
import { createWriteStream } from "fs";
import { join, dirname } from "path";
import Busboy from "busboy";

View file

@ -1,3 +1,5 @@
/* eslint-disable */
import { ReadStream } from "fs";
// eslint-disable-next-line @typescript-eslint/no-explicit-any

View file

@ -1,7 +1,7 @@
import { ADMIN_API_TOKEN, ADMIN_API_URL } from "../Enum/EnvironmentVariable";
import Axios from "axios";
import { MapDetailsData } from "./AdminApi/MapDetailsData";
import { RoomRedirect } from "./AdminApi/RoomRedirect";
import { isMapDetailsData, MapDetailsData } from "./AdminApi/MapDetailsData";
import { isRoomRedirect, RoomRedirect } from "./AdminApi/RoomRedirect";
class AdminApi {
async fetchMapDetails(playUri: string): Promise<MapDetailsData | RoomRedirect> {
@ -17,6 +17,12 @@ class AdminApi {
headers: { Authorization: `${ADMIN_API_TOKEN}` },
params,
});
if (!isMapDetailsData(res.data) && !isRoomRedirect(res.data)) {
console.error("Unexpected answer from the /api/map admin endpoint.", res.data);
throw new Error("Unexpected answer from the /api/map admin endpoint.");
}
return res.data;
}
}

View file

@ -1,4 +1,4 @@
const EventEmitter = require("events");
import { EventEmitter } from "events";
const clientJoinEvent = "clientJoin";
const clientLeaveEvent = "clientLeave";

View file

@ -32,7 +32,7 @@ class MapFetcher {
//throw new Error("Invalid map format for map " + mapUrl);
console.error("Invalid map format for map " + mapUrl);
}
/* eslint-disable-next-line @typescript-eslint/no-unsafe-return */
return res.data;
}

View file

@ -10,7 +10,19 @@ import {
import { UserSocket } from "_Model/User";
import { RoomSocket, ZoneSocket } from "../RoomManager";
export function emitError(Client: UserSocket, message: string): void {
function getMessageFromError(error: unknown): string {
if (error instanceof Error) {
return error.message;
} else if (typeof error === "string") {
return error;
} else {
return "Unknown error";
}
}
export function emitError(Client: UserSocket, error: unknown): void {
const message = getMessageFromError(error);
const errorMessage = new ErrorMessage();
errorMessage.setMessage(message);
@ -23,8 +35,9 @@ export function emitError(Client: UserSocket, message: string): void {
console.warn(message);
}
export function emitErrorOnRoomSocket(Client: RoomSocket, message: string): void {
console.error(message);
export function emitErrorOnRoomSocket(Client: RoomSocket, error: unknown): void {
console.error(error);
const message = getMessageFromError(error);
const errorMessage = new ErrorMessage();
errorMessage.setMessage(message);
@ -41,8 +54,9 @@ export function emitErrorOnRoomSocket(Client: RoomSocket, message: string): void
console.warn(message);
}
export function emitErrorOnZoneSocket(Client: ZoneSocket, message: string): void {
console.error(message);
export function emitErrorOnZoneSocket(Client: ZoneSocket, error: unknown): void {
console.error(error);
const message = getMessageFromError(error);
const errorMessage = new ErrorMessage();
errorMessage.setMessage(message);

View file

@ -36,6 +36,8 @@ import {
VariableMessage,
BatchToPusherRoomMessage,
SubToPusherRoomMessage,
SetPlayerDetailsMessage,
PlayerDetailsUpdatedMessage,
} from "../Messages/generated/messages_pb";
import { User, UserSocket } from "../Model/User";
import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils";
@ -154,20 +156,9 @@ export class SocketManager {
//room.setViewport(client, client.viewport);
}
// Useless now, will be useful again if we allow editing details in game
/*handleSetPlayerDetails(client: UserSocket, playerDetailsMessage: SetPlayerDetailsMessage) {
const playerDetails = {
name: playerDetailsMessage.getName(),
characterLayers: playerDetailsMessage.getCharacterlayersList()
};
//console.log(SocketIoEvent.SET_PLAYER_DETAILS, playerDetails);
if (!isSetPlayerDetailsMessage(playerDetails)) {
emitError(client, 'Invalid SET_PLAYER_DETAILS message received: ');
return;
}
client.name = playerDetails.name;
client.characterLayers = SocketManager.mergeCharacterLayersAndCustomTextures(playerDetails.characterLayers, client.textures);
}*/
handleSetPlayerDetails(room: GameRoom, user: User, playerDetailsMessage: SetPlayerDetailsMessage) {
room.updatePlayerDetails(user, playerDetailsMessage);
}
handleSilentMessage(room: GameRoom, user: User, silentMessage: SilentMessage) {
room.setSilent(user, silentMessage.getSilent());
@ -209,7 +200,7 @@ export class SocketManager {
webrtcSignalToClient.setSignal(data.getSignal());
// TODO: only compute credentials if data.signal.type === "offer"
if (TURN_STATIC_AUTH_SECRET !== "") {
const { username, password } = this.getTURNCredentials("" + user.id, TURN_STATIC_AUTH_SECRET);
const { username, password } = this.getTURNCredentials(user.id.toString(), TURN_STATIC_AUTH_SECRET);
webrtcSignalToClient.setWebrtcusername(username);
webrtcSignalToClient.setWebrtcpassword(password);
}
@ -239,7 +230,7 @@ export class SocketManager {
webrtcSignalToClient.setSignal(data.getSignal());
// TODO: only compute credentials if data.signal.type === "offer"
if (TURN_STATIC_AUTH_SECRET !== "") {
const { username, password } = this.getTURNCredentials("" + user.id, TURN_STATIC_AUTH_SECRET);
const { username, password } = this.getTURNCredentials(user.id.toString(), TURN_STATIC_AUTH_SECRET);
webrtcSignalToClient.setWebrtcusername(username);
webrtcSignalToClient.setWebrtcpassword(password);
}
@ -285,7 +276,9 @@ export class SocketManager {
(thing: Movable, newZone: Zone | null, listener: ZoneSocket) =>
this.onClientLeave(thing, newZone, listener),
(emoteEventMessage: EmoteEventMessage, listener: ZoneSocket) =>
this.onEmote(emoteEventMessage, listener)
this.onEmote(emoteEventMessage, listener),
(playerDetailsUpdatedMessage: PlayerDetailsUpdatedMessage, listener: ZoneSocket) =>
this.onPlayerDetailsUpdated(playerDetailsUpdatedMessage, listener)
)
.then((gameRoom) => {
gaugeManager.incNbRoomGauge();
@ -320,7 +313,7 @@ export class SocketManager {
if (thing instanceof User) {
const userJoinedZoneMessage = new UserJoinedZoneMessage();
if (!Number.isInteger(thing.id)) {
throw new Error("clientUser.userId is not an integer " + thing.id);
throw new Error(`clientUser.userId is not an integer ${thing.id}`);
}
userJoinedZoneMessage.setUserid(thing.id);
userJoinedZoneMessage.setUseruuid(thing.uuid);
@ -332,6 +325,12 @@ export class SocketManager {
userJoinedZoneMessage.setVisitcardurl(thing.visitCardUrl);
}
userJoinedZoneMessage.setCompanion(thing.companion);
if (thing.outlineColor === undefined) {
userJoinedZoneMessage.setHasoutline(false);
} else {
userJoinedZoneMessage.setHasoutline(true);
userJoinedZoneMessage.setOutlinecolor(thing.outlineColor);
}
const subMessage = new SubToPusherMessage();
subMessage.setUserjoinedzonemessage(userJoinedZoneMessage);
@ -381,6 +380,13 @@ export class SocketManager {
emitZoneMessage(subMessage, client);
}
private onPlayerDetailsUpdated(playerDetailsUpdatedMessage: PlayerDetailsUpdatedMessage, client: ZoneSocket) {
const subMessage = new SubToPusherMessage();
subMessage.setPlayerdetailsupdatedmessage(playerDetailsUpdatedMessage);
emitZoneMessage(subMessage, client);
}
private emitCreateUpdateGroupEvent(client: ZoneSocket, fromZone: Zone | null, group: Group): void {
const position = group.getPosition();
const pointMessage = new PointMessage();
@ -443,7 +449,10 @@ export class SocketManager {
webrtcStartMessage1.setUserid(otherUser.id);
webrtcStartMessage1.setInitiator(true);
if (TURN_STATIC_AUTH_SECRET !== "") {
const { username, password } = this.getTURNCredentials("" + otherUser.id, TURN_STATIC_AUTH_SECRET);
const { username, password } = this.getTURNCredentials(
otherUser.id.toString(),
TURN_STATIC_AUTH_SECRET
);
webrtcStartMessage1.setWebrtcusername(username);
webrtcStartMessage1.setWebrtcpassword(password);
}
@ -457,7 +466,7 @@ export class SocketManager {
webrtcStartMessage2.setUserid(user.id);
webrtcStartMessage2.setInitiator(false);
if (TURN_STATIC_AUTH_SECRET !== "") {
const { username, password } = this.getTURNCredentials("" + user.id, TURN_STATIC_AUTH_SECRET);
const { username, password } = this.getTURNCredentials(user.id.toString(), TURN_STATIC_AUTH_SECRET);
webrtcStartMessage2.setWebrtcusername(username);
webrtcStartMessage2.setWebrtcpassword(password);
}
@ -481,7 +490,7 @@ export class SocketManager {
hmac.setEncoding("base64");
hmac.write(username);
hmac.end();
const password = hmac.read();
const password = hmac.read() as string;
return {
username: username,
password: password,
@ -575,7 +584,7 @@ export class SocketManager {
user.socket.write(serverToClientMessage);
}
public handlerSendUserMessage(user: User, sendUserMessageToSend: SendUserMessage) {
public handleSendUserMessage(user: User, sendUserMessageToSend: SendUserMessage) {
const sendUserMessage = new SendUserMessage();
sendUserMessage.setMessage(sendUserMessageToSend.getMessage());
sendUserMessage.setType(sendUserMessageToSend.getType());
@ -694,7 +703,7 @@ export class SocketManager {
}
}
public async sendAdminMessage(roomId: string, recipientUuid: string, message: string): Promise<void> {
public async sendAdminMessage(roomId: string, recipientUuid: string, message: string, type: string): Promise<void> {
const room = await this.roomsPromises.get(roomId);
if (!room) {
console.error(
@ -718,7 +727,7 @@ export class SocketManager {
for (const recipient of recipients) {
const sendUserMessage = new SendUserMessage();
sendUserMessage.setMessage(message);
sendUserMessage.setType("ban"); //todo: is the type correct?
sendUserMessage.setType(type);
const serverToClientMessage = new ServerToClientMessage();
serverToClientMessage.setSendusermessage(sendUserMessage);

View file

@ -101,11 +101,11 @@ export class VariablesManager {
}
// We store a copy of the object (to make it immutable)
objects.set(object.name, this.iTiledObjectToVariable(object));
objects.set(object.name as string, this.iTiledObjectToVariable(object));
}
}
} else if (layer.type === "group") {
for (const innerLayer of layer.layers) {
for (const innerLayer of layer.layers as ITiledMapLayer[]) {
this.recursiveFindVariablesInLayer(innerLayer, objects);
}
}
@ -116,7 +116,7 @@ export class VariablesManager {
if (object.properties) {
for (const property of object.properties) {
const value = property.value;
const value = property.value as unknown;
switch (property.name) {
case "default":
variable.defaultValue = JSON.stringify(value);