merged develop
This commit is contained in:
commit
a1c96b0524
178 changed files with 6744 additions and 3541 deletions
|
@ -25,6 +25,7 @@
|
|||
],
|
||||
"rules": {
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-explicit-any": "error"
|
||||
"@typescript-eslint/no-explicit-any": "error",
|
||||
"no-throw-literal": "error"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
# protobuf build
|
||||
FROM node:14.15.4-buster-slim@sha256:cbae886186467bbfd274b82a234a1cdfbbd31201c2a6ee63a6893eefcf3c6e76 as builder
|
||||
FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d as builder
|
||||
WORKDIR /usr/src
|
||||
COPY messages .
|
||||
RUN yarn install && yarn proto
|
||||
|
||||
# typescript build
|
||||
FROM node:14.15.4-buster-slim@sha256:cbae886186467bbfd274b82a234a1cdfbbd31201c2a6ee63a6893eefcf3c6e76 as builder2
|
||||
FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d as builder2
|
||||
WORKDIR /usr/src
|
||||
COPY back/yarn.lock back/package.json ./
|
||||
RUN yarn install
|
||||
|
@ -15,7 +15,7 @@ ENV NODE_ENV=production
|
|||
RUN yarn run tsc
|
||||
|
||||
# final production image
|
||||
FROM node:14.15.4-buster-slim@sha256:cbae886186467bbfd274b82a234a1cdfbbd31201c2a6ee63a6893eefcf3c6e76
|
||||
FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d
|
||||
WORKDIR /usr/src
|
||||
COPY back/yarn.lock back/package.json ./
|
||||
COPY --from=builder2 /usr/src/dist /usr/src/dist
|
||||
|
|
|
@ -68,14 +68,14 @@
|
|||
"@types/mkdirp": "^1.0.1",
|
||||
"@types/redis": "^2.8.31",
|
||||
"@types/uuidv4": "^5.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^2.26.0",
|
||||
"@typescript-eslint/parser": "^2.26.0",
|
||||
"eslint": "^6.8.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.8.0",
|
||||
"@typescript-eslint/parser": "^5.8.0",
|
||||
"eslint": "^8.5.0",
|
||||
"jasmine": "^3.5.0",
|
||||
"lint-staged": "^11.0.0",
|
||||
"prettier": "^2.3.1",
|
||||
"ts-node-dev": "^1.0.0-pre.44",
|
||||
"typescript": "^3.8.3"
|
||||
"ts-node-dev": "^1.1.8",
|
||||
"typescript": "^4.5.4"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.ts": [
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
// lib/server.ts
|
||||
import App from "./src/App";
|
||||
import grpc from "grpc";
|
||||
import {roomManager} from "./src/RoomManager";
|
||||
import {IRoomManagerServer, RoomManagerService} from "./src/Messages/generated/messages_grpc_pb";
|
||||
import {HTTP_PORT, GRPC_PORT} from "./src/Enum/EnvironmentVariable";
|
||||
import { roomManager } from "./src/RoomManager";
|
||||
import { IRoomManagerServer, RoomManagerService } from "./src/Messages/generated/messages_grpc_pb";
|
||||
import { HTTP_PORT, GRPC_PORT } from "./src/Enum/EnvironmentVariable";
|
||||
|
||||
App.listen(HTTP_PORT, () => console.log(`WorkAdventure HTTP API starting on port %d!`, HTTP_PORT))
|
||||
App.listen(HTTP_PORT, () => console.log(`WorkAdventure HTTP API starting on port %d!`, HTTP_PORT));
|
||||
|
||||
const server = new grpc.Server();
|
||||
server.addService<IRoomManagerServer>(RoomManagerService, roomManager);
|
||||
|
||||
server.bind('0.0.0.0:'+GRPC_PORT, grpc.ServerCredentials.createInsecure());
|
||||
server.bind(`0.0.0.0:${GRPC_PORT}`, grpc.ServerCredentials.createInsecure());
|
||||
server.start();
|
||||
console.log('WorkAdventure HTTP/2 API starting on port %d!', GRPC_PORT);
|
||||
console.log("WorkAdventure HTTP/2 API starting on port %d!", GRPC_PORT);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
});
|
||||
|
||||
|
|
|
@ -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,9 +17,11 @@ import {
|
|||
EmoteEventMessage,
|
||||
ErrorMessage,
|
||||
JoinRoomMessage,
|
||||
SetPlayerDetailsMessage,
|
||||
SubToPusherRoomMessage,
|
||||
VariableMessage,
|
||||
VariableWithTagMessage,
|
||||
ServerToClientMessage,
|
||||
} from "../Messages/generated/messages_pb";
|
||||
import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils";
|
||||
import { RoomSocket, ZoneSocket } from "src/RoomManager";
|
||||
|
@ -27,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;
|
||||
|
@ -56,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(
|
||||
|
@ -71,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);
|
||||
|
||||
|
@ -85,16 +104,13 @@ export class GameRoom {
|
|||
onEnters,
|
||||
onMoves,
|
||||
onLeaves,
|
||||
onEmote
|
||||
onEmote,
|
||||
onPlayerDetailsUpdated
|
||||
);
|
||||
|
||||
return gameRoom;
|
||||
}
|
||||
|
||||
public getGroups(): Group[] {
|
||||
return Array.from(this.groups.values());
|
||||
}
|
||||
|
||||
public getUsers(): Map<number, User> {
|
||||
return this.users;
|
||||
}
|
||||
|
@ -157,6 +173,14 @@ export class GameRoom {
|
|||
if (userObj !== undefined && typeof userObj.group !== "undefined") {
|
||||
this.leaveGroup(userObj);
|
||||
}
|
||||
|
||||
if (user.hasFollowers()) {
|
||||
user.stopLeading();
|
||||
}
|
||||
if (user.following) {
|
||||
user.following.delFollower(user);
|
||||
}
|
||||
|
||||
this.users.delete(user.id);
|
||||
this.usersByUuid.delete(user.uuid);
|
||||
|
||||
|
@ -180,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();
|
||||
|
@ -187,8 +219,8 @@ export class GameRoom {
|
|||
if (user.silent) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (user.group === undefined) {
|
||||
const group = user.group;
|
||||
if (group === undefined) {
|
||||
// If the user is not part of a group:
|
||||
// should he join a group?
|
||||
|
||||
|
@ -219,13 +251,40 @@ export class GameRoom {
|
|||
} else {
|
||||
// If the user is part of a group:
|
||||
// should he leave the group?
|
||||
const distance = GameRoom.computeDistanceBetweenPositions(user.getPosition(), user.group.getPosition());
|
||||
if (distance > this.groupRadius) {
|
||||
this.leaveGroup(user);
|
||||
let noOneOutOfBounds = true;
|
||||
group.getUsers().forEach((foreignUser: User) => {
|
||||
if (foreignUser.group === undefined) {
|
||||
return;
|
||||
}
|
||||
const usrPos = foreignUser.getPosition();
|
||||
const grpPos = foreignUser.group.getPosition();
|
||||
const distance = GameRoom.computeDistanceBetweenPositions(usrPos, grpPos);
|
||||
|
||||
if (distance > this.groupRadius) {
|
||||
if (foreignUser.hasFollowers() || foreignUser.following) {
|
||||
// If one user is out of the group bounds BUT following, the group still exists... but should be hidden.
|
||||
// We put it in 'outOfBounds' mode
|
||||
group.setOutOfBounds(true);
|
||||
noOneOutOfBounds = false;
|
||||
} else {
|
||||
this.leaveGroup(foreignUser);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (noOneOutOfBounds && !user.group?.isEmpty()) {
|
||||
group.setOutOfBounds(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sendToOthersInGroupIncludingUser(user: User, message: ServerToClientMessage): void {
|
||||
user.group?.getUsers().forEach((currentUser: User) => {
|
||||
if (currentUser.id !== user.id) {
|
||||
currentUser.socket.write(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setSilent(user: User, silent: boolean) {
|
||||
if (user.silent === silent) {
|
||||
return;
|
||||
|
@ -253,12 +312,9 @@ export class GameRoom {
|
|||
}
|
||||
group.leave(user);
|
||||
if (group.isEmpty()) {
|
||||
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?
|
||||
|
@ -459,9 +515,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;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,10 @@ export class Group implements Movable {
|
|||
private wasDestroyed: boolean = false;
|
||||
private roomId: string;
|
||||
private currentZone: Zone | null = null;
|
||||
/**
|
||||
* When outOfBounds = true, a user if out of the bounds of the group BUT still considered inside it (because we are in following mode)
|
||||
*/
|
||||
private outOfBounds = false;
|
||||
|
||||
constructor(
|
||||
roomId: string,
|
||||
|
@ -78,6 +82,10 @@ export class Group implements Movable {
|
|||
this.x = x;
|
||||
this.y = y;
|
||||
|
||||
if (this.outOfBounds) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (oldX === undefined) {
|
||||
this.currentZone = this.positionNotifier.enter(this);
|
||||
} else {
|
||||
|
@ -116,7 +124,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;
|
||||
|
||||
|
@ -133,6 +141,10 @@ export class Group implements Movable {
|
|||
* Usually used when there is only one user left.
|
||||
*/
|
||||
destroy(): void {
|
||||
if (!this.outOfBounds) {
|
||||
this.positionNotifier.leave(this);
|
||||
}
|
||||
|
||||
for (const user of this.users) {
|
||||
this.leave(user);
|
||||
}
|
||||
|
@ -142,4 +154,26 @@ export class Group implements Movable {
|
|||
get getSize() {
|
||||
return this.users.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* A group can have at most one person leading the way in it.
|
||||
*/
|
||||
get leader(): User | undefined {
|
||||
for (const user of this.users) {
|
||||
if (user.hasFollowers()) {
|
||||
return user;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
setOutOfBounds(outOfBounds: boolean): void {
|
||||
if (this.outOfBounds === true && outOfBounds === false) {
|
||||
this.positionNotifier.enter(this);
|
||||
this.outOfBounds = false;
|
||||
} else if (this.outOfBounds === false && outOfBounds === true) {
|
||||
this.positionNotifier.leave(this);
|
||||
this.outOfBounds = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,11 @@ import { ServerDuplexStream } from "grpc";
|
|||
import {
|
||||
BatchMessage,
|
||||
CompanionMessage,
|
||||
FollowAbortMessage,
|
||||
FollowConfirmationMessage,
|
||||
PusherToBackMessage,
|
||||
ServerToClientMessage,
|
||||
SetPlayerDetailsMessage,
|
||||
SubMessage,
|
||||
} from "../Messages/generated/messages_pb";
|
||||
import { CharacterLayer } from "_Model/Websocket/CharacterLayer";
|
||||
|
@ -18,6 +21,8 @@ export type UserSocket = ServerDuplexStream<PusherToBackMessage, ServerToClientM
|
|||
export class User implements Movable {
|
||||
public listenedZones: Set<Zone>;
|
||||
public group?: Group;
|
||||
private _following: User | undefined;
|
||||
private followedBy: Set<User> = new Set<User>();
|
||||
|
||||
public constructor(
|
||||
public id: number,
|
||||
|
@ -31,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>();
|
||||
|
||||
|
@ -48,6 +54,45 @@ export class User implements Movable {
|
|||
this.positionNotifier.updatePosition(this, position, oldPosition);
|
||||
}
|
||||
|
||||
public addFollower(follower: User): void {
|
||||
this.followedBy.add(follower);
|
||||
follower._following = this;
|
||||
|
||||
const message = new FollowConfirmationMessage();
|
||||
message.setFollower(follower.id);
|
||||
message.setLeader(this.id);
|
||||
const clientMessage = new ServerToClientMessage();
|
||||
clientMessage.setFollowconfirmationmessage(message);
|
||||
this.socket.write(clientMessage);
|
||||
}
|
||||
|
||||
public delFollower(follower: User): void {
|
||||
this.followedBy.delete(follower);
|
||||
follower._following = undefined;
|
||||
|
||||
const message = new FollowAbortMessage();
|
||||
message.setFollower(follower.id);
|
||||
message.setLeader(this.id);
|
||||
const clientMessage = new ServerToClientMessage();
|
||||
clientMessage.setFollowabortmessage(message);
|
||||
this.socket.write(clientMessage);
|
||||
follower.socket.write(clientMessage);
|
||||
}
|
||||
|
||||
public hasFollowers(): boolean {
|
||||
return this.followedBy.size !== 0;
|
||||
}
|
||||
|
||||
get following(): User | undefined {
|
||||
return this._following;
|
||||
}
|
||||
|
||||
public stopLeading(): void {
|
||||
for (const follower of this.followedBy) {
|
||||
this.delFollower(follower);
|
||||
}
|
||||
}
|
||||
|
||||
private batchedMessages: BatchMessage = new BatchMessage();
|
||||
private batchTimeout: NodeJS.Timeout | null = null;
|
||||
|
||||
|
@ -69,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,9 +5,13 @@ import {
|
|||
AdminPusherToBackMessage,
|
||||
AdminRoomMessage,
|
||||
BanMessage,
|
||||
BanUserMessage,
|
||||
BatchToPusherMessage,
|
||||
BatchToPusherRoomMessage,
|
||||
EmotePromptMessage,
|
||||
FollowRequestMessage,
|
||||
FollowConfirmationMessage,
|
||||
FollowAbortMessage,
|
||||
EmptyMessage,
|
||||
ItemEventMessage,
|
||||
JoinRoomMessage,
|
||||
|
@ -16,7 +20,9 @@ import {
|
|||
QueryJitsiJwtMessage,
|
||||
RefreshRoomPromptMessage,
|
||||
RoomMessage,
|
||||
SendUserMessage,
|
||||
ServerToAdminClientMessage,
|
||||
SetPlayerDetailsMessage,
|
||||
SilentMessage,
|
||||
UserMovesMessage,
|
||||
VariableMessage,
|
||||
|
@ -100,11 +106,6 @@ const roomManager: IRoomManagerServer = {
|
|||
user,
|
||||
message.getWebrtcscreensharingsignaltoservermessage() as WebRtcSignalToServerMessage
|
||||
);
|
||||
} else if (message.hasPlayglobalmessage()) {
|
||||
socketManager.emitPlayGlobalMessage(
|
||||
room,
|
||||
message.getPlayglobalmessage() as PlayGlobalMessage
|
||||
);
|
||||
} else if (message.hasQueryjitsijwtmessage()) {
|
||||
socketManager.handleQueryJitsiJwtMessage(
|
||||
user,
|
||||
|
@ -116,16 +117,37 @@ const roomManager: IRoomManagerServer = {
|
|||
user,
|
||||
message.getEmotepromptmessage() as EmotePromptMessage
|
||||
);
|
||||
} else if (message.hasFollowrequestmessage()) {
|
||||
socketManager.handleFollowRequestMessage(
|
||||
room,
|
||||
user,
|
||||
message.getFollowrequestmessage() as FollowRequestMessage
|
||||
);
|
||||
} else if (message.hasFollowconfirmationmessage()) {
|
||||
socketManager.handleFollowConfirmationMessage(
|
||||
room,
|
||||
user,
|
||||
message.getFollowconfirmationmessage() as FollowConfirmationMessage
|
||||
);
|
||||
} else if (message.hasFollowabortmessage()) {
|
||||
socketManager.handleFollowAbortMessage(
|
||||
room,
|
||||
user,
|
||||
message.getFollowabortmessage() as FollowAbortMessage
|
||||
);
|
||||
} 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");
|
||||
}
|
||||
|
@ -160,7 +182,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", () => {
|
||||
|
@ -190,7 +212,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", () => {
|
||||
|
@ -251,7 +273,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());
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
/* eslint-disable */
|
||||
|
||||
import { Readable } from "stream";
|
||||
import { us_listen_socket_close, TemplatedApp, HttpResponse, HttpRequest } from "uWebSockets.js";
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
/* eslint-disable */
|
||||
|
||||
import { createWriteStream } from "fs";
|
||||
import { join, dirname } from "path";
|
||||
import Busboy from "busboy";
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
/* eslint-disable */
|
||||
|
||||
import { ReadStream } from "fs";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const EventEmitter = require("events");
|
||||
import { EventEmitter } from "events";
|
||||
|
||||
const clientJoinEvent = "clientJoin";
|
||||
const clientLeaveEvent = "clientLeave";
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -30,9 +30,14 @@ import {
|
|||
BanUserMessage,
|
||||
RefreshRoomMessage,
|
||||
EmotePromptMessage,
|
||||
FollowRequestMessage,
|
||||
FollowConfirmationMessage,
|
||||
FollowAbortMessage,
|
||||
VariableMessage,
|
||||
BatchToPusherRoomMessage,
|
||||
SubToPusherRoomMessage,
|
||||
SetPlayerDetailsMessage,
|
||||
PlayerDetailsUpdatedMessage,
|
||||
} from "../Messages/generated/messages_pb";
|
||||
import { User, UserSocket } from "../Model/User";
|
||||
import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils";
|
||||
|
@ -151,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());
|
||||
|
@ -206,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);
|
||||
}
|
||||
|
@ -236,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);
|
||||
}
|
||||
|
@ -282,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();
|
||||
|
@ -317,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);
|
||||
|
@ -329,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);
|
||||
|
@ -378,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();
|
||||
|
@ -440,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);
|
||||
}
|
||||
|
@ -454,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);
|
||||
}
|
||||
|
@ -478,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,
|
||||
|
@ -519,15 +531,6 @@ export class SocketManager {
|
|||
}
|
||||
}
|
||||
|
||||
emitPlayGlobalMessage(room: GameRoom, playGlobalMessage: PlayGlobalMessage) {
|
||||
const serverToClientMessage = new ServerToClientMessage();
|
||||
serverToClientMessage.setPlayglobalmessage(playGlobalMessage);
|
||||
|
||||
for (const [id, user] of room.getUsers().entries()) {
|
||||
user.socket.write(serverToClientMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public getWorlds(): Map<string, PromiseLike<GameRoom>> {
|
||||
return this.roomsPromises;
|
||||
}
|
||||
|
@ -572,7 +575,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());
|
||||
|
@ -691,7 +694,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(
|
||||
|
@ -715,7 +718,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);
|
||||
|
@ -833,6 +836,39 @@ export class SocketManager {
|
|||
emoteEventMessage.setActoruserid(user.id);
|
||||
room.emitEmoteEvent(user, emoteEventMessage);
|
||||
}
|
||||
|
||||
handleFollowRequestMessage(room: GameRoom, user: User, message: FollowRequestMessage) {
|
||||
const clientMessage = new ServerToClientMessage();
|
||||
clientMessage.setFollowrequestmessage(message);
|
||||
room.sendToOthersInGroupIncludingUser(user, clientMessage);
|
||||
}
|
||||
|
||||
handleFollowConfirmationMessage(room: GameRoom, user: User, message: FollowConfirmationMessage) {
|
||||
const leader = room.getUserById(message.getLeader());
|
||||
if (!leader) {
|
||||
const message = `Could not follow user "{message.getLeader()}" in room "{room.roomUrl}".`;
|
||||
console.info(message, "Maybe the user just left.");
|
||||
return;
|
||||
}
|
||||
|
||||
// By security, we look at the group leader. If the group leader is NOT the leader in the message,
|
||||
// everybody should stop following the group leader (to avoid having 2 group leaders)
|
||||
if (user?.group?.leader && user?.group?.leader !== leader) {
|
||||
user?.group?.leader?.stopLeading();
|
||||
}
|
||||
|
||||
leader.addFollower(user);
|
||||
}
|
||||
|
||||
handleFollowAbortMessage(room: GameRoom, user: User, message: FollowAbortMessage) {
|
||||
if (user.id === message.getLeader()) {
|
||||
user?.group?.leader?.stopLeading();
|
||||
} else {
|
||||
// Forward message
|
||||
const leader = room.getUserById(message.getLeader());
|
||||
leader?.delFollower(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const socketManager = new SocketManager();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -51,7 +51,8 @@ describe("GameRoom", () => {
|
|||
() => {},
|
||||
() => {},
|
||||
() => {},
|
||||
emote
|
||||
emote,
|
||||
() => {}
|
||||
);
|
||||
|
||||
const user1 = world.join(createMockUserSocket(), createJoinRoomMessage("1", 100, 100));
|
||||
|
@ -86,7 +87,8 @@ describe("GameRoom", () => {
|
|||
() => {},
|
||||
() => {},
|
||||
() => {},
|
||||
emote
|
||||
emote,
|
||||
() => {}
|
||||
);
|
||||
|
||||
const user1 = world.join(createMockUserSocket(), createJoinRoomMessage("1", 100, 100));
|
||||
|
@ -125,7 +127,8 @@ describe("GameRoom", () => {
|
|||
() => {},
|
||||
() => {},
|
||||
() => {},
|
||||
emote
|
||||
emote,
|
||||
() => {}
|
||||
);
|
||||
|
||||
const user1 = world.join(createMockUserSocket(), createJoinRoomMessage("1", 100, 100));
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import "jasmine";
|
||||
import {PositionNotifier} from "../src/Model/PositionNotifier";
|
||||
import {User, UserSocket} from "../src/Model/User";
|
||||
import {Zone} from "_Model/Zone";
|
||||
import {Movable} from "_Model/Movable";
|
||||
import {PositionInterface} from "_Model/PositionInterface";
|
||||
import {ZoneSocket} from "../src/RoomManager";
|
||||
|
||||
import { PositionNotifier } from "../src/Model/PositionNotifier";
|
||||
import { User, UserSocket } from "../src/Model/User";
|
||||
import { Zone } from "_Model/Zone";
|
||||
import { Movable } from "_Model/Movable";
|
||||
import { PositionInterface } from "_Model/PositionInterface";
|
||||
import { ZoneSocket } from "../src/RoomManager";
|
||||
|
||||
describe("PositionNotifier", () => {
|
||||
it("should receive notifications when player moves", () => {
|
||||
|
@ -13,27 +12,59 @@ describe("PositionNotifier", () => {
|
|||
let moveTriggered = false;
|
||||
let leaveTriggered = false;
|
||||
|
||||
const positionNotifier = new PositionNotifier(300, 300, (thing: Movable) => {
|
||||
enterTriggered = true;
|
||||
}, (thing: Movable, position: PositionInterface) => {
|
||||
moveTriggered = true;
|
||||
}, (thing: Movable) => {
|
||||
leaveTriggered = true;
|
||||
}, () => {});
|
||||
const positionNotifier = new PositionNotifier(
|
||||
300,
|
||||
300,
|
||||
(thing: Movable) => {
|
||||
enterTriggered = true;
|
||||
},
|
||||
(thing: Movable, position: PositionInterface) => {
|
||||
moveTriggered = true;
|
||||
},
|
||||
(thing: Movable) => {
|
||||
leaveTriggered = true;
|
||||
},
|
||||
() => {},
|
||||
() => {}
|
||||
);
|
||||
|
||||
const user1 = new User(1, 'test', '10.0.0.2', {
|
||||
x: 500,
|
||||
y: 500,
|
||||
moving: false,
|
||||
direction: 'down'
|
||||
}, false, positionNotifier, {} as UserSocket, [], null, 'foo', []);
|
||||
const user1 = new User(
|
||||
1,
|
||||
"test",
|
||||
"10.0.0.2",
|
||||
{
|
||||
x: 500,
|
||||
y: 500,
|
||||
moving: false,
|
||||
direction: "down",
|
||||
},
|
||||
false,
|
||||
positionNotifier,
|
||||
{} as UserSocket,
|
||||
[],
|
||||
null,
|
||||
"foo",
|
||||
[]
|
||||
);
|
||||
|
||||
const user2 = new User(2, 'test', '10.0.0.2', {
|
||||
x: -9999,
|
||||
y: -9999,
|
||||
moving: false,
|
||||
direction: 'down'
|
||||
}, false, positionNotifier, {} as UserSocket, [], null, 'foo', []);
|
||||
const user2 = new User(
|
||||
2,
|
||||
"test",
|
||||
"10.0.0.2",
|
||||
{
|
||||
x: -9999,
|
||||
y: -9999,
|
||||
moving: false,
|
||||
direction: "down",
|
||||
},
|
||||
false,
|
||||
positionNotifier,
|
||||
{} as UserSocket,
|
||||
[],
|
||||
null,
|
||||
"foo",
|
||||
[]
|
||||
);
|
||||
|
||||
positionNotifier.addZoneListener({} as ZoneSocket, 0, 0);
|
||||
positionNotifier.addZoneListener({} as ZoneSocket, 0, 1);
|
||||
|
@ -46,21 +77,21 @@ describe("PositionNotifier", () => {
|
|||
bottom: 500
|
||||
});*/
|
||||
|
||||
user2.setPosition({x: 500, y: 500, direction: 'down', moving: false});
|
||||
user2.setPosition({ x: 500, y: 500, direction: "down", moving: false });
|
||||
|
||||
expect(enterTriggered).toBe(true);
|
||||
expect(moveTriggered).toBe(false);
|
||||
enterTriggered = false;
|
||||
|
||||
// Move inside the zone
|
||||
user2.setPosition({x:501, y:500, direction: 'down', moving: false});
|
||||
user2.setPosition({ x: 501, y: 500, direction: "down", moving: false });
|
||||
|
||||
expect(enterTriggered).toBe(false);
|
||||
expect(moveTriggered).toBe(true);
|
||||
moveTriggered = false;
|
||||
|
||||
// Move out of the zone in a zone that we don't track
|
||||
user2.setPosition({x: 901, y: 500, direction: 'down', moving: false});
|
||||
user2.setPosition({ x: 901, y: 500, direction: "down", moving: false });
|
||||
|
||||
expect(enterTriggered).toBe(false);
|
||||
expect(moveTriggered).toBe(false);
|
||||
|
@ -68,7 +99,7 @@ describe("PositionNotifier", () => {
|
|||
leaveTriggered = false;
|
||||
|
||||
// Move back in
|
||||
user2.setPosition({x: 500, y: 500, direction: 'down', moving: false});
|
||||
user2.setPosition({ x: 500, y: 500, direction: "down", moving: false });
|
||||
expect(enterTriggered).toBe(true);
|
||||
expect(moveTriggered).toBe(false);
|
||||
expect(leaveTriggered).toBe(false);
|
||||
|
@ -88,27 +119,59 @@ describe("PositionNotifier", () => {
|
|||
let moveTriggered = false;
|
||||
let leaveTriggered = false;
|
||||
|
||||
const positionNotifier = new PositionNotifier(300, 300, (thing: Movable, fromZone: Zone|null ) => {
|
||||
enterTriggered = true;
|
||||
}, (thing: Movable, position: PositionInterface) => {
|
||||
moveTriggered = true;
|
||||
}, (thing: Movable) => {
|
||||
leaveTriggered = true;
|
||||
}, () => {});
|
||||
const positionNotifier = new PositionNotifier(
|
||||
300,
|
||||
300,
|
||||
(thing: Movable, fromZone: Zone | null) => {
|
||||
enterTriggered = true;
|
||||
},
|
||||
(thing: Movable, position: PositionInterface) => {
|
||||
moveTriggered = true;
|
||||
},
|
||||
(thing: Movable) => {
|
||||
leaveTriggered = true;
|
||||
},
|
||||
() => {},
|
||||
() => {}
|
||||
);
|
||||
|
||||
const user1 = new User(1, 'test', '10.0.0.2', {
|
||||
x: 500,
|
||||
y: 500,
|
||||
moving: false,
|
||||
direction: 'down'
|
||||
}, false, positionNotifier, {} as UserSocket, [], null, 'foo', []);
|
||||
const user1 = new User(
|
||||
1,
|
||||
"test",
|
||||
"10.0.0.2",
|
||||
{
|
||||
x: 500,
|
||||
y: 500,
|
||||
moving: false,
|
||||
direction: "down",
|
||||
},
|
||||
false,
|
||||
positionNotifier,
|
||||
{} as UserSocket,
|
||||
[],
|
||||
null,
|
||||
"foo",
|
||||
[]
|
||||
);
|
||||
|
||||
const user2 = new User(2, 'test', '10.0.0.2', {
|
||||
x: 0,
|
||||
y: 0,
|
||||
moving: false,
|
||||
direction: 'down'
|
||||
}, false, positionNotifier, {} as UserSocket, [], null, 'foo', []);
|
||||
const user2 = new User(
|
||||
2,
|
||||
"test",
|
||||
"10.0.0.2",
|
||||
{
|
||||
x: 0,
|
||||
y: 0,
|
||||
moving: false,
|
||||
direction: "down",
|
||||
},
|
||||
false,
|
||||
positionNotifier,
|
||||
{} as UserSocket,
|
||||
[],
|
||||
null,
|
||||
"foo",
|
||||
[]
|
||||
);
|
||||
|
||||
const listener = {} as ZoneSocket;
|
||||
positionNotifier.addZoneListener(listener, 0, 0);
|
||||
|
@ -124,14 +187,12 @@ describe("PositionNotifier", () => {
|
|||
positionNotifier.enter(user1);
|
||||
positionNotifier.enter(user2);
|
||||
|
||||
|
||||
//expect(newUsers.length).toBe(2);
|
||||
expect(enterTriggered).toBe(true);
|
||||
enterTriggered = false;
|
||||
|
||||
|
||||
//positionNotifier.updatePosition(user2, {x:500, y:500}, {x:0, y: 0})
|
||||
user2.setPosition({x: 500, y: 500, direction: 'down', moving: false});
|
||||
user2.setPosition({ x: 500, y: 500, direction: "down", moving: false });
|
||||
|
||||
expect(enterTriggered).toBe(true);
|
||||
expect(moveTriggered).toBe(false);
|
||||
|
@ -182,4 +243,4 @@ describe("PositionNotifier", () => {
|
|||
enterTriggered = false;
|
||||
//expect(newUsers.length).toBe(2);
|
||||
});
|
||||
})
|
||||
});
|
||||
|
|
2668
back/yarn.lock
2668
back/yarn.lock
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue