First working version with disconnection
authorDavid Négrier <d.negrier@thecodingmachine.com>
Wed, 29 Apr 2020 21:12:55 +0000 (23:12 +0200)
committerDavid Négrier <d.negrier@thecodingmachine.com>
Wed, 29 Apr 2020 21:12:55 +0000 (23:12 +0200)
back/src/Model/Group.ts
back/src/Model/World.ts
back/tests/WorldTest.ts

index 9f5c48c5203a094db88718d45b0269a47e07e27d..caf9b926d02e0c66a26c16e4179102e15f4937ba 100644 (file)
@@ -47,6 +47,10 @@ export class Group {
         return this.users.length >= Group.MAX_PER_GROUP;
     }
 
+    isEmpty(): boolean {
+        return this.users.length <= 1;
+    }
+
     join(user: UserInterface): void
     {
         // Broadcast on the right event
@@ -79,7 +83,7 @@ export class Group {
         return stillIn;
     }
 
-    removeFromGroup(users: UserInterface[]): void
+    /*removeFromGroup(users: UserInterface[]): void
     {
         for(let i = 0; i < users.length; i++){
             let user = users[i];
@@ -88,5 +92,32 @@ export class Group {
                 this.users.splice(index, 1);
             }
         }
+    }*/
+
+    leave(user: UserInterface): void
+    {
+        const index = this.users.indexOf(user, 0);
+        if (index === -1) {
+            throw new Error("Could not find user in the group");
+        }
+
+        this.users.splice(index, 1);
+        user.group = undefined;
+
+        // Broadcast on the right event
+        this.users.forEach((groupUser: UserInterface) => {
+            this.disconnectCallback(user.id, groupUser.id);
+        });
+    }
+
+    /**
+     * Let's kick everybody out.
+     * Usually used when there is only one user left.
+     */
+    destroy(): void
+    {
+        this.users.forEach((user: UserInterface) => {
+            this.leave(user);
+        })
     }
 }
index 32e1b383e8dc9e0a142316b13a5a984d02ccb088..06bcf884827ee3221a1614776bf56daefffd3b17 100644 (file)
@@ -29,6 +29,8 @@ export class World {
             id: userPosition.userId,
             position: userPosition.position
         });
+        // Let's call update position to trigger the join / leave room
+        this.updatePosition(userPosition);
     }
 
     public leave(user : ExSocketInterface){
@@ -60,18 +62,39 @@ export class World {
                         user,
                         closestUser
                     ], this.connectCallback, this.disconnectCallback);
+                    this.groups.push(group);
                 }
             }
 
         } else {
             // If the user is part of a group:
-            //  should we split the group?
+            //  should he leave the group?
+            let distance = World.computeDistanceBetweenPositions(user.position, user.group.getPosition());
+            if (distance > World.MIN_DISTANCE) {
+                this.leaveGroup(user);
+            }
+        }
+    }
 
-            // TODO: analyze the group of the user:
-            //  => take one user. "walk the tree of users, each branch being a link<MIN_DISTANCE"
-            //  If some users are not in the subgroup, take the other user and loop
-            //  At the end, we will have a list of subgroups. From this list, we can send disconnect messages
+    /**
+     * Makes a user leave a group and closes and destroy the group if the group contains only one remaining person.
+     *
+     * @param user
+     */
+    private leaveGroup(user: UserInterface): void {
+        let group = user.group;
+        if (typeof group === 'undefined') {
+            throw new Error("The user is part of no group");
+        }
+        group.leave(user);
 
+        if (group.isEmpty()) {
+            group.destroy();
+            const index = this.groups.indexOf(group, 0);
+            if (index === -1) {
+                throw new Error("Could not find group");
+            }
+            this.groups.splice(index, 1);
         }
     }
 
@@ -141,7 +164,6 @@ export class World {
                 return;
             }
             let distance = World.computeDistanceBetweenPositions(user.position, group.getPosition());
-
             if(distance <= minimumDistanceFound) {
                 minimumDistanceFound = distance;
                 matchingItem = group;
index 4511efc370e29ed4884b145c59600ff84f093d11..1d499727e3c2072aeff7f462e3783fb4890e65f9 100644 (file)
@@ -54,6 +54,50 @@ describe("World", () => {
         expect(connectCalled).toBe(false);
     });
 
+    it("should connect 3 users", () => {
+        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(200, 100)
+        }));
+
+        expect(connectCalled).toBe(true);
+        connectCalled = false;
+
+        // baz joins at the outer limit of the group
+        world.join(new MessageUserPosition({
+            userId: "baz",
+            roomId: 1,
+            position: new Point(311, 100)
+        }));
+
+        expect(connectCalled).toBe(false);
+
+        world.updatePosition(new MessageUserPosition({
+            userId: "baz",
+            roomId: 1,
+            position: new Point(309, 100)
+        }));
+
+        expect(connectCalled).toBe(true);
+    });
+
     it("should disconnect user1 and user2", () => {
         let connectCalled: boolean = false;
         let disconnectCalled: boolean = false;
@@ -78,12 +122,13 @@ describe("World", () => {
             position: new Point(259, 100)
         }));
 
-        expect(connectCalled).toBe(false);
+        expect(connectCalled).toBe(true);
+        expect(disconnectCalled).toBe(false);
 
         world.updatePosition(new MessageUserPosition({
             userId: "bar",
             roomId: 1,
-            position: new Point(261, 100)
+            position: new Point(100+160+160+1, 100)
         }));
 
         expect(disconnectCalled).toBe(true);