Showing
6 changed files
with
115 additions
and
9 deletions
... | @@ -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 | } | ... | ... |
server/test/admin.test.ts
0 → 100644
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 | }); | ... | ... |
-
Please register or login to post a comment