//Hydrate and manage error
hydrateMessageReceive(message : string) : MessageUserPosition | Error{
try {
- return new MessageUserPosition(message);
+ let data = JSON.parse(message);
+ return new MessageUserPosition(data);
}catch (err) {
//TODO log error
return new Error(err);
-// TODO
\ No newline at end of file
+import {MessageUserPosition} from "_Model/Websocket/MessageUserPosition";
+import {Distance} from "_Model/Distance";
+
+export class PositionController {
+ static readonly MIN_DISTANCE = 12;
+ static readonly MAX_PER_GROUP = 3;
+
+ constructor ()
+ {
+ // Injecter socket ?
+ }
+
+ getDistancesBetweenAllUsers(users: MessageUserPosition[]): Distance[]
+ {
+ let i = 0;
+ let distances: Distance[] = [];
+ users.forEach(function(user1, key1) {
+ users.forEach(function(user2, key2) {
+ if(key1 < key2) {
+ distances[i] = {
+ distance: PositionController.computeDistance(user1, user2),
+ user1: user1,
+ user2: user2
+ };
+ i++;
+ }
+ });
+ });
+
+ distances.sort(PositionController.compareDistances);
+
+ return distances;
+ }
+
+ createGroups(distances: Distance[], nbOfUsers: number, Oldgroups: MessageUserPosition[][]): MessageUserPosition[][]
+ {
+ // TODO : detect in existing groups if a user must be removed from the group
+ let alreadyInAGroup: any[string] = [];
+ let groups: MessageUserPosition[][] = [];
+
+ let roomId = 0;
+ for(let i = 0; i < distances.length; i++) {
+ let dist = distances[i];
+
+ if(dist.distance <= PositionController.MIN_DISTANCE) {
+ if(typeof groups[roomId] === 'undefined') {
+ groups[roomId] = [];
+ }
+
+ if(groups[roomId].indexOf(dist.user1) === -1 && typeof alreadyInAGroup[dist.user1.userId] === 'undefined') {
+ if(groups[roomId].length > 1) {
+ // if group is not empty we check current user can be added in the group according to its distance to the others already in it
+ for(let j = 0; j < groups[roomId].length; j++) {
+ let userTotest = groups[roomId][j];
+ if(PositionController.computeDistance(dist.user1, userTotest) <= PositionController.MIN_DISTANCE) {
+ groups[roomId].push(dist.user1);
+ alreadyInAGroup[dist.user1.userId] = true;
+ break;
+ }
+ }
+ } else {
+ groups[roomId].push(dist.user1);
+ alreadyInAGroup[dist.user1.userId] = true;
+ }
+ }
+
+ if(groups[roomId].length === PositionController.MAX_PER_GROUP) {
+ roomId++; // on créé un nouveau groupe
+ if(roomId > (nbOfUsers / PositionController.MAX_PER_GROUP)) {
+ console.log('There is no room left for user ID : ' + dist.user2.userId + ' !');
+ break;
+ }
+ continue;
+ }
+
+ if(groups[roomId].indexOf(dist.user2) === -1 && typeof alreadyInAGroup[dist.user2.userId] === 'undefined') {
+ if(groups[roomId].length > 1) {
+ // if group is not empty we check current user can be added in the group according to its distance to the others already in it
+ for(let j = 0; j < groups[roomId].length; j++) {
+ let userTotest = groups[roomId][j];
+ if(PositionController.computeDistance(dist.user2, userTotest) <= PositionController.MIN_DISTANCE) {
+ groups[roomId].push(dist.user2);
+ alreadyInAGroup[dist.user2.userId] = true;
+ break;
+ }
+ }
+ } else {
+ groups[roomId].push(dist.user2);
+ alreadyInAGroup[dist.user2.userId] = true;
+ }
+ }
+ }
+ }
+ return groups;
+ }
+
+ // FIXME
+ checkGroupDistance (groups: MessageUserPosition[][])
+ {
+ for(let i = 0; i < groups.length; i++) {
+ let group = groups[i];
+ group.forEach((user1, key1) => {
+ group.forEach((user2, key2) => {
+ if(key1 < key2) {
+ let distance = PositionController.computeDistance(user1, user2);
+ if(distance > PositionController.MIN_DISTANCE) {
+ // TODO : message a user1 et user2
+ }
+ }
+ });
+ });
+ }
+ }
+
+ private static computeDistance(user1: MessageUserPosition, user2: MessageUserPosition): number
+ {
+ return Math.sqrt(Math.pow(user2.position.x - user1.position.x, 2) + Math.pow(user2.position.y - user1.position.y, 2));
+ }
+
+ private static compareDistances(distA: Distance, distB: Distance): number
+ {
+ if (distA.distance < distB.distance) {
+ return -1;
+ }
+ if (distA.distance > distB.distance) {
+ return 1;
+ }
+ return 0;
+ }
+}
\ No newline at end of file
--- /dev/null
+import {MessageUserPosition} from "../Model/Websocket/MessageUserPosition";
+
+export interface Distance {
+ distance: number,
+ user1: MessageUserPosition,
+ user2: MessageUserPosition,
+}
\ No newline at end of file
--- /dev/null
+import {MessageUserPosition} from "./Websocket/MessageUserPosition";
+export class Group {
+ static readonly MAX_PER_GROUP = 4;
+
+ users: MessageUserPosition[];
+
+ constructor(users: MessageUserPosition[]) {
+ this.users = users;
+ }
+
+ getUsers(): MessageUserPosition[] {
+ return this.users;
+ }
+
+ isFull(): boolean {
+ return this.users.length >= Group.MAX_PER_GROUP;
+ }
+}
\ No newline at end of file
--- /dev/null
+import { Group } from "./Group";
+import { PointInterface } from "./Websocket/PointInterface";
+
+export interface Userinteface {
+ group: Group,
+ pointInterface: PointInterface
+}
\ No newline at end of file
--- /dev/null
+import {MessageUserPosition} from "./MessageUserPosition";
+export class Group {
+ static readonly MAX_PER_GROUP = 4;
+
+ users: MessageUserPosition[];
+
+ constructor(users: MessageUserPosition[]) {
+ this.users = users;
+ }
+
+ getUsers(): MessageUserPosition[] {
+ return this.users;
+ }
+
+ isFull(): boolean {
+ return this.users.length >= Group.MAX_PER_GROUP;
+ }
+}
\ No newline at end of file
userId: string;
roomId: string;
- constructor(message: string) {
- let data = JSON.parse(message);
+ constructor(data: any) {
if(!data.userId || !data.roomId){
throw Error("userId or roomId cannot be null");
}
export class MessageUserPosition extends Message{
position: PointInterface;
- constructor(message: string) {
- super(message);
- let data = JSON.parse(message);
+ constructor(data: any) {
+ super(data);
this.position = new Point(data.position.x, data.position.y);
}
--- /dev/null
+import {MessageUserPosition} from "./Websocket/MessageUserPosition";
+import {PointInterface} from "./Websocket/PointInterface";
+import {Group} from "./Group";
+
+export class World {
+ // Users, sorted by ID
+ private users: Map<string, PointInterface>;
+ private groups: Group[]
+ private connectCallback: (user1: string, user2: string) => void;
+ private disconnectCallback: (user1: string, user2: string) => void;
+
+ constructor(connectCallback: (user1: string, user2: string) => void, disconnectCallback: (user1: string, user2: string) => void)
+ {
+ this.users = new Map<string, PointInterface>();
+ this.groups = [];
+ this.connectCallback = connectCallback;
+ this.disconnectCallback = disconnectCallback;
+ }
+
+ public join(userPosition: MessageUserPosition): void {
+ this.users.set(userPosition.userId, userPosition.position);
+ }
+
+ public updatePosition(userPosition: MessageUserPosition): void {
+ if(typeof userPosition.userId === 'undefined') {
+ throw new Error('unkown id');
+ }
+ //this.users.get(userPosition.userId).x;
+
+ // TODO: compute distance between peers.
+
+ // Is the user in a group?
+
+ // Is the user leaving the group? (is the user at more than max distance of each player)
+
+ // Should we split the group? (is each player reachable from the current player?)
+ // This is needed if
+ // A <==> B <==> C <===> D
+ // becomes A <==> B <=====> C <> D
+ // If C moves right, the distance between B and C is too great and we must form 2 groups
+
+ // If the user is in no group
+ // is there someone in a group close enough and with room in the group?
+ }
+
+}
\ No newline at end of file
describe("Message Model", () => {
it("should find userId and roomId", () => {
- let message = JSON.stringify({userId: "test1", roomId: "test2"});
+ let message = {userId: "test1", roomId: "test2"};
let messageObject = new Message(message);
expect(messageObject.userId).toBe("test1");
expect(messageObject.roomId).toBe("test2");
})
it("should expose a toJson method", () => {
- let message = JSON.stringify({userId: "test1", roomId: "test2"});
+ let message = {userId: "test1", roomId: "test2"};
let messageObject = new Message(message);
expect(messageObject.toJson()).toEqual({userId: "test1", roomId: "test2"});
})
it("should find throw error when no userId", () => {
- let message = JSON.stringify({roomId: "test2"});
+ let message = {roomId: "test2"};
expect(() => {
let messageObject = new Message(message);
}).toThrow(new Error("userId or roomId cannot be null"));
})
it("should find throw error when no roomId", () => {
- let message = JSON.stringify({userId: "test1"});
+ let message = {userId: "test1"};
expect(() => {
let messageObject = new Message(message);
}).toThrow(new Error("userId or roomId cannot be null"));
--- /dev/null
+import "jasmine";
+import {Message} from "../src/Model/Websocket/Message";
+import {World} from "../src/Model/World";
+import {MessageUserPosition, Point} from "../src/Model/Websocket/MessageUserPosition";
+
+describe("World", () => {
+ it("should connect user1 and user2", () => {
+ let connectCalled: boolean = false;
+ let connect = (user1: string, user2: string): void => {
+ connectCalled = true;
+ }
+ let disconnect = (user1: string, user2: string): void => {
+
+ }
+
+ let world = new World(connect, disconnect);
+
+ world.join(new MessageUserPosition({
+ userId: "foo",
+ roomId: 1,
+ position: new Point(100, 100)
+ }));
+
+ world.join(new MessageUserPosition({
+ userId: "bar",
+ roomId: 1,
+ position: new Point(500, 100)
+ }));
+
+ world.updatePosition(new MessageUserPosition({
+ userId: "bar",
+ roomId: 1,
+ position: new Point(101, 100)
+ }));
+
+ expect(connectCalled).toBe(true);
+
+ })
+})
\ No newline at end of file