Merge pull request #1941 from thecodingmachine/feature-bubble-lock

Feature bubble lock
This commit is contained in:
David Négrier 2022-03-28 15:31:12 +02:00 committed by GitHub
commit 7ba3225ac4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 237 additions and 42 deletions

View File

@ -6,6 +6,7 @@ import {
EmoteCallback,
EntersCallback,
LeavesCallback,
LockGroupCallback,
MovesCallback,
PlayerDetailsUpdatedCallback,
} from "_Model/Zone";
@ -44,7 +45,7 @@ export class GameRoom {
// Users, sorted by ID
private readonly users = new Map<number, 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 itemsState = new Map<number, unknown>();
@ -66,6 +67,7 @@ export class GameRoom {
onMoves: MovesCallback,
onLeaves: LeavesCallback,
onEmote: EmoteCallback,
onLockGroup: LockGroupCallback,
onPlayerDetailsUpdated: PlayerDetailsUpdatedCallback
) {
// A zone is 10 sprites wide.
@ -76,6 +78,7 @@ export class GameRoom {
onMoves,
onLeaves,
onEmote,
onLockGroup,
onPlayerDetailsUpdated
);
}
@ -90,6 +93,7 @@ export class GameRoom {
onMoves: MovesCallback,
onLeaves: LeavesCallback,
onEmote: EmoteCallback,
onLockGroup: LockGroupCallback,
onPlayerDetailsUpdated: PlayerDetailsUpdatedCallback
): Promise<GameRoom> {
const mapDetails = await GameRoom.getMapDetails(roomUrl);
@ -105,6 +109,7 @@ export class GameRoom {
onMoves,
onLeaves,
onEmote,
onLockGroup,
onPlayerDetailsUpdated
);
@ -244,7 +249,7 @@ export class GameRoom {
this.disconnectCallback,
this.positionNotifier
);
this.groups.add(group);
this.groups.set(group.getId(), group);
}
}
} else {
@ -328,7 +333,7 @@ export class GameRoom {
this.disconnectCallback,
this.positionNotifier
);
this.groups.add(newGroup);
this.groups.set(newGroup.getId(), newGroup);
} else {
this.leaveGroup(user);
}
@ -375,10 +380,10 @@ export class GameRoom {
group.leave(user);
if (group.isEmpty()) {
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.`);
}
this.groups.delete(group);
this.groups.delete(group.getId());
//todo: is the group garbage collected?
} else {
group.updatePosition();
@ -418,7 +423,7 @@ export class GameRoom {
});
this.groups.forEach((group: Group) => {
if (group.isFull()) {
if (group.isFull() || group.isLocked()) {
return;
}
const distance = GameRoom.computeDistanceBetweenPositions(user.getPosition(), group.getPosition());
@ -544,6 +549,10 @@ export class GameRoom {
this.positionNotifier.emitEmoteEvent(user, emoteEventMessage);
}
public emitLockGroupEvent(user: User, groupId: number) {
this.positionNotifier.emitLockGroupEvent(user, groupId);
}
public addRoomListener(socket: RoomSocket) {
this.roomListeners.add(socket);
}
@ -657,4 +666,8 @@ export class GameRoom {
const variablesManager = await this.getVariableManager();
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 y!: number;
private wasDestroyed: boolean = false;
private locked: boolean = false;
private roomId: string;
private currentZone: Zone | null = null;
/**
@ -141,15 +142,19 @@ export class Group implements Movable {
return this.users.size >= MAX_PER_GROUP;
}
isLocked(): boolean {
return this.locked;
}
isEmpty(): boolean {
return this.users.size <= 1;
}
join(user: User): void {
// Broadcast on the right event
this.connectCallback(user, this);
this.users.add(user);
user.group = this;
this.connectCallback(user, this);
}
leave(user: User): void {
@ -167,6 +172,10 @@ export class Group implements Movable {
this.disconnectCallback(user, this);
}
lock(lock: boolean = true): void {
this.locked = lock;
}
/**
* Let's kick everybody out.
* Usually used when there is only one user left.

View File

@ -12,6 +12,7 @@ import {
EmoteCallback,
EntersCallback,
LeavesCallback,
LockGroupCallback,
MovesCallback,
PlayerDetailsUpdatedCallback,
Zone,
@ -50,6 +51,7 @@ export class PositionNotifier {
private onUserMoves: MovesCallback,
private onUserLeaves: LeavesCallback,
private onEmote: EmoteCallback,
private onLockGroup: LockGroupCallback,
private onPlayerDetailsUpdated: PlayerDetailsUpdatedCallback
) {}
@ -111,6 +113,7 @@ export class PositionNotifier {
this.onUserMoves,
this.onUserLeaves,
this.onEmote,
this.onLockGroup,
this.onPlayerDetailsUpdated,
i,
j
@ -137,6 +140,12 @@ export class PositionNotifier {
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> {
const zoneDescriptor = this.getZoneDescriptorFromCoordinates(zone.x, zone.y);
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 LeavesCallback = (thing: Movable, newZone: Zone | null, listener: ZoneSocket) => void;
export type EmoteCallback = (emoteEventMessage: EmoteEventMessage, listener: ZoneSocket) => void;
export type LockGroupCallback = (groupId: number, listener: ZoneSocket) => void;
export type PlayerDetailsUpdatedCallback = (
playerDetailsUpdatedMessage: PlayerDetailsUpdatedMessage,
listener: ZoneSocket
@ -27,6 +28,7 @@ export class Zone {
private onMoves: MovesCallback,
private onLeaves: LeavesCallback,
private onEmote: EmoteCallback,
private onLockGroup: LockGroupCallback,
private onPlayerDetailsUpdated: PlayerDetailsUpdatedCallback,
public readonly x: 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) {
const playerDetailsUpdatedMessage = new PlayerDetailsUpdatedMessage();
playerDetailsUpdatedMessage.setUserid(user.id);

View File

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

View File

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

View File

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

View File

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

View File

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

@ -44,6 +44,12 @@ export interface GroupCreatedUpdatedMessageInterface {
position: PositionInterface;
groupId: number;
groupSize: number;
locked: boolean;
}
export interface GroupUsersUpdateMessageInterface {
groupId: number;
userIds: number[];
}
export interface WebRtcDisconnectMessageInterface {

View File

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

View File

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

View File

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

View File

@ -99,6 +99,10 @@ message FollowAbortMessage {
int32 follower = 2;
}
message LockGroupPromptMessage {
bool lock = 1;
}
message ClientToServerMessage {
oneof message {
UserMovesMessage userMovesMessage = 2;
@ -117,6 +121,7 @@ message ClientToServerMessage {
FollowRequestMessage followRequestMessage = 15;
FollowConfirmationMessage followConfirmationMessage = 16;
FollowAbortMessage followAbortMessage = 17;
LockGroupPromptMessage lockGroupPromptMessage = 18;
}
}
@ -185,6 +190,7 @@ message GroupUpdateMessage {
int32 groupId = 1;
PointMessage position = 2;
int32 groupSize = 3;
bool locked = 4;
}
message GroupDeleteMessage {
@ -216,6 +222,11 @@ message ItemStateMessage {
string stateJson = 2;
}
message GroupUsersUpdateMessage {
int32 groupId = 1;
repeated int32 userIds = 2;
}
message RoomJoinedMessage {
//repeated UserJoinedMessage user = 1;
//repeated GroupUpdateMessage group = 2;
@ -316,6 +327,7 @@ message ServerToClientMessage {
FollowConfirmationMessage followConfirmationMessage = 22;
FollowAbortMessage followAbortMessage = 23;
InvalidTextureMessage invalidTextureMessage = 24;
GroupUsersUpdateMessage groupUsersUpdateMessage = 25;
}
}
@ -356,8 +368,9 @@ message UserLeftZoneMessage {
message GroupUpdateZoneMessage {
int32 groupId = 1;
PointMessage position = 2;
int32 groupSize = 3;
int32 groupSize = 3;
Zone fromZone = 4;
bool locked = 5;
}
message GroupLeftZoneMessage {
@ -403,6 +416,7 @@ message PusherToBackMessage {
FollowRequestMessage followRequestMessage = 16;
FollowConfirmationMessage followConfirmationMessage = 17;
FollowAbortMessage followAbortMessage = 18;
LockGroupPromptMessage lockGroupPromptMessage = 19;
}
}

View File

@ -21,6 +21,7 @@ import {
FollowConfirmationMessage,
FollowAbortMessage,
VariableMessage,
LockGroupPromptMessage,
} from "../Messages/generated/messages_pb";
import { UserMovesMessage } from "../Messages/generated/messages_pb";
import { parse } from "query-string";
@ -561,6 +562,11 @@ export class IoSocketController {
);
} else if (message.hasFollowabortmessage()) {
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 */

View File

@ -123,19 +123,25 @@ export class UserDescriptor {
}
export class GroupDescriptor {
private constructor(public readonly groupId: number, private groupSize: number, private position: PointMessage) {}
private constructor(
public readonly groupId: number,
private groupSize: number,
private position: PointMessage,
private locked: boolean
) {}
public static createFromGroupUpdateZoneMessage(message: GroupUpdateZoneMessage): GroupDescriptor {
const position = message.getPosition();
if (position === undefined) {
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) {
this.groupSize = groupDescriptor.groupSize;
this.position = groupDescriptor.position;
this.locked = groupDescriptor.locked;
}
public toGroupUpdateMessage(): GroupUpdateMessage {
@ -146,7 +152,7 @@ export class GroupDescriptor {
groupUpdateMessage.setGroupid(this.groupId);
groupUpdateMessage.setGroupsize(this.groupSize);
groupUpdateMessage.setPosition(this.position);
groupUpdateMessage.setLocked(this.locked);
return groupUpdateMessage;
}
}
@ -206,9 +212,7 @@ export class Zone {
this.notifyGroupMove(groupDescriptor);
} else {
this.groups.set(groupId, groupDescriptor);
const fromZone = groupUpdateZoneMessage.getFromzone();
this.notifyGroupEnter(groupDescriptor, fromZone?.toObject());
}
} else if (message.hasUserleftzonemessage()) {

View File

@ -38,6 +38,7 @@ import {
ErrorMessage,
WorldFullMessage,
PlayerDetailsUpdatedMessage,
LockGroupPromptMessage,
InvalidTextureMessage,
} from "../Messages/generated/messages_pb";
import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils";
@ -297,6 +298,12 @@ export class SocketManager implements ZoneEventListener {
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 {
const subMessage = new SubMessage();
subMessage.setEmoteeventmessage(emoteMessage);