Merge branch 'master' of http://khuhub.khu.ac.kr/2020105578/nodejs-game
Showing
26 changed files
with
481 additions
and
494 deletions
1 | -import { UserData } from "../user/types"; | ||
2 | - | ||
3 | /** | 1 | /** |
4 | * 방 리스트에서 사용됩니다. | 2 | * 방 리스트에서 사용됩니다. |
5 | */ | 3 | */ |
... | @@ -19,3 +17,7 @@ export interface RoomInfo { | ... | @@ -19,3 +17,7 @@ export interface RoomInfo { |
19 | maxUsers: number; | 17 | maxUsers: number; |
20 | users: UserData[]; | 18 | users: UserData[]; |
21 | } | 19 | } |
20 | + | ||
21 | +export interface UserData { | ||
22 | + username: string; | ||
23 | +} | ... | ... |
common/index.ts
0 → 100644
1 | +export * from "./message"; |
common/message.ts
0 → 100644
1 | +import { RoomDescription, RoomInfo } from "./dataType"; | ||
2 | + | ||
3 | +// 서버로 들어오는 메세지 타입을 정의합니다. | ||
4 | +// 'result' 속성은 서버 요청 결과에만 포함되는 특별한 속성입니다. | ||
5 | +interface ServerInboundMessageMap { | ||
6 | + // 로그인을 시도합니다. | ||
7 | + login: { | ||
8 | + username: string; | ||
9 | + }; | ||
10 | + | ||
11 | + // 방 목록을 요청합니다. | ||
12 | + roomList: { | ||
13 | + result: RoomDescription[]; | ||
14 | + }; | ||
15 | + | ||
16 | + // 방에 접속합니다. | ||
17 | + joinRoom: { | ||
18 | + uuid: string; | ||
19 | + result: RoomInfo; | ||
20 | + }; | ||
21 | + | ||
22 | + // 방에서 나갑니다. | ||
23 | + leaveRoom: {}; | ||
24 | + | ||
25 | + // 채팅을 보냅니다. | ||
26 | + chat: { | ||
27 | + message: string; | ||
28 | + }; | ||
29 | + | ||
30 | + // drawer가 단어를 선택합니다. | ||
31 | + chooseWord: { | ||
32 | + word: string; | ||
33 | + }; | ||
34 | + | ||
35 | + // 브러시 정보를 변경합니다. | ||
36 | + setBrush: { | ||
37 | + size: number; | ||
38 | + color: string; | ||
39 | + drawing: boolean; | ||
40 | + }; | ||
41 | + | ||
42 | + // 브러시를 이동합니다. | ||
43 | + moveBrush: { | ||
44 | + x: number; | ||
45 | + y: number; | ||
46 | + }; | ||
47 | +} | ||
48 | + | ||
49 | +// 서버에서 나가는 메세지 타입을 정의합니다. | ||
50 | +interface ServerOutboundMessageMap { | ||
51 | + // 방에 접속 중인 유저 목록이 업데이트 되었습니다. | ||
52 | + updateRoomUser: { | ||
53 | + state: "added" | "updated" | "removed"; | ||
54 | + user: { | ||
55 | + username: string; | ||
56 | + }; | ||
57 | + }; | ||
58 | + | ||
59 | + // 다른 유저가 채팅을 보냈습니다. | ||
60 | + chat: { | ||
61 | + sender: string; | ||
62 | + message: string; | ||
63 | + }; | ||
64 | + | ||
65 | + // 라운드가 시작되었습니다. | ||
66 | + startRound: { | ||
67 | + round: number; | ||
68 | + duration: number; | ||
69 | + roles: { | ||
70 | + username: string; | ||
71 | + role: "drawer" | "guesser" | "winner" | "spectator"; | ||
72 | + }; | ||
73 | + }; | ||
74 | + | ||
75 | + // drawer에게 선택할 수 있는 단어가 주어졌습니다. | ||
76 | + wordSet: { | ||
77 | + words: string[]; | ||
78 | + }; | ||
79 | + | ||
80 | + // 이번 라운드의 단어가 선택되었습니다. | ||
81 | + wordChosen: { | ||
82 | + length: number; | ||
83 | + }; | ||
84 | + | ||
85 | + // 라운드 타이머 정보를 동기화합니다. | ||
86 | + timer: { | ||
87 | + state: "started" | "stopped"; | ||
88 | + time: number; | ||
89 | + }; | ||
90 | + | ||
91 | + // 라운드가 종료되었습니다. | ||
92 | + finishRound: { | ||
93 | + answer: string; | ||
94 | + }; | ||
95 | + | ||
96 | + // 역할이 변경되었습니다. | ||
97 | + role: { | ||
98 | + username: string; | ||
99 | + role: "drawer" | "guesser" | "winner" | "spectator"; | ||
100 | + }; | ||
101 | + | ||
102 | + // 보낸 단어가 정답 처리 되었습니다. | ||
103 | + answerAccepted: { | ||
104 | + answer: string; | ||
105 | + }; | ||
106 | + | ||
107 | + // 게임이 종료되었습니다. | ||
108 | + finishGame: {}; | ||
109 | + | ||
110 | + // 브러시 정보가 변경되었습니다. | ||
111 | + setBrush: { | ||
112 | + size: number; | ||
113 | + color: string; | ||
114 | + drawing: boolean; | ||
115 | + }; | ||
116 | + | ||
117 | + // 브러시가 이동되었습니다. | ||
118 | + moveBrush: { | ||
119 | + x: number; | ||
120 | + y: number; | ||
121 | + }; | ||
122 | +} | ||
123 | + | ||
124 | +export interface RawMessage { | ||
125 | + type: string; | ||
126 | + message: any; | ||
127 | +} | ||
128 | + | ||
129 | +export type ServerInboundMessageKey = keyof ServerInboundMessageMap; | ||
130 | + | ||
131 | +export type ServerInboundMessage<Key extends ServerInboundMessageKey> = Omit< | ||
132 | + ServerInboundMessageMap[Key], | ||
133 | + "result" | ||
134 | +>; | ||
135 | + | ||
136 | +export interface ServerResponse<Key extends ServerInboundMessageKey> { | ||
137 | + ok: boolean; | ||
138 | + reason?: string; | ||
139 | + result?: "result" extends keyof ServerInboundMessageMap[Key] | ||
140 | + ? ServerInboundMessageMap[Key]["result"] | ||
141 | + : never; | ||
142 | +} | ||
143 | + | ||
144 | +export type ServerOutboundMessage<Key extends keyof ServerOutboundMessageMap> = | ||
145 | + ServerOutboundMessageMap[Key]; | ||
146 | + | ||
147 | +export type ServerOutboundMessageKey = keyof ServerOutboundMessageMap; |
common/package.json
0 → 100644
common/tsconfig.json
0 → 100644
common/yarn.lock
0 → 100644
1 | import { Socket } from "socket.io"; | 1 | import { Socket } from "socket.io"; |
2 | -import { MessageHandlerRegistry } from "../message/MessageHandlerRegistry"; | 2 | +import { ServerOutboundMessage, ServerOutboundMessageKey } from "../../common"; |
3 | -import { Message } from "../message/types"; | 3 | +import { MessageHandlerChain } from "../message/MessageHandlerChain"; |
4 | import { Room } from "../room/Room"; | 4 | import { Room } from "../room/Room"; |
5 | import { User } from "../user/User"; | 5 | import { User } from "../user/User"; |
6 | 6 | ||
... | @@ -9,17 +9,20 @@ export class Connection { | ... | @@ -9,17 +9,20 @@ export class Connection { |
9 | 9 | ||
10 | public user?: User; | 10 | public user?: User; |
11 | 11 | ||
12 | + private messageHandlerChain: MessageHandlerChain; | ||
13 | + | ||
12 | constructor(socket: Socket) { | 14 | constructor(socket: Socket) { |
13 | this.socket = socket; | 15 | this.socket = socket; |
14 | - | 16 | + this.messageHandlerChain = new MessageHandlerChain(this); |
15 | - MessageHandlerRegistry.registerHandlers(this); | ||
16 | - } | ||
17 | - | ||
18 | - public get authenticated(): boolean { | ||
19 | - return this.user !== undefined; | ||
20 | } | 17 | } |
21 | 18 | ||
22 | - public send(message: Message) { | 19 | + public send<T extends ServerOutboundMessageKey>( |
23 | - this.socket.emit(message.type, message); | 20 | + type: T, |
21 | + message: ServerOutboundMessage<T> | ||
22 | + ) { | ||
23 | + this.socket.emit("msg", { | ||
24 | + type: type as string, | ||
25 | + message: message, | ||
26 | + }); | ||
24 | } | 27 | } |
25 | } | 28 | } | ... | ... |
server/game/Game.ts
0 → 100644
server/game/WordGuessingGame.ts
0 → 100644
1 | +import { roomChatHandler } from "../message/handler/roomChatHandler"; | ||
2 | +import { Room } from "../room/Room"; | ||
3 | +import { User } from "../user/User"; | ||
4 | +import { Game } from "./Game"; | ||
5 | + | ||
6 | +export class WorldGuessingGame implements Game { | ||
7 | + room: Room; | ||
8 | + maxRound: number; | ||
9 | + round: number; | ||
10 | + | ||
11 | + constructor(room: Room) { | ||
12 | + this.room = room; | ||
13 | + if (this.room.users.length < 2) { | ||
14 | + throw new Error("인원이 부족합니다."); | ||
15 | + } | ||
16 | + | ||
17 | + // TODO: 방장이 설정 | ||
18 | + this.maxRound = 5; | ||
19 | + this.round = 1; | ||
20 | + } | ||
21 | + | ||
22 | + join(user: User): void { | ||
23 | + throw new Error("Method not implemented."); | ||
24 | + } | ||
25 | + | ||
26 | + leave(user: User): void { | ||
27 | + throw new Error("Method not implemented."); | ||
28 | + } | ||
29 | +} |
server/message/MessageHandler.ts
0 → 100644
1 | +import { Connection } from "../connection/Connection"; | ||
2 | +import { | ||
3 | + ServerInboundMessage, | ||
4 | + ServerInboundMessageKey, | ||
5 | + ServerResponse, | ||
6 | +} from "../../common/index"; | ||
7 | +import { User } from "../user/User"; | ||
8 | + | ||
9 | +type UserHandlerMap = { | ||
10 | + [Key in ServerInboundMessageKey]?: ( | ||
11 | + user: User, | ||
12 | + message: ServerInboundMessage<Key> | ||
13 | + ) => ServerResponse<Key>; | ||
14 | +}; | ||
15 | + | ||
16 | +export class MessageHandler { | ||
17 | + private handlers: UserHandlerMap; | ||
18 | + | ||
19 | + constructor(handlers: UserHandlerMap) { | ||
20 | + this.handlers = handlers; | ||
21 | + } | ||
22 | + | ||
23 | + public handle( | ||
24 | + type: ServerInboundMessageKey, | ||
25 | + user: User, | ||
26 | + message: any, | ||
27 | + callback: Function | ||
28 | + ): boolean { | ||
29 | + const handler = this.handlers[type]; | ||
30 | + if (!handler) return false; | ||
31 | + const response = handler(user, message); | ||
32 | + callback(response); | ||
33 | + return true; | ||
34 | + } | ||
35 | +} |
server/message/MessageHandlerChain.ts
0 → 100644
1 | +import { Connection } from "../connection/Connection"; | ||
2 | +import { | ||
3 | + RawMessage, | ||
4 | + ServerInboundMessage, | ||
5 | + ServerInboundMessageKey, | ||
6 | + ServerResponse, | ||
7 | +} from "../../common/index"; | ||
8 | +import { User } from "../user/User"; | ||
9 | + | ||
10 | +export class MessageHandlerChain { | ||
11 | + connection: Connection; | ||
12 | + | ||
13 | + constructor(connection: Connection) { | ||
14 | + this.connection = connection; | ||
15 | + | ||
16 | + this.connection.socket.on("msg", (raw: RawMessage, callback: Function) => { | ||
17 | + this.handleRaw(connection, raw, callback); | ||
18 | + }); | ||
19 | + } | ||
20 | + | ||
21 | + private handleRaw( | ||
22 | + connection: Connection, | ||
23 | + raw: RawMessage, | ||
24 | + callback: Function | ||
25 | + ) { | ||
26 | + const type = raw.type as ServerInboundMessageKey; | ||
27 | + const message = raw.message; | ||
28 | + | ||
29 | + // 유저 정보가 없으므로 로그인은 따로 핸들링 | ||
30 | + if (type === "login") { | ||
31 | + this.handleLogin(connection, message, callback); | ||
32 | + return; | ||
33 | + } | ||
34 | + | ||
35 | + // Game > Room > User 순으로 전달 | ||
36 | + if ( | ||
37 | + connection?.user?.room && | ||
38 | + connection.user.room.handler.handle( | ||
39 | + type, | ||
40 | + connection.user, | ||
41 | + message, | ||
42 | + callback | ||
43 | + ) | ||
44 | + ) | ||
45 | + return; | ||
46 | + | ||
47 | + if ( | ||
48 | + connection?.user && | ||
49 | + connection.user.handler.handle(type, connection.user, message, callback) | ||
50 | + ) | ||
51 | + return; | ||
52 | + } | ||
53 | + | ||
54 | + private handleLogin( | ||
55 | + connection: Connection, | ||
56 | + message: ServerInboundMessage<"login">, | ||
57 | + callback: Function | ||
58 | + ) { | ||
59 | + connection.user = new User(message.username, connection); | ||
60 | + console.log(`User ${message.username} has logged in!`); | ||
61 | + | ||
62 | + callback({ ok: true }); | ||
63 | + } | ||
64 | +} |
1 | -import { Connection } from "../connection/Connection"; | ||
2 | -import { User } from "../user/User"; | ||
3 | -import { loginHandler } from "./handler/loginHandler"; | ||
4 | -import { roomChatHandler } from "./handler/roomChatHandler"; | ||
5 | -import { roomJoinHandler } from "./handler/roomJoinHandler"; | ||
6 | -import { roomLeaveHandler } from "./handler/roomLeaveHandler"; | ||
7 | -import { roomListRequestHandler } from "./handler/roomListRequestHandler"; | ||
8 | -import { Message, MessageResponse, MessageType } from "./types"; | ||
9 | - | ||
10 | -export class MessageHandlerRegistry { | ||
11 | - static registerHandlers(connection: Connection) { | ||
12 | - this.registerHandler(connection, MessageType.LOGIN, loginHandler); | ||
13 | - this.registerHandlerAuthed( | ||
14 | - connection, | ||
15 | - MessageType.ROOM_LIST_REQUEST, | ||
16 | - roomListRequestHandler | ||
17 | - ); | ||
18 | - this.registerHandlerAuthed( | ||
19 | - connection, | ||
20 | - MessageType.ROOM_JOIN, | ||
21 | - roomJoinHandler | ||
22 | - ); | ||
23 | - this.registerHandlerAuthed( | ||
24 | - connection, | ||
25 | - MessageType.ROOM_LEAVE, | ||
26 | - roomLeaveHandler | ||
27 | - ); | ||
28 | - this.registerHandlerAuthed( | ||
29 | - connection, | ||
30 | - MessageType.ROOM_CHAT, | ||
31 | - roomChatHandler | ||
32 | - ); | ||
33 | - } | ||
34 | - | ||
35 | - private static registerHandler<T extends Message, S>( | ||
36 | - connection: Connection, | ||
37 | - typeName: string, | ||
38 | - handler: (connection: Connection, message: T) => MessageResponse<S> | ||
39 | - ) { | ||
40 | - connection.socket.on(typeName, (message: T, callback: Function) => { | ||
41 | - const response = handler(connection, message); | ||
42 | - callback(response); | ||
43 | - }); | ||
44 | - } | ||
45 | - | ||
46 | - private static registerHandlerAuthed<T extends Message, S>( | ||
47 | - connection: Connection, | ||
48 | - typeName: string, | ||
49 | - handler: (user: User, message: T) => MessageResponse<S> | ||
50 | - ) { | ||
51 | - connection.socket.on(typeName, (message: T, callback: Function) => { | ||
52 | - if (connection.user !== undefined) { | ||
53 | - const response = handler(connection.user, message); | ||
54 | - callback(response); | ||
55 | - } else { | ||
56 | - callback({ ok: false }); | ||
57 | - } | ||
58 | - }); | ||
59 | - } | ||
60 | -} |
1 | -import { Connection } from "../../connection/Connection"; | ||
2 | -import { RoomManager } from "../../room/RoomManager"; | ||
3 | -import { User } from "../../user/User"; | ||
4 | -import { LoginMessage, MessageResponse } from "../types"; | ||
5 | - | ||
6 | -export function loginHandler( | ||
7 | - connection: Connection, | ||
8 | - message: LoginMessage | ||
9 | -): MessageResponse<undefined> { | ||
10 | - connection.user = new User(message.username, connection); | ||
11 | - console.log(`User ${message.username} has logged in!`); | ||
12 | - | ||
13 | - return { ok: true }; | ||
14 | -} |
1 | -import { Connection } from "../../connection/Connection"; | ||
2 | -import { RoomManager } from "../../room/RoomManager"; | ||
3 | -import { User } from "../../user/User"; | ||
4 | -import { MessageResponse, RoomChatMessage, RoomJoinMessage } from "../types"; | ||
5 | - | ||
6 | -export function roomChatHandler( | ||
7 | - user: User, | ||
8 | - message: RoomChatMessage | ||
9 | -): MessageResponse<undefined> { | ||
10 | - user.room?.sendChat(user, message.message); | ||
11 | - return { ok: true }; | ||
12 | -} |
1 | -import { Connection } from "../../connection/Connection"; | ||
2 | -import { RoomManager } from "../../room/RoomManager"; | ||
3 | -import { RoomInfo } from "../../room/types"; | ||
4 | -import { User } from "../../user/User"; | ||
5 | -import { MessageResponse, RoomJoinMessage } from "../types"; | ||
6 | - | ||
7 | -export function roomJoinHandler( | ||
8 | - user: User, | ||
9 | - message: RoomJoinMessage | ||
10 | -): MessageResponse<RoomInfo> { | ||
11 | - const room = RoomManager.instance().get(message.uuid); | ||
12 | - if (room !== undefined) { | ||
13 | - room.connect(user); | ||
14 | - return { ok: user.room !== undefined, result: user.room?.getInfo() }; | ||
15 | - } | ||
16 | - return { ok: false }; | ||
17 | -} |
1 | -import { Connection } from "../../connection/Connection"; | ||
2 | -import { RoomManager } from "../../room/RoomManager"; | ||
3 | -import { User } from "../../user/User"; | ||
4 | -import { MessageResponse, RoomLeaveMessage } from "../types"; | ||
5 | - | ||
6 | -export function roomLeaveHandler( | ||
7 | - user: User, | ||
8 | - message: RoomLeaveMessage | ||
9 | -): MessageResponse<undefined> { | ||
10 | - user.room?.disconnect(user); | ||
11 | - return { ok: true }; | ||
12 | -} |
1 | -import { Connection } from "../../connection/Connection"; | ||
2 | -import { RoomManager } from "../../room/RoomManager"; | ||
3 | -import { RoomDescription, RoomInfo } from "../../room/types"; | ||
4 | -import { User } from "../../user/User"; | ||
5 | -import { MessageResponse, RoomListRequestMessage } from "../types"; | ||
6 | - | ||
7 | -export function roomListRequestHandler( | ||
8 | - user: User, | ||
9 | - message: RoomListRequestMessage | ||
10 | -): MessageResponse<RoomDescription[]> { | ||
11 | - return { ok: true, result: RoomManager.instance().list() }; | ||
12 | -} |
server/message/types.ts
deleted
100644 → 0
1 | -import { RoomDescription } from "../room/types"; | ||
2 | -import { UserData } from "../user/types"; | ||
3 | - | ||
4 | -export interface Message { | ||
5 | - readonly type: string; | ||
6 | -} | ||
7 | - | ||
8 | -/** | ||
9 | - * 클라 -> 서버 : 클라이언트에서 서버로 요청할 때 사용하는 메세지입니다. 요청 결과는 MessageResponse<undefined>입니다. | ||
10 | - * 클라 -> 서버 -> T : 위와 동일하지만 요청 결과가 MessageResponse<T>입니다. | ||
11 | - * 서버 -> 클라 : 서버에서 클라이언트로 전송되는 메세지입니다. | ||
12 | - * 클라 <-> 서버 : 양방향으로 사용되는 메세지입니다. | ||
13 | - */ | ||
14 | - | ||
15 | -/** | ||
16 | - * 서버에 Event를 보냈을 때 요청에 대한 결과를 전송받습니다. | ||
17 | - * @param ok 요청의 성공 여부입니다. | ||
18 | - * @param reason 요청 실패 사유입니다. 필요한 경우에만 포함됩니다. | ||
19 | - * @param result 요청에 대한 결과 메세지입니다. 특정한 메세지에 대해 요청이 성공하였을 때만 포함됩니다. | ||
20 | - */ | ||
21 | -export interface MessageResponse<T> { | ||
22 | - ok: boolean; | ||
23 | - reason?: string; | ||
24 | - result?: T; | ||
25 | -} | ||
26 | - | ||
27 | -/** | ||
28 | - * 클라 -> 서버 | ||
29 | - * 로그인 정보를 서버에게 전송합니다. | ||
30 | - */ | ||
31 | -export class LoginMessage implements Message { | ||
32 | - readonly type = MessageType.LOGIN; | ||
33 | - constructor(public username: string) {} | ||
34 | -} | ||
35 | - | ||
36 | -/** | ||
37 | - * 클라 -> 서버 -> RoomDescription[] | ||
38 | - * 방 목록을 요청합니다. | ||
39 | - */ | ||
40 | -export class RoomListRequestMessage implements Message { | ||
41 | - readonly type = MessageType.ROOM_LIST_REQUEST; | ||
42 | - constructor() {} | ||
43 | -} | ||
44 | - | ||
45 | -/** | ||
46 | - * 클라 -> 서버 -> RoomInfo | ||
47 | - * 방에 접속합니다. | ||
48 | - */ | ||
49 | -export class RoomJoinMessage implements Message { | ||
50 | - readonly type = MessageType.ROOM_JOIN; | ||
51 | - constructor(public uuid: string) {} | ||
52 | -} | ||
53 | - | ||
54 | -/** | ||
55 | - * 클라 -> 서버 | ||
56 | - * 방에서 나갑니다. | ||
57 | - */ | ||
58 | -export class RoomLeaveMessage implements Message { | ||
59 | - readonly type = MessageType.ROOM_LEAVE; | ||
60 | - constructor() {} | ||
61 | -} | ||
62 | - | ||
63 | -/** | ||
64 | - * 클라 <- 서버 | ||
65 | - * 접속한 방에 새로운 유저가 들어오거나 나갈 때 전송됩니다. | ||
66 | - * @param state 유저가 입장하면 added, 퇴장하면 removed 값을 가집니다. | ||
67 | - * @param userdata 대상 유저입니다. | ||
68 | - */ | ||
69 | -export class RoomUserUpdateMessage implements Message { | ||
70 | - readonly type = MessageType.ROOM_USER_UPDATE; | ||
71 | - constructor( | ||
72 | - public state: "added" | "updated" | "removed", | ||
73 | - public userdata: UserData | ||
74 | - ) {} | ||
75 | -} | ||
76 | - | ||
77 | -/** | ||
78 | - * 클라 <-> 서버 | ||
79 | - * 접속한 방에서 채팅을 보내거나 받을 때 사용됩니다. 자신이 보낸 채팅은 서버에 의해 수신되지 않습니다. | ||
80 | - * @param message 메세지 내용입니다. | ||
81 | - * @param sender 채팅을 보낸 유저의 username입니다. 채팅이 클라이언트로 수신 될 경우에만 값을 가집니다. | ||
82 | - */ | ||
83 | -export class RoomChatMessage implements Message { | ||
84 | - readonly type = MessageType.ROOM_CHAT; | ||
85 | - constructor(public message: string, public sender?: string) {} | ||
86 | -} | ||
87 | - | ||
88 | -/** | ||
89 | - * 클라 <- 서버 | ||
90 | - * 라운드가 시작되었음을 알립니다. | ||
91 | - * @param round 현재 라운드 넘버입니다. (1부터 시작) | ||
92 | - * @param duration 초 단위의 라운드 시간입니다. | ||
93 | - * @param roles 모든 방 접속 인원의 역할입니다. | ||
94 | - */ | ||
95 | -export class RoundStartMessage implements Message { | ||
96 | - readonly type = MessageType.ROUND_START; | ||
97 | - constructor( | ||
98 | - public round: number, | ||
99 | - public duration: number, | ||
100 | - public roles: { | ||
101 | - username: string; | ||
102 | - role: "drawer" | "guesser" | "winner" | "spectator"; | ||
103 | - }[] | ||
104 | - ) {} | ||
105 | -} | ||
106 | - | ||
107 | -/** | ||
108 | - * 클라 <- 서버 | ||
109 | - * 라운드 시작시에 오직 drawer에게만 전송되는 메세지로, drawer가 선택할 수 있는 단어들입니다. | ||
110 | - */ | ||
111 | -export class RoundWordSetMessage implements Message { | ||
112 | - readonly type = MessageType.ROUND_WORD_SET; | ||
113 | - constructor(public words: string[]) {} | ||
114 | -} | ||
115 | - | ||
116 | -/** | ||
117 | - * 클라 -> 서버 | ||
118 | - * drawer가 단어를 선택하면 해당 메세지가 서버로 전송됩니다. | ||
119 | - * @param word RoundWordSetMessage에서 수신받은 단어 중 drawer가 선택한 단어입니다. | ||
120 | - */ | ||
121 | -export class RoundChooseWordMessage implements Message { | ||
122 | - readonly type = MessageType.ROUND_CHOOSE_WORD; | ||
123 | - constructor(public word: string) {} | ||
124 | -} | ||
125 | - | ||
126 | -/** | ||
127 | - * 클라 <- 서버 | ||
128 | - * drawer가 단어를 선택하였음을 알립니다. | ||
129 | - * @param length 정답 단어의 길이입니다. | ||
130 | - */ | ||
131 | -export class RoundWordChosenMessage implements Message { | ||
132 | - readonly type = MessageType.ROUND_WORD_CHOSEN; | ||
133 | - constructor(public length: number) {} | ||
134 | -} | ||
135 | - | ||
136 | -/** | ||
137 | - * 클라 <- 서버 | ||
138 | - * 서버가 클라이언트의 타이머를 동기화하기 위해 사용됩니다. 라운드가 시작하면 타이머는 초기화되며 기본 상태는 stopped입니다. | ||
139 | - * @param state 타이머의 동작, 정지 여부입니다. | ||
140 | - * @param time 타이머의 남은 시간입니다. 초 단위로 주어집니다. | ||
141 | - */ | ||
142 | -export class RoundTimerMessage implements Message { | ||
143 | - readonly type = MessageType.ROUND_TIMER; | ||
144 | - constructor(public state: "started" | "stopped", public time: number) {} | ||
145 | -} | ||
146 | - | ||
147 | -/** | ||
148 | - * 클라 <- 서버 | ||
149 | - * 라운드가 종료되었음을 알립니다. | ||
150 | - * @param answer 이번 라운드의 정답입니다. | ||
151 | - */ | ||
152 | -export class RoundFinishMessage implements Message { | ||
153 | - readonly type = MessageType.ROUND_FINISH; | ||
154 | - constructor(public answer: string) {} | ||
155 | -} | ||
156 | - | ||
157 | -/** | ||
158 | - * 클라 <- 서버 | ||
159 | - * 플레이어의 역할이 바뀌었음을 알립니다. | ||
160 | - * @param username 대상 유저의 username입니다. | ||
161 | - * @param role 대상 유저의 새로운 역할입니다. | ||
162 | - */ | ||
163 | -export class RoundRoleMessage implements Message { | ||
164 | - readonly type = MessageType.ROUND_ROLE; | ||
165 | - constructor( | ||
166 | - public username: string, | ||
167 | - public role: "drawer" | "guesser" | "winner" | "spectator" | ||
168 | - ) {} | ||
169 | -} | ||
170 | - | ||
171 | -/** | ||
172 | - * 클라 <- 서버 | ||
173 | - * 플레이어가 정답을 맞췄음을 알립니다. | ||
174 | - * @param answer 이번 라운드의 정답입니다. | ||
175 | - */ | ||
176 | -export class AnswerAcceptedMessage implements Message { | ||
177 | - readonly type = MessageType.ANSWER_ACCEPTED; | ||
178 | - constructor(public answer: string) {} | ||
179 | -} | ||
180 | - | ||
181 | -/** | ||
182 | - * 클라 <- 서버 | ||
183 | - * 게임이 종료되었음을 알립니다. 다시 준비 화면으로 돌아갑니다. | ||
184 | - */ | ||
185 | -export class GameFinishMessage implements Message { | ||
186 | - readonly type = MessageType.GAME_FINISH; | ||
187 | - constructor() {} | ||
188 | -} | ||
189 | - | ||
190 | -/** | ||
191 | - * 클라 <-> 서버 | ||
192 | - * 브러시 설정을 동기화합니다. drawer는 메세지를 서버에 보내고, 나머지 플레이어들은 서버에서 수신받습니다. | ||
193 | - * @param size 픽셀 단위의 브러시 지름입니다. | ||
194 | - * @param color 6자리 소문자 16진수로 표현된 브러시 색상입니다. | ||
195 | - * @param drawing 현재 브러시가 캔버스에 닿은 상태인지를 나타냅니다. | ||
196 | - */ | ||
197 | -export class PaintBrushMessage implements Message { | ||
198 | - readonly type = MessageType.PAINT_BRUSH; | ||
199 | - constructor( | ||
200 | - public size: number, | ||
201 | - public color: string, | ||
202 | - public drawing: boolean | ||
203 | - ) {} | ||
204 | -} | ||
205 | - | ||
206 | -/** | ||
207 | - * 클라 <-> 서버 | ||
208 | - * 브러시 위치를 동기화합니다. drawer는 메세지를 서버에 보내고, 나머지 플레이어들은 서버에서 수신받습니다. | ||
209 | - * 왼쪽 하단이 원점입니다. | ||
210 | - * @param x 픽셀 단위의 가로 위치입니다. | ||
211 | - * @param y 픽셀 단위의 세로 위치입니다. | ||
212 | - */ | ||
213 | -export class PaintMoveMessage implements Message { | ||
214 | - readonly type = MessageType.PAINT_MOVE; | ||
215 | - constructor(public x: number, public y: number) {} | ||
216 | -} | ||
217 | - | ||
218 | -export class MessageType { | ||
219 | - static readonly LOGIN = "login"; | ||
220 | - static readonly ROOM_LIST_REQUEST = "room_list_request"; | ||
221 | - static readonly ROOM_JOIN = "room_join"; | ||
222 | - static readonly ROOM_LEAVE = "room_leave"; | ||
223 | - static readonly ROOM_USER_UPDATE = "room_user_update"; | ||
224 | - static readonly ROOM_CHAT = "room_chat"; | ||
225 | - static readonly ROUND_START = "round_start"; | ||
226 | - static readonly ROUND_TIMER = "round_timer"; | ||
227 | - static readonly ROUND_FINISH = "round_finish"; | ||
228 | - static readonly ROUND_ROLE = "round_role"; | ||
229 | - static readonly ANSWER_ACCEPTED = "answer_accepted"; | ||
230 | - static readonly GAME_FINISH = "game_finish"; | ||
231 | - static readonly ROUND_WORD_SET = "round_word_set"; | ||
232 | - static readonly ROUND_CHOOSE_WORD = "round_choose_word"; | ||
233 | - static readonly ROUND_WORD_CHOSEN = "round_word_chosen"; | ||
234 | - static readonly PAINT_BRUSH = "paint_brush"; | ||
235 | - static readonly PAINT_MOVE = "paint_move"; | ||
236 | -} |
1 | import { Connection } from "../connection/Connection"; | 1 | import { Connection } from "../connection/Connection"; |
2 | import { v4 as uuidv4 } from "uuid"; | 2 | import { v4 as uuidv4 } from "uuid"; |
3 | -import { RoomDescription, RoomInfo } from "./types"; | ||
4 | -import { | ||
5 | - Message, | ||
6 | - RoomChatMessage, | ||
7 | - RoomUserUpdateMessage, | ||
8 | -} from "../message/types"; | ||
9 | -import { UserData } from "../user/types"; | ||
10 | import { User } from "../user/User"; | 3 | import { User } from "../user/User"; |
4 | +import { MessageHandlerChain } from "../message/MessageHandlerChain"; | ||
5 | +import { MessageHandler } from "../message/MessageHandler"; | ||
6 | +import { | ||
7 | + ServerInboundMessage, | ||
8 | + ServerOutboundMessage, | ||
9 | + ServerOutboundMessageKey, | ||
10 | +} from "../../common"; | ||
11 | +import { RoomDescription, RoomInfo, UserData } from "../../common/dataType"; | ||
11 | 12 | ||
12 | export class Room { | 13 | export class Room { |
13 | public readonly uuid: string; | 14 | public readonly uuid: string; |
... | @@ -15,14 +16,27 @@ export class Room { | ... | @@ -15,14 +16,27 @@ export class Room { |
15 | public name: string; | 16 | public name: string; |
16 | public readonly maxUsers: number; | 17 | public readonly maxUsers: number; |
17 | 18 | ||
18 | - private users: User[] = []; | 19 | + public users: User[] = []; |
19 | 20 | ||
20 | private closed: boolean = false; | 21 | private closed: boolean = false; |
21 | 22 | ||
23 | + public handler: MessageHandler; | ||
24 | + | ||
22 | constructor(name: string, maxUsers: number = 8) { | 25 | constructor(name: string, maxUsers: number = 8) { |
23 | this.uuid = uuidv4(); | 26 | this.uuid = uuidv4(); |
24 | this.name = name; | 27 | this.name = name; |
25 | this.maxUsers = maxUsers; | 28 | this.maxUsers = maxUsers; |
29 | + | ||
30 | + this.handler = new MessageHandler({ | ||
31 | + chat: (user, message) => { | ||
32 | + this.sendChat(user, message.message); | ||
33 | + return { ok: true }; | ||
34 | + }, | ||
35 | + leaveRoom: (user, message) => { | ||
36 | + this.disconnect(user); | ||
37 | + return { ok: true }; | ||
38 | + }, | ||
39 | + }); | ||
26 | } | 40 | } |
27 | 41 | ||
28 | public connect(user: User): void { | 42 | public connect(user: User): void { |
... | @@ -30,10 +44,10 @@ export class Room { | ... | @@ -30,10 +44,10 @@ export class Room { |
30 | return; | 44 | return; |
31 | } | 45 | } |
32 | 46 | ||
33 | - this.broadcast(new RoomUserUpdateMessage("added", user.getData())); | 47 | + this.broadcast("updateRoomUser", { state: "added", user: user.getData() }); |
34 | 48 | ||
35 | this.users.push(user); | 49 | this.users.push(user); |
36 | - user.room = this; // TODO: 더 나은 관리 | 50 | + user.room = this; |
37 | } | 51 | } |
38 | 52 | ||
39 | public disconnect(user: User): void { | 53 | public disconnect(user: User): void { |
... | @@ -42,12 +56,15 @@ export class Room { | ... | @@ -42,12 +56,15 @@ export class Room { |
42 | this.users.splice(index, 1); | 56 | this.users.splice(index, 1); |
43 | user.room = undefined; | 57 | user.room = undefined; |
44 | 58 | ||
45 | - this.broadcast(new RoomUserUpdateMessage("removed", user.getData())); | 59 | + this.broadcast("updateRoomUser", { |
60 | + state: "removed", | ||
61 | + user: user.getData(), | ||
62 | + }); | ||
46 | } | 63 | } |
47 | } | 64 | } |
48 | 65 | ||
49 | public sendChat(user: User, message: string): void { | 66 | public sendChat(user: User, message: string): void { |
50 | - this.broadcast(new RoomChatMessage(message, user.username), user); | 67 | + this.broadcast("chat", { sender: user.username, message: message }); |
51 | } | 68 | } |
52 | 69 | ||
53 | public getDescription(): RoomDescription { | 70 | public getDescription(): RoomDescription { |
... | @@ -69,10 +86,14 @@ export class Room { | ... | @@ -69,10 +86,14 @@ export class Room { |
69 | }; | 86 | }; |
70 | } | 87 | } |
71 | 88 | ||
72 | - public broadcast(message: Message, except?: User): void { | 89 | + public broadcast<T extends ServerOutboundMessageKey>( |
90 | + type: T, | ||
91 | + message: ServerOutboundMessage<T>, | ||
92 | + except?: User | ||
93 | + ): void { | ||
73 | this.users.forEach((u) => { | 94 | this.users.forEach((u) => { |
74 | if (u !== except) { | 95 | if (u !== except) { |
75 | - u.connection.send(message); | 96 | + u.connection.send(type, message); |
76 | } | 97 | } |
77 | }); | 98 | }); |
78 | } | 99 | } | ... | ... |
1 | +import { RoomDescription } from "../../common/dataType"; | ||
1 | import { Room } from "./Room"; | 2 | import { Room } from "./Room"; |
2 | -import { RoomDescription } from "./types"; | ||
3 | 3 | ||
4 | export class RoomManager { | 4 | export class RoomManager { |
5 | private static _instance: RoomManager; | 5 | private static _instance: RoomManager; | ... | ... |
1 | import ioclient, { Socket } from "socket.io-client"; | 1 | import ioclient, { Socket } from "socket.io-client"; |
2 | -import { | ||
3 | - LoginMessage, | ||
4 | - MessageResponse, | ||
5 | - MessageType, | ||
6 | - RoomChatMessage, | ||
7 | - RoomJoinMessage, | ||
8 | - RoomLeaveMessage, | ||
9 | - RoomListRequestMessage, | ||
10 | - RoomUserUpdateMessage, | ||
11 | -} from "./message/types"; | ||
12 | import { expect } from "chai"; | 2 | import { expect } from "chai"; |
13 | import { Server } from "./Server"; | 3 | import { Server } from "./Server"; |
14 | -import { RoomDescription, RoomInfo } from "./room/types"; | ||
15 | import { response } from "express"; | 4 | import { response } from "express"; |
5 | +import { | ||
6 | + RawMessage, | ||
7 | + ServerInboundMessage, | ||
8 | + ServerInboundMessageKey, | ||
9 | + ServerOutboundMessage, | ||
10 | + ServerOutboundMessageKey, | ||
11 | + ServerResponse, | ||
12 | +} from "../common"; | ||
16 | 13 | ||
17 | describe("server", () => { | 14 | describe("server", () => { |
18 | const PORT = 3000; | 15 | const PORT = 3000; |
... | @@ -39,39 +36,58 @@ describe("server", () => { | ... | @@ -39,39 +36,58 @@ describe("server", () => { |
39 | client2.close(); | 36 | client2.close(); |
40 | }); | 37 | }); |
41 | 38 | ||
42 | - var roomUserUpdateMessage: RoomUserUpdateMessage; | 39 | + var roomUserUpdateMessage: ServerOutboundMessage<"updateRoomUser">; |
43 | - var roomChatMessage: RoomChatMessage; | 40 | + var roomChatMessage: ServerOutboundMessage<"chat">; |
41 | + | ||
42 | + const send = <T extends ServerInboundMessageKey>( | ||
43 | + socket: Socket, | ||
44 | + type: T, | ||
45 | + message: ServerInboundMessage<T>, | ||
46 | + callback: (response: ServerResponse<T>) => void | ||
47 | + ) => { | ||
48 | + socket.emit( | ||
49 | + "msg", | ||
50 | + { | ||
51 | + type: type as string, | ||
52 | + message: message, | ||
53 | + }, | ||
54 | + callback | ||
55 | + ); | ||
56 | + }; | ||
44 | 57 | ||
45 | step("register listeners", () => { | 58 | step("register listeners", () => { |
46 | - client1.on( | 59 | + client1.on("msg", (raw: RawMessage) => { |
47 | - MessageType.ROOM_USER_UPDATE, | 60 | + if (raw.type == "updateRoomUser") roomUserUpdateMessage = raw.message; |
48 | - (message: RoomUserUpdateMessage) => { | 61 | + }); |
49 | - roomUserUpdateMessage = message; | ||
50 | - } | ||
51 | - ); | ||
52 | 62 | ||
53 | - client1.on(MessageType.ROOM_CHAT, (message: RoomChatMessage) => { | 63 | + client1.on("msg", (raw: RawMessage) => { |
54 | - roomChatMessage = message; | 64 | + if (raw.type == "chat") roomChatMessage = raw.message; |
55 | }); | 65 | }); |
56 | }); | 66 | }); |
57 | 67 | ||
58 | step("login 1", (done) => { | 68 | step("login 1", (done) => { |
59 | - client1.emit( | 69 | + send( |
60 | - MessageType.LOGIN, | 70 | + client1, |
61 | - new LoginMessage("guest1"), | 71 | + "login", |
62 | - (response: MessageResponse<undefined>) => { | 72 | + { |
63 | - expect(response.ok).to.equals(true); | 73 | + username: "guest1", |
74 | + }, | ||
75 | + (response) => { | ||
76 | + expect(response.ok).to.eq(true); | ||
64 | done(); | 77 | done(); |
65 | } | 78 | } |
66 | ); | 79 | ); |
67 | }); | 80 | }); |
68 | 81 | ||
69 | step("login 2", (done) => { | 82 | step("login 2", (done) => { |
70 | - client2.emit( | 83 | + send( |
71 | - MessageType.LOGIN, | 84 | + client2, |
72 | - new LoginMessage("guest2"), | 85 | + "login", |
73 | - (response: MessageResponse<undefined>) => { | 86 | + { |
74 | - expect(response.ok).to.equals(true); | 87 | + username: "guest2", |
88 | + }, | ||
89 | + (response) => { | ||
90 | + expect(response.ok).to.eq(true); | ||
75 | done(); | 91 | done(); |
76 | } | 92 | } |
77 | ); | 93 | ); |
... | @@ -80,52 +96,40 @@ describe("server", () => { | ... | @@ -80,52 +96,40 @@ describe("server", () => { |
80 | var roomToJoin: string; | 96 | var roomToJoin: string; |
81 | 97 | ||
82 | step("room list", (done) => { | 98 | step("room list", (done) => { |
83 | - client1.emit( | 99 | + send(client1, "roomList", {}, (response) => { |
84 | - MessageType.ROOM_LIST_REQUEST, | 100 | + expect(response.ok).to.eq(true); |
85 | - new RoomListRequestMessage(), | 101 | + expect(response.result !== undefined).to.eq(true); |
86 | - (response: MessageResponse<RoomDescription[]>) => { | 102 | + if (response.result) { |
87 | - expect(response.ok).to.eq(true); | 103 | + expect(response.result[0].name).to.eq("테스트 방 #1"); |
88 | - expect(response.result !== undefined).to.eq(true); | 104 | + roomToJoin = response.result[0].uuid; |
89 | - if (response.result) { | ||
90 | - expect(response.result[0].name).to.eq("테스트 방 #1"); | ||
91 | - roomToJoin = response.result[0].uuid; | ||
92 | - } | ||
93 | - done(); | ||
94 | } | 105 | } |
95 | - ); | 106 | + done(); |
107 | + }); | ||
96 | }); | 108 | }); |
97 | 109 | ||
98 | step("room join 1", (done) => { | 110 | step("room join 1", (done) => { |
99 | - client1.emit( | 111 | + send(client1, "joinRoom", { uuid: roomToJoin }, (response) => { |
100 | - MessageType.ROOM_JOIN, | 112 | + expect(response.ok).to.eq(true); |
101 | - new RoomJoinMessage(roomToJoin), | 113 | + expect(response.result !== undefined).to.eq(true); |
102 | - (response: MessageResponse<RoomInfo>) => { | 114 | + if (response.result) { |
103 | - expect(response.ok).to.eq(true); | 115 | + expect(response.result.uuid).to.eq(roomToJoin); |
104 | - expect(response.result !== undefined).to.eq(true); | 116 | + expect(response.result.users.length).to.eq(1); |
105 | - if (response.result) { | 117 | + expect(response.result.users[0].username).to.eq("guest1"); |
106 | - expect(response.result.uuid).to.eq(roomToJoin); | ||
107 | - expect(response.result.users.length).to.eq(1); | ||
108 | - expect(response.result.users[0].username).to.eq("guest1"); | ||
109 | - } | ||
110 | - done(); | ||
111 | } | 118 | } |
112 | - ); | 119 | + done(); |
120 | + }); | ||
113 | }); | 121 | }); |
114 | 122 | ||
115 | step("room join 2", (done) => { | 123 | step("room join 2", (done) => { |
116 | - client2.emit( | 124 | + send(client2, "joinRoom", { uuid: roomToJoin }, (response) => { |
117 | - MessageType.ROOM_JOIN, | 125 | + expect(response.ok).to.eq(true); |
118 | - new RoomJoinMessage(roomToJoin), | 126 | + expect(response.result !== undefined).to.eq(true); |
119 | - (response: MessageResponse<RoomInfo>) => { | 127 | + if (response.result) { |
120 | - expect(response.ok).to.eq(true); | 128 | + expect(response.result.uuid).to.eq(roomToJoin); |
121 | - expect(response.result !== undefined).to.eq(true); | 129 | + expect(response.result.users.length).to.eq(2); |
122 | - if (response.result) { | ||
123 | - expect(response.result.uuid).to.eq(roomToJoin); | ||
124 | - expect(response.result.users.length).to.eq(2); | ||
125 | - } | ||
126 | - done(); | ||
127 | } | 130 | } |
128 | - ); | 131 | + done(); |
132 | + }); | ||
129 | }); | 133 | }); |
130 | 134 | ||
131 | // TODO: RoomUserUpdateMessage가 아직 도착하지 않았는데 실행되는 경우 | 135 | // TODO: RoomUserUpdateMessage가 아직 도착하지 않았는데 실행되는 경우 |
... | @@ -133,19 +137,15 @@ describe("server", () => { | ... | @@ -133,19 +137,15 @@ describe("server", () => { |
133 | expect(roomUserUpdateMessage !== undefined).to.eq(true); | 137 | expect(roomUserUpdateMessage !== undefined).to.eq(true); |
134 | if (roomUserUpdateMessage) { | 138 | if (roomUserUpdateMessage) { |
135 | expect(roomUserUpdateMessage.state).to.eq("added"); | 139 | expect(roomUserUpdateMessage.state).to.eq("added"); |
136 | - expect(roomUserUpdateMessage.userdata.username).to.eq("guest2"); | 140 | + expect(roomUserUpdateMessage.user.username).to.eq("guest2"); |
137 | } | 141 | } |
138 | }); | 142 | }); |
139 | 143 | ||
140 | step("client 2 send chat", (done) => { | 144 | step("client 2 send chat", (done) => { |
141 | - client2.emit( | 145 | + send(client2, "chat", { message: "Hello World" }, (response) => { |
142 | - MessageType.ROOM_CHAT, | 146 | + expect(response.ok).to.eq(true); |
143 | - new RoomChatMessage("Hello World"), | 147 | + done(); |
144 | - (response: MessageResponse<undefined>) => { | 148 | + }); |
145 | - expect(response.ok).to.eq(true); | ||
146 | - done(); | ||
147 | - } | ||
148 | - ); | ||
149 | }); | 149 | }); |
150 | 150 | ||
151 | step("client 1 received chat", () => { | 151 | step("client 1 received chat", () => { |
... | @@ -157,21 +157,17 @@ describe("server", () => { | ... | @@ -157,21 +157,17 @@ describe("server", () => { |
157 | }); | 157 | }); |
158 | 158 | ||
159 | step("client 2 leave", (done) => { | 159 | step("client 2 leave", (done) => { |
160 | - client2.emit( | 160 | + send(client2, "leaveRoom", {}, (response) => { |
161 | - MessageType.ROOM_LEAVE, | 161 | + expect(response.ok).to.eq(true); |
162 | - new RoomLeaveMessage(), | 162 | + done(); |
163 | - (response: MessageResponse<undefined>) => { | 163 | + }); |
164 | - expect(response.ok).to.eq(true); | ||
165 | - done(); | ||
166 | - } | ||
167 | - ); | ||
168 | }); | 164 | }); |
169 | 165 | ||
170 | step("client 1 received user update", () => { | 166 | step("client 1 received user update", () => { |
171 | expect(roomUserUpdateMessage !== undefined).to.eq(true); | 167 | expect(roomUserUpdateMessage !== undefined).to.eq(true); |
172 | if (roomUserUpdateMessage) { | 168 | if (roomUserUpdateMessage) { |
173 | expect(roomUserUpdateMessage.state).to.eq("removed"); | 169 | expect(roomUserUpdateMessage.state).to.eq("removed"); |
174 | - expect(roomUserUpdateMessage.userdata.username).to.eq("guest2"); | 170 | + expect(roomUserUpdateMessage.user.username).to.eq("guest2"); |
175 | } | 171 | } |
176 | }); | 172 | }); |
177 | }); | 173 | }); | ... | ... |
... | @@ -4,8 +4,8 @@ | ... | @@ -4,8 +4,8 @@ |
4 | 4 | ||
5 | /* Basic Options */ | 5 | /* Basic Options */ |
6 | // "incremental": true, /* Enable incremental compilation */ | 6 | // "incremental": true, /* Enable incremental compilation */ |
7 | - "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ | 7 | + "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, |
8 | - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ | 8 | + "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, |
9 | // "lib": [], /* Specify library files to be included in the compilation. */ | 9 | // "lib": [], /* Specify library files to be included in the compilation. */ |
10 | // "allowJs": true, /* Allow javascript files to be compiled. */ | 10 | // "allowJs": true, /* Allow javascript files to be compiled. */ |
11 | // "checkJs": true, /* Report errors in .js files. */ | 11 | // "checkJs": true, /* Report errors in .js files. */ |
... | @@ -25,7 +25,7 @@ | ... | @@ -25,7 +25,7 @@ |
25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ | 25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ |
26 | 26 | ||
27 | /* Strict Type-Checking Options */ | 27 | /* Strict Type-Checking Options */ |
28 | - "strict": true, /* Enable all strict type-checking options. */ | 28 | + "strict": true /* Enable all strict type-checking options. */, |
29 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ | 29 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ |
30 | // "strictNullChecks": true, /* Enable strict null checks. */ | 30 | // "strictNullChecks": true, /* Enable strict null checks. */ |
31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ | 31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ |
... | @@ -50,7 +50,7 @@ | ... | @@ -50,7 +50,7 @@ |
50 | // "typeRoots": [], /* List of folders to include type definitions from. */ | 50 | // "typeRoots": [], /* List of folders to include type definitions from. */ |
51 | // "types": [], /* Type declaration files to be included in compilation. */ | 51 | // "types": [], /* Type declaration files to be included in compilation. */ |
52 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ | 52 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ |
53 | - "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ | 53 | + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, |
54 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ | 54 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ |
55 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ | 55 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ |
56 | 56 | ||
... | @@ -65,7 +65,12 @@ | ... | @@ -65,7 +65,12 @@ |
65 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ | 65 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ |
66 | 66 | ||
67 | /* Advanced Options */ | 67 | /* Advanced Options */ |
68 | - "skipLibCheck": true, /* Skip type checking of declaration files. */ | 68 | + "skipLibCheck": true /* Skip type checking of declaration files. */, |
69 | - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ | 69 | + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ |
70 | - } | 70 | + }, |
71 | + "references": [ | ||
72 | + { | ||
73 | + "path": "../common" | ||
74 | + } | ||
75 | + ] | ||
71 | } | 76 | } | ... | ... |
1 | +import { UserData } from "../../common/dataType"; | ||
1 | import { Connection } from "../connection/Connection"; | 2 | import { Connection } from "../connection/Connection"; |
3 | +import { MessageHandler } from "../message/MessageHandler"; | ||
2 | import { Room } from "../room/Room"; | 4 | import { Room } from "../room/Room"; |
3 | -import { UserData } from "./types"; | 5 | +import { RoomManager } from "../room/RoomManager"; |
4 | 6 | ||
5 | export class User { | 7 | export class User { |
6 | public readonly username: string; | 8 | public readonly username: string; |
... | @@ -9,9 +11,28 @@ export class User { | ... | @@ -9,9 +11,28 @@ export class User { |
9 | 11 | ||
10 | public room?: Room; | 12 | public room?: Room; |
11 | 13 | ||
14 | + public handler: MessageHandler; | ||
15 | + | ||
12 | constructor(username: string, connection: Connection) { | 16 | constructor(username: string, connection: Connection) { |
13 | this.username = username; | 17 | this.username = username; |
14 | this.connection = connection; | 18 | this.connection = connection; |
19 | + this.handler = new MessageHandler({ | ||
20 | + roomList: (user, message) => { | ||
21 | + return { ok: true, result: RoomManager.instance().list() }; | ||
22 | + }, | ||
23 | + joinRoom: (user, message) => { | ||
24 | + const room = RoomManager.instance().get(message.uuid); | ||
25 | + if (user.room || !room) { | ||
26 | + return { ok: false }; | ||
27 | + } | ||
28 | + // TODO: 방 접속 실패 처리 | ||
29 | + room.connect(user); | ||
30 | + if (user.room === undefined) { | ||
31 | + return { ok: false }; | ||
32 | + } | ||
33 | + return { ok: true, result: room.getInfo() }; | ||
34 | + }, | ||
35 | + }); | ||
15 | } | 36 | } |
16 | 37 | ||
17 | public getData(): UserData { | 38 | public getData(): UserData { | ... | ... |
server/user/types.ts
deleted
100644 → 0
1 | { | 1 | { |
2 | "compilerOptions": { | 2 | "compilerOptions": { |
3 | "target": "es5", | 3 | "target": "es5", |
4 | - "lib": [ | 4 | + "lib": ["dom", "dom.iterable", "esnext"], |
5 | - "dom", | ||
6 | - "dom.iterable", | ||
7 | - "esnext" | ||
8 | - ], | ||
9 | "allowJs": true, | 5 | "allowJs": true, |
10 | "skipLibCheck": true, | 6 | "skipLibCheck": true, |
11 | "esModuleInterop": true, | 7 | "esModuleInterop": true, |
... | @@ -20,7 +16,10 @@ | ... | @@ -20,7 +16,10 @@ |
20 | "noEmit": true, | 16 | "noEmit": true, |
21 | "jsx": "react-jsx" | 17 | "jsx": "react-jsx" |
22 | }, | 18 | }, |
23 | - "include": [ | 19 | + "include": ["src"], |
24 | - "src" | 20 | + "references": [ |
21 | + { | ||
22 | + "path": "../common" | ||
23 | + } | ||
25 | ] | 24 | ] |
26 | } | 25 | } | ... | ... |
-
Please register or login to post a comment