Showing
6 changed files
with
224 additions
and
6 deletions
... | @@ -42,6 +42,11 @@ export class ServerInboundMessageRecordMap { | ... | @@ -42,6 +42,11 @@ export class ServerInboundMessageRecordMap { |
42 | message: String, | 42 | message: String, |
43 | }); | 43 | }); |
44 | 44 | ||
45 | + // 준비합니다. | ||
46 | + ready = Record({ | ||
47 | + ready: Boolean, | ||
48 | + }); | ||
49 | + | ||
45 | // drawer가 단어를 선택합니다. | 50 | // drawer가 단어를 선택합니다. |
46 | chooseWord = Record({ | 51 | chooseWord = Record({ |
47 | word: String, | 52 | word: String, |
... | @@ -75,6 +80,7 @@ interface ServerOutboundMessageMap { | ... | @@ -75,6 +80,7 @@ interface ServerOutboundMessageMap { |
75 | user: { | 80 | user: { |
76 | username: string; | 81 | username: string; |
77 | admin: boolean; | 82 | admin: boolean; |
83 | + ready: boolean; | ||
78 | }; | 84 | }; |
79 | }; | 85 | }; |
80 | 86 | ... | ... |
... | @@ -41,9 +41,6 @@ export class WorldGuessingGame implements Game { | ... | @@ -41,9 +41,6 @@ export class WorldGuessingGame implements Game { |
41 | 41 | ||
42 | constructor(room: Room) { | 42 | constructor(room: Room) { |
43 | this.room = room; | 43 | this.room = room; |
44 | - if (this.room.users.length < 2) { | ||
45 | - throw new Error("인원이 부족합니다."); | ||
46 | - } | ||
47 | 44 | ||
48 | // TODO: 방장이 설정 | 45 | // TODO: 방장이 설정 |
49 | this.maxRound = 5; | 46 | this.maxRound = 5; | ... | ... |
... | @@ -19,6 +19,7 @@ export class Room { | ... | @@ -19,6 +19,7 @@ export class Room { |
19 | public readonly roomManager: RoomManager; | 19 | public readonly roomManager: RoomManager; |
20 | 20 | ||
21 | public users: User[] = []; | 21 | public users: User[] = []; |
22 | + public usersReady: User[] = []; | ||
22 | public admin?: User; | 23 | public admin?: User; |
23 | 24 | ||
24 | public closed: boolean = false; | 25 | public closed: boolean = false; |
... | @@ -46,6 +47,13 @@ export class Room { | ... | @@ -46,6 +47,13 @@ export class Room { |
46 | this.disconnect(user); | 47 | this.disconnect(user); |
47 | return { ok: true }; | 48 | return { ok: true }; |
48 | }, | 49 | }, |
50 | + ready: (user, message) => { | ||
51 | + if (user === this.admin) { | ||
52 | + return { ok: false }; | ||
53 | + } | ||
54 | + this.setReady(user, message.ready); | ||
55 | + return { ok: true }; | ||
56 | + }, | ||
49 | }); | 57 | }); |
50 | 58 | ||
51 | if (this.admin) { | 59 | if (this.admin) { |
... | @@ -63,6 +71,7 @@ export class Room { | ... | @@ -63,6 +71,7 @@ export class Room { |
63 | user: { | 71 | user: { |
64 | username: user.username, | 72 | username: user.username, |
65 | admin: user === this.admin, | 73 | admin: user === this.admin, |
74 | + ready: this.usersReady.includes(user), | ||
66 | }, | 75 | }, |
67 | }); | 76 | }); |
68 | 77 | ||
... | @@ -74,6 +83,7 @@ export class Room { | ... | @@ -74,6 +83,7 @@ export class Room { |
74 | const index = this.users.indexOf(user); | 83 | const index = this.users.indexOf(user); |
75 | if (index > -1) { | 84 | if (index > -1) { |
76 | this.users.splice(index, 1); | 85 | this.users.splice(index, 1); |
86 | + this.usersReady = this.usersReady.filter((u) => u !== user); | ||
77 | user.room = undefined; | 87 | user.room = undefined; |
78 | 88 | ||
79 | this.broadcast("updateRoomUser", { | 89 | this.broadcast("updateRoomUser", { |
... | @@ -81,6 +91,7 @@ export class Room { | ... | @@ -81,6 +91,7 @@ export class Room { |
81 | user: { | 91 | user: { |
82 | username: user.username, | 92 | username: user.username, |
83 | admin: user === this.admin, | 93 | admin: user === this.admin, |
94 | + ready: this.usersReady.includes(user), | ||
84 | }, | 95 | }, |
85 | }); | 96 | }); |
86 | 97 | ||
... | @@ -92,15 +103,73 @@ export class Room { | ... | @@ -92,15 +103,73 @@ export class Room { |
92 | } | 103 | } |
93 | } | 104 | } |
94 | 105 | ||
106 | + public setAdmin(user: User) { | ||
107 | + if (this.users.includes(user)) { | ||
108 | + const prevAdmin = this.admin; | ||
109 | + this.admin = user; | ||
110 | + this.usersReady = this.usersReady.filter((u) => u !== user); | ||
111 | + if (prevAdmin) { | ||
112 | + this.updateUserStatus(prevAdmin); | ||
113 | + } | ||
114 | + this.updateUserStatus(user); | ||
115 | + } | ||
116 | + } | ||
117 | + | ||
95 | private setNextAdmin(): void { | 118 | private setNextAdmin(): void { |
96 | const nextAdmin = this.users[Math.floor(Math.random() * this.users.length)]; | 119 | const nextAdmin = this.users[Math.floor(Math.random() * this.users.length)]; |
97 | - this.admin = nextAdmin; | 120 | + this.setAdmin(nextAdmin); |
121 | + } | ||
122 | + | ||
123 | + public isAdmin(user: User): boolean { | ||
124 | + return this.admin === user; | ||
125 | + } | ||
126 | + | ||
127 | + public clearReady(): void { | ||
128 | + this.usersReady = []; | ||
129 | + } | ||
130 | + | ||
131 | + public setReady(user: User, ready: boolean) { | ||
132 | + if (this.users.includes(user) && this.admin !== user) { | ||
133 | + if (ready && !this.usersReady.includes(user)) { | ||
134 | + this.usersReady.push(user); | ||
135 | + } else if (!ready && this.usersReady.includes(user)) { | ||
136 | + this.usersReady.splice(this.usersReady.indexOf(user), 1); | ||
137 | + } | ||
138 | + this.updateUserStatus(user); | ||
139 | + } | ||
140 | + } | ||
141 | + | ||
142 | + public isReady(user: User): boolean { | ||
143 | + return this.usersReady.includes(user); | ||
144 | + } | ||
145 | + | ||
146 | + public canStart(): boolean { | ||
147 | + if (this.users.length < 2) { | ||
148 | + return false; | ||
149 | + } | ||
150 | + for (let i = 0; i < this.users.length; i++) { | ||
151 | + if (!this.isAdmin(this.users[i]) && !this.isReady(this.users[i])) { | ||
152 | + return false; | ||
153 | + } | ||
154 | + } | ||
155 | + return true; | ||
98 | } | 156 | } |
99 | 157 | ||
100 | public sendChat(user: User, message: string): void { | 158 | public sendChat(user: User, message: string): void { |
101 | this.broadcast("chat", { sender: user.username, message: message }); | 159 | this.broadcast("chat", { sender: user.username, message: message }); |
102 | } | 160 | } |
103 | 161 | ||
162 | + private updateUserStatus(user: User) { | ||
163 | + this.broadcast("updateRoomUser", { | ||
164 | + state: "updated", | ||
165 | + user: { | ||
166 | + username: user.username, | ||
167 | + admin: this.isAdmin(user), | ||
168 | + ready: this.isReady(user), | ||
169 | + }, | ||
170 | + }); | ||
171 | + } | ||
172 | + | ||
104 | public getDescription(): RoomDescription { | 173 | public getDescription(): RoomDescription { |
105 | return { | 174 | return { |
106 | uuid: this.uuid, | 175 | uuid: this.uuid, |
... | @@ -118,7 +187,8 @@ export class Room { | ... | @@ -118,7 +187,8 @@ export class Room { |
118 | users: this.users.map((u) => { | 187 | users: this.users.map((u) => { |
119 | return { | 188 | return { |
120 | username: u.username, | 189 | username: u.username, |
121 | - admin: u === this.admin, | 190 | + admin: this.isAdmin(u), |
191 | + ready: this.usersReady.includes(u), | ||
122 | }; | 192 | }; |
123 | }), | 193 | }), |
124 | }; | 194 | }; | ... | ... |
... | @@ -12,7 +12,7 @@ describe("방장", () => { | ... | @@ -12,7 +12,7 @@ describe("방장", () => { |
12 | const room = roomManager.create("테스트", 2, socket.connection.user); | 12 | const room = roomManager.create("테스트", 2, socket.connection.user); |
13 | expect(room.admin).eq(socket.connection.user); | 13 | expect(room.admin).eq(socket.connection.user); |
14 | }); | 14 | }); |
15 | - it("나중에 들어온 유저는 방장이 되지 않습니다", () => { | 15 | + it("나중에 들어온 유저는 방장 정보를 받지만 방장이 되지 않습니다", () => { |
16 | const socket1 = new SocketTester(roomManager); | 16 | const socket1 = new SocketTester(roomManager); |
17 | const socket2 = new SocketTester(roomManager); | 17 | const socket2 = new SocketTester(roomManager); |
18 | socket1.login("guest1"); | 18 | socket1.login("guest1"); |
... | @@ -45,4 +45,50 @@ describe("방장", () => { | ... | @@ -45,4 +45,50 @@ describe("방장", () => { |
45 | expect(socket1.test("leaveRoom", {}).ok).eq(true); | 45 | expect(socket1.test("leaveRoom", {}).ok).eq(true); |
46 | expect(room.admin).eq(socket2.connection.user); | 46 | expect(room.admin).eq(socket2.connection.user); |
47 | }); | 47 | }); |
48 | + it("방장을 인계하면 정보가 업데이트됩니다", () => { | ||
49 | + const socket1 = new SocketTester(roomManager); | ||
50 | + const socket2 = new SocketTester(roomManager); | ||
51 | + socket1.login("guest1"); | ||
52 | + socket2.login("guest2"); | ||
53 | + | ||
54 | + expect(socket1.connection.user !== undefined).eq(true); | ||
55 | + expect(socket2.connection.user !== undefined).eq(true); | ||
56 | + const room = roomManager.create("테스트", 2, socket1.connection.user); | ||
57 | + expect(socket2.test("joinRoom", { uuid: room.uuid }).ok).eq(true); | ||
58 | + | ||
59 | + expect(room.admin).eq(socket1.connection.user); | ||
60 | + expect(room.admin).not.eq(socket2.connection.user); | ||
61 | + | ||
62 | + if (socket2.connection.user) { | ||
63 | + room.setAdmin(socket2.connection.user); | ||
64 | + expect(room.admin).eq(socket2.connection.user); | ||
65 | + | ||
66 | + // guest1은 guest2가 접속했다는 메세지를 먼저 받은 상태 | ||
67 | + let message = socket1.socket.received("updateRoomUser"); | ||
68 | + expect(message.state).eq("added"); | ||
69 | + expect(message.user.username).eq("guest2"); | ||
70 | + | ||
71 | + // 먼저 이전 방장의 업데이트를 받음 | ||
72 | + message = socket1.socket.received("updateRoomUser"); | ||
73 | + expect(message.state).eq("updated"); | ||
74 | + expect(message.user.username).eq("guest1"); | ||
75 | + expect(message.user.admin).eq(false); | ||
76 | + | ||
77 | + message = socket2.socket.received("updateRoomUser"); | ||
78 | + expect(message.state).eq("updated"); | ||
79 | + expect(message.user.username).eq("guest1"); | ||
80 | + expect(message.user.admin).eq(false); | ||
81 | + | ||
82 | + // 현재 방장의 업데이트를 받음 | ||
83 | + message = socket1.socket.received("updateRoomUser"); | ||
84 | + expect(message.state).eq("updated"); | ||
85 | + expect(message.user.username).eq("guest2"); | ||
86 | + expect(message.user.admin).eq(true); | ||
87 | + | ||
88 | + message = socket2.socket.received("updateRoomUser"); | ||
89 | + expect(message.state).eq("updated"); | ||
90 | + expect(message.user.username).eq("guest2"); | ||
91 | + expect(message.user.admin).eq(true); | ||
92 | + } | ||
93 | + }); | ||
48 | }); | 94 | }); | ... | ... |
server/test/ready.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("guest1"); | ||
10 | + | ||
11 | + const room = roomManager.create("테스트", 2); | ||
12 | + const response = socket.test("joinRoom", { uuid: room.uuid }); | ||
13 | + expect(response.ok).eq(true); | ||
14 | + expect(response.result?.users[0]?.username).eq("guest1"); | ||
15 | + expect(response.result?.users[0]?.ready).eq(false); | ||
16 | + }); | ||
17 | + it("준비 상태를 설정합니다", () => { | ||
18 | + const socket = new SocketTester(roomManager); | ||
19 | + socket.login("guest1"); | ||
20 | + | ||
21 | + const room = roomManager.create("테스트", 2); | ||
22 | + expect(socket.test("joinRoom", { uuid: room.uuid }).ok).eq(true); | ||
23 | + expect(socket.connection.user !== undefined).eq(true); | ||
24 | + if (socket.connection.user) { | ||
25 | + expect(room.isReady(socket.connection.user)).eq(false); | ||
26 | + expect(socket.test("ready", { ready: true }).ok).eq(true); | ||
27 | + expect(room.isReady(socket.connection.user)).eq(true); | ||
28 | + expect(socket.test("ready", { ready: false }).ok).eq(true); | ||
29 | + expect(room.isReady(socket.connection.user)).eq(false); | ||
30 | + } | ||
31 | + }); | ||
32 | + it("방장은 준비할 수 없습니다", () => { | ||
33 | + const socket = new SocketTester(roomManager); | ||
34 | + socket.login("guest1"); | ||
35 | + | ||
36 | + expect(socket.connection.user !== undefined).eq(true); | ||
37 | + if (socket.connection.user) { | ||
38 | + const room = roomManager.create("테스트", 2, socket.connection.user); | ||
39 | + expect(socket.test("ready", { ready: true }).ok).eq(false); | ||
40 | + expect(room.isReady(socket.connection.user)).eq(false); | ||
41 | + } | ||
42 | + }); | ||
43 | + it("방장이 되면 준비 상태가 해제됩니다", () => { | ||
44 | + const socket1 = new SocketTester(roomManager); | ||
45 | + const socket2 = new SocketTester(roomManager); | ||
46 | + socket1.login("guest1"); | ||
47 | + socket2.login("guest2"); | ||
48 | + | ||
49 | + expect(socket1.connection.user !== undefined).eq(true); | ||
50 | + expect(socket2.connection.user !== undefined).eq(true); | ||
51 | + | ||
52 | + if (socket1.connection.user && socket2.connection.user) { | ||
53 | + const room = roomManager.create("테스트", 2, socket1.connection.user); | ||
54 | + expect(socket2.test("joinRoom", { uuid: room.uuid }).ok).eq(true); | ||
55 | + expect(socket2.test("ready", { ready: true }).ok).eq(true); | ||
56 | + expect(room.isReady(socket2.connection.user)); | ||
57 | + room.setAdmin(socket2.connection.user); | ||
58 | + expect(room.isReady(socket2.connection.user)).eq(false); | ||
59 | + } | ||
60 | + }); | ||
61 | + it("모두가 준비해야 게임을 시작할 수 있습니다", () => { | ||
62 | + const socket1 = new SocketTester(roomManager); | ||
63 | + const socket2 = new SocketTester(roomManager); | ||
64 | + const socket3 = new SocketTester(roomManager); | ||
65 | + socket1.login("guest1"); | ||
66 | + socket2.login("guest2"); | ||
67 | + socket3.login("guest3"); | ||
68 | + | ||
69 | + expect(socket1.connection.user !== undefined).eq(true); | ||
70 | + expect(socket2.connection.user !== undefined).eq(true); | ||
71 | + expect(socket3.connection.user !== undefined).eq(true); | ||
72 | + | ||
73 | + if ( | ||
74 | + socket1.connection.user && | ||
75 | + socket2.connection.user && | ||
76 | + socket3.connection.user | ||
77 | + ) { | ||
78 | + const room = roomManager.create("테스트", 3, socket1.connection.user); | ||
79 | + expect(socket2.test("joinRoom", { uuid: room.uuid }).ok).eq(true); | ||
80 | + expect(socket3.test("joinRoom", { uuid: room.uuid }).ok).eq(true); | ||
81 | + | ||
82 | + // 2, 3 모두 준비 안함 | ||
83 | + expect(room.canStart()).eq(false); | ||
84 | + | ||
85 | + // 2만 준비 | ||
86 | + expect(socket2.test("ready", { ready: true }).ok).eq(true); | ||
87 | + | ||
88 | + // 3만 준비 | ||
89 | + expect(socket2.test("ready", { ready: false }).ok).eq(true); | ||
90 | + expect(socket3.test("ready", { ready: true }).ok).eq(true); | ||
91 | + expect(room.canStart()).eq(false); | ||
92 | + | ||
93 | + // 2, 3 모두 준비 | ||
94 | + expect(socket2.test("ready", { ready: true }).ok).eq(true); | ||
95 | + expect(room.canStart()).eq(true); | ||
96 | + } | ||
97 | + }); | ||
98 | +}); |
-
Please register or login to post a comment