Merge branch 'develop' into new_custom_woka_scene

This commit is contained in:
Piotr 'pwh' Hanusiak 2022-03-29 15:52:16 +02:00
commit 21e84064b9
31 changed files with 296 additions and 83 deletions

View File

@ -6,6 +6,7 @@ import {
EmoteCallback, EmoteCallback,
EntersCallback, EntersCallback,
LeavesCallback, LeavesCallback,
LockGroupCallback,
MovesCallback, MovesCallback,
PlayerDetailsUpdatedCallback, PlayerDetailsUpdatedCallback,
} from "_Model/Zone"; } from "_Model/Zone";
@ -44,7 +45,7 @@ export class GameRoom {
// Users, sorted by ID // Users, sorted by ID
private readonly users = new Map<number, User>(); private readonly users = new Map<number, User>();
private readonly usersByUuid = new Map<string, User>(); private readonly usersByUuid = new Map<string, User>();
private readonly groups = new Set<Group>(); private readonly groups: Map<number, Group> = new Map<number, Group>();
private readonly admins = new Set<Admin>(); private readonly admins = new Set<Admin>();
private itemsState = new Map<number, unknown>(); private itemsState = new Map<number, unknown>();
@ -66,6 +67,7 @@ export class GameRoom {
onMoves: MovesCallback, onMoves: MovesCallback,
onLeaves: LeavesCallback, onLeaves: LeavesCallback,
onEmote: EmoteCallback, onEmote: EmoteCallback,
onLockGroup: LockGroupCallback,
onPlayerDetailsUpdated: PlayerDetailsUpdatedCallback onPlayerDetailsUpdated: PlayerDetailsUpdatedCallback
) { ) {
// A zone is 10 sprites wide. // A zone is 10 sprites wide.
@ -76,6 +78,7 @@ export class GameRoom {
onMoves, onMoves,
onLeaves, onLeaves,
onEmote, onEmote,
onLockGroup,
onPlayerDetailsUpdated onPlayerDetailsUpdated
); );
} }
@ -90,6 +93,7 @@ export class GameRoom {
onMoves: MovesCallback, onMoves: MovesCallback,
onLeaves: LeavesCallback, onLeaves: LeavesCallback,
onEmote: EmoteCallback, onEmote: EmoteCallback,
onLockGroup: LockGroupCallback,
onPlayerDetailsUpdated: PlayerDetailsUpdatedCallback onPlayerDetailsUpdated: PlayerDetailsUpdatedCallback
): Promise<GameRoom> { ): Promise<GameRoom> {
const mapDetails = await GameRoom.getMapDetails(roomUrl); const mapDetails = await GameRoom.getMapDetails(roomUrl);
@ -105,6 +109,7 @@ export class GameRoom {
onMoves, onMoves,
onLeaves, onLeaves,
onEmote, onEmote,
onLockGroup,
onPlayerDetailsUpdated onPlayerDetailsUpdated
); );
@ -244,7 +249,7 @@ export class GameRoom {
this.disconnectCallback, this.disconnectCallback,
this.positionNotifier this.positionNotifier
); );
this.groups.add(group); this.groups.set(group.getId(), group);
} }
} }
} else { } else {
@ -328,7 +333,7 @@ export class GameRoom {
this.disconnectCallback, this.disconnectCallback,
this.positionNotifier this.positionNotifier
); );
this.groups.add(newGroup); this.groups.set(newGroup.getId(), newGroup);
} else { } else {
this.leaveGroup(user); this.leaveGroup(user);
} }
@ -375,10 +380,10 @@ export class GameRoom {
group.leave(user); group.leave(user);
if (group.isEmpty()) { if (group.isEmpty()) {
group.destroy(); group.destroy();
if (!this.groups.has(group)) { if (!this.groups.has(group.getId())) {
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); this.groups.delete(group.getId());
//todo: is the group garbage collected? //todo: is the group garbage collected?
} else { } else {
group.updatePosition(); group.updatePosition();
@ -418,7 +423,7 @@ export class GameRoom {
}); });
this.groups.forEach((group: Group) => { this.groups.forEach((group: Group) => {
if (group.isFull()) { if (group.isFull() || group.isLocked()) {
return; return;
} }
const distance = GameRoom.computeDistanceBetweenPositions(user.getPosition(), group.getPosition()); const distance = GameRoom.computeDistanceBetweenPositions(user.getPosition(), group.getPosition());
@ -544,6 +549,10 @@ export class GameRoom {
this.positionNotifier.emitEmoteEvent(user, emoteEventMessage); this.positionNotifier.emitEmoteEvent(user, emoteEventMessage);
} }
public emitLockGroupEvent(user: User, groupId: number) {
this.positionNotifier.emitLockGroupEvent(user, groupId);
}
public addRoomListener(socket: RoomSocket) { public addRoomListener(socket: RoomSocket) {
this.roomListeners.add(socket); this.roomListeners.add(socket);
} }
@ -657,4 +666,8 @@ export class GameRoom {
const variablesManager = await this.getVariableManager(); const variablesManager = await this.getVariableManager();
return variablesManager.getVariablesForTags(tags); return variablesManager.getVariablesForTags(tags);
} }
public getGroupById(id: number): Group | undefined {
return this.groups.get(id);
}
} }

View File

@ -14,6 +14,7 @@ export class Group implements Movable {
private x!: number; private x!: number;
private y!: number; private y!: number;
private wasDestroyed: boolean = false; private wasDestroyed: boolean = false;
private locked: boolean = false;
private roomId: string; private roomId: string;
private currentZone: Zone | null = null; private currentZone: Zone | null = null;
/** /**
@ -141,15 +142,19 @@ export class Group implements Movable {
return this.users.size >= MAX_PER_GROUP; return this.users.size >= MAX_PER_GROUP;
} }
isLocked(): boolean {
return this.locked;
}
isEmpty(): boolean { isEmpty(): boolean {
return this.users.size <= 1; return this.users.size <= 1;
} }
join(user: User): void { join(user: User): void {
// Broadcast on the right event // Broadcast on the right event
this.connectCallback(user, this);
this.users.add(user); this.users.add(user);
user.group = this; user.group = this;
this.connectCallback(user, this);
} }
leave(user: User): void { leave(user: User): void {
@ -167,6 +172,10 @@ export class Group implements Movable {
this.disconnectCallback(user, this); this.disconnectCallback(user, this);
} }
lock(lock: boolean = true): void {
this.locked = lock;
}
/** /**
* Let's kick everybody out. * Let's kick everybody out.
* Usually used when there is only one user left. * Usually used when there is only one user left.

View File

@ -12,6 +12,7 @@ import {
EmoteCallback, EmoteCallback,
EntersCallback, EntersCallback,
LeavesCallback, LeavesCallback,
LockGroupCallback,
MovesCallback, MovesCallback,
PlayerDetailsUpdatedCallback, PlayerDetailsUpdatedCallback,
Zone, Zone,
@ -50,6 +51,7 @@ export class PositionNotifier {
private onUserMoves: MovesCallback, private onUserMoves: MovesCallback,
private onUserLeaves: LeavesCallback, private onUserLeaves: LeavesCallback,
private onEmote: EmoteCallback, private onEmote: EmoteCallback,
private onLockGroup: LockGroupCallback,
private onPlayerDetailsUpdated: PlayerDetailsUpdatedCallback private onPlayerDetailsUpdated: PlayerDetailsUpdatedCallback
) {} ) {}
@ -111,6 +113,7 @@ export class PositionNotifier {
this.onUserMoves, this.onUserMoves,
this.onUserLeaves, this.onUserLeaves,
this.onEmote, this.onEmote,
this.onLockGroup,
this.onPlayerDetailsUpdated, this.onPlayerDetailsUpdated,
i, i,
j j
@ -137,6 +140,12 @@ export class PositionNotifier {
zone.emitEmoteEvent(emoteEventMessage); zone.emitEmoteEvent(emoteEventMessage);
} }
public emitLockGroupEvent(user: User, groupId: number) {
const zoneDesc = this.getZoneDescriptorFromCoordinates(user.getPosition().x, user.getPosition().y);
const zone = this.getZone(zoneDesc.i, zoneDesc.j);
zone.emitLockGroupEvent(groupId);
}
public *getAllUsersInSquareAroundZone(zone: Zone): Generator<User> { public *getAllUsersInSquareAroundZone(zone: Zone): Generator<User> {
const zoneDescriptor = this.getZoneDescriptorFromCoordinates(zone.x, zone.y); const zoneDescriptor = this.getZoneDescriptorFromCoordinates(zone.x, zone.y);
for (const d of getNearbyDescriptorsMatrix(zoneDescriptor)) { for (const d of getNearbyDescriptorsMatrix(zoneDescriptor)) {

View File

@ -13,6 +13,7 @@ export type EntersCallback = (thing: Movable, fromZone: Zone | null, listener: Z
export type MovesCallback = (thing: Movable, position: PositionInterface, 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 LeavesCallback = (thing: Movable, newZone: Zone | null, listener: ZoneSocket) => void;
export type EmoteCallback = (emoteEventMessage: EmoteEventMessage, listener: ZoneSocket) => void; export type EmoteCallback = (emoteEventMessage: EmoteEventMessage, listener: ZoneSocket) => void;
export type LockGroupCallback = (groupId: number, listener: ZoneSocket) => void;
export type PlayerDetailsUpdatedCallback = ( export type PlayerDetailsUpdatedCallback = (
playerDetailsUpdatedMessage: PlayerDetailsUpdatedMessage, playerDetailsUpdatedMessage: PlayerDetailsUpdatedMessage,
listener: ZoneSocket listener: ZoneSocket
@ -27,6 +28,7 @@ export class Zone {
private onMoves: MovesCallback, private onMoves: MovesCallback,
private onLeaves: LeavesCallback, private onLeaves: LeavesCallback,
private onEmote: EmoteCallback, private onEmote: EmoteCallback,
private onLockGroup: LockGroupCallback,
private onPlayerDetailsUpdated: PlayerDetailsUpdatedCallback, private onPlayerDetailsUpdated: PlayerDetailsUpdatedCallback,
public readonly x: number, public readonly x: number,
public readonly y: number public readonly y: number
@ -108,6 +110,12 @@ export class Zone {
} }
} }
public emitLockGroupEvent(groupId: number) {
for (const listener of this.listeners) {
this.onLockGroup(groupId, listener);
}
}
public updatePlayerDetails(user: User, playerDetails: SetPlayerDetailsMessage) { public updatePlayerDetails(user: User, playerDetails: SetPlayerDetailsMessage) {
const playerDetailsUpdatedMessage = new PlayerDetailsUpdatedMessage(); const playerDetailsUpdatedMessage = new PlayerDetailsUpdatedMessage();
playerDetailsUpdatedMessage.setUserid(user.id); playerDetailsUpdatedMessage.setUserid(user.id);

View File

@ -29,6 +29,7 @@ import {
WebRtcSignalToServerMessage, WebRtcSignalToServerMessage,
WorldFullWarningToRoomMessage, WorldFullWarningToRoomMessage,
ZoneMessage, ZoneMessage,
LockGroupPromptMessage,
} from "./Messages/generated/messages_pb"; } from "./Messages/generated/messages_pb";
import { sendUnaryData, ServerDuplexStream, ServerUnaryCall, ServerWritableStream } from "grpc"; import { sendUnaryData, ServerDuplexStream, ServerUnaryCall, ServerWritableStream } from "grpc";
import { socketManager } from "./Services/SocketManager"; import { socketManager } from "./Services/SocketManager";
@ -135,6 +136,12 @@ const roomManager: IRoomManagerServer = {
user, user,
message.getFollowabortmessage() as FollowAbortMessage message.getFollowabortmessage() as FollowAbortMessage
); );
} else if (message.hasLockgrouppromptmessage()) {
socketManager.handleLockGroupPromptMessage(
room,
user,
message.getLockgrouppromptmessage() as LockGroupPromptMessage
);
} else if (message.hasSendusermessage()) { } else if (message.hasSendusermessage()) {
const sendUserMessage = message.getSendusermessage(); const sendUserMessage = message.getSendusermessage();
socketManager.handleSendUserMessage(user, sendUserMessage as SendUserMessage); socketManager.handleSendUserMessage(user, sendUserMessage as SendUserMessage);

View File

@ -38,6 +38,9 @@ import {
SubToPusherRoomMessage, SubToPusherRoomMessage,
SetPlayerDetailsMessage, SetPlayerDetailsMessage,
PlayerDetailsUpdatedMessage, PlayerDetailsUpdatedMessage,
GroupUsersUpdateMessage,
LockGroupPromptMessage,
RoomMessage,
} from "../Messages/generated/messages_pb"; } from "../Messages/generated/messages_pb";
import { User, UserSocket } from "../Model/User"; import { User, UserSocket } from "../Model/User";
import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils"; import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils";
@ -68,7 +71,6 @@ function emitZoneMessage(subMessage: SubToPusherMessage, socket: ZoneSocket): vo
// TODO: should we batch those every 100ms? // TODO: should we batch those every 100ms?
const batchMessage = new BatchToPusherMessage(); const batchMessage = new BatchToPusherMessage();
batchMessage.addPayload(subMessage); batchMessage.addPayload(subMessage);
socket.write(batchMessage); socket.write(batchMessage);
} }
@ -266,18 +268,28 @@ export class SocketManager {
if (roomPromise === undefined) { if (roomPromise === undefined) {
roomPromise = GameRoom.create( roomPromise = GameRoom.create(
roomId, roomId,
(user: User, group: Group) => this.joinWebRtcRoom(user, group), (user: User, group: Group) => {
(user: User, group: Group) => this.disConnectedUser(user, group), this.joinWebRtcRoom(user, group);
this.sendGroupUsersUpdateToGroupMembers(group);
},
(user: User, group: Group) => {
this.disConnectedUser(user, group);
this.sendGroupUsersUpdateToGroupMembers(group);
},
MINIMUM_DISTANCE, MINIMUM_DISTANCE,
GROUP_RADIUS, GROUP_RADIUS,
(thing: Movable, fromZone: Zone | null, listener: ZoneSocket) => (thing: Movable, fromZone: Zone | null, listener: ZoneSocket) => {
this.onZoneEnter(thing, fromZone, listener), this.onZoneEnter(thing, fromZone, listener);
},
(thing: Movable, position: PositionInterface, listener: ZoneSocket) => (thing: Movable, position: PositionInterface, listener: ZoneSocket) =>
this.onClientMove(thing, position, listener), this.onClientMove(thing, position, listener),
(thing: Movable, newZone: Zone | null, listener: ZoneSocket) => (thing: Movable, newZone: Zone | null, listener: ZoneSocket) =>
this.onClientLeave(thing, newZone, listener), this.onClientLeave(thing, newZone, listener),
(emoteEventMessage: EmoteEventMessage, listener: ZoneSocket) => (emoteEventMessage: EmoteEventMessage, listener: ZoneSocket) =>
this.onEmote(emoteEventMessage, listener), this.onEmote(emoteEventMessage, listener),
(groupId: number, listener: ZoneSocket) => {
void this.onLockGroup(groupId, listener, roomPromise);
},
(playerDetailsUpdatedMessage: PlayerDetailsUpdatedMessage, listener: ZoneSocket) => (playerDetailsUpdatedMessage: PlayerDetailsUpdatedMessage, listener: ZoneSocket) =>
this.onPlayerDetailsUpdated(playerDetailsUpdatedMessage, listener) this.onPlayerDetailsUpdated(playerDetailsUpdatedMessage, listener)
) )
@ -381,10 +393,24 @@ export class SocketManager {
emitZoneMessage(subMessage, client); emitZoneMessage(subMessage, client);
} }
private async onLockGroup(
groupId: number,
client: ZoneSocket,
roomPromise: PromiseLike<GameRoom> | undefined
): Promise<void> {
if (!roomPromise) {
return;
}
const group = (await roomPromise).getGroupById(groupId);
if (!group) {
return;
}
this.emitCreateUpdateGroupEvent(client, null, group);
}
private onPlayerDetailsUpdated(playerDetailsUpdatedMessage: PlayerDetailsUpdatedMessage, client: ZoneSocket) { private onPlayerDetailsUpdated(playerDetailsUpdatedMessage: PlayerDetailsUpdatedMessage, client: ZoneSocket) {
const subMessage = new SubToPusherMessage(); const subMessage = new SubToPusherMessage();
subMessage.setPlayerdetailsupdatedmessage(playerDetailsUpdatedMessage); subMessage.setPlayerdetailsupdatedmessage(playerDetailsUpdatedMessage);
emitZoneMessage(subMessage, client); emitZoneMessage(subMessage, client);
} }
@ -398,6 +424,7 @@ export class SocketManager {
groupUpdateMessage.setPosition(pointMessage); groupUpdateMessage.setPosition(pointMessage);
groupUpdateMessage.setGroupsize(group.getSize); groupUpdateMessage.setGroupsize(group.getSize);
groupUpdateMessage.setFromzone(this.toProtoZone(fromZone)); groupUpdateMessage.setFromzone(this.toProtoZone(fromZone));
groupUpdateMessage.setLocked(group.isLocked());
const subMessage = new SubToPusherMessage(); const subMessage = new SubToPusherMessage();
subMessage.setGroupupdatezonemessage(groupUpdateMessage); subMessage.setGroupupdatezonemessage(groupUpdateMessage);
@ -413,7 +440,6 @@ export class SocketManager {
const subMessage = new SubToPusherMessage(); const subMessage = new SubToPusherMessage();
subMessage.setGroupleftzonemessage(groupDeleteMessage); subMessage.setGroupleftzonemessage(groupDeleteMessage);
emitZoneMessage(subMessage, client); emitZoneMessage(subMessage, client);
//user.emitInBatch(subMessage); //user.emitInBatch(subMessage);
} }
@ -425,7 +451,6 @@ export class SocketManager {
const subMessage = new SubToPusherMessage(); const subMessage = new SubToPusherMessage();
subMessage.setUserleftzonemessage(userLeftMessage); subMessage.setUserleftzonemessage(userLeftMessage);
emitZoneMessage(subMessage, client); emitZoneMessage(subMessage, client);
} }
@ -439,6 +464,19 @@ export class SocketManager {
return undefined; return undefined;
} }
private sendGroupUsersUpdateToGroupMembers(group: Group) {
const groupUserUpdateMessage = new GroupUsersUpdateMessage();
groupUserUpdateMessage.setGroupid(group.getId());
groupUserUpdateMessage.setUseridsList(group.getUsers().map((user) => user.id));
const clientMessage = new ServerToClientMessage();
clientMessage.setGroupusersupdatemessage(groupUserUpdateMessage);
group.getUsers().forEach((currentUser: User) => {
currentUser.socket.write(clientMessage);
});
}
private joinWebRtcRoom(user: User, group: Group) { private joinWebRtcRoom(user: User, group: Group) {
for (const otherUser of group.getUsers()) { for (const otherUser of group.getUsers()) {
if (user === otherUser) { if (user === otherUser) {
@ -634,6 +672,7 @@ export class SocketManager {
const groupUpdateMessage = new GroupUpdateZoneMessage(); const groupUpdateMessage = new GroupUpdateZoneMessage();
groupUpdateMessage.setGroupid(thing.getId()); groupUpdateMessage.setGroupid(thing.getId());
groupUpdateMessage.setPosition(ProtobufUtils.toPointMessage(thing.getPosition())); groupUpdateMessage.setPosition(ProtobufUtils.toPointMessage(thing.getPosition()));
groupUpdateMessage.setLocked(thing.isLocked());
const subMessage = new SubToPusherMessage(); const subMessage = new SubToPusherMessage();
subMessage.setGroupupdatezonemessage(groupUpdateMessage); subMessage.setGroupupdatezonemessage(groupUpdateMessage);
@ -870,6 +909,15 @@ export class SocketManager {
leader?.delFollower(user); leader?.delFollower(user);
} }
} }
handleLockGroupPromptMessage(room: GameRoom, user: User, message: LockGroupPromptMessage) {
const group = user.group;
if (!group) {
return;
}
group.lock(message.getLock());
room.emitLockGroupEvent(user, group.getId());
}
} }
export const socketManager = new SocketManager(); export const socketManager = new SocketManager();

View File

@ -52,6 +52,7 @@ describe("GameRoom", () => {
() => {}, () => {},
() => {}, () => {},
emote, emote,
() => {},
() => {} () => {}
); );
@ -88,6 +89,7 @@ describe("GameRoom", () => {
() => {}, () => {},
() => {}, () => {},
emote, emote,
() => {},
() => {} () => {}
); );
@ -128,6 +130,7 @@ describe("GameRoom", () => {
() => {}, () => {},
() => {}, () => {},
emote, emote,
() => {},
() => {} () => {}
); );

View File

@ -25,6 +25,7 @@ describe("PositionNotifier", () => {
leaveTriggered = true; leaveTriggered = true;
}, },
() => {}, () => {},
() => {},
() => {} () => {}
); );
@ -132,6 +133,7 @@ describe("PositionNotifier", () => {
leaveTriggered = true; leaveTriggered = true;
}, },
() => {}, () => {},
() => {},
() => {} () => {}
); );

View File

@ -295,9 +295,9 @@ ansi-escapes@^4.2.1:
type-fest "^0.21.3" type-fest "^0.21.3"
ansi-regex@^4.1.0: ansi-regex@^4.1.0:
version "4.1.0" version "4.1.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed"
integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==
ansi-regex@^5.0.1: ansi-regex@^5.0.1:
version "5.0.1" version "5.0.1"
@ -2409,9 +2409,9 @@ pirates@^4.0.1:
integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==
plist@^3.0.1, plist@^3.0.4: plist@^3.0.1, plist@^3.0.4:
version "3.0.4" version "3.0.5"
resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.4.tgz#a62df837e3aed2bb3b735899d510c4f186019cbe" resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.5.tgz#2cbeb52d10e3cdccccf0c11a63a85d830970a987"
integrity sha512-ksrr8y9+nXOxQB2osVNqrgvX/XQPOXaU4BQMKjYq8PvaY1U18mo+fKgBSwzK+luSyinOuPae956lSVcBwxlAMg== integrity sha512-83vX4eYdQp3vP9SxuYgEM/G/pJQqLUz/V/xzPrzruLs7fz7jxGQ1msZ/mg1nwZxUSuOp4sb+/bEIbRrbzZRxDA==
dependencies: dependencies:
base64-js "^1.5.1" base64-js "^1.5.1"
xmlbuilder "^9.0.7" xmlbuilder "^9.0.7"

View File

@ -587,9 +587,9 @@ minimatch@^3.0.4:
brace-expansion "^1.1.7" brace-expansion "^1.1.7"
minimist@^1.2.0, minimist@^1.2.5: minimist@^1.2.0, minimist@^1.2.5:
version "1.2.5" version "1.2.6"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
mkdirp@^0.5.1: mkdirp@^0.5.1:
version "0.5.5" version "0.5.5"

View File

@ -10,12 +10,14 @@
import layoutPresentationImg from "./images/layout-presentation.svg"; import layoutPresentationImg from "./images/layout-presentation.svg";
import layoutChatImg from "./images/layout-chat.svg"; import layoutChatImg from "./images/layout-chat.svg";
import followImg from "./images/follow.svg"; import followImg from "./images/follow.svg";
import lockImg from "./images/lock.svg";
import { LayoutMode } from "../WebRtc/LayoutManager"; import { LayoutMode } from "../WebRtc/LayoutManager";
import { peerStore } from "../Stores/PeerStore"; import { peerStore } from "../Stores/PeerStore";
import { onDestroy } from "svelte"; import { onDestroy } from "svelte";
import { embedScreenLayout } from "../Stores/EmbedScreensStore"; import { embedScreenLayout } from "../Stores/EmbedScreensStore";
import { followRoleStore, followStateStore, followUsersStore } from "../Stores/FollowStore"; import { followRoleStore, followStateStore, followUsersStore } from "../Stores/FollowStore";
import { gameManager } from "../Phaser/Game/GameManager"; import { gameManager } from "../Phaser/Game/GameManager";
import { currentPlayerGroupLockStateStore } from "../Stores/CurrentPlayerGroupStore";
const gameScene = gameManager.getCurrentGameScene(); const gameScene = gameManager.getCurrentGameScene();
@ -70,6 +72,10 @@
} }
} }
function lockClick() {
gameScene.connection?.emitLockGroup(!$currentPlayerGroupLockStateStore);
}
let isSilent: boolean; let isSilent: boolean;
const unsubscribeIsSilent = isSilentStore.subscribe((value) => { const unsubscribeIsSilent = isSilentStore.subscribe((value) => {
isSilent = value; isSilent = value;
@ -95,6 +101,15 @@
<img class="noselect" src={followImg} alt="" /> <img class="noselect" src={followImg} alt="" />
</div> </div>
<div
class="btn-lock"
class:hide={$peerStore.size === 0 || isSilent}
class:disabled={$currentPlayerGroupLockStateStore}
on:click={lockClick}
>
<img class="noselect" src={lockImg} alt="" />
</div>
<div <div
class="btn-monitor" class="btn-monitor"
on:click={screenSharingClick} on:click={screenSharingClick}
@ -162,7 +177,7 @@
transform: translateY(15px); transform: translateY(15px);
transition-timing-function: ease-in-out; transition-timing-function: ease-in-out;
transition: all 0.3s; transition: all 0.3s;
margin: 0 4%; margin: 0 2%;
&.hide { &.hide {
transform: translateY(60px); transform: translateY(60px);
@ -211,6 +226,14 @@
} }
} }
.btn-lock {
pointer-events: auto;
img {
filter: brightness(0) invert(1);
}
}
@media (hover: none) { @media (hover: none) {
/** /**
* If we cannot hover over elements, let's display camera button in full. * If we cannot hover over elements, let's display camera button in full.

View File

@ -0,0 +1 @@
<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50px" height="50px"><path d="M 25 3 C 18.363281 3 13 8.363281 13 15 L 13 20 L 9 20 C 7.355469 20 6 21.355469 6 23 L 6 47 C 6 48.644531 7.355469 50 9 50 L 41 50 C 42.644531 50 44 48.644531 44 47 L 44 23 C 44 21.355469 42.644531 20 41 20 L 37 20 L 37 15 C 37 8.363281 31.636719 3 25 3 Z M 25 5 C 30.566406 5 35 9.433594 35 15 L 35 20 L 15 20 L 15 15 C 15 9.433594 19.433594 5 25 5 Z M 9 22 L 41 22 C 41.554688 22 42 22.445313 42 23 L 42 47 C 42 47.554688 41.554688 48 41 48 L 9 48 C 8.445313 48 8 47.554688 8 47 L 8 23 C 8 22.445313 8.445313 22 9 22 Z M 25 30 C 23.300781 30 22 31.300781 22 33 C 22 33.898438 22.398438 34.6875 23 35.1875 L 23 38 C 23 39.101563 23.898438 40 25 40 C 26.101563 40 27 39.101563 27 38 L 27 35.1875 C 27.601563 34.6875 28 33.898438 28 33 C 28 31.300781 26.699219 30 25 30 Z"/></svg>

After

Width:  |  Height:  |  Size: 891 B

View File

@ -1,5 +1,5 @@
import { Subject } from "rxjs"; import { Subject } from "rxjs";
import type { BanUserMessage, SendUserMessage } from "../Messages/ts-proto-generated/messages"; import type { BanUserMessage, SendUserMessage } from "../Messages/ts-proto-generated/protos/messages";
export enum AdminMessageEventTypes { export enum AdminMessageEventTypes {
admin = "message", admin = "message",

View File

@ -88,8 +88,7 @@ class ConnectionManager {
* @return returns a promise to the Room we are going to load OR a pointer to the URL we must redirect to if authentication is needed. * @return returns a promise to the Room we are going to load OR a pointer to the URL we must redirect to if authentication is needed.
*/ */
public async initGameConnexion(): Promise<Room | URL> { public async initGameConnexion(): Promise<Room | URL> {
const connexionType = urlManager.getGameConnexionType(); this.connexionType = urlManager.getGameConnexionType();
this.connexionType = connexionType;
this._currentRoom = null; this._currentRoom = null;
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
@ -102,14 +101,15 @@ class ConnectionManager {
urlParams.delete("token"); urlParams.delete("token");
} }
if (connexionType === GameConnexionTypes.login) { if (this.connexionType === GameConnexionTypes.login) {
this._currentRoom = await Room.createRoom(new URL(localUserStore.getLastRoomUrl())); this._currentRoom = await Room.createRoom(new URL(localUserStore.getLastRoomUrl()));
const redirect = this.loadOpenIDScreen(); const redirect = this.loadOpenIDScreen();
if (redirect !== null) { if (redirect !== null) {
return redirect; return redirect;
} }
urlManager.pushRoomIdToUrl(this._currentRoom); urlManager.pushRoomIdToUrl(this._currentRoom);
} else if (connexionType === GameConnexionTypes.jwt) { } else if (this.connexionType === GameConnexionTypes.jwt) {
/** @deprecated */
if (!token) { if (!token) {
const code = urlParams.get("code"); const code = urlParams.get("code");
const state = urlParams.get("state"); const state = urlParams.get("state");
@ -135,8 +135,9 @@ class ConnectionManager {
return redirect; return redirect;
} }
urlManager.pushRoomIdToUrl(this._currentRoom); urlManager.pushRoomIdToUrl(this._currentRoom);
} else if (connexionType === GameConnexionTypes.register) { }
//@deprecated //@deprecated
else if (this.connexionType === GameConnexionTypes.register) {
const organizationMemberToken = urlManager.getOrganizationToken(); const organizationMemberToken = urlManager.getOrganizationToken();
const data = await Axios.post(`${PUSHER_URL}/register`, { organizationMemberToken }).then( const data = await Axios.post(`${PUSHER_URL}/register`, { organizationMemberToken }).then(
(res) => res.data (res) => res.data
@ -165,11 +166,11 @@ class ConnectionManager {
) )
); );
urlManager.pushRoomIdToUrl(this._currentRoom); urlManager.pushRoomIdToUrl(this._currentRoom);
} else if (connexionType === GameConnexionTypes.room || connexionType === GameConnexionTypes.empty) { } else if (this.connexionType === GameConnexionTypes.room || this.connexionType === GameConnexionTypes.empty) {
this.authToken = localUserStore.getAuthToken(); this.authToken = localUserStore.getAuthToken();
let roomPath: string; let roomPath: string;
if (connexionType === GameConnexionTypes.empty) { if (this.connexionType === GameConnexionTypes.empty) {
roomPath = localUserStore.getLastRoomUrl(); roomPath = localUserStore.getLastRoomUrl();
//get last room path from cache api //get last room path from cache api
try { try {

View File

@ -43,7 +43,13 @@ export interface PositionInterface {
export interface GroupCreatedUpdatedMessageInterface { export interface GroupCreatedUpdatedMessageInterface {
position: PositionInterface; position: PositionInterface;
groupId: number; groupId: number;
groupSize: number; groupSize?: number;
locked?: boolean;
}
export interface GroupUsersUpdateMessageInterface {
groupId: number;
userIds: number[];
} }
export interface WebRtcDisconnectMessageInterface { export interface WebRtcDisconnectMessageInterface {

View File

@ -5,47 +5,35 @@ import type { UserSimplePeerInterface } from "../WebRtc/SimplePeer";
import { ProtobufClientUtils } from "../Network/ProtobufClientUtils"; import { ProtobufClientUtils } from "../Network/ProtobufClientUtils";
import type { import type {
GroupCreatedUpdatedMessageInterface, GroupCreatedUpdatedMessageInterface,
ItemEventMessageInterface, GroupUsersUpdateMessageInterface,
MessageUserJoined, MessageUserJoined,
OnConnectInterface,
PlayerDetailsUpdatedMessageInterface,
PlayGlobalMessageInterface, PlayGlobalMessageInterface,
PositionInterface, PositionInterface,
RoomJoinedMessageInterface, RoomJoinedMessageInterface,
ViewportInterface, ViewportInterface,
WebRtcDisconnectMessageInterface,
WebRtcSignalReceivedMessageInterface, WebRtcSignalReceivedMessageInterface,
} from "./ConnexionModels"; } from "./ConnexionModels";
import type { BodyResourceDescriptionInterface } from "../Phaser/Entity/PlayerTextures"; import type { BodyResourceDescriptionInterface } from "../Phaser/Entity/PlayerTextures";
import { adminMessagesService } from "./AdminMessagesService"; import { adminMessagesService } from "./AdminMessagesService";
import { connectionManager } from "./ConnectionManager"; import { connectionManager } from "./ConnectionManager";
import { get } from "svelte/store"; import { get } from "svelte/store";
import { followRoleStore, followUsersStore } from "../Stores/FollowStore";
import { menuIconVisiblilityStore, menuVisiblilityStore, warningContainerStore } from "../Stores/MenuStore"; import { menuIconVisiblilityStore, menuVisiblilityStore, warningContainerStore } from "../Stores/MenuStore";
import { followStateStore, followRoleStore, followUsersStore } from "../Stores/FollowStore";
import { localUserStore } from "./LocalUserStore"; import { localUserStore } from "./LocalUserStore";
import { import {
RefreshRoomMessage,
ServerToClientMessage as ServerToClientMessageTsProto, ServerToClientMessage as ServerToClientMessageTsProto,
TokenExpiredMessage, TokenExpiredMessage,
WorldConnexionMessage, WorldConnexionMessage,
WorldFullMessage,
ErrorMessage as ErrorMessageTsProto, ErrorMessage as ErrorMessageTsProto,
UserMovedMessage as UserMovedMessageTsProto, UserMovedMessage as UserMovedMessageTsProto,
GroupUpdateMessage as GroupUpdateMessageTsProto, GroupUpdateMessage as GroupUpdateMessageTsProto,
GroupDeleteMessage as GroupDeleteMessageTsProto, GroupDeleteMessage as GroupDeleteMessageTsProto,
UserJoinedMessage as UserJoinedMessageTsProto, UserJoinedMessage as UserJoinedMessageTsProto,
UserLeftMessage as UserLeftMessageTsProto, UserLeftMessage as UserLeftMessageTsProto,
ItemEventMessage as ItemEventMessageTsProto,
EmoteEventMessage as EmoteEventMessageTsProto, EmoteEventMessage as EmoteEventMessageTsProto,
VariableMessage as VariableMessageTsProto,
PlayerDetailsUpdatedMessage as PlayerDetailsUpdatedMessageTsProto, PlayerDetailsUpdatedMessage as PlayerDetailsUpdatedMessageTsProto,
WorldFullWarningMessage,
WebRtcDisconnectMessage as WebRtcDisconnectMessageTsProto, WebRtcDisconnectMessage as WebRtcDisconnectMessageTsProto,
PlayGlobalMessage as PlayGlobalMessageTsProto,
StopGlobalMessage as StopGlobalMessageTsProto,
SendJitsiJwtMessage as SendJitsiJwtMessageTsProto, SendJitsiJwtMessage as SendJitsiJwtMessageTsProto,
SendUserMessage as SendUserMessageTsProto,
BanUserMessage as BanUserMessageTsProto,
ClientToServerMessage as ClientToServerMessageTsProto, ClientToServerMessage as ClientToServerMessageTsProto,
PositionMessage as PositionMessageTsProto, PositionMessage as PositionMessageTsProto,
ViewportMessage as ViewportMessageTsProto, ViewportMessage as ViewportMessageTsProto,
@ -53,10 +41,8 @@ import {
SetPlayerDetailsMessage as SetPlayerDetailsMessageTsProto, SetPlayerDetailsMessage as SetPlayerDetailsMessageTsProto,
PingMessage as PingMessageTsProto, PingMessage as PingMessageTsProto,
CharacterLayerMessage, CharacterLayerMessage,
} from "../Messages/ts-proto-generated/messages"; } from "../Messages/ts-proto-generated/protos/messages";
import { Subject } from "rxjs"; import { Subject } from "rxjs";
import { OpenPopupEvent } from "../Api/Events/OpenPopupEvent";
import { match } from "assert";
import { selectCharacterSceneVisibleStore } from "../Stores/SelectCharacterStore"; import { selectCharacterSceneVisibleStore } from "../Stores/SelectCharacterStore";
import { gameManager } from "../Phaser/Game/GameManager"; import { gameManager } from "../Phaser/Game/GameManager";
import { SelectCharacterScene, SelectCharacterSceneName } from "../Phaser/Login/SelectCharacterScene"; import { SelectCharacterScene, SelectCharacterSceneName } from "../Phaser/Login/SelectCharacterScene";
@ -116,6 +102,9 @@ export class RoomConnection implements RoomConnection {
private readonly _groupUpdateMessageStream = new Subject<GroupCreatedUpdatedMessageInterface>(); private readonly _groupUpdateMessageStream = new Subject<GroupCreatedUpdatedMessageInterface>();
public readonly groupUpdateMessageStream = this._groupUpdateMessageStream.asObservable(); public readonly groupUpdateMessageStream = this._groupUpdateMessageStream.asObservable();
private readonly _groupUsersUpdateMessageStream = new Subject<GroupUsersUpdateMessageInterface>();
public readonly groupUsersUpdateMessageStream = this._groupUsersUpdateMessageStream.asObservable();
private readonly _groupDeleteMessageStream = new Subject<GroupDeleteMessageTsProto>(); private readonly _groupDeleteMessageStream = new Subject<GroupDeleteMessageTsProto>();
public readonly groupDeleteMessageStream = this._groupDeleteMessageStream.asObservable(); public readonly groupDeleteMessageStream = this._groupDeleteMessageStream.asObservable();
@ -443,6 +432,10 @@ export class RoomConnection implements RoomConnection {
this._sendJitsiJwtMessageStream.next(message.sendJitsiJwtMessage); this._sendJitsiJwtMessageStream.next(message.sendJitsiJwtMessage);
break; break;
} }
case "groupUsersUpdateMessage": {
this._groupUsersUpdateMessageStream.next(message.groupUsersUpdateMessage);
break;
}
case "sendUserMessage": { case "sendUserMessage": {
adminMessagesService.onSendusermessage(message.sendUserMessage); adminMessagesService.onSendusermessage(message.sendUserMessage);
break; break;
@ -675,6 +668,7 @@ export class RoomConnection implements RoomConnection {
groupId: message.groupId, groupId: message.groupId,
position: position, position: position,
groupSize: message.groupSize, groupSize: message.groupSize,
locked: message.locked,
}; };
} }
@ -890,6 +884,19 @@ export class RoomConnection implements RoomConnection {
this.socket.send(bytes); this.socket.send(bytes);
} }
public emitLockGroup(lock: boolean = true): void {
const bytes = ClientToServerMessageTsProto.encode({
message: {
$case: "lockGroupPromptMessage",
lockGroupPromptMessage: {
lock,
},
},
}).finish();
this.socket.send(bytes);
}
public getAllTags(): string[] { public getAllTags(): string[] {
return this.tags; return this.tags;
} }

View File

@ -1,4 +1,4 @@
import { PositionMessage, PositionMessage_Direction } from "../Messages/ts-proto-generated/messages"; import { PositionMessage, PositionMessage_Direction } from "../Messages/ts-proto-generated/protos/messages";
import type { PointInterface } from "../Connexion/ConnexionModels"; import type { PointInterface } from "../Connexion/ConnexionModels";

View File

@ -76,6 +76,7 @@ import { userIsAdminStore } from "../../Stores/GameStore";
import { contactPageStore } from "../../Stores/MenuStore"; import { contactPageStore } from "../../Stores/MenuStore";
import type { WasCameraUpdatedEvent } from "../../Api/Events/WasCameraUpdatedEvent"; import type { WasCameraUpdatedEvent } from "../../Api/Events/WasCameraUpdatedEvent";
import { audioManagerFileStore } from "../../Stores/AudioManagerStore"; import { audioManagerFileStore } from "../../Stores/AudioManagerStore";
import { currentPlayerGroupLockStateStore } from "../../Stores/CurrentPlayerGroupStore";
import EVENT_TYPE = Phaser.Scenes.Events; import EVENT_TYPE = Phaser.Scenes.Events;
import Texture = Phaser.Textures.Texture; import Texture = Phaser.Textures.Texture;
@ -177,6 +178,7 @@ export class GameScene extends DirtyScene {
private volumeStoreUnsubscribers: Map<number, Unsubscriber> = new Map<number, Unsubscriber>(); private volumeStoreUnsubscribers: Map<number, Unsubscriber> = new Map<number, Unsubscriber>();
private localVolumeStoreUnsubscriber: Unsubscriber | undefined; private localVolumeStoreUnsubscriber: Unsubscriber | undefined;
private followUsersColorStoreUnsubscribe!: Unsubscriber; private followUsersColorStoreUnsubscribe!: Unsubscriber;
private currentPlayerGroupIdStoreUnsubscribe!: Unsubscriber;
private biggestAvailableAreaStoreUnsubscribe!: () => void; private biggestAvailableAreaStoreUnsubscribe!: () => void;
MapUrlFile: string; MapUrlFile: string;
@ -218,6 +220,7 @@ export class GameScene extends DirtyScene {
private loader: Loader; private loader: Loader;
private lastCameraEvent: WasCameraUpdatedEvent | undefined; private lastCameraEvent: WasCameraUpdatedEvent | undefined;
private firstCameraUpdateSent: boolean = false; private firstCameraUpdateSent: boolean = false;
private currentPlayerGroupId?: number;
public readonly superLoad: SuperLoaderPlugin; public readonly superLoad: SuperLoaderPlugin;
constructor(private room: Room, MapUrlFile: string, customKey?: string | undefined) { constructor(private room: Room, MapUrlFile: string, customKey?: string | undefined) {
@ -839,6 +842,10 @@ export class GameScene extends DirtyScene {
}); });
}); });
this.connection.groupUsersUpdateMessageStream.subscribe((message) => {
this.currentPlayerGroupId = message.groupId;
});
/** /**
* Triggered when we receive the JWT token to connect to Jitsi * Triggered when we receive the JWT token to connect to Jitsi
*/ */
@ -969,7 +976,9 @@ export class GameScene extends DirtyScene {
context.arc(48, 48, 48, 0, 2 * Math.PI, false); context.arc(48, 48, 48, 0, 2 * Math.PI, false);
// context.lineWidth = 5; // context.lineWidth = 5;
context.strokeStyle = "#ffffff"; context.strokeStyle = "#ffffff";
context.fillStyle = "#ffffff44";
context.stroke(); context.stroke();
context.fill();
this.circleTexture.refresh(); this.circleTexture.refresh();
//create red circle canvas use to create sprite //create red circle canvas use to create sprite
@ -979,7 +988,9 @@ export class GameScene extends DirtyScene {
contextRed.arc(48, 48, 48, 0, 2 * Math.PI, false); contextRed.arc(48, 48, 48, 0, 2 * Math.PI, false);
//context.lineWidth = 5; //context.lineWidth = 5;
contextRed.strokeStyle = "#ff0000"; contextRed.strokeStyle = "#ff0000";
contextRed.fillStyle = "#ff000044";
contextRed.stroke(); contextRed.stroke();
contextRed.fill();
this.circleRedTexture.refresh(); this.circleRedTexture.refresh();
} }
@ -1849,12 +1860,14 @@ ${escapedMessage}
case "GroupCreatedUpdatedEvent": case "GroupCreatedUpdatedEvent":
this.doShareGroupPosition(event.event); this.doShareGroupPosition(event.event);
break; break;
case "DeleteGroupEvent":
this.doDeleteGroup(event.groupId);
break;
case "PlayerDetailsUpdated": case "PlayerDetailsUpdated":
this.doUpdatePlayerDetails(event.details); this.doUpdatePlayerDetails(event.details);
break; break;
case "DeleteGroupEvent": {
this.doDeleteGroup(event.groupId);
currentPlayerGroupLockStateStore.set(undefined);
break;
}
default: { default: {
const tmp: never = event; const tmp: never = event;
} }
@ -2028,11 +2041,16 @@ ${escapedMessage}
this, this,
Math.round(groupPositionMessage.position.x), Math.round(groupPositionMessage.position.x),
Math.round(groupPositionMessage.position.y), Math.round(groupPositionMessage.position.y),
groupPositionMessage.groupSize === MAX_PER_GROUP ? "circleSprite-red" : "circleSprite-white" groupPositionMessage.groupSize === MAX_PER_GROUP || groupPositionMessage.locked
? "circleSprite-red"
: "circleSprite-white"
); );
sprite.setDisplayOrigin(48, 48); sprite.setDisplayOrigin(48, 48);
this.add.existing(sprite); this.add.existing(sprite);
this.groups.set(groupPositionMessage.groupId, sprite); this.groups.set(groupPositionMessage.groupId, sprite);
if (this.currentPlayerGroupId === groupPositionMessage.groupId) {
currentPlayerGroupLockStateStore.set(groupPositionMessage.locked);
}
return sprite; return sprite;
} }

View File

@ -280,6 +280,9 @@ export class UserInputManager {
); );
this.scene.input.keyboard.on("keyup-SPACE", (event: Event) => { this.scene.input.keyboard.on("keyup-SPACE", (event: Event) => {
if (this.isInputDisabled) {
return;
}
this.userInputHandler.handleSpaceKeyUpEvent(event); this.userInputHandler.handleSpaceKeyUpEvent(event);
}); });
} }

View File

@ -0,0 +1,3 @@
import { writable } from "svelte/store";
export const currentPlayerGroupLockStateStore = writable<boolean | undefined>(undefined);

View File

@ -3,10 +3,10 @@ import { localUserStore } from "../Connexion/LocalUserStore";
export enum GameConnexionTypes { export enum GameConnexionTypes {
room = 1, room = 1,
register, register /*@deprecated*/,
empty, empty,
unknown, unknown,
jwt, jwt /*@deprecated*/,
login, login,
} }
@ -16,11 +16,15 @@ class UrlManager {
const url = window.location.pathname.toString(); const url = window.location.pathname.toString();
if (url === "/login") { if (url === "/login") {
return GameConnexionTypes.login; return GameConnexionTypes.login;
} else if (url === "/jwt") { }
//@deprecated jwt url will be replace by "?token=<private access token>"
else if (url === "/jwt") {
return GameConnexionTypes.jwt; return GameConnexionTypes.jwt;
} else if (url.includes("_/") || url.includes("*/") || url.includes("@/")) { } else if (url.includes("_/") || url.includes("*/") || url.includes("@/")) {
return GameConnexionTypes.room; return GameConnexionTypes.room;
} else if (url.includes("register/")) { }
//@deprecated register url will be replace by "?token=<private access token>"
else if (url.includes("register/")) {
return GameConnexionTypes.register; return GameConnexionTypes.register;
} else if (url === "/") { } else if (url === "/") {
return GameConnexionTypes.empty; return GameConnexionTypes.empty;
@ -29,6 +33,9 @@ class UrlManager {
} }
} }
/**
* @deprecated
*/
public getOrganizationToken(): string | null { public getOrganizationToken(): string | null {
const match = /\/register\/(.+)/.exec(window.location.pathname.toString()); const match = /\/register\/(.+)/.exec(window.location.pathname.toString());
return match ? match[1] : null; return match ? match[1] : null;

View File

@ -124,9 +124,9 @@ ansi-escapes@^4.2.1:
type-fest "^0.21.3" type-fest "^0.21.3"
ansi-regex@^4.1.0: ansi-regex@^4.1.0:
version "4.1.0" version "4.1.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed"
integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==
ansi-regex@^5.0.0: ansi-regex@^5.0.0:
version "5.0.1" version "5.0.1"

View File

@ -6,7 +6,7 @@
"proto": "grpc_tools_node_protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts --grpc_out=generated --js_out=\"import_style=commonjs,binary:generated\" --ts_out=generated -I ./protos protos/*.proto", "proto": "grpc_tools_node_protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts --grpc_out=generated --js_out=\"import_style=commonjs,binary:generated\" --ts_out=generated -I ./protos protos/*.proto",
"ts-proto": "grpc_tools_node_protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=ts-proto-generated --ts_proto_opt=oneof=unions --ts_proto_opt=esModuleInterop=true protos/*.proto", "ts-proto": "grpc_tools_node_protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=ts-proto-generated --ts_proto_opt=oneof=unions --ts_proto_opt=esModuleInterop=true protos/*.proto",
"copy-to-back": "rm -rf ../back/src/Messages/generated && cp -rf generated/ ../back/src/Messages/generated", "copy-to-back": "rm -rf ../back/src/Messages/generated && cp -rf generated/ ../back/src/Messages/generated",
"copy-to-front-ts-proto": "sed 's/import { Observable } from \"rxjs\";/import type { Observable } from \"rxjs\";/g' ts-proto-generated/protos/messages.ts > ../front/src/Messages/ts-proto-generated/messages.ts", "copy-to-front-ts-proto": "cp -rf ts-proto-generated/* ../front/src/Messages/ts-proto-generated/ && sed -i 's/import { Observable } from \"rxjs\";/import type { Observable } from \"rxjs\";/g' ../front/src/Messages/ts-proto-generated/protos/messages.ts",
"copy-to-pusher": "rm -rf ../pusher/src/Messages/generated && cp -rf generated/ ../pusher/src/Messages/generated", "copy-to-pusher": "rm -rf ../pusher/src/Messages/generated && cp -rf generated/ ../pusher/src/Messages/generated",
"json-copy-to-pusher": "rm -rf ../pusher/src/Messages/JsonMessages/* && cp -rf JsonMessages/* ../pusher/src/Messages/JsonMessages/", "json-copy-to-pusher": "rm -rf ../pusher/src/Messages/JsonMessages/* && cp -rf JsonMessages/* ../pusher/src/Messages/JsonMessages/",
"json-copy-to-back": "rm -rf ../back/src/Messages/JsonMessages/* && cp -rf JsonMessages/* ../back/src/Messages/JsonMessages/", "json-copy-to-back": "rm -rf ../back/src/Messages/JsonMessages/* && cp -rf JsonMessages/* ../back/src/Messages/JsonMessages/",

View File

@ -1,5 +1,7 @@
syntax = "proto3"; syntax = "proto3";
import "google/protobuf/wrappers.proto";
/*********** PARTIAL MESSAGES **************/ /*********** PARTIAL MESSAGES **************/
message PositionMessage { message PositionMessage {
@ -99,6 +101,10 @@ message FollowAbortMessage {
int32 follower = 2; int32 follower = 2;
} }
message LockGroupPromptMessage {
bool lock = 1;
}
message ClientToServerMessage { message ClientToServerMessage {
oneof message { oneof message {
UserMovesMessage userMovesMessage = 2; UserMovesMessage userMovesMessage = 2;
@ -117,6 +123,7 @@ message ClientToServerMessage {
FollowRequestMessage followRequestMessage = 15; FollowRequestMessage followRequestMessage = 15;
FollowConfirmationMessage followConfirmationMessage = 16; FollowConfirmationMessage followConfirmationMessage = 16;
FollowAbortMessage followAbortMessage = 17; FollowAbortMessage followAbortMessage = 17;
LockGroupPromptMessage lockGroupPromptMessage = 18;
} }
} }
@ -184,7 +191,8 @@ message BatchMessage {
message GroupUpdateMessage { message GroupUpdateMessage {
int32 groupId = 1; int32 groupId = 1;
PointMessage position = 2; PointMessage position = 2;
int32 groupSize = 3; google.protobuf.UInt32Value groupSize = 3;
google.protobuf.BoolValue locked = 4;
} }
message GroupDeleteMessage { message GroupDeleteMessage {
@ -216,6 +224,11 @@ message ItemStateMessage {
string stateJson = 2; string stateJson = 2;
} }
message GroupUsersUpdateMessage {
int32 groupId = 1;
repeated int32 userIds = 2;
}
message RoomJoinedMessage { message RoomJoinedMessage {
//repeated UserJoinedMessage user = 1; //repeated UserJoinedMessage user = 1;
//repeated GroupUpdateMessage group = 2; //repeated GroupUpdateMessage group = 2;
@ -316,6 +329,7 @@ message ServerToClientMessage {
FollowConfirmationMessage followConfirmationMessage = 22; FollowConfirmationMessage followConfirmationMessage = 22;
FollowAbortMessage followAbortMessage = 23; FollowAbortMessage followAbortMessage = 23;
InvalidTextureMessage invalidTextureMessage = 24; InvalidTextureMessage invalidTextureMessage = 24;
GroupUsersUpdateMessage groupUsersUpdateMessage = 25;
} }
} }
@ -356,8 +370,9 @@ message UserLeftZoneMessage {
message GroupUpdateZoneMessage { message GroupUpdateZoneMessage {
int32 groupId = 1; int32 groupId = 1;
PointMessage position = 2; PointMessage position = 2;
int32 groupSize = 3; int32 groupSize = 3;
Zone fromZone = 4; Zone fromZone = 4;
bool locked = 5;
} }
message GroupLeftZoneMessage { message GroupLeftZoneMessage {
@ -403,6 +418,7 @@ message PusherToBackMessage {
FollowRequestMessage followRequestMessage = 16; FollowRequestMessage followRequestMessage = 16;
FollowConfirmationMessage followConfirmationMessage = 17; FollowConfirmationMessage followConfirmationMessage = 17;
FollowAbortMessage followAbortMessage = 18; FollowAbortMessage followAbortMessage = 18;
LockGroupPromptMessage lockGroupPromptMessage = 19;
} }
} }

View File

@ -320,10 +320,11 @@ export class AuthenticateController extends BaseHttpController {
//todo: what to do if the organizationMemberToken is already used? //todo: what to do if the organizationMemberToken is already used?
const organizationMemberToken: string | null = param.organizationMemberToken; const organizationMemberToken: string | null = param.organizationMemberToken;
const playUri: string | null = param.playUri;
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, playUri);
const userUuid = data.userUuid; const userUuid = data.userUuid;
const email = data.email; const email = data.email;
const roomUrl = data.roomUrl; const roomUrl = data.roomUrl;

View File

@ -21,6 +21,7 @@ import {
FollowConfirmationMessage, FollowConfirmationMessage,
FollowAbortMessage, FollowAbortMessage,
VariableMessage, VariableMessage,
LockGroupPromptMessage,
} from "../Messages/generated/messages_pb"; } from "../Messages/generated/messages_pb";
import { UserMovesMessage } from "../Messages/generated/messages_pb"; import { UserMovesMessage } from "../Messages/generated/messages_pb";
import { parse } from "query-string"; import { parse } from "query-string";
@ -561,6 +562,11 @@ export class IoSocketController {
); );
} else if (message.hasFollowabortmessage()) { } else if (message.hasFollowabortmessage()) {
socketManager.handleFollowAbort(client, message.getFollowabortmessage() as FollowAbortMessage); socketManager.handleFollowAbort(client, message.getFollowabortmessage() as FollowAbortMessage);
} else if (message.hasLockgrouppromptmessage()) {
socketManager.handleLockGroup(
client,
message.getLockgrouppromptmessage() as LockGroupPromptMessage
);
} }
/* Ok is false if backpressure was built up, wait for drain */ /* Ok is false if backpressure was built up, wait for drain */

View File

@ -13,14 +13,14 @@ export class OpenIdProfileController extends BaseHttpController {
} }
try { try {
const resCheckTokenAuth = await openIDClient.checkTokenAuth(accessToken as string); const resCheckTokenAuth = await openIDClient.checkTokenAuth(accessToken as string);
if (!resCheckTokenAuth.email) { if (!resCheckTokenAuth.sub) {
throw new Error("Email was not found"); throw new Error("Email was not found");
} }
res.send( res.send(
this.buildHtml( this.buildHtml(
OPID_CLIENT_ISSUER, OPID_CLIENT_ISSUER,
resCheckTokenAuth.email as string, resCheckTokenAuth.sub
resCheckTokenAuth.picture as string | undefined /*resCheckTokenAuth.picture as string | undefined*/
) )
); );
return; return;

View File

@ -22,6 +22,7 @@ import {
import { ClientReadableStream } from "grpc"; import { ClientReadableStream } from "grpc";
import { PositionDispatcher } from "_Model/PositionDispatcher"; import { PositionDispatcher } from "_Model/PositionDispatcher";
import Debug from "debug"; import Debug from "debug";
import { BoolValue, UInt32Value } from "google-protobuf/google/protobuf/wrappers_pb";
const debug = Debug("zone"); const debug = Debug("zone");
@ -123,19 +124,25 @@ export class UserDescriptor {
} }
export class GroupDescriptor { export class GroupDescriptor {
private constructor(public readonly groupId: number, private groupSize: number, private position: PointMessage) {} private constructor(
public readonly groupId: number,
private groupSize: number | undefined,
private position: PointMessage,
private locked: boolean | undefined
) {}
public static createFromGroupUpdateZoneMessage(message: GroupUpdateZoneMessage): GroupDescriptor { public static createFromGroupUpdateZoneMessage(message: GroupUpdateZoneMessage): GroupDescriptor {
const position = message.getPosition(); const position = message.getPosition();
if (position === undefined) { if (position === undefined) {
throw new Error("Missing position"); throw new Error("Missing position");
} }
return new GroupDescriptor(message.getGroupid(), message.getGroupsize(), position); return new GroupDescriptor(message.getGroupid(), message.getGroupsize(), position, message.getLocked());
} }
public update(groupDescriptor: GroupDescriptor) { public update(groupDescriptor: GroupDescriptor) {
this.groupSize = groupDescriptor.groupSize; this.groupSize = groupDescriptor.groupSize;
this.position = groupDescriptor.position; this.position = groupDescriptor.position;
this.locked = groupDescriptor.locked;
} }
public toGroupUpdateMessage(): GroupUpdateMessage { public toGroupUpdateMessage(): GroupUpdateMessage {
@ -144,9 +151,13 @@ export class GroupDescriptor {
throw new Error("GroupDescriptor.groupId is not an integer: " + this.groupId); throw new Error("GroupDescriptor.groupId is not an integer: " + this.groupId);
} }
groupUpdateMessage.setGroupid(this.groupId); groupUpdateMessage.setGroupid(this.groupId);
groupUpdateMessage.setGroupsize(this.groupSize); if (this.groupSize !== undefined) {
groupUpdateMessage.setGroupsize(new UInt32Value().setValue(this.groupSize));
}
groupUpdateMessage.setPosition(this.position); groupUpdateMessage.setPosition(this.position);
if (this.locked !== undefined) {
groupUpdateMessage.setLocked(new BoolValue().setValue(this.locked));
}
return groupUpdateMessage; return groupUpdateMessage;
} }
} }
@ -206,9 +217,7 @@ export class Zone {
this.notifyGroupMove(groupDescriptor); this.notifyGroupMove(groupDescriptor);
} else { } else {
this.groups.set(groupId, groupDescriptor); this.groups.set(groupId, groupDescriptor);
const fromZone = groupUpdateZoneMessage.getFromzone(); const fromZone = groupUpdateZoneMessage.getFromzone();
this.notifyGroupEnter(groupDescriptor, fromZone?.toObject()); this.notifyGroupEnter(groupDescriptor, fromZone?.toObject());
} }
} else if (message.hasUserleftzonemessage()) { } else if (message.hasUserleftzonemessage()) {

View File

@ -61,7 +61,7 @@ class AdminApi {
async fetchMemberDataByUuid( async fetchMemberDataByUuid(
userIdentifier: string | null, userIdentifier: string | null,
roomId: string, playUri: string,
ipAddress: string, ipAddress: string,
characterLayers: string[] characterLayers: string[]
): Promise<FetchMemberDataByUuidResponse> { ): Promise<FetchMemberDataByUuidResponse> {
@ -69,7 +69,12 @@ class AdminApi {
return Promise.reject(new Error("No admin backoffice set!")); return Promise.reject(new Error("No admin backoffice set!"));
} }
const res = await Axios.get<unknown, AxiosResponse<unknown>>(ADMIN_API_URL + "/api/room/access", { const res = await Axios.get<unknown, AxiosResponse<unknown>>(ADMIN_API_URL + "/api/room/access", {
params: { userIdentifier, roomId, ipAddress, characterLayers }, params: {
userIdentifier,
playUri,
ipAddress,
characterLayers,
},
headers: { Authorization: `${ADMIN_API_TOKEN}` }, headers: { Authorization: `${ADMIN_API_TOKEN}` },
paramsSerializer: (p) => { paramsSerializer: (p) => {
return qs.stringify(p, { arrayFormat: "brackets" }); return qs.stringify(p, { arrayFormat: "brackets" });
@ -84,12 +89,13 @@ class AdminApi {
return res.data; return res.data;
} }
async fetchMemberDataByToken(organizationMemberToken: string): Promise<AdminApiData> { async fetchMemberDataByToken(organizationMemberToken: string, playUri: string | null): Promise<AdminApiData> {
if (!ADMIN_API_URL) { if (!ADMIN_API_URL) {
return Promise.reject(new Error("No admin backoffice set!")); return Promise.reject(new Error("No admin backoffice set!"));
} }
//todo: this call can fail if the corresponding world is not activated or if the token is invalid. Handle that case. //todo: this call can fail if the corresponding world is not activated or if the token is invalid. Handle that case.
const res = await Axios.get(ADMIN_API_URL + "/api/login-url/" + organizationMemberToken, { const res = await Axios.get(ADMIN_API_URL + "/api/login-url/" + organizationMemberToken, {
params: { playUri },
headers: { Authorization: `${ADMIN_API_TOKEN}` }, headers: { Authorization: `${ADMIN_API_TOKEN}` },
}); });
if (!isAdminApiData(res.data)) { if (!isAdminApiData(res.data)) {

View File

@ -38,6 +38,7 @@ import {
ErrorMessage, ErrorMessage,
WorldFullMessage, WorldFullMessage,
PlayerDetailsUpdatedMessage, PlayerDetailsUpdatedMessage,
LockGroupPromptMessage,
InvalidTextureMessage, InvalidTextureMessage,
} from "../Messages/generated/messages_pb"; } from "../Messages/generated/messages_pb";
import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils"; import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils";
@ -297,6 +298,12 @@ export class SocketManager implements ZoneEventListener {
client.backConnection.write(pusherToBackMessage); client.backConnection.write(pusherToBackMessage);
} }
handleLockGroup(client: ExSocketInterface, message: LockGroupPromptMessage): void {
const pusherToBackMessage = new PusherToBackMessage();
pusherToBackMessage.setLockgrouppromptmessage(message);
client.backConnection.write(pusherToBackMessage);
}
onEmote(emoteMessage: EmoteEventMessage, listener: ExSocketInterface): void { onEmote(emoteMessage: EmoteEventMessage, listener: ExSocketInterface): void {
const subMessage = new SubMessage(); const subMessage = new SubMessage();
subMessage.setEmoteeventmessage(emoteMessage); subMessage.setEmoteeventmessage(emoteMessage);

View File

@ -174,9 +174,9 @@ ansi-escapes@^4.2.1:
type-fest "^0.11.0" type-fest "^0.11.0"
ansi-regex@^4.1.0: ansi-regex@^4.1.0:
version "4.1.0" version "4.1.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed"
integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==
ansi-regex@^5.0.0: ansi-regex@^5.0.0:
version "5.0.0" version "5.0.0"