diff --git a/back/src/Model/Group.ts b/back/src/Model/Group.ts index ed57e473..38b91257 100644 --- a/back/src/Model/Group.ts +++ b/back/src/Model/Group.ts @@ -1,15 +1,26 @@ import {MessageUserPosition} from "./Websocket/MessageUserPosition"; import { World } from "./World"; +import { UserInterface } from "./UserInterface"; + export class Group { static readonly MAX_PER_GROUP = 4; - users: MessageUserPosition[]; + private users: UserInterface[]; + private connectCallback: (user1: string, user2: string) => void; + private disconnectCallback: (user1: string, user2: string) => void; - constructor(users: MessageUserPosition[]) { - this.users = users; + + constructor(users: UserInterface[], connectCallback: (user1: string, user2: string) => void, disconnectCallback: (user1: string, user2: string) => void) { + this.users = []; + this.connectCallback = connectCallback; + this.disconnectCallback = disconnectCallback; + + users.forEach((user: UserInterface) => { + this.join(user); + }); } - getUsers(): MessageUserPosition[] { + getUsers(): UserInterface[] { return this.users; } @@ -17,12 +28,22 @@ export class Group { return this.users.length >= Group.MAX_PER_GROUP; } - isPartOfGroup(user: MessageUserPosition): boolean + join(user: UserInterface): void + { + // Broadcast on the right event + this.users.forEach((groupUser: UserInterface) => { + this.connectCallback(user.id, groupUser.id); + }); + this.users.push(user); + user.group = this; + } + + isPartOfGroup(user: UserInterface): boolean { return this.users.indexOf(user) !== -1; } - isStillIn(user: MessageUserPosition): boolean + isStillIn(user: UserInterface): boolean { if(!this.isPartOfGroup(user)) { return false; @@ -30,7 +51,7 @@ export class Group { let stillIn = true; for(let i = 0; i <= this.users.length; i++) { let userInGroup = this.users[i]; - let distance = World.computeDistance(user.position, userInGroup.position); + let distance = World.computeDistance(user, userInGroup); if(distance > World.MIN_DISTANCE) { stillIn = false; break; @@ -39,7 +60,7 @@ export class Group { return stillIn; } - removeFromGroup(users: MessageUserPosition[]): void + removeFromGroup(users: UserInterface[]): void { for(let i = 0; i < users.length; i++) { let user = users[i]; diff --git a/back/src/Model/UserInterface.ts b/back/src/Model/UserInterface.ts index c75d3f3a..743f8b4d 100644 --- a/back/src/Model/UserInterface.ts +++ b/back/src/Model/UserInterface.ts @@ -1,7 +1,8 @@ import { Group } from "./Group"; import { PointInterface } from "./Websocket/PointInterface"; -export interface Userinteface { - group: Group, - pointInterface: PointInterface +export interface UserInterface { + id: string, + group?: Group, + position: PointInterface } \ No newline at end of file diff --git a/back/src/Model/World.ts b/back/src/Model/World.ts index dd4ec3a9..804a176b 100644 --- a/back/src/Model/World.ts +++ b/back/src/Model/World.ts @@ -2,12 +2,13 @@ import {MessageUserPosition, Point} from "./Websocket/MessageUserPosition"; import {PointInterface} from "./Websocket/PointInterface"; import {Group} from "./Group"; import {Distance} from "./Distance"; +import {UserInterface} from "./UserInterface"; export class World { - static readonly MIN_DISTANCE = 12; + static readonly MIN_DISTANCE = 160; // Users, sorted by ID - private users: Map; + private users: Map; private groups: Group[]; private connectCallback: (user1: string, user2: string) => void; @@ -15,22 +16,96 @@ export class World { constructor(connectCallback: (user1: string, user2: string) => void, disconnectCallback: (user1: string, user2: string) => void) { - this.users = new Map(); + this.users = new Map(); this.groups = []; this.connectCallback = connectCallback; this.disconnectCallback = disconnectCallback; } public join(userPosition: MessageUserPosition): void { - this.users.set(userPosition.userId, userPosition.position); + this.users.set(userPosition.userId, { + id: userPosition.userId, + position: userPosition.position + }); } public updatePosition(userPosition: MessageUserPosition): void { let context = this; + let user = this.users.get(userPosition.userId); + if(typeof user === 'undefined') { + return; + } + + user.position.x = userPosition.position.x; + user.position.y = userPosition.position.y; + + if (typeof user.group === 'undefined') { + // If the user is not part of a group: + // should he join a group? + let closestUser: UserInterface|null = this.searchClosestAvailableUser(user); + + if (closestUser !== null) { + // Is the closest user part of a group? + if (typeof closestUser.group === 'undefined') { + let group: Group = new Group([ + user, + closestUser + ], this.connectCallback, this.disconnectCallback); + } else { + closestUser.group.join(user); + } + } + + } + // TODO : vérifier qu'ils ne sont pas déja dans un groupe plein + } + + /** + * Looks for the closest user that is: + * - close enough (distance <= MIN_DISTANCE) + * - not in a group OR in a group that is not full + */ + private searchClosestAvailableUser(user: UserInterface): UserInterface|null + { +/* + let sortedUsersByDistance: UserInteface[] = Array.from(this.users.values()).sort((user1: UserInteface, user2: UserInteface): number => { + let distance1 = World.computeDistance(user, user1); + let distance2 = World.computeDistance(user, user2); + return distance1 - distance2; + }); + + // The first element should be the current user (distance 0). Let's remove it. + if (sortedUsersByDistance[0] === user) { + sortedUsersByDistance.shift(); + } + + for(let i = 0; i < sortedUsersByDistance.length; i++) { + let currentUser = sortedUsersByDistance[i]; + let distance = World.computeDistance(currentUser, user); + if(distance > World.MIN_DISTANCE) { + return; + } + } +*/ let usersToBeGroupedWith: Distance[] = []; - this.users.forEach(function(user, userId) { - let distance = World.computeDistance(userPosition.position, user); // compute distance between peers. - if(distance <= World.MIN_DISTANCE) { + let minimumDistanceFound: number = World.MIN_DISTANCE; + let matchingUser: UserInterface | null = null; + this.users.forEach(function(currentUser, userId) { + if(currentUser === user) { + return; + } + + let distance = World.computeDistance(user, currentUser); // compute distance between peers. + + if(distance <= minimumDistanceFound) { + + if (typeof currentUser.group === 'undefined' || !currentUser.group.isFull()) { + // We found a user we can bind to. + minimumDistanceFound = distance; + matchingUser = currentUser; + return; + } + /* if(context.groups.length > 0) { context.groups.forEach(group => { @@ -52,28 +127,27 @@ export class World { } else { // Aucun groupe n'existe donc je stock les users assez proches de moi - let dist = { + let dist: Distance = { distance: distance, first: userPosition, second: user // TODO: convertir en messageUserPosition } usersToBeGroupedWith.push(dist); } + */ } - }, context); - - usersToBeGroupedWith.sort(World.compareDistances); - // TODO : vérifier qu'ils ne sont pas déja dans un groupe plein + }, this.users); + return matchingUser; } - public static computeDistance(user1: PointInterface, user2: PointInterface): number + public static computeDistance(user1: UserInterface, user2: UserInterface): number { - return Math.sqrt(Math.pow(user2.x - user1.x, 2) + Math.pow(user2.y - user1.y, 2)); + return Math.sqrt(Math.pow(user2.position.x - user1.position.x, 2) + Math.pow(user2.position.y - user1.position.y, 2)); } - getDistancesBetweenGroupUsers(group: Group): Distance[] + /*getDistancesBetweenGroupUsers(group: Group): Distance[] { let i = 0; let users = group.getUsers(); @@ -82,7 +156,7 @@ export class World { users.forEach(function(user2, key2) { if(key1 < key2) { distances[i] = { - distance: World.computeDistance(user1.position, user2.position), + distance: World.computeDistance(user1, user2), first: user1, second: user2 }; @@ -132,5 +206,5 @@ export class World { return 1; } return 0; - } + }*/ } \ No newline at end of file diff --git a/back/tests/WorldTest.ts b/back/tests/WorldTest.ts index a0cced43..1f5affc8 100644 --- a/back/tests/WorldTest.ts +++ b/back/tests/WorldTest.ts @@ -29,15 +29,31 @@ describe("World", () => { position: new Point(500, 100) })); + world.updatePosition(new MessageUserPosition({ + userId: "bar", + roomId: 1, + position: new Point(261, 100) + })); + + expect(connectCalled).toBe(false); + world.updatePosition(new MessageUserPosition({ userId: "bar", roomId: 1, position: new Point(101, 100) })); - //expect(connectCalled).toBe(true); + expect(connectCalled).toBe(true); - }), + connectCalled = false; + world.updatePosition(new MessageUserPosition({ + userId: "bar", + roomId: 1, + position: new Point(102, 100) + })); + expect(connectCalled).toBe(false); + }); + /** it('Should return the distances between all users', () => { let connectCalled: boolean = false; let connect = (user1: string, user2: string): void => { @@ -83,4 +99,5 @@ describe("World", () => { //expect(distances).toBe([]); }) + **/ }) \ No newline at end of file