From f5f9dcac04dcc13c66aa2e0dab550d5357dcf96c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Wed, 16 Sep 2020 16:06:43 +0200 Subject: [PATCH] Making groups part of zones --- back/src/Controller/IoSocketController.ts | 97 +++++++++++++------ back/src/Model/Group.ts | 21 ++-- back/src/Model/Movable.ts | 8 ++ back/src/Model/PositionNotifier.ts | 37 ++++--- back/src/Model/User.ts | 23 +++++ back/src/Model/UserInterface.ts | 11 --- .../Model/Websocket/GroupUpdateInterface.ts | 6 ++ back/src/Model/World.ts | 58 +++++------ back/src/Model/Zone.ts | 80 +++++++-------- back/tests/PositionNotifierTest.ts | 88 +++++++---------- front/src/Connection.ts | 3 +- front/src/Phaser/Game/GameScene.ts | 1 + 12 files changed, 245 insertions(+), 188 deletions(-) create mode 100644 back/src/Model/Movable.ts create mode 100644 back/src/Model/User.ts delete mode 100644 back/src/Model/UserInterface.ts create mode 100644 back/src/Model/Websocket/GroupUpdateInterface.ts diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 1ce0c2db..f220aedb 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -7,7 +7,7 @@ import Jwt, {JsonWebTokenError} from "jsonwebtoken"; import {SECRET_KEY, MINIMUM_DISTANCE, GROUP_RADIUS, ALLOW_ARTILLERY} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..." import {World} from "../Model/World"; import {Group} from "../Model/Group"; -import {UserInterface} from "../Model/UserInterface"; +import {User} from "../Model/User"; import {isSetPlayerDetailsMessage,} from "../Model/Websocket/SetPlayerDetailsMessage"; import {MessageUserJoined} from "../Model/Websocket/MessageUserJoined"; import {MessageUserMoved} from "../Model/Websocket/MessageUserMoved"; @@ -22,6 +22,8 @@ import {isItemEventMessageInterface} from "../Model/Websocket/ItemEventMessage"; import {uuid} from 'uuidv4'; import {isUserMovesInterface} from "../Model/Websocket/UserMovesMessage"; import {isViewport} from "../Model/Websocket/ViewportMessage"; +import {GroupUpdateInterface} from "_Model/Websocket/GroupUpdateInterface"; +import {Movable} from "../Model/Movable"; enum SockerIoEvent { CONNECTION = "connection", @@ -159,7 +161,7 @@ export class IoSocketController { }); } - private sendDeleteGroupEvent(uuid: string, lastUser: UserInterface): void { + private sendDeleteGroupEvent(uuid: string, lastUser: User): void { // Let's get the room of the group. To do this, let's get anyone in the group and find its room. const userId = lastUser.id; const client: ExSocketInterface = this.searchClientByIdOrFail(userId); @@ -215,15 +217,29 @@ export class IoSocketController { //join new previous room const world = this.joinRoom(Client, roomId, message.position); - const users = world.setViewport(Client, message.viewport); - const listOfUsers = users.map((user: UserInterface) => { - const player: ExSocketInterface|undefined = this.sockets.get(user.id); - if (player === undefined) { - console.warn('Something went wrong. The World contains a user "'+user.id+"' but this user does not exist in the sockets list!"); - return null; + const things = world.setViewport(Client, message.viewport); + + const listOfUsers: Array = []; + const listOfGroups: Array = []; + + for (const thing of things) { + if (thing instanceof User) { + const player: ExSocketInterface|undefined = this.sockets.get(thing.id); + if (player === undefined) { + console.warn('Something went wrong. The World contains a user "'+thing.id+"' but this user does not exist in the sockets list!"); + continue; + } + + listOfUsers.push(new MessageUserPosition(thing.id, player.name, player.characterLayers, player.position)); + } else if (thing instanceof Group) { + listOfGroups.push({ + groupId: thing.getId(), + position: thing.getPosition(), + }); + } else { + console.error("Unexpected type for Movable returned by setViewport"); } - return new MessageUserPosition(user.id, player.name, player.characterLayers, player.position); - }, users); + } const listOfItems: {[itemId: string]: unknown} = {}; for (const [itemId, item] of world.getItemsState().entries()) { @@ -232,15 +248,13 @@ export class IoSocketController { //console.warn('ANSWER PLAYER POSITIONS', listOfUsers); if (answerFn === undefined && ALLOW_ARTILLERY === true) { - /*console.error("TYPEOF answerFn", typeof(answerFn)); - console.error("answerFn", answerFn); - process.exit(1)*/ // For some reason, answerFn can be undefined if we use Artillery (?) return; } answerFn({ users: listOfUsers, + groups: listOfGroups, items: listOfItems }); } catch (e) { @@ -485,28 +499,51 @@ export class IoSocketController { }, (user1: string, group: Group) => { this.disConnectedUser(user1, group); }, MINIMUM_DISTANCE, GROUP_RADIUS, (group: Group) => { - this.sendUpdateGroupEvent(group); - }, (groupUuid: string, lastUser: UserInterface) => { - this.sendDeleteGroupEvent(groupUuid, lastUser); - }, (user, listener) => { - const clientUser = this.searchClientByIdOrFail(user.id); + //this.sendUpdateGroupEvent(group); + }, (groupUuid: string, lastUser: User) => { + //this.sendDeleteGroupEvent(groupUuid, lastUser); + }, (thing: Movable, listener: User) => { const clientListener = this.searchClientByIdOrFail(listener.id); - const messageUserJoined = new MessageUserJoined(clientUser.userId, clientUser.name, clientUser.characterLayers, clientUser.position); + if (thing instanceof User) { + const clientUser = this.searchClientByIdOrFail(thing.id); + const messageUserJoined = new MessageUserJoined(clientUser.userId, clientUser.name, clientUser.characterLayers, clientUser.position); - clientListener.emit(SockerIoEvent.JOIN_ROOM, messageUserJoined); - //console.log("Sending JOIN_ROOM event"); - }, (user, position, listener) => { - const clientUser = this.searchClientByIdOrFail(user.id); + clientListener.emit(SockerIoEvent.JOIN_ROOM, messageUserJoined); + } else if (thing instanceof Group) { + clientListener.emit(SockerIoEvent.GROUP_CREATE_UPDATE, { + position: thing.getPosition(), + groupId: thing.getId() + } as GroupUpdateInterface); + } else { + console.error('Unexpected type for Movable.'); + } + }, (thing: Movable, position, listener) => { const clientListener = this.searchClientByIdOrFail(listener.id); + if (thing instanceof User) { + const clientUser = this.searchClientByIdOrFail(thing.id); - clientListener.emitInBatch(SockerIoEvent.USER_MOVED, new MessageUserMoved(clientUser.userId, clientUser.position)); - //console.log("Sending USER_MOVED event"); - }, (user, listener) => { - const clientUser = this.searchClientByIdOrFail(user.id); + clientListener.emitInBatch(SockerIoEvent.USER_MOVED, new MessageUserMoved(clientUser.userId, clientUser.position)); + //console.log("Sending USER_MOVED event"); + } else if (thing instanceof Group) { + clientListener.emit(SockerIoEvent.GROUP_CREATE_UPDATE, { + position: thing.getPosition(), + groupId: thing.getId() + } as GroupUpdateInterface); + } else { + console.error('Unexpected type for Movable.'); + } + }, (thing: Movable, listener) => { const clientListener = this.searchClientByIdOrFail(listener.id); + if (thing instanceof User) { + const clientUser = this.searchClientByIdOrFail(thing.id); + clientListener.emit(SockerIoEvent.USER_LEFT, clientUser.userId); + //console.log("Sending USER_LEFT event"); + } else if (thing instanceof Group) { + clientListener.emit(SockerIoEvent.GROUP_DELETE, thing.getId()); + } else { + console.error('Unexpected type for Movable.'); + } - clientListener.emit(SockerIoEvent.USER_LEFT, clientUser.userId); - //console.log("Sending USER_LEFT event"); }); this.Worlds.set(roomId, world); } @@ -516,7 +553,7 @@ export class IoSocketController { Client.emit(SockerIoEvent.GROUP_CREATE_UPDATE, { position: group.getPosition(), groupId: group.getId() - }); + } as GroupUpdateInterface); }); //join world world.join(Client, Client.position); diff --git a/back/src/Model/Group.ts b/back/src/Model/Group.ts index 4c597557..4909b660 100644 --- a/back/src/Model/Group.ts +++ b/back/src/Model/Group.ts @@ -1,29 +1,30 @@ import { World, ConnectCallback, DisconnectCallback } from "./World"; -import { UserInterface } from "./UserInterface"; +import { User } from "./User"; import {PositionInterface} from "_Model/PositionInterface"; import {uuid} from "uuidv4"; +import {Movable} from "_Model/Movable"; -export class Group { +export class Group implements Movable { static readonly MAX_PER_GROUP = 4; private id: string; - private users: Set; + private users: Set; private connectCallback: ConnectCallback; private disconnectCallback: DisconnectCallback; - constructor(users: UserInterface[], connectCallback: ConnectCallback, disconnectCallback: DisconnectCallback) { - this.users = new Set(); + constructor(users: User[], connectCallback: ConnectCallback, disconnectCallback: DisconnectCallback) { + this.users = new Set(); this.connectCallback = connectCallback; this.disconnectCallback = disconnectCallback; this.id = uuid(); - users.forEach((user: UserInterface) => { + users.forEach((user: User) => { this.join(user); }); } - getUsers(): UserInterface[] { + getUsers(): User[] { return Array.from(this.users.values()); } @@ -38,7 +39,7 @@ export class Group { let x = 0; let y = 0; // Let's compute the barycenter of all users. - this.users.forEach((user: UserInterface) => { + this.users.forEach((user: User) => { x += user.position.x; y += user.position.y; }); @@ -58,7 +59,7 @@ export class Group { return this.users.size <= 1; } - join(user: UserInterface): void + join(user: User): void { // Broadcast on the right event this.connectCallback(user.id, this); @@ -66,7 +67,7 @@ export class Group { user.group = this; } - leave(user: UserInterface): void + leave(user: User): void { const success = this.users.delete(user); if (success === false) { diff --git a/back/src/Model/Movable.ts b/back/src/Model/Movable.ts new file mode 100644 index 00000000..173db0ae --- /dev/null +++ b/back/src/Model/Movable.ts @@ -0,0 +1,8 @@ +import {PositionInterface} from "_Model/PositionInterface"; + +/** + * A physical object that can be placed into a Zone + */ +export interface Movable { + getPosition(): PositionInterface +} diff --git a/back/src/Model/PositionNotifier.ts b/back/src/Model/PositionNotifier.ts index 9d6975e3..0e5b4b2f 100644 --- a/back/src/Model/PositionNotifier.ts +++ b/back/src/Model/PositionNotifier.ts @@ -8,10 +8,12 @@ * The PositionNotifier is important for performance. It allows us to send the position of players only to a restricted * number of players around the current player. */ -import {UserEntersCallback, UserLeavesCallback, UserMovesCallback, Zone} from "./Zone"; +import {EntersCallback, LeavesCallback, MovesCallback, Zone} from "./Zone"; import {PointInterface} from "_Model/Websocket/PointInterface"; -import {UserInterface} from "_Model/UserInterface"; +import {User} from "_Model/User"; import {ViewportInterface} from "_Model/Websocket/ViewportMessage"; +import {Movable} from "_Model/Movable"; +import {PositionInterface} from "_Model/PositionInterface"; interface ZoneDescriptor { i: number; @@ -24,7 +26,7 @@ export class PositionNotifier { private zones: Zone[][] = []; - constructor(private zoneWidth: number, private zoneHeight: number, private onUserEnters: UserEntersCallback, private onUserMoves: UserMovesCallback, private onUserLeaves: UserLeavesCallback) { + constructor(private zoneWidth: number, private zoneHeight: number, private onUserEnters: EntersCallback, private onUserMoves: MovesCallback, private onUserLeaves: LeavesCallback) { } private getZoneDescriptorFromCoordinates(x: number, y: number): ZoneDescriptor { @@ -38,7 +40,7 @@ export class PositionNotifier { * Sets the viewport coordinates. * Returns the list of new users to add */ - public setViewport(user: UserInterface, viewport: ViewportInterface): UserInterface[] { + public setViewport(user: User, viewport: ViewportInterface): Movable[] { if (viewport.left > viewport.right || viewport.top > viewport.bottom) { console.warn('Invalid viewport received: ', viewport); return []; @@ -60,43 +62,46 @@ export class PositionNotifier { const removedZones = [...oldZones].filter(x => !newZones.has(x)); - let users: UserInterface[] = []; + let things: Movable[] = []; for (const zone of addedZones) { zone.startListening(user); - users = users.concat(Array.from(zone.getPlayers())) + things = things.concat(Array.from(zone.getThings())) } for (const zone of removedZones) { zone.stopListening(user); } - return users; + return things; } - public updatePosition(user: UserInterface, userPosition: PointInterface): void { + public updatePosition(thing: Movable, newPosition: PositionInterface, oldPosition: PositionInterface): void { // Did we change zone? - const oldZoneDesc = this.getZoneDescriptorFromCoordinates(user.position.x, user.position.y); - const newZoneDesc = this.getZoneDescriptorFromCoordinates(userPosition.x, userPosition.y); + const oldZoneDesc = this.getZoneDescriptorFromCoordinates(oldPosition.x, oldPosition.y); + const newZoneDesc = this.getZoneDescriptorFromCoordinates(newPosition.x, newPosition.y); if (oldZoneDesc.i != newZoneDesc.i || oldZoneDesc.j != newZoneDesc.j) { const oldZone = this.getZone(oldZoneDesc.i, oldZoneDesc.j); const newZone = this.getZone(newZoneDesc.i, newZoneDesc.j); // Leave old zone - oldZone.leave(user, newZone); + oldZone.leave(thing, newZone); // Enter new zone - newZone.enter(user, oldZone, userPosition); + newZone.enter(thing, oldZone, newPosition); } else { const zone = this.getZone(oldZoneDesc.i, oldZoneDesc.j); - zone.move(user, userPosition); + zone.move(thing, newPosition); } } - public leave(user: UserInterface): void { - const oldZoneDesc = this.getZoneDescriptorFromCoordinates(user.position.x, user.position.y); + public leave(thing: Movable): void { + const oldPosition = thing.getPosition(); + const oldZoneDesc = this.getZoneDescriptorFromCoordinates(oldPosition.x, oldPosition.y); const oldZone = this.getZone(oldZoneDesc.i, oldZoneDesc.j); - oldZone.leave(user, null); + oldZone.leave(thing, null); + } + public removeViewport(user: User): void { // Also, let's stop listening on viewports for (const zone of user.listenedZones) { zone.stopListening(user); diff --git a/back/src/Model/User.ts b/back/src/Model/User.ts new file mode 100644 index 00000000..160a101c --- /dev/null +++ b/back/src/Model/User.ts @@ -0,0 +1,23 @@ +import { Group } from "./Group"; +import { PointInterface } from "./Websocket/PointInterface"; +import {Zone} from "_Model/Zone"; +import {Movable} from "_Model/Movable"; +import {PositionInterface} from "_Model/PositionInterface"; + +export class User implements Movable { + public listenedZones: Set; + public group?: Group; + + public constructor( + public id: string, + public position: PointInterface, + public silent: boolean, + + ) { + this.listenedZones = new Set(); + } + + public getPosition(): PositionInterface { + return this.position; + } +} diff --git a/back/src/Model/UserInterface.ts b/back/src/Model/UserInterface.ts deleted file mode 100644 index d19ecd6f..00000000 --- a/back/src/Model/UserInterface.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Group } from "./Group"; -import { PointInterface } from "./Websocket/PointInterface"; -import {Zone} from "_Model/Zone"; - -export interface UserInterface { - id: string, - group?: Group, - position: PointInterface, - silent: boolean, - listenedZones: Set -} diff --git a/back/src/Model/Websocket/GroupUpdateInterface.ts b/back/src/Model/Websocket/GroupUpdateInterface.ts new file mode 100644 index 00000000..45e64ea4 --- /dev/null +++ b/back/src/Model/Websocket/GroupUpdateInterface.ts @@ -0,0 +1,6 @@ +import {PositionInterface} from "_Model/PositionInterface"; + +export interface GroupUpdateInterface { + position: PositionInterface, + groupId: string, +} diff --git a/back/src/Model/World.ts b/back/src/Model/World.ts index 05651ad4..9a5e35ff 100644 --- a/back/src/Model/World.ts +++ b/back/src/Model/World.ts @@ -2,27 +2,28 @@ import {MessageUserPosition, Point} from "./Websocket/MessageUserPosition"; import {PointInterface} from "./Websocket/PointInterface"; import {Group} from "./Group"; import {Distance} from "./Distance"; -import {UserInterface} from "./UserInterface"; +import {User} from "./User"; import {ExSocketInterface} from "_Model/Websocket/ExSocketInterface"; import {PositionInterface} from "_Model/PositionInterface"; import {Identificable} from "_Model/Websocket/Identificable"; -import {UserEntersCallback, UserLeavesCallback, UserMovesCallback, Zone} from "_Model/Zone"; +import {EntersCallback, LeavesCallback, MovesCallback, Zone} from "_Model/Zone"; import {PositionNotifier} from "./PositionNotifier"; import {ViewportInterface} from "_Model/Websocket/ViewportMessage"; +import {Movable} from "_Model/Movable"; export type ConnectCallback = (user: string, group: Group) => void; export type DisconnectCallback = (user: string, group: Group) => void; // callback called when a group is created or moved or changes users export type GroupUpdatedCallback = (group: Group) => void; -export type GroupDeletedCallback = (uuid: string, lastUser: UserInterface) => void; +export type GroupDeletedCallback = (uuid: string, lastUser: User) => void; export class World { private readonly minDistance: number; private readonly groupRadius: number; // Users, sorted by ID - private readonly users: Map; + private readonly users: Map; private readonly groups: Set; private readonly connectCallback: ConnectCallback; @@ -40,11 +41,11 @@ export class World { groupRadius: number, groupUpdatedCallback: GroupUpdatedCallback, groupDeletedCallback: GroupDeletedCallback, - onUserEnters: UserEntersCallback, - onUserMoves: UserMovesCallback, - onUserLeaves: UserLeavesCallback) + onEnters: EntersCallback, + onMoves: MovesCallback, + onLeaves: LeavesCallback) { - this.users = new Map(); + this.users = new Map(); this.groups = new Set(); this.connectCallback = connectCallback; this.disconnectCallback = disconnectCallback; @@ -53,24 +54,19 @@ export class World { this.groupUpdatedCallback = groupUpdatedCallback; this.groupDeletedCallback = groupDeletedCallback; // A zone is 10 sprites wide. - this.positionNotifier = new PositionNotifier(320, 320, onUserEnters, onUserMoves, onUserLeaves); + this.positionNotifier = new PositionNotifier(320, 320, onEnters, onMoves, onLeaves); } public getGroups(): Group[] { return Array.from(this.groups.values()); } - public getUsers(): Map { + public getUsers(): Map { return this.users; } public join(socket : Identificable, userPosition: PointInterface): void { - this.users.set(socket.userId, { - id: socket.userId, - position: userPosition, - silent: false, // FIXME: silent should be set at the correct value when joining a room. - listenedZones: new Set() - }); + this.users.set(socket.userId, new User(socket.userId, userPosition, false)); // Let's call update position to trigger the join / leave room this.updatePosition(socket, userPosition); } @@ -87,6 +83,7 @@ export class World { if (userObj !== undefined) { this.positionNotifier.leave(userObj); + this.positionNotifier.removeViewport(userObj); } } @@ -100,7 +97,9 @@ export class World { return; } - this.positionNotifier.updatePosition(user, userPosition); + this.positionNotifier.updatePosition(user, userPosition, user.position); + + const oldGroupPosition = user.group?.getPosition(); user.position = userPosition; @@ -108,17 +107,17 @@ export class World { return; } - if (typeof user.group === 'undefined') { + if (user.group === undefined) { // If the user is not part of a group: // should he join a group? - const closestItem: UserInterface|Group|null = this.searchClosestAvailableUserOrGroup(user); + const closestItem: User|Group|null = this.searchClosestAvailableUserOrGroup(user); if (closestItem !== null) { if (closestItem instanceof Group) { // Let's join the group! closestItem.join(user); } else { - const closestUser : UserInterface = closestItem; + const closestUser : User = closestItem; const group: Group = new Group([ user, closestUser @@ -138,7 +137,8 @@ export class World { // At the very end, if the user is part of a group, let's call the callback to update group position if (typeof user.group !== 'undefined') { - this.groupUpdatedCallback(user.group); + this.positionNotifier.updatePosition(user.group, user.group.getPosition(), oldGroupPosition ? oldGroupPosition : user.group.getPosition()); + //this.groupUpdatedCallback(user.group); } } @@ -167,21 +167,23 @@ export class World { * * @param user */ - private leaveGroup(user: UserInterface): void { + private leaveGroup(user: User): void { const group = user.group; if (typeof group === 'undefined') { throw new Error("The user is part of no group"); } group.leave(user); if (group.isEmpty()) { - this.groupDeletedCallback(group.getId(), user); + //this.groupDeletedCallback(group.getId(), user); + this.positionNotifier.leave(group); group.destroy(); if (!this.groups.has(group)) { throw new Error("Could not find group "+group.getId()+" referenced by user "+user.id+" in World."); } this.groups.delete(group); } else { - this.groupUpdatedCallback(group); + this.positionNotifier.updatePosition(group, group.getPosition(), group.getPosition()); + //this.groupUpdatedCallback(group); } } @@ -193,10 +195,10 @@ export class World { * OR * - close enough to a group (distance <= groupRadius) */ - private searchClosestAvailableUserOrGroup(user: UserInterface): UserInterface|Group|null + private searchClosestAvailableUserOrGroup(user: User): User|Group|null { let minimumDistanceFound: number = Math.max(this.minDistance, this.groupRadius); - let matchingItem: UserInterface | Group | null = null; + let matchingItem: User | Group | null = null; this.users.forEach((currentUser, userId) => { // Let's only check users that are not part of a group if (typeof currentUser.group !== 'undefined') { @@ -265,7 +267,7 @@ export class World { return matchingItem; } - public static computeDistance(user1: UserInterface, user2: UserInterface): number + public static computeDistance(user1: User, user2: User): number { return Math.sqrt(Math.pow(user2.position.x - user1.position.x, 2) + Math.pow(user2.position.y - user1.position.y, 2)); } @@ -343,7 +345,7 @@ export class World { } return 0; }*/ - setViewport(socket : Identificable, viewport: ViewportInterface): UserInterface[] { + setViewport(socket : Identificable, viewport: ViewportInterface): Movable[] { const user = this.users.get(socket.userId); if(typeof user === 'undefined') { console.warn('In setViewport, could not find user with ID "'+socket.userId+'" in world.'); diff --git a/back/src/Model/Zone.ts b/back/src/Model/Zone.ts index bd748b0f..9933637c 100644 --- a/back/src/Model/Zone.ts +++ b/back/src/Model/Zone.ts @@ -1,77 +1,77 @@ -import {UserInterface} from "./UserInterface"; -import {PointInterface} from "_Model/Websocket/PointInterface"; +import {User} from "./User"; import {PositionInterface} from "_Model/PositionInterface"; +import {Movable} from "_Model/Movable"; -export type UserEntersCallback = (user: UserInterface, listener: UserInterface) => void; -export type UserMovesCallback = (user: UserInterface, position: PointInterface, listener: UserInterface) => void; -export type UserLeavesCallback = (user: UserInterface, listener: UserInterface) => void; +export type EntersCallback = (thing: Movable, listener: User) => void; +export type MovesCallback = (thing: Movable, position: PositionInterface, listener: User) => void; +export type LeavesCallback = (thing: Movable, listener: User) => void; export class Zone { - private players: Set = new Set(); - private listeners: Set = new Set(); + private things: Set = new Set(); + private listeners: Set = new Set(); - constructor(private onUserEnters: UserEntersCallback, private onUserMoves: UserMovesCallback, private onUserLeaves: UserLeavesCallback) { + constructor(private onEnters: EntersCallback, private onMoves: MovesCallback, private onLeaves: LeavesCallback) { } /** - * A user leaves the zone + * A user/thing leaves the zone */ - public leave(user: UserInterface, newZone: Zone|null) { - this.players.delete(user); - this.notifyUserLeft(user, newZone); + public leave(thing: Movable, newZone: Zone|null) { + this.things.delete(thing); + this.notifyLeft(thing, newZone); } /** - * Notify listeners of this zone that this user left + * Notify listeners of this zone that this user/thing left */ - private notifyUserLeft(user: UserInterface, newZone: Zone|null) { + private notifyLeft(thing: Movable, newZone: Zone|null) { for (const listener of this.listeners) { - if (listener !== user && (newZone === null || !listener.listenedZones.has(newZone))) { - this.onUserLeaves(user, listener); + if (listener !== thing && (newZone === null || !listener.listenedZones.has(newZone))) { + this.onLeaves(thing, listener); } } } - public enter(user: UserInterface, oldZone: Zone|null, position: PointInterface) { - this.players.add(user); - this.notifyUserEnter(user, oldZone, position); + public enter(thing: Movable, oldZone: Zone|null, position: PositionInterface) { + this.things.add(thing); + this.notifyUserEnter(thing, oldZone, position); } /** * Notify listeners of this zone that this user entered */ - private notifyUserEnter(user: UserInterface, oldZone: Zone|null, position: PointInterface) { + private notifyUserEnter(thing: Movable, oldZone: Zone|null, position: PositionInterface) { for (const listener of this.listeners) { - if (listener === user) { + if (listener === thing) { continue; } if (oldZone === null || !listener.listenedZones.has(oldZone)) { - this.onUserEnters(user, listener); + this.onEnters(thing, listener); } else { - this.onUserMoves(user, position, listener); + this.onMoves(thing, position, listener); } } } - public move(user: UserInterface, position: PointInterface) { - if (!this.players.has(user)) { - this.players.add(user); - const foo = this.players; - this.notifyUserEnter(user, null, position); + public move(thing: Movable, position: PositionInterface) { + if (!this.things.has(thing)) { + this.things.add(thing); + const foo = this.things; + this.notifyUserEnter(thing, null, position); return; } for (const listener of this.listeners) { - if (listener !== user) { - this.onUserMoves(user,position, listener); + if (listener !== thing) { + this.onMoves(thing,position, listener); } } } - public startListening(listener: UserInterface): void { - for (const player of this.players) { - if (player !== listener) { - this.onUserEnters(player, listener); + public startListening(listener: User): void { + for (const thing of this.things) { + if (thing !== listener) { + this.onEnters(thing, listener); } } @@ -79,10 +79,10 @@ export class Zone { listener.listenedZones.add(this); } - public stopListening(listener: UserInterface): void { - for (const player of this.players) { - if (player !== listener) { - this.onUserLeaves(player, listener); + public stopListening(listener: User): void { + for (const thing of this.things) { + if (thing !== listener) { + this.onLeaves(thing, listener); } } @@ -90,7 +90,7 @@ export class Zone { listener.listenedZones.delete(this); } - public getPlayers(): Set { - return this.players; + public getThings(): Set { + return this.things; } } diff --git a/back/tests/PositionNotifierTest.ts b/back/tests/PositionNotifierTest.ts index 0b8b466f..936efb08 100644 --- a/back/tests/PositionNotifierTest.ts +++ b/back/tests/PositionNotifierTest.ts @@ -3,17 +3,16 @@ import {World, ConnectCallback, DisconnectCallback } from "../src/Model/World"; import {Point} from "../src/Model/Websocket/MessageUserPosition"; import { Group } from "../src/Model/Group"; import {PositionNotifier} from "../src/Model/PositionNotifier"; -import {UserInterface} from "../src/Model/UserInterface"; +import {User} from "../src/Model/User"; import {PointInterface} from "../src/Model/Websocket/PointInterface"; import {Zone} from "_Model/Zone"; +import {Movable} from "_Model/Movable"; -function move(user: UserInterface, x: number, y: number, positionNotifier: PositionNotifier): void { +function move(user: User, x: number, y: number, positionNotifier: PositionNotifier): void { positionNotifier.updatePosition(user, { x, - y, - moving: false, - direction: 'down' - }); + y + }, user.position); user.position.x = x; user.position.y = y; } @@ -24,35 +23,27 @@ describe("PositionNotifier", () => { let moveTriggered = false; let leaveTriggered = false; - const positionNotifier = new PositionNotifier(300, 300, (user: UserInterface) => { + const positionNotifier = new PositionNotifier(300, 300, (thing: Movable) => { enterTriggered = true; - }, (user: UserInterface, position: PointInterface) => { + }, (thing: Movable, position: PointInterface) => { moveTriggered = true; - }, (user: UserInterface) => { + }, (thing: Movable) => { leaveTriggered = true; }); - const user1 = { - id: "1", - position: { - x: 500, - y: 500, - moving: false, - direction: 'down' - }, - listenedZones: new Set(), - } as UserInterface; + const user1 = new User("1", { + x: 500, + y: 500, + moving: false, + direction: 'down' + }, false); - const user2 = { - id: "2", - position: { - x: -9999, - y: -9999, - moving: false, - direction: 'down' - }, - listenedZones: new Set(), - } as UserInterface; + const user2 = new User("2", { + x: -9999, + y: -9999, + moving: false, + direction: 'down' + }, false); positionNotifier.setViewport(user1, { left: 200, @@ -98,6 +89,7 @@ describe("PositionNotifier", () => { // Leave the room positionNotifier.leave(user2); + positionNotifier.removeViewport(user2); expect(enterTriggered).toBe(false); expect(moveTriggered).toBe(false); expect(leaveTriggered).toBe(true); @@ -109,35 +101,27 @@ describe("PositionNotifier", () => { let moveTriggered = false; let leaveTriggered = false; - const positionNotifier = new PositionNotifier(300, 300, (user: UserInterface) => { + const positionNotifier = new PositionNotifier(300, 300, (thing: Movable) => { enterTriggered = true; - }, (user: UserInterface, position: PointInterface) => { + }, (thing: Movable, position: PointInterface) => { moveTriggered = true; - }, (user: UserInterface) => { + }, (thing: Movable) => { leaveTriggered = true; }); - const user1 = { - id: "1", - position: { - x: 500, - y: 500, - moving: false, - direction: 'down' - }, - listenedZones: new Set(), - } as UserInterface; + const user1 = new User("1", { + x: 500, + y: 500, + moving: false, + direction: 'down' + }, false); - const user2 = { - id: "2", - position: { - x: -9999, - y: -9999, - moving: false, - direction: 'down' - }, - listenedZones: new Set(), - } as UserInterface; + const user2 = new User("2", { + x: -9999, + y: -9999, + moving: false, + direction: 'down' + }, false); let newUsers = positionNotifier.setViewport(user1, { left: 200, diff --git a/front/src/Connection.ts b/front/src/Connection.ts index 68fcc206..5062ca7f 100644 --- a/front/src/Connection.ts +++ b/front/src/Connection.ts @@ -123,7 +123,8 @@ export interface ItemEventMessageInterface { } export interface RoomJoinedMessageInterface { - users: MessageUserPositionInterface[] + users: MessageUserPositionInterface[], + groups: GroupCreatedUpdatedMessageInterface[], items: { [itemId: number] : unknown } } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 62c7c97a..ad378bc3 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -780,6 +780,7 @@ export class GameScene extends Phaser.Scene implements CenterListener { this.initUsersPosition(roomJoinedMessage.users); this.connectionAnswerPromiseResolve(roomJoinedMessage); }); + // FIXME: weirdly enough we don't use the result of joinARoom !!!!!! //listen event to share position of user this.CurrentPlayer.on(hasMovedEventName, this.pushPlayerPosition.bind(this))