강동현

메세지 핸들러, 전송 재작성

1 import { Socket } from "socket.io"; 1 import { Socket } from "socket.io";
2 -import { ServerOutboundMessageMap } from "../../common"; 2 +import { ServerOutboundMessage, ServerOutboundMessageMap } from "../../common";
3 -import { loginHandler } from "../message/handler/loginHandler";
4 import { MessageHandlerChain } from "../message/MessageHandlerChain"; 3 import { MessageHandlerChain } from "../message/MessageHandlerChain";
5 import { Room } from "../room/Room"; 4 import { Room } from "../room/Room";
6 import { User } from "../user/User"; 5 import { User } from "../user/User";
...@@ -19,7 +18,7 @@ export class Connection { ...@@ -19,7 +18,7 @@ export class Connection {
19 18
20 public send<T extends keyof ServerOutboundMessageMap>( 19 public send<T extends keyof ServerOutboundMessageMap>(
21 type: T, 20 type: T,
22 - message: ServerOutboundMessageMap[T] 21 + message: ServerOutboundMessage<T>
23 ) { 22 ) {
24 this.socket.emit(type as string, message); 23 this.socket.emit(type as string, message);
25 } 24 }
......
...@@ -4,33 +4,31 @@ import { ...@@ -4,33 +4,31 @@ import {
4 ServerInboundMessageMap, 4 ServerInboundMessageMap,
5 ServerResponse, 5 ServerResponse,
6 } from "../../common/index"; 6 } from "../../common/index";
7 +import { User } from "../user/User";
7 8
8 -type ServerHandlerMap<T> = { 9 +type UserHandlerMap = {
9 [Key in keyof ServerInboundMessageMap]?: ( 10 [Key in keyof ServerInboundMessageMap]?: (
10 - connection: Connection, 11 + user: User,
11 - message: ServerInboundMessage<Key>, 12 + message: ServerInboundMessage<Key>
12 - scope: T
13 ) => ServerResponse<Key>; 13 ) => ServerResponse<Key>;
14 }; 14 };
15 15
16 -export class HandlerMap<T> { 16 +export class MessageHandler {
17 - private scope: T; 17 + private handlers: UserHandlerMap;
18 - private handlers: ServerHandlerMap<T>;
19 18
20 - constructor(scope: T, handlers: ServerHandlerMap<T>) { 19 + constructor(handlers: UserHandlerMap) {
21 - this.scope = scope;
22 this.handlers = handlers; 20 this.handlers = handlers;
23 } 21 }
24 22
25 public handle( 23 public handle(
26 type: keyof ServerInboundMessageMap, 24 type: keyof ServerInboundMessageMap,
27 - connection: Connection, 25 + user: User,
28 message: any, 26 message: any,
29 callback: Function 27 callback: Function
30 ): boolean { 28 ): boolean {
31 const handler = this.handlers[type]; 29 const handler = this.handlers[type];
32 if (!handler) return false; 30 if (!handler) return false;
33 - const response = handler(connection, message, this.scope); 31 + const response = handler(user, message);
34 callback(response); 32 callback(response);
35 return true; 33 return true;
36 } 34 }
......
1 import { Connection } from "../connection/Connection"; 1 import { Connection } from "../connection/Connection";
2 -import { ServerInboundMessageMap, ServerResponse } from "../../common/index"; 2 +import {
3 + ServerInboundMessage,
4 + ServerInboundMessageMap,
5 + ServerResponse,
6 +} from "../../common/index";
3 import { keys } from "ts-transformer-keys"; 7 import { keys } from "ts-transformer-keys";
4 -import { HandlerMap } from "./MessageHandler"; 8 +import { User } from "../user/User";
5 -import { loginHandler } from "./handler/loginHandler";
6 9
7 export class MessageHandlerChain { 10 export class MessageHandlerChain {
8 connection: Connection; 11 connection: Connection;
9 12
10 - handler: HandlerMap<undefined>;
11 -
12 constructor(connection: Connection) { 13 constructor(connection: Connection) {
13 this.connection = connection; 14 this.connection = connection;
14 - this.handler = new HandlerMap(undefined, { 15 +
15 - login: loginHandler, 16 + // 유저 정보가 없으므로 로그인은 따로 핸들링
16 - }); 17 + this.connection.socket.on(
18 + "login",
19 + (message: ServerInboundMessage<"login">, callback: Function) => {
20 + connection.user = new User(message.username, connection);
21 + console.log(`User ${message.username} has logged in!`);
22 +
23 + callback({ ok: true });
24 + }
25 + );
17 26
18 for (const key in keys<ServerInboundMessageMap>()) { 27 for (const key in keys<ServerInboundMessageMap>()) {
19 const type = key as keyof ServerInboundMessageMap; 28 const type = key as keyof ServerInboundMessageMap;
20 this.connection.socket.on(key, (message: any, callback: Function) => { 29 this.connection.socket.on(key, (message: any, callback: Function) => {
30 + // Game > Room > User 순으로 전달
21 if ( 31 if (
22 connection?.user?.room && 32 connection?.user?.room &&
23 connection.user.room.handler.handle( 33 connection.user.room.handler.handle(
24 type, 34 type,
25 - connection, 35 + connection.user,
26 message, 36 message,
27 callback 37 callback
28 ) 38 )
...@@ -31,11 +41,14 @@ export class MessageHandlerChain { ...@@ -31,11 +41,14 @@ export class MessageHandlerChain {
31 41
32 if ( 42 if (
33 connection?.user && 43 connection?.user &&
34 - connection.user.handler.handle(type, connection, message, callback) 44 + connection.user.handler.handle(
45 + type,
46 + connection.user,
47 + message,
48 + callback
49 + )
35 ) 50 )
36 return; 51 return;
37 -
38 - this.handler.handle(type, connection, message, callback);
39 }); 52 });
40 } 53 }
41 } 54 }
......
1 -import { ServerInboundMessageMap, ServerResponse } from "../../../common";
2 -import { Connection } from "../../connection/Connection";
3 -import { RoomManager } from "../../room/RoomManager";
4 -import { User } from "../../user/User";
5 -
6 -export function loginHandler(
7 - connection: Connection,
8 - message: ServerInboundMessageMap["login"],
9 - scope: undefined
10 -): ServerResponse<"login"> {
11 - connection.user = new User(message.username, connection);
12 - console.log(`User ${message.username} has logged in!`);
13 -
14 - return { ok: true };
15 -}
1 -import { ServerInboundMessage, ServerResponse } from "../../../common";
2 -import { Connection } from "../../connection/Connection";
3 -import { Room } from "../../room/Room";
4 -import { RoomManager } from "../../room/RoomManager";
5 -import { User } from "../../user/User";
6 -
7 -export function chatHandler(
8 - connection: Connection,
9 - message: ServerInboundMessage<"chat">,
10 - scope: Room
11 -): ServerResponse<"chat"> {
12 - scope.sendChat(user, message.message);
13 - return { ok: true };
14 -}
1 -import { ServerInboundMessage, ServerResponse } from "../../../common";
2 -import { Connection } from "../../connection/Connection";
3 -import { Room } from "../../room/Room";
4 -import { RoomManager } from "../../room/RoomManager";
5 -import { User } from "../../user/User";
6 -
7 -export function roomJoinHandler(
8 - connection: Connection,
9 - message: ServerInboundMessage<"joinRoom">,
10 - scope: Room
11 -): ServerResponse<"joinRoom"> {
12 - const room = RoomManager.instance().get(message.uuid);
13 - if (room !== undefined) {
14 - room.connect(user);
15 - return { ok: user.room !== undefined, result: user.room?.getInfo() };
16 - }
17 - return { ok: false };
18 -}
1 -import { ServerInboundMessage, ServerResponse } from "../../../common";
2 -import { Connection } from "../../connection/Connection";
3 -import { Room } from "../../room/Room";
4 -import { RoomManager } from "../../room/RoomManager";
5 -import { User } from "../../user/User";
6 -
7 -export function roomLeaveHandler(
8 - connection: Connection,
9 - message: ServerInboundMessage<"leaveRoom">,
10 - scope: Room
11 -): ServerResponse<"leaveRoom"> {
12 - user.room?.disconnect(user);
13 - return { ok: true };
14 -}
1 -import {
2 - ServerInboundMessage,
3 - ServerInboundMessageMap,
4 - ServerResponse,
5 -} from "../../../common";
6 -import { RoomDescription } from "../../../common/dataType";
7 -import { Connection } from "../../connection/Connection";
8 -import { RoomManager } from "../../room/RoomManager";
9 -import { User } from "../../user/User";
10 -
11 -export function roomListRequestHandler(
12 - connection: Connection,
13 - message: ServerInboundMessage<"roomList">,
14 - scope: User
15 -): ServerResponse<"roomList"> {
16 - return { ok: true, result: RoomManager.instance().list() };
17 -}
...@@ -2,10 +2,13 @@ import { Connection } from "../connection/Connection"; ...@@ -2,10 +2,13 @@ import { Connection } from "../connection/Connection";
2 import { v4 as uuidv4 } from "uuid"; 2 import { v4 as uuidv4 } from "uuid";
3 import { User } from "../user/User"; 3 import { User } from "../user/User";
4 import { MessageHandlerChain } from "../message/MessageHandlerChain"; 4 import { MessageHandlerChain } from "../message/MessageHandlerChain";
5 -import { HandlerMap } from "../message/MessageHandler"; 5 +import { MessageHandler } from "../message/MessageHandler";
6 -import { roomJoinHandler } from "../message/handler/roomJoinHandler"; 6 +import {
7 -import { roomLeaveHandler } from "../message/handler/roomLeaveHandler"; 7 + ServerInboundMessage,
8 -import { chatHandler } from "../message/handler/roomChatHandler"; 8 + ServerOutboundMessage,
9 + ServerOutboundMessageMap,
10 +} from "../../common";
11 +import { RoomDescription, RoomInfo, UserData } from "../../common/dataType";
9 12
10 export class Room { 13 export class Room {
11 public readonly uuid: string; 14 public readonly uuid: string;
...@@ -17,16 +20,22 @@ export class Room { ...@@ -17,16 +20,22 @@ export class Room {
17 20
18 private closed: boolean = false; 21 private closed: boolean = false;
19 22
20 - public handler: HandlerMap<Room>; 23 + public handler: MessageHandler;
21 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;
26 - this.handler = new HandlerMap<Room>(this, { 29 +
27 - joinRoom: roomJoinHandler, 30 + this.handler = new MessageHandler({
28 - leaveRoom: roomLeaveHandler, 31 + chat: (user, message) => {
29 - chat: chatHandler, 32 + this.sendChat(user, message.message);
33 + return { ok: true };
34 + },
35 + leaveRoom: (user, message) => {
36 + this.disconnect(user);
37 + return { ok: true };
38 + },
30 }); 39 });
31 } 40 }
32 41
...@@ -35,10 +44,10 @@ export class Room { ...@@ -35,10 +44,10 @@ export class Room {
35 return; 44 return;
36 } 45 }
37 46
38 - this.broadcast(new RoomUserUpdateMessage("added", user.getData())); 47 + this.broadcast("updateRoomUser", { state: "added", user: user.getData() });
39 48
40 this.users.push(user); 49 this.users.push(user);
41 - user.room = this; // TODO: 더 나은 관리 50 + user.room = this;
42 } 51 }
43 52
44 public disconnect(user: User): void { 53 public disconnect(user: User): void {
...@@ -47,12 +56,15 @@ export class Room { ...@@ -47,12 +56,15 @@ export class Room {
47 this.users.splice(index, 1); 56 this.users.splice(index, 1);
48 user.room = undefined; 57 user.room = undefined;
49 58
50 - this.broadcast(new RoomUserUpdateMessage("removed", user.getData())); 59 + this.broadcast("updateRoomUser", {
60 + state: "removed",
61 + user: user.getData(),
62 + });
51 } 63 }
52 } 64 }
53 65
54 public sendChat(user: User, message: string): void { 66 public sendChat(user: User, message: string): void {
55 - this.broadcast(new RoomChatMessage(message, user.username), user); 67 + this.broadcast("chat", { sender: user.username, message: message });
56 } 68 }
57 69
58 public getDescription(): RoomDescription { 70 public getDescription(): RoomDescription {
...@@ -74,10 +86,14 @@ export class Room { ...@@ -74,10 +86,14 @@ export class Room {
74 }; 86 };
75 } 87 }
76 88
77 - public broadcast(message: Message, except?: User): void { 89 + public broadcast<T extends keyof ServerOutboundMessageMap>(
90 + type: T,
91 + message: ServerOutboundMessage<T>,
92 + except?: User
93 + ): void {
78 this.users.forEach((u) => { 94 this.users.forEach((u) => {
79 if (u !== except) { 95 if (u !== except) {
80 - u.connection.send(message); 96 + u.connection.send(type, message);
81 } 97 }
82 }); 98 });
83 } 99 }
......
1 import { UserData } from "../../common/dataType"; 1 import { UserData } from "../../common/dataType";
2 import { Connection } from "../connection/Connection"; 2 import { Connection } from "../connection/Connection";
3 -import { roomListRequestHandler } from "../message/handler/roomListRequestHandler"; 3 +import { MessageHandler } from "../message/MessageHandler";
4 -import { HandlerMap } from "../message/MessageHandler";
5 import { Room } from "../room/Room"; 4 import { Room } from "../room/Room";
5 +import { RoomManager } from "../room/RoomManager";
6 6
7 export class User { 7 export class User {
8 public readonly username: string; 8 public readonly username: string;
...@@ -11,13 +11,27 @@ export class User { ...@@ -11,13 +11,27 @@ export class User {
11 11
12 public room?: Room; 12 public room?: Room;
13 13
14 - public handler: HandlerMap<User>; 14 + public handler: MessageHandler;
15 15
16 constructor(username: string, connection: Connection) { 16 constructor(username: string, connection: Connection) {
17 this.username = username; 17 this.username = username;
18 this.connection = connection; 18 this.connection = connection;
19 - this.handler = new HandlerMap<User>(this, { 19 + this.handler = new MessageHandler({
20 - roomList: roomListRequestHandler, 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 + },
21 }); 35 });
22 } 36 }
23 37
......