Merge branch 'develop' into player-report
# Conflicts: # back/src/Controller/IoSocketController.ts
This commit is contained in:
commit
f6ae7d8d3b
@ -40,7 +40,6 @@ export class AuthenticateController extends BaseController {
|
|||||||
try {
|
try {
|
||||||
if (typeof organizationMemberToken != 'string') throw new Error('No organization token');
|
if (typeof organizationMemberToken != 'string') throw new Error('No organization token');
|
||||||
const data = await adminApi.fetchMemberDataByToken(organizationMemberToken);
|
const data = await adminApi.fetchMemberDataByToken(organizationMemberToken);
|
||||||
|
|
||||||
const userUuid = data.userUuid;
|
const userUuid = data.userUuid;
|
||||||
const organizationSlug = data.organizationSlug;
|
const organizationSlug = data.organizationSlug;
|
||||||
const worldSlug = data.worldSlug;
|
const worldSlug = data.worldSlug;
|
||||||
@ -58,7 +57,7 @@ export class AuthenticateController extends BaseController {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("An error happened", e)
|
console.error("An error happened", e)
|
||||||
res.writeStatus(e.status || "500 Internal Server Error").end('An error happened');
|
res.writeStatus(e.status || "500 Internal Server Error").end('An error happened');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {ExSocketInterface} from "../Model/Websocket/ExSocketInterface"; //TODO fix import by "_Model/.."
|
import {ExSocketInterface} from "../Model/Websocket/ExSocketInterface"; //TODO fix import by "_Model/.."
|
||||||
import {MINIMUM_DISTANCE, GROUP_RADIUS, ADMIN_API_URL, ADMIN_API_TOKEN} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..."
|
import {MINIMUM_DISTANCE, GROUP_RADIUS, ADMIN_API_URL, ADMIN_API_TOKEN} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..."
|
||||||
import {GameRoom} from "../Model/GameRoom";
|
import {GameRoom, GameRoomPolicyTypes} from "../Model/GameRoom";
|
||||||
import {Group} from "../Model/Group";
|
import {Group} from "../Model/Group";
|
||||||
import {User} from "../Model/User";
|
import {User} from "../Model/User";
|
||||||
import {isSetPlayerDetailsMessage,} from "../Model/Websocket/SetPlayerDetailsMessage";
|
import {isSetPlayerDetailsMessage,} from "../Model/Websocket/SetPlayerDetailsMessage";
|
||||||
@ -43,8 +43,8 @@ import {cpuTracker} from "../Services/CpuTracker";
|
|||||||
import {ViewportInterface} from "../Model/Websocket/ViewportMessage";
|
import {ViewportInterface} from "../Model/Websocket/ViewportMessage";
|
||||||
import {jwtTokenManager} from "../Services/JWTTokenManager";
|
import {jwtTokenManager} from "../Services/JWTTokenManager";
|
||||||
import {adminApi} from "../Services/AdminApi";
|
import {adminApi} from "../Services/AdminApi";
|
||||||
import {RoomIdentifier} from "../Model/RoomIdentifier";
|
|
||||||
import Axios from "axios";
|
import Axios from "axios";
|
||||||
|
import {PositionInterface} from "../Model/PositionInterface";
|
||||||
|
|
||||||
function emitInBatch(socket: ExSocketInterface, payload: SubMessage): void {
|
function emitInBatch(socket: ExSocketInterface, payload: SubMessage): void {
|
||||||
socket.batchedMessages.addPayload(payload);
|
socket.batchedMessages.addPayload(payload);
|
||||||
@ -116,11 +116,9 @@ export class IoSocketController {
|
|||||||
const websocketExtensions = req.getHeader('sec-websocket-extensions');
|
const websocketExtensions = req.getHeader('sec-websocket-extensions');
|
||||||
|
|
||||||
const roomId = query.roomId;
|
const roomId = query.roomId;
|
||||||
//todo: better validation: /\/_\/.*\/.*/ or /\/@\/.*\/.*\/.*/
|
|
||||||
if (typeof roomId !== 'string') {
|
if (typeof roomId !== 'string') {
|
||||||
throw new Error('Undefined room ID: ');
|
throw new Error('Undefined room ID: ');
|
||||||
}
|
}
|
||||||
const roomIdentifier = new RoomIdentifier(roomId);
|
|
||||||
|
|
||||||
const token = query.token;
|
const token = query.token;
|
||||||
const x = Number(query.x);
|
const x = Number(query.x);
|
||||||
@ -146,17 +144,20 @@ export class IoSocketController {
|
|||||||
|
|
||||||
|
|
||||||
const userUuid = await jwtTokenManager.getUserUuidFromToken(token);
|
const userUuid = await jwtTokenManager.getUserUuidFromToken(token);
|
||||||
console.log('uuid', userUuid);
|
|
||||||
|
|
||||||
let memberTags: string[] = [];
|
let memberTags: string[] = [];
|
||||||
if (roomIdentifier.anonymous === false) {
|
const room = await this.getOrCreateRoom(roomId);
|
||||||
const grants = await adminApi.memberIsGrantedAccessToRoom(userUuid, roomIdentifier);
|
if (!room.anonymous && room.policyType !== GameRoomPolicyTypes.ANONYMUS_POLICY) {
|
||||||
if (!grants.granted) {
|
try {
|
||||||
|
const userData = await adminApi.fetchMemberDataByUuid(userUuid);
|
||||||
|
memberTags = userData.tags;
|
||||||
|
if (room.policyType === GameRoomPolicyTypes.USE_TAGS_POLICY && !room.canAccess(memberTags)) {
|
||||||
|
throw new Error('No correct tags')
|
||||||
|
}
|
||||||
|
console.log('access granted for user '+userUuid+' and room '+roomId);
|
||||||
|
} catch (e) {
|
||||||
console.log('access not granted for user '+userUuid+' and room '+roomId);
|
console.log('access not granted for user '+userUuid+' and room '+roomId);
|
||||||
throw new Error('Client cannot acces this ressource.')
|
throw new Error('Client cannot acces this ressource.')
|
||||||
} else {
|
|
||||||
memberTags = grants.memberTags;
|
|
||||||
console.log('access granted for user '+userUuid+' and room '+roomId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,6 +176,7 @@ export class IoSocketController {
|
|||||||
roomId,
|
roomId,
|
||||||
name,
|
name,
|
||||||
characterLayers,
|
characterLayers,
|
||||||
|
tags: memberTags,
|
||||||
position: {
|
position: {
|
||||||
x: x,
|
x: x,
|
||||||
y: y,
|
y: y,
|
||||||
@ -186,8 +188,7 @@ export class IoSocketController {
|
|||||||
right,
|
right,
|
||||||
bottom,
|
bottom,
|
||||||
left
|
left
|
||||||
},
|
}
|
||||||
tags: memberTags
|
|
||||||
},
|
},
|
||||||
/* Spell these correctly */
|
/* Spell these correctly */
|
||||||
websocketKey,
|
websocketKey,
|
||||||
@ -222,9 +223,9 @@ export class IoSocketController {
|
|||||||
client.disconnecting = false;
|
client.disconnecting = false;
|
||||||
|
|
||||||
client.name = ws.name;
|
client.name = ws.name;
|
||||||
|
client.tags = ws.tags;
|
||||||
client.characterLayers = ws.characterLayers;
|
client.characterLayers = ws.characterLayers;
|
||||||
client.roomId = ws.roomId;
|
client.roomId = ws.roomId;
|
||||||
client.tags = ws.tags;
|
|
||||||
|
|
||||||
this.sockets.set(client.userId, client);
|
this.sockets.set(client.userId, client);
|
||||||
|
|
||||||
@ -233,7 +234,7 @@ export class IoSocketController {
|
|||||||
console.log(new Date().toISOString() + ' A user joined (', this.sockets.size, ' connected users)');
|
console.log(new Date().toISOString() + ' A user joined (', this.sockets.size, ' connected users)');
|
||||||
|
|
||||||
// Let's join the room
|
// Let's join the room
|
||||||
this.handleJoinRoom(client, client.roomId, client.position, client.viewport, client.name, client.characterLayers);
|
this.handleJoinRoom(client, client.position, client.viewport);
|
||||||
},
|
},
|
||||||
message: (ws, arrayBuffer, isBinary): void => {
|
message: (ws, arrayBuffer, isBinary): void => {
|
||||||
const client = ws as ExSocketInterface;
|
const client = ws as ExSocketInterface;
|
||||||
@ -271,11 +272,6 @@ export class IoSocketController {
|
|||||||
Client.disconnecting = true;
|
Client.disconnecting = true;
|
||||||
//leave room
|
//leave room
|
||||||
this.leaveRoom(Client);
|
this.leaveRoom(Client);
|
||||||
|
|
||||||
//delete all socket information
|
|
||||||
/*delete Client.roomId;
|
|
||||||
delete Client.token;
|
|
||||||
delete Client.position;*/
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('An error occurred on "disconnect"');
|
console.error('An error occurred on "disconnect"');
|
||||||
console.error(e);
|
console.error(e);
|
||||||
@ -288,21 +284,6 @@ export class IoSocketController {
|
|||||||
console.log('A user left (', this.sockets.size, ' connected users)');
|
console.log('A user left (', this.sockets.size, ' connected users)');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: finish this!
|
|
||||||
/*this.Io.on(SocketIoEvent.CONNECTION, (socket: Socket) => {
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
socket.on(SocketIoEvent.WEBRTC_SIGNAL, (data: unknown) => {
|
|
||||||
this.emitVideo((socket as ExSocketInterface), data);
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on(SocketIoEvent.WEBRTC_SCREEN_SHARING_SIGNAL, (data: unknown) => {
|
|
||||||
this.emitScreenSharing((socket as ExSocketInterface), data);
|
|
||||||
});
|
|
||||||
|
|
||||||
});*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private emitError(Client: ExSocketInterface, message: string): void {
|
private emitError(Client: ExSocketInterface, message: string): void {
|
||||||
@ -318,10 +299,10 @@ export class IoSocketController {
|
|||||||
console.warn(message);
|
console.warn(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleJoinRoom(client: ExSocketInterface, roomId: string, position: PointInterface, viewport: ViewportInterface, name: string, characterLayers: string[]): void {
|
private handleJoinRoom(client: ExSocketInterface, position: PointInterface, viewport: ViewportInterface): void {
|
||||||
try {
|
try {
|
||||||
//join new previous room
|
//join new previous room
|
||||||
const gameRoom = this.joinRoom(client, roomId, position);
|
const gameRoom = this.joinRoom(client, position);
|
||||||
|
|
||||||
const things = gameRoom.setViewport(client, viewport);
|
const things = gameRoom.setViewport(client, viewport);
|
||||||
|
|
||||||
@ -362,7 +343,6 @@ export class IoSocketController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
roomJoinedMessage.setCurrentuserid(client.userId);
|
roomJoinedMessage.setCurrentuserid(client.userId);
|
||||||
roomJoinedMessage.setTagList(client.tags);
|
|
||||||
|
|
||||||
const serverToClientMessage = new ServerToClientMessage();
|
const serverToClientMessage = new ServerToClientMessage();
|
||||||
serverToClientMessage.setRoomjoinedmessage(roomJoinedMessage);
|
serverToClientMessage.setRoomjoinedmessage(roomJoinedMessage);
|
||||||
@ -604,76 +584,41 @@ export class IoSocketController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private joinRoom(client : ExSocketInterface, roomId: string, position: PointInterface): GameRoom {
|
private async getOrCreateRoom(roomId: string): Promise<GameRoom> {
|
||||||
|
|
||||||
//join user in room
|
|
||||||
this.nbClientsPerRoomGauge.inc({ room: roomId });
|
|
||||||
client.roomId = roomId;
|
|
||||||
client.position = position;
|
|
||||||
|
|
||||||
//check and create new world for a room
|
//check and create new world for a room
|
||||||
let world = this.Worlds.get(roomId)
|
let world = this.Worlds.get(roomId)
|
||||||
if(world === undefined){
|
if(world === undefined){
|
||||||
world = new GameRoom((user1: User, group: Group) => {
|
world = new GameRoom(
|
||||||
this.joinWebRtcRoom(user1, group);
|
roomId,
|
||||||
}, (user1: User, group: Group) => {
|
(user: User, group: Group) => this.joinWebRtcRoom(user, group),
|
||||||
this.disConnectedUser(user1, group);
|
(user: User, group: Group) => this.disConnectedUser(user, group),
|
||||||
}, MINIMUM_DISTANCE, GROUP_RADIUS, (thing: Movable, listener: User) => {
|
MINIMUM_DISTANCE,
|
||||||
const clientListener = this.searchClientByIdOrFail(listener.id);
|
GROUP_RADIUS,
|
||||||
if (thing instanceof User) {
|
(thing: Movable, listener: User) => this.onRoomEnter(thing, listener),
|
||||||
const clientUser = this.searchClientByIdOrFail(thing.id);
|
(thing: Movable, position:PositionInterface, listener:User) => this.onClientMove(thing, position, listener),
|
||||||
|
(thing: Movable, listener:User) => this.onClientLeave(thing, listener)
|
||||||
const userJoinedMessage = new UserJoinedMessage();
|
);
|
||||||
if (!Number.isInteger(clientUser.userId)) {
|
if (!world.anonymous) {
|
||||||
throw new Error('clientUser.userId is not an integer '+clientUser.userId);
|
const data = await adminApi.fetchMapDetails(world.organizationSlug, world.worldSlug, world.roomSlug)
|
||||||
}
|
world.tags = data.tags
|
||||||
userJoinedMessage.setUserid(clientUser.userId);
|
world.policyType = Number(data.policy_type)
|
||||||
userJoinedMessage.setName(clientUser.name);
|
}
|
||||||
userJoinedMessage.setCharacterlayersList(clientUser.characterLayers);
|
|
||||||
userJoinedMessage.setPosition(ProtobufUtils.toPositionMessage(clientUser.position));
|
|
||||||
|
|
||||||
const subMessage = new SubMessage();
|
|
||||||
subMessage.setUserjoinedmessage(userJoinedMessage);
|
|
||||||
|
|
||||||
emitInBatch(clientListener, subMessage);
|
|
||||||
} else if (thing instanceof Group) {
|
|
||||||
this.emitCreateUpdateGroupEvent(clientListener, thing);
|
|
||||||
} else {
|
|
||||||
console.error('Unexpected type for Movable.');
|
|
||||||
}
|
|
||||||
}, (thing: Movable, position, listener) => {
|
|
||||||
const clientListener = this.searchClientByIdOrFail(listener.id);
|
|
||||||
if (thing instanceof User) {
|
|
||||||
const clientUser = this.searchClientByIdOrFail(thing.id);
|
|
||||||
|
|
||||||
const userMovedMessage = new UserMovedMessage();
|
|
||||||
userMovedMessage.setUserid(clientUser.userId);
|
|
||||||
userMovedMessage.setPosition(ProtobufUtils.toPositionMessage(clientUser.position));
|
|
||||||
|
|
||||||
const subMessage = new SubMessage();
|
|
||||||
subMessage.setUsermovedmessage(userMovedMessage);
|
|
||||||
|
|
||||||
clientListener.emitInBatch(subMessage);
|
|
||||||
//console.log("Sending USER_MOVED event");
|
|
||||||
} else if (thing instanceof Group) {
|
|
||||||
this.emitCreateUpdateGroupEvent(clientListener, thing);
|
|
||||||
} else {
|
|
||||||
console.error('Unexpected type for Movable.');
|
|
||||||
}
|
|
||||||
}, (thing: Movable, listener) => {
|
|
||||||
const clientListener = this.searchClientByIdOrFail(listener.id);
|
|
||||||
if (thing instanceof User) {
|
|
||||||
const clientUser = this.searchClientByIdOrFail(thing.id);
|
|
||||||
this.emitUserLeftEvent(clientListener, clientUser.userId);
|
|
||||||
} else if (thing instanceof Group) {
|
|
||||||
this.emitDeleteGroupEvent(clientListener, thing.getId());
|
|
||||||
} else {
|
|
||||||
console.error('Unexpected type for Movable.');
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
this.Worlds.set(roomId, world);
|
this.Worlds.set(roomId, world);
|
||||||
}
|
}
|
||||||
|
return Promise.resolve(world)
|
||||||
|
}
|
||||||
|
|
||||||
|
private joinRoom(client : ExSocketInterface, position: PointInterface): GameRoom {
|
||||||
|
|
||||||
|
const roomId = client.roomId;
|
||||||
|
//join user in room
|
||||||
|
this.nbClientsPerRoomGauge.inc({ room: roomId });
|
||||||
|
client.position = position;
|
||||||
|
|
||||||
|
const world = this.Worlds.get(roomId)
|
||||||
|
if(world === undefined){
|
||||||
|
throw new Error('Could not find room for ID: '+client.roomId)
|
||||||
|
}
|
||||||
|
|
||||||
// Dispatch groups position to newly connected user
|
// Dispatch groups position to newly connected user
|
||||||
world.getGroups().forEach((group: Group) => {
|
world.getGroups().forEach((group: Group) => {
|
||||||
@ -684,6 +629,64 @@ export class IoSocketController {
|
|||||||
return world;
|
return world;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private onRoomEnter(thing: Movable, listener: User) {
|
||||||
|
const clientListener = this.searchClientByIdOrFail(listener.id);
|
||||||
|
if (thing instanceof User) {
|
||||||
|
const clientUser = this.searchClientByIdOrFail(thing.id);
|
||||||
|
|
||||||
|
const userJoinedMessage = new UserJoinedMessage();
|
||||||
|
if (!Number.isInteger(clientUser.userId)) {
|
||||||
|
throw new Error('clientUser.userId is not an integer '+clientUser.userId);
|
||||||
|
}
|
||||||
|
userJoinedMessage.setUserid(clientUser.userId);
|
||||||
|
userJoinedMessage.setName(clientUser.name);
|
||||||
|
userJoinedMessage.setCharacterlayersList(clientUser.characterLayers);
|
||||||
|
userJoinedMessage.setPosition(ProtobufUtils.toPositionMessage(clientUser.position));
|
||||||
|
|
||||||
|
const subMessage = new SubMessage();
|
||||||
|
subMessage.setUserjoinedmessage(userJoinedMessage);
|
||||||
|
|
||||||
|
emitInBatch(clientListener, subMessage);
|
||||||
|
} else if (thing instanceof Group) {
|
||||||
|
this.emitCreateUpdateGroupEvent(clientListener, thing);
|
||||||
|
} else {
|
||||||
|
console.error('Unexpected type for Movable.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onClientMove(thing: Movable, position:PositionInterface, listener:User): void {
|
||||||
|
const clientListener = this.searchClientByIdOrFail(listener.id);
|
||||||
|
if (thing instanceof User) {
|
||||||
|
const clientUser = this.searchClientByIdOrFail(thing.id);
|
||||||
|
|
||||||
|
const userMovedMessage = new UserMovedMessage();
|
||||||
|
userMovedMessage.setUserid(clientUser.userId);
|
||||||
|
userMovedMessage.setPosition(ProtobufUtils.toPositionMessage(clientUser.position));
|
||||||
|
|
||||||
|
const subMessage = new SubMessage();
|
||||||
|
subMessage.setUsermovedmessage(userMovedMessage);
|
||||||
|
|
||||||
|
clientListener.emitInBatch(subMessage);
|
||||||
|
//console.log("Sending USER_MOVED event");
|
||||||
|
} else if (thing instanceof Group) {
|
||||||
|
this.emitCreateUpdateGroupEvent(clientListener, thing);
|
||||||
|
} else {
|
||||||
|
console.error('Unexpected type for Movable.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onClientLeave(thing: Movable, listener:User) {
|
||||||
|
const clientListener = this.searchClientByIdOrFail(listener.id);
|
||||||
|
if (thing instanceof User) {
|
||||||
|
const clientUser = this.searchClientByIdOrFail(thing.id);
|
||||||
|
this.emitUserLeftEvent(clientListener, clientUser.userId);
|
||||||
|
} else if (thing instanceof Group) {
|
||||||
|
this.emitDeleteGroupEvent(clientListener, thing.getId());
|
||||||
|
} else {
|
||||||
|
console.error('Unexpected type for Movable.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private emitCreateUpdateGroupEvent(client: ExSocketInterface, group: Group): void {
|
private emitCreateUpdateGroupEvent(client: ExSocketInterface, group: Group): void {
|
||||||
const position = group.getPosition();
|
const position = group.getPosition();
|
||||||
const pointMessage = new PointMessage();
|
const pointMessage = new PointMessage();
|
||||||
|
@ -8,10 +8,18 @@ import {EntersCallback, LeavesCallback, MovesCallback} from "_Model/Zone";
|
|||||||
import {PositionNotifier} from "./PositionNotifier";
|
import {PositionNotifier} from "./PositionNotifier";
|
||||||
import {ViewportInterface} from "_Model/Websocket/ViewportMessage";
|
import {ViewportInterface} from "_Model/Websocket/ViewportMessage";
|
||||||
import {Movable} from "_Model/Movable";
|
import {Movable} from "_Model/Movable";
|
||||||
|
import {extractDataFromPrivateRoomId, extractRoomSlugPublicRoomId, isRoomAnonymous} from "./RoomIdentifier";
|
||||||
|
import {arrayIntersect} from "../Services/ArrayHelper";
|
||||||
|
|
||||||
export type ConnectCallback = (user: User, group: Group) => void;
|
export type ConnectCallback = (user: User, group: Group) => void;
|
||||||
export type DisconnectCallback = (user: User, group: Group) => void;
|
export type DisconnectCallback = (user: User, group: Group) => void;
|
||||||
|
|
||||||
|
export enum GameRoomPolicyTypes {
|
||||||
|
ANONYMUS_POLICY = 1,
|
||||||
|
MEMBERS_ONLY_POLICY,
|
||||||
|
USE_TAGS_POLICY,
|
||||||
|
}
|
||||||
|
|
||||||
export class GameRoom {
|
export class GameRoom {
|
||||||
private readonly minDistance: number;
|
private readonly minDistance: number;
|
||||||
private readonly groupRadius: number;
|
private readonly groupRadius: number;
|
||||||
@ -26,8 +34,16 @@ export class GameRoom {
|
|||||||
private itemsState: Map<number, unknown> = new Map<number, unknown>();
|
private itemsState: Map<number, unknown> = new Map<number, unknown>();
|
||||||
|
|
||||||
private readonly positionNotifier: PositionNotifier;
|
private readonly positionNotifier: PositionNotifier;
|
||||||
|
public readonly roomId: string;
|
||||||
|
public readonly anonymous: boolean;
|
||||||
|
public tags: string[];
|
||||||
|
public policyType: GameRoomPolicyTypes;
|
||||||
|
public readonly roomSlug: string;
|
||||||
|
public readonly worldSlug: string = '';
|
||||||
|
public readonly organizationSlug: string = '';
|
||||||
|
|
||||||
constructor(connectCallback: ConnectCallback,
|
constructor(roomId: string,
|
||||||
|
connectCallback: ConnectCallback,
|
||||||
disconnectCallback: DisconnectCallback,
|
disconnectCallback: DisconnectCallback,
|
||||||
minDistance: number,
|
minDistance: number,
|
||||||
groupRadius: number,
|
groupRadius: number,
|
||||||
@ -35,6 +51,21 @@ export class GameRoom {
|
|||||||
onMoves: MovesCallback,
|
onMoves: MovesCallback,
|
||||||
onLeaves: LeavesCallback)
|
onLeaves: LeavesCallback)
|
||||||
{
|
{
|
||||||
|
this.roomId = roomId;
|
||||||
|
this.anonymous = isRoomAnonymous(roomId);
|
||||||
|
this.tags = [];
|
||||||
|
this.policyType = GameRoomPolicyTypes.ANONYMUS_POLICY;
|
||||||
|
|
||||||
|
if (this.anonymous) {
|
||||||
|
this.roomSlug = extractRoomSlugPublicRoomId(this.roomId);
|
||||||
|
} else {
|
||||||
|
const {organizationSlug, worldSlug, roomSlug} = extractDataFromPrivateRoomId(this.roomId);
|
||||||
|
this.roomSlug = roomSlug;
|
||||||
|
this.organizationSlug = organizationSlug;
|
||||||
|
this.worldSlug = worldSlug;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
this.users = new Map<number, User>();
|
this.users = new Map<number, User>();
|
||||||
this.groups = new Set<Group>();
|
this.groups = new Set<Group>();
|
||||||
this.connectCallback = connectCallback;
|
this.connectCallback = connectCallback;
|
||||||
@ -248,4 +279,8 @@ export class GameRoom {
|
|||||||
}
|
}
|
||||||
return this.positionNotifier.setViewport(user, viewport);
|
return this.positionNotifier.setViewport(user, viewport);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canAccess(userTags: string[]): boolean {
|
||||||
|
return arrayIntersect(userTags, this.tags);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,30 @@
|
|||||||
export class RoomIdentifier {
|
//helper functions to parse room IDs
|
||||||
public readonly anonymous: boolean;
|
|
||||||
public readonly id:string
|
|
||||||
public readonly organizationSlug: string|undefined;
|
|
||||||
public readonly worldSlug: string|undefined;
|
|
||||||
public readonly roomSlug: string|undefined;
|
|
||||||
constructor(roomID: string) {
|
|
||||||
if (roomID.startsWith('_/')) {
|
|
||||||
this.anonymous = true;
|
|
||||||
} else if(roomID.startsWith('@/')) {
|
|
||||||
this.anonymous = false;
|
|
||||||
|
|
||||||
const match = /@\/([^/]+)\/([^/]+)\/(.+)/.exec(roomID);
|
export const isRoomAnonymous = (roomID: string): boolean => {
|
||||||
if (!match) {
|
if (roomID.startsWith('_/')) {
|
||||||
throw new Error('Could not extract info from "'+roomID+'"');
|
return true;
|
||||||
}
|
} else if(roomID.startsWith('@/')) {
|
||||||
this.organizationSlug = match[1];
|
return false;
|
||||||
this.worldSlug = match[2];
|
} else {
|
||||||
this.roomSlug = match[3];
|
throw new Error('Incorrect room ID: '+roomID);
|
||||||
} else {
|
|
||||||
throw new Error('Incorrect room ID: '+roomID);
|
|
||||||
}
|
|
||||||
this.id = roomID;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const extractRoomSlugPublicRoomId = (roomId: string): string => {
|
||||||
|
const idParts = roomId.split('/');
|
||||||
|
if (idParts.length < 3) throw new Error('Incorrect roomId: '+roomId);
|
||||||
|
return idParts.slice(2).join('/');
|
||||||
|
}
|
||||||
|
export interface extractDataFromPrivateRoomIdResponse {
|
||||||
|
organizationSlug: string;
|
||||||
|
worldSlug: string;
|
||||||
|
roomSlug: string;
|
||||||
|
}
|
||||||
|
export const extractDataFromPrivateRoomId = (roomId: string): extractDataFromPrivateRoomIdResponse => {
|
||||||
|
const idParts = roomId.split('/');
|
||||||
|
if (idParts.length < 4) throw new Error('Incorrect roomId: '+roomId);
|
||||||
|
const organizationSlug = idParts[1];
|
||||||
|
const worldSlug = idParts[2];
|
||||||
|
const roomSlug = idParts[3];
|
||||||
|
return {organizationSlug, worldSlug, roomSlug}
|
||||||
|
}
|
@ -1,12 +1,13 @@
|
|||||||
import {ADMIN_API_TOKEN, ADMIN_API_URL} from "../Enum/EnvironmentVariable";
|
import {ADMIN_API_TOKEN, ADMIN_API_URL} from "../Enum/EnvironmentVariable";
|
||||||
import Axios from "axios";
|
import Axios from "axios";
|
||||||
import {RoomIdentifier} from "../Model/RoomIdentifier";
|
|
||||||
|
|
||||||
export interface AdminApiData {
|
export interface AdminApiData {
|
||||||
organizationSlug: string
|
organizationSlug: string
|
||||||
worldSlug: string
|
worldSlug: string
|
||||||
roomSlug: string
|
roomSlug: string
|
||||||
mapUrlStart: string
|
mapUrlStart: string
|
||||||
|
tags: string[]
|
||||||
|
policy_type: number
|
||||||
userUuid: string
|
userUuid: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -15,6 +16,11 @@ export interface GrantedApiData {
|
|||||||
memberTags: string[]
|
memberTags: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface fetchMemberDataByUuidResponse {
|
||||||
|
uuid: string;
|
||||||
|
tags: string[];
|
||||||
|
}
|
||||||
|
|
||||||
class AdminApi {
|
class AdminApi {
|
||||||
|
|
||||||
async fetchMapDetails(organizationSlug: string, worldSlug: string, roomSlug: string|undefined): Promise<AdminApiData> {
|
async fetchMapDetails(organizationSlug: string, worldSlug: string, roomSlug: string|undefined): Promise<AdminApiData> {
|
||||||
@ -40,6 +46,16 @@ class AdminApi {
|
|||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fetchMemberDataByUuid(uuid: string): Promise<fetchMemberDataByUuidResponse> {
|
||||||
|
if (!ADMIN_API_URL) {
|
||||||
|
return Promise.reject('No admin backoffice set!');
|
||||||
|
}
|
||||||
|
const res = await Axios.get(ADMIN_API_URL+'/membership/'+uuid,
|
||||||
|
{ headers: {"Authorization" : `${ADMIN_API_TOKEN}`} }
|
||||||
|
)
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
|
||||||
async fetchMemberDataByToken(organizationMemberToken: string): Promise<AdminApiData> {
|
async fetchMemberDataByToken(organizationMemberToken: string): Promise<AdminApiData> {
|
||||||
if (!ADMIN_API_URL) {
|
if (!ADMIN_API_URL) {
|
||||||
return Promise.reject('No admin backoffice set!');
|
return Promise.reject('No admin backoffice set!');
|
||||||
@ -50,24 +66,6 @@ class AdminApi {
|
|||||||
)
|
)
|
||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async memberIsGrantedAccessToRoom(memberId: string, roomIdentifier: RoomIdentifier): Promise<GrantedApiData> {
|
|
||||||
if (!ADMIN_API_URL) {
|
|
||||||
return Promise.reject('No admin backoffice set!');
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const res = await Axios.get(ADMIN_API_URL+'/api/member/is-granted-access',
|
|
||||||
{ headers: {"Authorization" : `${ADMIN_API_TOKEN}`}, params: {memberId, organizationSlug: roomIdentifier.organizationSlug, worldSlug: roomIdentifier.worldSlug, roomSlug: roomIdentifier.roomSlug} }
|
|
||||||
)
|
|
||||||
return res.data;
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e.message)
|
|
||||||
return {
|
|
||||||
granted: false,
|
|
||||||
memberTags: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const adminApi = new AdminApi();
|
export const adminApi = new AdminApi();
|
||||||
|
3
back/src/Services/ArrayHelper.ts
Normal file
3
back/src/Services/ArrayHelper.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export const arrayIntersect = (array1: string[], array2: string[]) : boolean => {
|
||||||
|
return array1.filter(value => array2.includes(value)).length > 0;
|
||||||
|
}
|
14
back/tests/ArrayHelperTest.ts
Normal file
14
back/tests/ArrayHelperTest.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import {arrayIntersect} from "../src/Services/ArrayHelper";
|
||||||
|
|
||||||
|
|
||||||
|
describe("RoomIdentifier", () => {
|
||||||
|
it("should return true on intersect", () => {
|
||||||
|
expect(arrayIntersect(['admin', 'user'], ['admin', 'superAdmin'])).toBe(true);
|
||||||
|
});
|
||||||
|
it("should be reflexive", () => {
|
||||||
|
expect(arrayIntersect(['admin', 'superAdmin'], ['admin', 'user'])).toBe(true);
|
||||||
|
});
|
||||||
|
it("should return false on non intersect", () => {
|
||||||
|
expect(arrayIntersect(['admin', 'user'], ['superAdmin'])).toBe(false);
|
||||||
|
});
|
||||||
|
})
|
19
back/tests/RoomIdentifierTest.ts
Normal file
19
back/tests/RoomIdentifierTest.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import {extractDataFromPrivateRoomId, extractRoomSlugPublicRoomId, isRoomAnonymous} from "../src/Model/RoomIdentifier";
|
||||||
|
|
||||||
|
describe("RoomIdentifier", () => {
|
||||||
|
it("should flag public id as anonymous", () => {
|
||||||
|
expect(isRoomAnonymous('_/global/test')).toBe(true);
|
||||||
|
});
|
||||||
|
it("should flag public id as not anonymous", () => {
|
||||||
|
expect(isRoomAnonymous('@/afup/afup2020/1floor')).toBe(false);
|
||||||
|
});
|
||||||
|
it("should extract roomSlug from public ID", () => {
|
||||||
|
expect(extractRoomSlugPublicRoomId('_/global/npeguin/test.json')).toBe('npeguin/test.json');
|
||||||
|
});
|
||||||
|
it("should extract correct from private ID", () => {
|
||||||
|
const {organizationSlug, worldSlug, roomSlug} = extractDataFromPrivateRoomId('@/afup/afup2020/1floor');
|
||||||
|
expect(organizationSlug).toBe('afup');
|
||||||
|
expect(worldSlug).toBe('afup2020');
|
||||||
|
expect(roomSlug).toBe('1floor');
|
||||||
|
});
|
||||||
|
})
|
@ -21,7 +21,7 @@ describe("World", () => {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const world = new GameRoom(connect, disconnect, 160, 160, () => {}, () => {}, () => {});
|
const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {});
|
||||||
|
|
||||||
world.join(createMockUser(1), new Point(100, 100));
|
world.join(createMockUser(1), new Point(100, 100));
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ describe("World", () => {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const world = new GameRoom(connect, disconnect, 160, 160, () => {}, () => {}, () => {});
|
const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {});
|
||||||
|
|
||||||
world.join(createMockUser(1), new Point(100, 100));
|
world.join(createMockUser(1), new Point(100, 100));
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ describe("World", () => {
|
|||||||
disconnectCallNumber++;
|
disconnectCallNumber++;
|
||||||
}
|
}
|
||||||
|
|
||||||
const world = new GameRoom(connect, disconnect, 160, 160, () => {}, () => {}, () => {});
|
const world = new GameRoom('_/global/test.json', connect, disconnect, 160, 160, () => {}, () => {}, () => {});
|
||||||
|
|
||||||
world.join(createMockUser(1), new Point(100, 100));
|
world.join(createMockUser(1), new Point(100, 100));
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user