강동현

방장 구현

...@@ -35,7 +35,12 @@ export const RoomInfoRecord = Record({ ...@@ -35,7 +35,12 @@ export const RoomInfoRecord = Record({
35 uuid: String, 35 uuid: String,
36 name: String, 36 name: String,
37 maxUsers: Number, 37 maxUsers: Number,
38 - users: Array(UserDataRecord), 38 + users: Array(
39 + Record({
40 + username: String,
41 + admin: Boolean,
42 + })
43 + ),
39 }); 44 });
40 45
41 export type RoomInfo = Static<typeof RoomInfoRecord>; 46 export type RoomInfo = Static<typeof RoomInfoRecord>;
......
...@@ -74,6 +74,7 @@ interface ServerOutboundMessageMap { ...@@ -74,6 +74,7 @@ interface ServerOutboundMessageMap {
74 state: "added" | "updated" | "removed"; 74 state: "added" | "updated" | "removed";
75 user: { 75 user: {
76 username: string; 76 username: string;
77 + admin: boolean;
77 }; 78 };
78 }; 79 };
79 80
......
...@@ -8,6 +8,7 @@ import { ...@@ -8,6 +8,7 @@ import {
8 ServerOutboundMessageKey, 8 ServerOutboundMessageKey,
9 } from "../../common"; 9 } from "../../common";
10 import { RoomDescription, RoomInfo, UserData } from "../../common/dataType"; 10 import { RoomDescription, RoomInfo, UserData } from "../../common/dataType";
11 +import { RoomManager } from "./RoomManager";
11 12
12 export class Room { 13 export class Room {
13 public readonly uuid: string; 14 public readonly uuid: string;
...@@ -15,16 +16,26 @@ export class Room { ...@@ -15,16 +16,26 @@ export class Room {
15 public name: string; 16 public name: string;
16 public readonly maxUsers: number; 17 public readonly maxUsers: number;
17 18
19 + public readonly roomManager: RoomManager;
20 +
18 public users: User[] = []; 21 public users: User[] = [];
22 + public admin?: User;
19 23
20 - private closed: boolean = false; 24 + public closed: boolean = false;
21 25
22 public handler: MessageHandler; 26 public handler: MessageHandler;
23 27
24 - constructor(name: string, maxUsers: number = 8) { 28 + constructor(
29 + roomManager: RoomManager,
30 + name: string,
31 + admin?: User,
32 + maxUsers: number = 8
33 + ) {
25 this.uuid = uuidv4(); 34 this.uuid = uuidv4();
26 this.name = name; 35 this.name = name;
27 this.maxUsers = maxUsers; 36 this.maxUsers = maxUsers;
37 + this.roomManager = roomManager;
38 + this.admin = admin;
28 39
29 this.handler = new MessageHandler({ 40 this.handler = new MessageHandler({
30 chat: (user, message) => { 41 chat: (user, message) => {
...@@ -36,6 +47,10 @@ export class Room { ...@@ -36,6 +47,10 @@ export class Room {
36 return { ok: true }; 47 return { ok: true };
37 }, 48 },
38 }); 49 });
50 +
51 + if (this.admin) {
52 + this.connect(this.admin);
53 + }
39 } 54 }
40 55
41 public connect(user: User): void { 56 public connect(user: User): void {
...@@ -43,7 +58,13 @@ export class Room { ...@@ -43,7 +58,13 @@ export class Room {
43 return; 58 return;
44 } 59 }
45 60
46 - this.broadcast("updateRoomUser", { state: "added", user: user.getData() }); 61 + this.broadcast("updateRoomUser", {
62 + state: "added",
63 + user: {
64 + username: user.username,
65 + admin: user === this.admin,
66 + },
67 + });
47 68
48 this.users.push(user); 69 this.users.push(user);
49 user.room = this; 70 user.room = this;
...@@ -57,11 +78,25 @@ export class Room { ...@@ -57,11 +78,25 @@ export class Room {
57 78
58 this.broadcast("updateRoomUser", { 79 this.broadcast("updateRoomUser", {
59 state: "removed", 80 state: "removed",
60 - user: user.getData(), 81 + user: {
82 + username: user.username,
83 + admin: user === this.admin,
84 + },
61 }); 85 });
86 +
87 + if (this.users.length === 0) {
88 + this.close();
89 + } else {
90 + this.setNextAdmin();
91 + }
62 } 92 }
63 } 93 }
64 94
95 + private setNextAdmin(): void {
96 + const nextAdmin = this.users[Math.floor(Math.random() * this.users.length)];
97 + this.admin = nextAdmin;
98 + }
99 +
65 public sendChat(user: User, message: string): void { 100 public sendChat(user: User, message: string): void {
66 this.broadcast("chat", { sender: user.username, message: message }); 101 this.broadcast("chat", { sender: user.username, message: message });
67 } 102 }
...@@ -76,12 +111,16 @@ export class Room { ...@@ -76,12 +111,16 @@ export class Room {
76 } 111 }
77 112
78 public getInfo(): RoomInfo { 113 public getInfo(): RoomInfo {
79 - var users: UserData[] = this.users.map((u) => u.getData());
80 return { 114 return {
81 uuid: this.uuid, 115 uuid: this.uuid,
82 name: this.name, 116 name: this.name,
83 maxUsers: this.maxUsers, 117 maxUsers: this.maxUsers,
84 - users: users, 118 + users: this.users.map((u) => {
119 + return {
120 + username: u.username,
121 + admin: u === this.admin,
122 + };
123 + }),
85 }; 124 };
86 } 125 }
87 126
...@@ -101,6 +140,7 @@ export class Room { ...@@ -101,6 +140,7 @@ export class Room {
101 if (!this.closed) { 140 if (!this.closed) {
102 this.users.forEach((u) => this.disconnect(u)); 141 this.users.forEach((u) => this.disconnect(u));
103 this.closed = true; 142 this.closed = true;
143 + this.roomManager.delete(this.uuid);
104 } 144 }
105 } 145 }
106 } 146 }
......
1 import { RoomDescription } from "../../common/dataType"; 1 import { RoomDescription } from "../../common/dataType";
2 +import { User } from "../user/User";
2 import { Room } from "./Room"; 3 import { Room } from "./Room";
3 4
4 export class RoomManager { 5 export class RoomManager {
...@@ -8,8 +9,8 @@ export class RoomManager { ...@@ -8,8 +9,8 @@ export class RoomManager {
8 this.rooms = new Map<string, Room>(); 9 this.rooms = new Map<string, Room>();
9 } 10 }
10 11
11 - public create(name: string, maxConnections: number): Room { 12 + public create(name: string, maxConnections: number, admin?: User): Room {
12 - const room = new Room(name, maxConnections); 13 + const room = new Room(this, name, admin, maxConnections);
13 this.rooms.set(room.uuid, room); 14 this.rooms.set(room.uuid, room);
14 return room; 15 return room;
15 } 16 }
......
1 +import { expect } from "chai";
2 +import { RoomManager } from "../room/RoomManager";
3 +import { SocketTester } from "./util/SocketTester";
4 +
5 +describe("방장", () => {
6 + const roomManager = new RoomManager();
7 + it("방을 만든 유저가 방장이 됩니다", () => {
8 + const socket = new SocketTester(roomManager);
9 + socket.login("guest");
10 +
11 + expect(socket.connection.user !== undefined).eq(true);
12 + const room = roomManager.create("테스트", 2, socket.connection.user);
13 + expect(room.admin).eq(socket.connection.user);
14 + });
15 + it("나중에 들어온 유저는 방장이 되지 않습니다", () => {
16 + const socket1 = new SocketTester(roomManager);
17 + const socket2 = new SocketTester(roomManager);
18 + socket1.login("guest1");
19 + socket2.login("guest2");
20 +
21 + expect(socket1.connection.user !== undefined).eq(true);
22 + expect(socket2.connection.user !== undefined).eq(true);
23 + const room = roomManager.create("테스트", 2, socket1.connection.user);
24 + const response = socket2.test("joinRoom", { uuid: room.uuid });
25 + expect(response.ok).eq(true);
26 + expect(room.admin).eq(socket1.connection.user);
27 + expect(room.admin).not.eq(socket2.connection.user);
28 + expect(response.result?.users[0]?.username).eq("guest1");
29 + expect(response.result?.users[0]?.admin).eq(true);
30 + });
31 + it("방장이 나가면 방장이 인계됩니다", () => {
32 + const socket1 = new SocketTester(roomManager);
33 + const socket2 = new SocketTester(roomManager);
34 + socket1.login("guest1");
35 + socket2.login("guest2");
36 +
37 + expect(socket1.connection.user !== undefined).eq(true);
38 + expect(socket2.connection.user !== undefined).eq(true);
39 + const room = roomManager.create("테스트", 2, socket1.connection.user);
40 + expect(socket2.test("joinRoom", { uuid: room.uuid }).ok).eq(true);
41 +
42 + expect(room.admin).eq(socket1.connection.user);
43 + expect(room.admin).not.eq(socket2.connection.user);
44 +
45 + expect(socket1.test("leaveRoom", {}).ok).eq(true);
46 + expect(room.admin).eq(socket2.connection.user);
47 + });
48 +});
...@@ -52,4 +52,15 @@ describe("방 퇴장", () => { ...@@ -52,4 +52,15 @@ describe("방 퇴장", () => {
52 expect(updated.state).eq("removed"); 52 expect(updated.state).eq("removed");
53 expect(updated.user.username).eq("guest2"); 53 expect(updated.user.username).eq("guest2");
54 }); 54 });
55 + it("방에서 퇴장한 뒤 아무도 없으면 방이 닫힙니다", () => {
56 + const socket = new SocketTester(roomManager);
57 + socket.login("guest1");
58 +
59 + const room = roomManager.create("테스트", 1);
60 +
61 + expect(socket.test("joinRoom", { uuid: room.uuid }).ok).eq(true);
62 + expect(room.closed).eq(false);
63 + expect(socket.test("leaveRoom", {}).ok).eq(true);
64 + expect(room.closed).eq(true);
65 + });
55 }); 66 });
......