Release 1.9.6 (#2021)

* Change accès token with query privateAccessToken in the url

Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com>

* Add play uri param

Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com>

* PlayUri parameter

Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com>

* removed unused imports

* send group userIds to players

* sending info about group lock state. wip

* listening to lockGroupMessage on front

* cleanup

* Updating GroupDescriptors on LockGroupMessage

* remove console logs

* group circles with color fill

* fix GameRoom text

* remove obsolete check

* cr fixes #1 wip

* much cleaner approach to group lock update

* fix compilation error

* well-known values wip

* Removing old unused images

Just a bit of cleanup!

* update with dan suggest

Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com>

* Change roomId by playUri

Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com>

* hide voice indicator for player leaving still existing bubble conversation

* Refactor access by token

Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com>

* Refactor connexion manager

Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com>

* Adds settings options and localUser functions

* PrivacyShutdownStore modifies constraints according to the user's setting

* switches dropdown for checkboxes and adjusts wording

* Adds the todo about German translation

* Adds test map

* Makes the default setting: camera off and mic on

* Add z-index on embeds screens and action menu

* Fixes tileset import

* Adds settings options and localUser functions

* PrivacyShutdownStore modifies constraints according to the user's setting

* switches dropdown for checkboxes and adjusts wording

* Adds the todo about German translation

* Adds test map

* Makes the default setting: camera off and mic on

* Fixes tileset import

* Applying German translation, thanks to @Lurkars

* removed obsolete console.log

* fix linter issues

* make use of proto well knows types

* fixed types for RoomConnection

* fix locking bubble when returning from away mode

* Fix run pretty

Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com>

* prevent actions menu from appearing when inserting space key on chat

* Bump ansi-regex from 4.1.0 to 4.1.1 in /maps

Bumps [ansi-regex](https://github.com/chalk/ansi-regex) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/chalk/ansi-regex/releases)
- [Commits](https://github.com/chalk/ansi-regex/compare/v4.1.0...v4.1.1)

---
updated-dependencies:
- dependency-name: ansi-regex
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump minimist from 1.2.5 to 1.2.6 in /desktop/local-app

Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump ansi-regex from 4.1.0 to 4.1.1 in /desktop/electron

Bumps [ansi-regex](https://github.com/chalk/ansi-regex) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/chalk/ansi-regex/releases)
- [Commits](https://github.com/chalk/ansi-regex/compare/v4.1.0...v4.1.1)

---
updated-dependencies:
- dependency-name: ansi-regex
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump ansi-regex from 4.1.0 to 4.1.1 in /uploader

Bumps [ansi-regex](https://github.com/chalk/ansi-regex) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/chalk/ansi-regex/releases)
- [Commits](https://github.com/chalk/ansi-regex/compare/v4.1.0...v4.1.1)

---
updated-dependencies:
- dependency-name: ansi-regex
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump plist from 3.0.4 to 3.0.5 in /desktop/electron

Bumps [plist](https://github.com/TooTallNate/node-plist) from 3.0.4 to 3.0.5.
- [Release notes](https://github.com/TooTallNate/node-plist/releases)
- [Changelog](https://github.com/TooTallNate/plist.js/blob/master/History.md)
- [Commits](https://github.com/TooTallNate/node-plist/commits)

---
updated-dependencies:
- dependency-name: plist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: Hanusiak Piotr <piotr@ltmp.co>
Co-authored-by: David Négrier <d.negrier@thecodingmachine.com>
Co-authored-by: Piotr 'pwh' Hanusiak <p.hanusiak@workadventu.re>
Co-authored-by: Benedicte Quimbert <b.quimbert@workadventu.re>
Co-authored-by: Alexis Faizeau <a.faizeau@workadventu.re>
Co-authored-by: Bénédicte Q <37311765+HimeShaman@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
grégoire parant 2022-03-30 11:13:27 +02:00 committed by GitHub
parent 987325e787
commit 003bc86262
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 533 additions and 98 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

@ -77,6 +77,7 @@
height: max-content !important; height: max-content !important;
max-height: 40vh; max-height: 40vh;
margin-top: 200px; margin-top: 200px;
z-index: 425;
pointer-events: auto; pointer-events: auto;
font-family: "Press Start 2P"; font-family: "Press Start 2P";

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

@ -18,5 +18,7 @@
display: flex; display: flex;
padding-top: 2%; padding-top: 2%;
height: 100%; height: 100%;
position: relative;
z-index: 200;
} }
</style> </style>

View File

@ -54,6 +54,7 @@
}); });
</script> </script>
<!-- Components ordered by z-index -->
<div id="main-layout" bind:this={mainLayout}> <div id="main-layout" bind:this={mainLayout}>
<aside id="main-layout-left-aside"> <aside id="main-layout-left-aside">
{#if $menuIconVisiblilityStore} {#if $menuIconVisiblilityStore}
@ -104,14 +105,14 @@
<ShareLinkMapModal /> <ShareLinkMapModal />
{/if} {/if}
{#if $followStateStore !== "off" || $peerStore.size > 0}
<FollowMenu />
{/if}
{#if $actionsMenuStore} {#if $actionsMenuStore}
<ActionsMenu /> <ActionsMenu />
{/if} {/if}
{#if $followStateStore !== "off" || $peerStore.size > 0}
<FollowMenu />
{/if}
{#if $requestVisitCardsStore} {#if $requestVisitCardsStore}
<VisitCard visitCardUrl={$requestVisitCardsStore} /> <VisitCard visitCardUrl={$requestVisitCardsStore} />
{/if} {/if}

View File

@ -17,9 +17,14 @@
let valueGame: number = localUserStore.getGameQualityValue(); let valueGame: number = localUserStore.getGameQualityValue();
let valueVideo: number = localUserStore.getVideoQualityValue(); let valueVideo: number = localUserStore.getVideoQualityValue();
let valueLocale: string = $locale; let valueLocale: string = $locale;
let valueCameraPrivacySettings = localUserStore.getCameraPrivacySettings();
let valueMicrophonePrivacySettings = localUserStore.getMicrophonePrivacySettings();
let previewValueGame = valueGame; let previewValueGame = valueGame;
let previewValueVideo = valueVideo; let previewValueVideo = valueVideo;
let previewValueLocale = valueLocale; let previewValueLocale = valueLocale;
let previewCameraPrivacySettings = valueCameraPrivacySettings;
let previewMicrophonePrivacySettings = valueMicrophonePrivacySettings;
function saveSetting() { function saveSetting() {
let change = false; let change = false;
@ -40,6 +45,16 @@
change = true; change = true;
} }
if (valueCameraPrivacySettings !== previewCameraPrivacySettings) {
previewCameraPrivacySettings = valueCameraPrivacySettings;
localUserStore.setCameraPrivacySettings(valueCameraPrivacySettings);
}
if (valueMicrophonePrivacySettings !== previewMicrophonePrivacySettings) {
previewMicrophonePrivacySettings = valueMicrophonePrivacySettings;
localUserStore.setMicrophonePrivacySettings(valueMicrophonePrivacySettings);
}
audioManagerVolumeStore.setDecreaseWhileTalking(decreaseAudioPlayerVolumeWhileTalking); audioManagerVolumeStore.setDecreaseWhileTalking(decreaseAudioPlayerVolumeWhileTalking);
if (change) { if (change) {
@ -162,6 +177,19 @@
</select> </select>
</div> </div>
</section> </section>
<section>
<h3>{$LL.menu.settings.privacySettings.title()}</h3>
<p>{$LL.menu.settings.privacySettings.explanation()}</p>
<label>
<input type="checkbox" class="nes-checkbox is-dark" bind:checked={valueCameraPrivacySettings} />
<span>{$LL.menu.settings.privacySettings.cameraToggle()}</span>
</label>
<label>
<input type="checkbox" class="nes-checkbox is-dark" bind:checked={valueMicrophonePrivacySettings} />
<span>{$LL.menu.settings.privacySettings.microphoneToggle()}</span>
</label>
</section>
<section class="settings-section-save"> <section class="settings-section-save">
<p>{$LL.menu.settings.save.warning()}</p> <p>{$LL.menu.settings.save.warning()}</p>
<button type="button" class="nes-btn is-primary" on:click|preventDefault={saveSetting} <button type="button" class="nes-btn is-primary" on:click|preventDefault={saveSetting}
@ -204,7 +232,6 @@
on:change={changeIgnoreFollowRequests} on:change={changeIgnoreFollowRequests}
/> />
<span>{$LL.menu.settings.ignoreFollowRequest()}</span> <span>{$LL.menu.settings.ignoreFollowRequest()}</span>
</label>
<label> <label>
<input <input
type="checkbox" type="checkbox"
@ -214,6 +241,7 @@
/> />
<span>{$LL.audio.manager.reduce()}</span> <span>{$LL.audio.manager.reduce()}</span>
</label> </label>
</label>
</section> </section>
</div> </div>
@ -234,12 +262,15 @@
outline: none; outline: none;
} }
} }
section.settings-section-save { section.settings-section-save {
text-align: center; text-align: center;
p { p {
margin: 16px 0; margin: 16px 0;
} }
} }
section.settings-section-noSaveOption { section.settings-section-noSaveOption {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@ -76,6 +76,7 @@
transform: translate(-50%, 0); transform: translate(-50%, 0);
margin-top: 200px; margin-top: 200px;
max-width: 80vw; max-width: 80vw;
z-index: 350;
iframe { iframe {
border: 0; border: 0;

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

@ -25,11 +25,14 @@ const code = "code";
const cameraSetup = "cameraSetup"; const cameraSetup = "cameraSetup";
const cacheAPIIndex = "workavdenture-cache"; const cacheAPIIndex = "workavdenture-cache";
const userProperties = "user-properties"; const userProperties = "user-properties";
const cameraPrivacySettings = "cameraPrivacySettings";
const microphonePrivacySettings = "microphonePrivacySettings";
class LocalUserStore { class LocalUserStore {
saveUser(localUser: LocalUser) { saveUser(localUser: LocalUser) {
localStorage.setItem("localUser", JSON.stringify(localUser)); localStorage.setItem("localUser", JSON.stringify(localUser));
} }
getLocalUser(): LocalUser | null { getLocalUser(): LocalUser | null {
const data = localStorage.getItem("localUser"); const data = localStorage.getItem("localUser");
return data ? JSON.parse(data) : null; return data ? JSON.parse(data) : null;
@ -38,6 +41,7 @@ class LocalUserStore {
setName(name: string): void { setName(name: string): void {
localStorage.setItem(playerNameKey, name); localStorage.setItem(playerNameKey, name);
} }
getName(): string | null { getName(): string | null {
const value = localStorage.getItem(playerNameKey) || ""; const value = localStorage.getItem(playerNameKey) || "";
return isUserNameValid(value) ? value : null; return isUserNameValid(value) ? value : null;
@ -46,6 +50,7 @@ class LocalUserStore {
setPlayerCharacterIndex(playerCharacterIndex: number): void { setPlayerCharacterIndex(playerCharacterIndex: number): void {
localStorage.setItem(selectedPlayerKey, "" + playerCharacterIndex); localStorage.setItem(selectedPlayerKey, "" + playerCharacterIndex);
} }
getPlayerCharacterIndex(): number { getPlayerCharacterIndex(): number {
return parseInt(localStorage.getItem(selectedPlayerKey) || ""); return parseInt(localStorage.getItem(selectedPlayerKey) || "");
} }
@ -53,6 +58,7 @@ class LocalUserStore {
setCustomCursorPosition(activeRow: number, selectedLayers: number[]): void { setCustomCursorPosition(activeRow: number, selectedLayers: number[]): void {
localStorage.setItem(customCursorPositionKey, JSON.stringify({ activeRow, selectedLayers })); localStorage.setItem(customCursorPositionKey, JSON.stringify({ activeRow, selectedLayers }));
} }
getCustomCursorPosition(): { activeRow: number; selectedLayers: number[] } | null { getCustomCursorPosition(): { activeRow: number; selectedLayers: number[] } | null {
return JSON.parse(localStorage.getItem(customCursorPositionKey) || "null"); return JSON.parse(localStorage.getItem(customCursorPositionKey) || "null");
} }
@ -60,6 +66,7 @@ class LocalUserStore {
setCharacterLayers(layers: string[]): void { setCharacterLayers(layers: string[]): void {
localStorage.setItem(characterLayersKey, JSON.stringify(layers)); localStorage.setItem(characterLayersKey, JSON.stringify(layers));
} }
getCharacterLayers(): string[] | null { getCharacterLayers(): string[] | null {
const value = JSON.parse(localStorage.getItem(characterLayersKey) || "null"); const value = JSON.parse(localStorage.getItem(characterLayersKey) || "null");
return areCharacterLayersValid(value) ? value : null; return areCharacterLayersValid(value) ? value : null;
@ -68,6 +75,7 @@ class LocalUserStore {
setCompanion(companion: string | null): void { setCompanion(companion: string | null): void {
return localStorage.setItem(companionKey, JSON.stringify(companion)); return localStorage.setItem(companionKey, JSON.stringify(companion));
} }
getCompanion(): string | null { getCompanion(): string | null {
const companion = JSON.parse(localStorage.getItem(companionKey) || "null"); const companion = JSON.parse(localStorage.getItem(companionKey) || "null");
@ -77,6 +85,7 @@ class LocalUserStore {
return companion; return companion;
} }
wasCompanionSet(): boolean { wasCompanionSet(): boolean {
return localStorage.getItem(companionKey) ? true : false; return localStorage.getItem(companionKey) ? true : false;
} }
@ -84,6 +93,7 @@ class LocalUserStore {
setGameQualityValue(value: number): void { setGameQualityValue(value: number): void {
localStorage.setItem(gameQualityKey, "" + value); localStorage.setItem(gameQualityKey, "" + value);
} }
getGameQualityValue(): number { getGameQualityValue(): number {
return parseInt(localStorage.getItem(gameQualityKey) || "60"); return parseInt(localStorage.getItem(gameQualityKey) || "60");
} }
@ -91,6 +101,7 @@ class LocalUserStore {
setVideoQualityValue(value: number): void { setVideoQualityValue(value: number): void {
localStorage.setItem(videoQualityKey, "" + value); localStorage.setItem(videoQualityKey, "" + value);
} }
getVideoQualityValue(): number { getVideoQualityValue(): number {
return parseInt(localStorage.getItem(videoQualityKey) || "20"); return parseInt(localStorage.getItem(videoQualityKey) || "20");
} }
@ -98,6 +109,7 @@ class LocalUserStore {
setAudioPlayerVolume(value: number): void { setAudioPlayerVolume(value: number): void {
localStorage.setItem(audioPlayerVolumeKey, "" + value); localStorage.setItem(audioPlayerVolumeKey, "" + value);
} }
getAudioPlayerVolume(): number { getAudioPlayerVolume(): number {
return parseFloat(localStorage.getItem(audioPlayerVolumeKey) || "1"); return parseFloat(localStorage.getItem(audioPlayerVolumeKey) || "1");
} }
@ -105,6 +117,7 @@ class LocalUserStore {
setAudioPlayerMuted(value: boolean): void { setAudioPlayerMuted(value: boolean): void {
localStorage.setItem(audioPlayerMuteKey, value.toString()); localStorage.setItem(audioPlayerMuteKey, value.toString());
} }
getAudioPlayerMuted(): boolean { getAudioPlayerMuted(): boolean {
return localStorage.getItem(audioPlayerMuteKey) === "true"; return localStorage.getItem(audioPlayerMuteKey) === "true";
} }
@ -112,6 +125,7 @@ class LocalUserStore {
setHelpCameraSettingsShown(): void { setHelpCameraSettingsShown(): void {
localStorage.setItem(helpCameraSettingsShown, "1"); localStorage.setItem(helpCameraSettingsShown, "1");
} }
getHelpCameraSettingsShown(): boolean { getHelpCameraSettingsShown(): boolean {
return localStorage.getItem(helpCameraSettingsShown) === "1"; return localStorage.getItem(helpCameraSettingsShown) === "1";
} }
@ -119,6 +133,7 @@ class LocalUserStore {
setFullscreen(value: boolean): void { setFullscreen(value: boolean): void {
localStorage.setItem(fullscreenKey, value.toString()); localStorage.setItem(fullscreenKey, value.toString());
} }
getFullscreen(): boolean { getFullscreen(): boolean {
return localStorage.getItem(fullscreenKey) === "true"; return localStorage.getItem(fullscreenKey) === "true";
} }
@ -126,6 +141,7 @@ class LocalUserStore {
setForceCowebsiteTrigger(value: boolean): void { setForceCowebsiteTrigger(value: boolean): void {
localStorage.setItem(forceCowebsiteTriggerKey, value.toString()); localStorage.setItem(forceCowebsiteTriggerKey, value.toString());
} }
getForceCowebsiteTrigger(): boolean { getForceCowebsiteTrigger(): boolean {
return localStorage.getItem(forceCowebsiteTriggerKey) === "true"; return localStorage.getItem(forceCowebsiteTriggerKey) === "true";
} }
@ -133,6 +149,7 @@ class LocalUserStore {
setIgnoreFollowRequests(value: boolean): void { setIgnoreFollowRequests(value: boolean): void {
localStorage.setItem(ignoreFollowRequests, value.toString()); localStorage.setItem(ignoreFollowRequests, value.toString());
} }
getIgnoreFollowRequests(): boolean { getIgnoreFollowRequests(): boolean {
return localStorage.getItem(ignoreFollowRequests) === "true"; return localStorage.getItem(ignoreFollowRequests) === "true";
} }
@ -155,11 +172,13 @@ class LocalUserStore {
} }
} }
} }
getLastRoomUrl(): string { getLastRoomUrl(): string {
return ( return (
localStorage.getItem(lastRoomUrl) ?? window.location.protocol + "//" + window.location.host + START_ROOM_URL localStorage.getItem(lastRoomUrl) ?? window.location.protocol + "//" + window.location.host + START_ROOM_URL
); );
} }
getLastRoomUrlCacheApi(): Promise<string | undefined> { getLastRoomUrlCacheApi(): Promise<string | undefined> {
if (!("caches" in window)) { if (!("caches" in window)) {
return Promise.resolve(undefined); return Promise.resolve(undefined);
@ -176,6 +195,7 @@ class LocalUserStore {
setAuthToken(value: string | null) { setAuthToken(value: string | null) {
value ? localStorage.setItem(authToken, value) : localStorage.removeItem(authToken); value ? localStorage.setItem(authToken, value) : localStorage.removeItem(authToken);
} }
getAuthToken(): string | null { getAuthToken(): string | null {
return localStorage.getItem(authToken); return localStorage.getItem(authToken);
} }
@ -202,23 +222,29 @@ class LocalUserStore {
} }
return oldValue === value; return oldValue === value;
} }
setState(value: string) { setState(value: string) {
localStorage.setItem(state, value); localStorage.setItem(state, value);
} }
getState(): string | null { getState(): string | null {
return localStorage.getItem(state); return localStorage.getItem(state);
} }
generateNonce(): string { generateNonce(): string {
const newNonce = uuidv4(); const newNonce = uuidv4();
localStorage.setItem(nonce, newNonce); localStorage.setItem(nonce, newNonce);
return newNonce; return newNonce;
} }
getNonce(): string | null { getNonce(): string | null {
return localStorage.getItem(nonce); return localStorage.getItem(nonce);
} }
setCode(value: string): void { setCode(value: string): void {
localStorage.setItem(code, value); localStorage.setItem(code, value);
} }
getCode(): string | null { getCode(): string | null {
return localStorage.getItem(code); return localStorage.getItem(code);
} }
@ -226,11 +252,36 @@ class LocalUserStore {
setCameraSetup(cameraId: string) { setCameraSetup(cameraId: string) {
localStorage.setItem(cameraSetup, cameraId); localStorage.setItem(cameraSetup, cameraId);
} }
getCameraSetup(): { video: unknown; audio: unknown } | undefined { getCameraSetup(): { video: unknown; audio: unknown } | undefined {
const cameraSetupValues = localStorage.getItem(cameraSetup); const cameraSetupValues = localStorage.getItem(cameraSetup);
return cameraSetupValues != undefined ? JSON.parse(cameraSetupValues) : undefined; return cameraSetupValues != undefined ? JSON.parse(cameraSetupValues) : undefined;
} }
setCameraPrivacySettings(option: boolean) {
localStorage.setItem(cameraPrivacySettings, option.toString());
}
getCameraPrivacySettings() {
//if this setting doesn't exist in LocalUserStore, we set a default value
if (localStorage.getItem(cameraPrivacySettings) == null) {
localStorage.setItem(cameraPrivacySettings, "false");
}
return localStorage.getItem(cameraPrivacySettings) === "true";
}
setMicrophonePrivacySettings(option: boolean) {
localStorage.setItem(microphonePrivacySettings, option.toString());
}
getMicrophonePrivacySettings() {
//if this setting doesn't exist in LocalUserStore, we set a default value
if (localStorage.getItem(microphonePrivacySettings) == null) {
localStorage.setItem(microphonePrivacySettings, "true");
}
return localStorage.getItem(microphonePrivacySettings) === "true";
}
getAllUserProperties(): Map<string, unknown> { getAllUserProperties(): Map<string, unknown> {
const result = new Map<string, string>(); const result = new Map<string, string>();
for (let i = 0; i < localStorage.length; i++) { for (let i = 0; i < localStorage.length; i++) {

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

@ -11,7 +11,7 @@ import { peerStore } from "./PeerStore";
import { privacyShutdownStore } from "./PrivacyShutdownStore"; import { privacyShutdownStore } from "./PrivacyShutdownStore";
import { MediaStreamConstraintsError } from "./Errors/MediaStreamConstraintsError"; import { MediaStreamConstraintsError } from "./Errors/MediaStreamConstraintsError";
import { SoundMeter } from "../Phaser/Components/SoundMeter"; import { SoundMeter } from "../Phaser/Components/SoundMeter";
import { AudioContext } from "standardized-audio-context"; import { visibilityStore } from "./VisibilityStore";
/** /**
* A store that contains the camera state requested by the user (on or off). * A store that contains the camera state requested by the user (on or off).
@ -242,6 +242,7 @@ export const mediaStreamConstraintsStore = derived(
privacyShutdownStore, privacyShutdownStore,
cameraEnergySavingStore, cameraEnergySavingStore,
isSilentStore, isSilentStore,
visibilityStore,
], ],
( (
[ [
@ -254,6 +255,7 @@ export const mediaStreamConstraintsStore = derived(
$privacyShutdownStore, $privacyShutdownStore,
$cameraEnergySavingStore, $cameraEnergySavingStore,
$isSilentStore, $isSilentStore,
$visibilityStore,
], ],
set set
) => { ) => {
@ -292,8 +294,15 @@ export const mediaStreamConstraintsStore = derived(
// Disable webcam for privacy reasons (the game is not visible and we were talking to no one) // Disable webcam for privacy reasons (the game is not visible and we were talking to no one)
if ($privacyShutdownStore === true) { if ($privacyShutdownStore === true) {
const userMicrophonePrivacySetting = localUserStore.getMicrophonePrivacySettings();
const userCameraPrivacySetting = localUserStore.getCameraPrivacySettings();
if (!userMicrophonePrivacySetting) {
currentAudioConstraint = false;
}
if (!userCameraPrivacySetting) {
currentVideoConstraint = false; currentVideoConstraint = false;
} }
}
// Disable webcam for energy reasons (the user is not moving and we are talking to no one) // Disable webcam for energy reasons (the user is not moving and we are talking to no one)
if ($cameraEnergySavingStore === true) { if ($cameraEnergySavingStore === true) {

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

@ -57,6 +57,13 @@ const menu: NonNullable<Translation["menu"]> = {
language: { language: {
title: "Sprache", title: "Sprache",
}, },
privacySettings: {
title: "Einstellungen Abwesenheitsmodus",
explanation:
"Falls der WorkAdventure Tab nicht aktiv ist wird in den Abwesenheitsmodus umgeschaltet. Für diesen Modus kann eingestellt werden, ob die Kamera und/oder das Mikrofon deaktiviert sind solange der Tab nicht sichtbar ist.",
cameraToggle: "Kamera",
microphoneToggle: "Mikrofon",
},
save: { save: {
warning: "(Das Spiel wird nach dem Speichern neugestartet)", warning: "(Das Spiel wird nach dem Speichern neugestartet)",
button: "Speichern", button: "Speichern",

View File

@ -57,6 +57,13 @@ const menu: BaseTranslation = {
language: { language: {
title: "Language", title: "Language",
}, },
privacySettings: {
title: "Away mode settings",
explanation:
'When the WorkAdventure tab is not visible, it switches to "away mode". In this mode, you can decide to automatically disable your webcam and/or microphone for as long as the tab stays hidden.',
cameraToggle: "Camera",
microphoneToggle: "Microphone",
},
save: { save: {
warning: "(Saving these settings will restart the game)", warning: "(Saving these settings will restart the game)",
button: "Save", button: "Save",

View File

@ -57,6 +57,13 @@ const menu: NonNullable<Translation["menu"]> = {
language: { language: {
title: "Langage", title: "Langage",
}, },
privacySettings: {
title: "Paramètres du mode absent",
explanation:
"Quand l'onglet WorkAdventure n'est pas visible, vous passez en \"mode absent\". Lorsque ce mode est actif, vous pouvez décider de garder vos webcam et/ou micro désactivés tant que vous ne revenez pas sur l'onglet",
cameraToggle: "Camera",
microphoneToggle: "Microphone",
},
save: { save: {
warning: "(La sauvegarde de ces paramètres redémarre le jeu)", warning: "(La sauvegarde de ces paramètres redémarre le jeu)",
button: "Sauvegarder", button: "Sauvegarder",

View File

@ -0,0 +1,97 @@
{ "compressionlevel":-1,
"height":20,
"infinite":false,
"layers":[
{
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"height":20,
"id":42,
"name":"start",
"opacity":1,
"type":"tilelayer",
"visible":true,
"width":20,
"x":0,
"y":0
},
{
"data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
"height":20,
"id":39,
"name":"floor",
"opacity":1,
"type":"tilelayer",
"visible":true,
"width":20,
"x":0,
"y":0
},
{
"draworder":"topdown",
"id":43,
"name":"Test",
"objects":[
{
"height":225.333333333333,
"id":13,
"name":"",
"rotation":0,
"text":
{
"text":"Test: \n- Open two windows (you can use Private Mode) so that you control two Wokas.\n\n- On woka A window: go to Menu > Settings and set your away mode options\n- On woka A window: open a new tab in the browser, so that your WA tab is not visible\n\n- On woka B window: move to woka A to check that the options were applied",
"wrap":true
},
"type":"",
"visible":true,
"width":434.773333333333,
"x":97.9466666666667,
"y":33.8366666666667
},
{
"height":155,
"id":16,
"name":"",
"rotation":0,
"text":
{
"color":"#00007f",
"text":"Reminder: \nThere are 4 cases to test for your away mode (WA tab hidden) settings. \nCamera and microphone stay enabled\nOnly camera stays enabled\nOnly microphone stays enabled\nBoth are disabled",
"wrap":true
},
"type":"",
"visible":true,
"width":407.4375,
"x":96.9479166666667,
"y":322.5
}],
"opacity":1,
"type":"objectgroup",
"visible":true,
"x":0,
"y":0
}],
"nextlayerid":44,
"nextobjectid":17,
"orientation":"orthogonal",
"renderorder":"right-down",
"tiledversion":"1.7.2",
"tileheight":32,
"tilesets":[
{
"columns":11,
"firstgid":1,
"image":"..\/tileset1.png",
"imageheight":352,
"imagewidth":352,
"margin":0,
"name":"tileset1",
"spacing":0,
"tilecount":121,
"tileheight":32,
"tilewidth":32
}],
"tilewidth":32,
"type":"map",
"version":"1.6",
"width":20
}

View File

@ -463,6 +463,14 @@
<a href="#" class="testLink" data-testmap="mousewheel.json" target="_blank">Testing zoom via mouse wheel</a> <a href="#" class="testLink" data-testmap="mousewheel.json" target="_blank">Testing zoom via mouse wheel</a>
</td> </td>
</tr> </tr>
<tr>
<td>
<input type="radio" name="test-mouse-wheel"> Success <input type="radio" name="test-mouse-wheel"> Failure <input type="radio" name="test-mouse-wheel" checked> Pending
</td>
<td>
<a href="#" class="testLink" data-testmap="AwayModeSettings/away_mode_settings.json" target="_blank">Away mode settings</a>
</td>
</tr>
</table> </table>
</div> </div>
<script> <script>

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;
} }
} }
@ -358,6 +372,7 @@ message GroupUpdateZoneMessage {
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"