Showing
9 changed files
with
59 additions
and
25 deletions
... | @@ -25,6 +25,7 @@ export class Connection { | ... | @@ -25,6 +25,7 @@ export class Connection { |
25 | this.socket = socket; | 25 | this.socket = socket; |
26 | this.roomManager = roomManager; | 26 | this.roomManager = roomManager; |
27 | socket.setHandler((raw) => this.handleRaw(raw)); | 27 | socket.setHandler((raw) => this.handleRaw(raw)); |
28 | + socket.setDisconnectHandler(() => this.handleDisconnect()); | ||
28 | } | 29 | } |
29 | 30 | ||
30 | public send<T extends ServerOutboundMessageKey>( | 31 | public send<T extends ServerOutboundMessageKey>( |
... | @@ -81,4 +82,8 @@ export class Connection { | ... | @@ -81,4 +82,8 @@ export class Connection { |
81 | 82 | ||
82 | return { ok: true }; | 83 | return { ok: true }; |
83 | } | 84 | } |
85 | + | ||
86 | + public handleDisconnect(): void { | ||
87 | + this.user?.disconnected(); | ||
88 | + } | ||
84 | } | 89 | } | ... | ... |
... | @@ -3,6 +3,7 @@ import { RawMessage, ServerResponse } from "../../common"; | ... | @@ -3,6 +3,7 @@ import { RawMessage, ServerResponse } from "../../common"; |
3 | 3 | ||
4 | export interface SocketWrapper { | 4 | export interface SocketWrapper { |
5 | setHandler: (listener: (raw: RawMessage) => ServerResponse<any>) => void; | 5 | setHandler: (listener: (raw: RawMessage) => ServerResponse<any>) => void; |
6 | + setDisconnectHandler: (listener: () => void) => void; | ||
6 | send: (raw: RawMessage) => void; | 7 | send: (raw: RawMessage) => void; |
7 | } | 8 | } |
8 | 9 | ||
... | @@ -19,6 +20,12 @@ export class SocketIoWrapper implements SocketWrapper { | ... | @@ -19,6 +20,12 @@ export class SocketIoWrapper implements SocketWrapper { |
19 | }); | 20 | }); |
20 | } | 21 | } |
21 | 22 | ||
23 | + public setDisconnectHandler(listener: () => void) { | ||
24 | + this.socketIo.on("disconnect", () => { | ||
25 | + listener(); | ||
26 | + }); | ||
27 | + } | ||
28 | + | ||
22 | public send(raw: RawMessage) { | 29 | public send(raw: RawMessage) { |
23 | this.socketIo.emit("msg", raw); | 30 | this.socketIo.emit("msg", raw); |
24 | } | 31 | } | ... | ... |
... | @@ -11,7 +11,7 @@ export class Game { | ... | @@ -11,7 +11,7 @@ export class Game { |
11 | roundDuration: number; | 11 | roundDuration: number; |
12 | readonly roundTerm: number = 5; // 다음 라운드 시작까지 기다리는 시간 | 12 | readonly roundTerm: number = 5; // 다음 라운드 시작까지 기다리는 시간 |
13 | wordCandidates: string[] = []; | 13 | wordCandidates: string[] = []; |
14 | - word: string = ""; | 14 | + word?: string; |
15 | timer: { | 15 | timer: { |
16 | startTimeMillis: number; | 16 | startTimeMillis: number; |
17 | timeLeftMillis: number; | 17 | timeLeftMillis: number; |
... | @@ -66,7 +66,11 @@ export class Game { | ... | @@ -66,7 +66,11 @@ export class Game { |
66 | }, | 66 | }, |
67 | chat: (user, message) => { | 67 | chat: (user, message) => { |
68 | const text = message.message.trim(); | 68 | const text = message.message.trim(); |
69 | - if (this.roles.get(user) === "guesser" && text === this.word) { | 69 | + if ( |
70 | + this.roles.get(user) === "guesser" && | ||
71 | + this.roundState === "running" && | ||
72 | + text === this.word | ||
73 | + ) { | ||
70 | this.acceptAnswer(user); | 74 | this.acceptAnswer(user); |
71 | } else { | 75 | } else { |
72 | this.room.sendChat(user, text); | 76 | this.room.sendChat(user, text); |
... | @@ -122,6 +126,7 @@ export class Game { | ... | @@ -122,6 +126,7 @@ export class Game { |
122 | 126 | ||
123 | private startNextRound(): void { | 127 | private startNextRound(): void { |
124 | this.roundState = "choosing"; | 128 | this.roundState = "choosing"; |
129 | + this.word = undefined; | ||
125 | this.round++; | 130 | this.round++; |
126 | 131 | ||
127 | this.roles.clear(); | 132 | this.roles.clear(); |
... | @@ -164,7 +169,9 @@ export class Game { | ... | @@ -164,7 +169,9 @@ export class Game { |
164 | 169 | ||
165 | this.stopTimer(); | 170 | this.stopTimer(); |
166 | 171 | ||
167 | - this.room.broadcast("finishRound", { answer: this.word }); | 172 | + if (this.word) { |
173 | + this.room.broadcast("finishRound", { answer: this.word }); | ||
174 | + } | ||
168 | 175 | ||
169 | this.prepareNextRound(); | 176 | this.prepareNextRound(); |
170 | } | 177 | } |
... | @@ -192,12 +199,15 @@ export class Game { | ... | @@ -192,12 +199,15 @@ export class Game { |
192 | if (this.nextRoundTimerId) { | 199 | if (this.nextRoundTimerId) { |
193 | clearTimeout(this.nextRoundTimerId); | 200 | clearTimeout(this.nextRoundTimerId); |
194 | } | 201 | } |
195 | - this.room.broadcast("finishRound", { answer: this.word }); | 202 | + if (this.word) { |
203 | + this.room.broadcast("finishRound", { answer: this.word }); | ||
204 | + } | ||
205 | + | ||
196 | this.finishGame(); | 206 | this.finishGame(); |
197 | } | 207 | } |
198 | 208 | ||
199 | private acceptAnswer(user: User): void { | 209 | private acceptAnswer(user: User): void { |
200 | - user.connection.send("answerAccepted", { answer: this.word }); | 210 | + user.connection.send("answerAccepted", { answer: this.word! }); |
201 | this.changeRole(user, "winner"); | 211 | this.changeRole(user, "winner"); |
202 | 212 | ||
203 | let noGuesser = true; | 213 | let noGuesser = true; |
... | @@ -265,7 +275,7 @@ export class Game { | ... | @@ -265,7 +275,7 @@ export class Game { |
265 | this.room.broadcast("role", { username: user.username, role }); | 275 | this.room.broadcast("role", { username: user.username, role }); |
266 | } | 276 | } |
267 | 277 | ||
268 | - join(user: User): void { | 278 | + joined(user: User): void { |
269 | this.changeRole(user, "spectator"); | 279 | this.changeRole(user, "spectator"); |
270 | this.sendTimer(user); | 280 | this.sendTimer(user); |
271 | user.connection.send("startRound", { | 281 | user.connection.send("startRound", { |
... | @@ -273,7 +283,7 @@ export class Game { | ... | @@ -273,7 +283,7 @@ export class Game { |
273 | duration: this.roundDuration, | 283 | duration: this.roundDuration, |
274 | roles: this.makeRoleArray(), | 284 | roles: this.makeRoleArray(), |
275 | }); | 285 | }); |
276 | - if (this.roundState === "done") { | 286 | + if (this.roundState === "done" && this.word) { |
277 | user.connection.send("finishRound", { | 287 | user.connection.send("finishRound", { |
278 | answer: this.word, | 288 | answer: this.word, |
279 | }); | 289 | }); |
... | @@ -289,7 +299,7 @@ export class Game { | ... | @@ -289,7 +299,7 @@ export class Game { |
289 | }); | 299 | }); |
290 | } | 300 | } |
291 | 301 | ||
292 | - leave(user: User): void { | 302 | + left(user: User): void { |
293 | if (this.room.users.length < 2) { | 303 | if (this.room.users.length < 2) { |
294 | this.forceFinishGame(); | 304 | this.forceFinishGame(); |
295 | return; | 305 | return; | ... | ... |
... | @@ -122,6 +122,8 @@ export class Room { | ... | @@ -122,6 +122,8 @@ export class Room { |
122 | this.usersReady = this.usersReady.filter((u) => u !== user); | 122 | this.usersReady = this.usersReady.filter((u) => u !== user); |
123 | user.room = undefined; | 123 | user.room = undefined; |
124 | 124 | ||
125 | + this.game?.left(user); | ||
126 | + | ||
125 | this.broadcast("updateRoomUser", { | 127 | this.broadcast("updateRoomUser", { |
126 | state: "removed", | 128 | state: "removed", |
127 | user: { | 129 | user: { | ... | ... |
... | @@ -109,7 +109,7 @@ describe("라운드", () => { | ... | @@ -109,7 +109,7 @@ describe("라운드", () => { |
109 | guesserSockets[0].disconnect(); | 109 | guesserSockets[0].disconnect(); |
110 | drawerSocket.socket.notReceived("finishRound"); | 110 | drawerSocket.socket.notReceived("finishRound"); |
111 | guesserSockets[1].disconnect(); | 111 | guesserSockets[1].disconnect(); |
112 | - drawerSocket.socket.received("finishRound"); | 112 | + // 단어가 선택되지 않았으므로 finishRound가 수신되지 않습니다. |
113 | drawerSocket.socket.received("finishGame"); | 113 | drawerSocket.socket.received("finishGame"); |
114 | }); | 114 | }); |
115 | it("drawer가 단어를 선택하고 모든 guesser가 나가면 인원이 부족하므로 게임이 종료됩니다", () => { | 115 | it("drawer가 단어를 선택하고 모든 guesser가 나가면 인원이 부족하므로 게임이 종료됩니다", () => { |
... | @@ -143,6 +143,7 @@ describe("라운드", () => { | ... | @@ -143,6 +143,7 @@ describe("라운드", () => { |
143 | const word = drawerSocket.socket.received("wordSet").words[0]; | 143 | const word = drawerSocket.socket.received("wordSet").words[0]; |
144 | drawerSocket.testOk("chooseWord", { word }); | 144 | drawerSocket.testOk("chooseWord", { word }); |
145 | guesserSockets[0].testOk("chat", { message: word }); | 145 | guesserSockets[0].testOk("chat", { message: word }); |
146 | + guesserSockets[1].testOk("chat", { message: word }); | ||
146 | 147 | ||
147 | guesserSockets[0].socket.received("finishRound"); | 148 | guesserSockets[0].socket.received("finishRound"); |
148 | guesserSockets[0].socket.notReceived("startRound"); | 149 | guesserSockets[0].socket.notReceived("startRound"); | ... | ... |
... | @@ -15,19 +15,8 @@ describe("라운드 브러시 설정", () => { | ... | @@ -15,19 +15,8 @@ describe("라운드 브러시 설정", () => { |
15 | brushSettings | 15 | brushSettings |
16 | ); | 16 | ); |
17 | }); | 17 | }); |
18 | - it("올바르지 않은 브러시 설정은 허용되지 않습니다", () => { | 18 | + it("올바르지 않은 브러시 색상은 허용되지 않습니다", () => { |
19 | const { drawerSocket } = prepareGame(2); | 19 | const { drawerSocket } = prepareGame(2); |
20 | - | ||
21 | - drawerSocket.testNotOk("setBrush", { | ||
22 | - size: 0, | ||
23 | - color: "000000", | ||
24 | - drawing: true, | ||
25 | - }); | ||
26 | - drawerSocket.testNotOk("setBrush", { | ||
27 | - size: 100, | ||
28 | - color: "000000", | ||
29 | - drawing: true, | ||
30 | - }); | ||
31 | drawerSocket.testNotOk("setBrush", { | 20 | drawerSocket.testNotOk("setBrush", { |
32 | size: 1, | 21 | size: 1, |
33 | color: "000", | 22 | color: "000", |
... | @@ -39,6 +28,21 @@ describe("라운드 브러시 설정", () => { | ... | @@ -39,6 +28,21 @@ describe("라운드 브러시 설정", () => { |
39 | drawing: true, | 28 | drawing: true, |
40 | }); | 29 | }); |
41 | }); | 30 | }); |
31 | + it("올바르지 않은 브러시 사이즈는 Clamp 됩니다", () => { | ||
32 | + const { drawerSocket, guesserSockets } = prepareGame(2); | ||
33 | + drawerSocket.testOk("setBrush", { | ||
34 | + size: 0, | ||
35 | + color: "000000", | ||
36 | + drawing: true, | ||
37 | + }); | ||
38 | + expect(guesserSockets[0].socket.received("setBrush").size).eq(1); | ||
39 | + drawerSocket.testOk("setBrush", { | ||
40 | + size: 100, | ||
41 | + color: "000000", | ||
42 | + drawing: true, | ||
43 | + }); | ||
44 | + expect(guesserSockets[0].socket.received("setBrush").size).eq(64); | ||
45 | + }); | ||
42 | it("drawer가 아닌 다른 사람들은 브러시를 설정할 수 없습니다", () => { | 46 | it("drawer가 아닌 다른 사람들은 브러시를 설정할 수 없습니다", () => { |
43 | const { guesserSockets } = prepareGame(2); | 47 | const { guesserSockets } = prepareGame(2); |
44 | 48 | ... | ... |
... | @@ -9,11 +9,12 @@ import { SocketWrapper } from "../../connection/SocketWrapper"; | ... | @@ -9,11 +9,12 @@ import { SocketWrapper } from "../../connection/SocketWrapper"; |
9 | 9 | ||
10 | export class DummySocket implements SocketWrapper { | 10 | export class DummySocket implements SocketWrapper { |
11 | public handler?: (raw: RawMessage) => ServerResponse<any>; | 11 | public handler?: (raw: RawMessage) => ServerResponse<any>; |
12 | + public disconnectHandler?: () => void; | ||
12 | public receivedMessages: RawMessage[] = []; | 13 | public receivedMessages: RawMessage[] = []; |
13 | 14 | ||
14 | - public setHandler(handler: (raw: RawMessage) => ServerResponse<any>) { | 15 | + public setHandler(handler: (raw: RawMessage) => ServerResponse<any>) {} |
15 | - this.handler = handler; | 16 | + |
16 | - } | 17 | + public setDisconnectHandler(handler: () => void) {} |
17 | 18 | ||
18 | public send(raw: RawMessage): void { | 19 | public send(raw: RawMessage): void { |
19 | this.receivedMessages.push(raw); | 20 | this.receivedMessages.push(raw); | ... | ... |
-
Please register or login to post a comment