강동현
Builds for 1 pipeline failed in 1 minute 18 seconds

준비 상태 구현

...@@ -39,6 +39,7 @@ export const RoomInfoRecord = Record({ ...@@ -39,6 +39,7 @@ export const RoomInfoRecord = Record({
39 Record({ 39 Record({
40 username: String, 40 username: String,
41 admin: Boolean, 41 admin: Boolean,
42 + ready: Boolean,
42 }) 43 })
43 ), 44 ),
44 }); 45 });
......
...@@ -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 });
......
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 +});