강동현

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

import { Socket } from "socket.io";
import { ServerOutboundMessageMap } from "../../common";
import { loginHandler } from "../message/handler/loginHandler";
import { ServerOutboundMessage, ServerOutboundMessageMap } from "../../common";
import { MessageHandlerChain } from "../message/MessageHandlerChain";
import { Room } from "../room/Room";
import { User } from "../user/User";
......@@ -19,7 +18,7 @@ export class Connection {
public send<T extends keyof ServerOutboundMessageMap>(
type: T,
message: ServerOutboundMessageMap[T]
message: ServerOutboundMessage<T>
) {
this.socket.emit(type as string, message);
}
......
......@@ -4,33 +4,31 @@ import {
ServerInboundMessageMap,
ServerResponse,
} from "../../common/index";
import { User } from "../user/User";
type ServerHandlerMap<T> = {
type UserHandlerMap = {
[Key in keyof ServerInboundMessageMap]?: (
connection: Connection,
message: ServerInboundMessage<Key>,
scope: T
user: User,
message: ServerInboundMessage<Key>
) => ServerResponse<Key>;
};
export class HandlerMap<T> {
private scope: T;
private handlers: ServerHandlerMap<T>;
export class MessageHandler {
private handlers: UserHandlerMap;
constructor(scope: T, handlers: ServerHandlerMap<T>) {
this.scope = scope;
constructor(handlers: UserHandlerMap) {
this.handlers = handlers;
}
public handle(
type: keyof ServerInboundMessageMap,
connection: Connection,
user: User,
message: any,
callback: Function
): boolean {
const handler = this.handlers[type];
if (!handler) return false;
const response = handler(connection, message, this.scope);
const response = handler(user, message);
callback(response);
return true;
}
......
import { Connection } from "../connection/Connection";
import { ServerInboundMessageMap, ServerResponse } from "../../common/index";
import {
ServerInboundMessage,
ServerInboundMessageMap,
ServerResponse,
} from "../../common/index";
import { keys } from "ts-transformer-keys";
import { HandlerMap } from "./MessageHandler";
import { loginHandler } from "./handler/loginHandler";
import { User } from "../user/User";
export class MessageHandlerChain {
connection: Connection;
handler: HandlerMap<undefined>;
constructor(connection: Connection) {
this.connection = connection;
this.handler = new HandlerMap(undefined, {
login: loginHandler,
});
// 유저 정보가 없으므로 로그인은 따로 핸들링
this.connection.socket.on(
"login",
(message: ServerInboundMessage<"login">, callback: Function) => {
connection.user = new User(message.username, connection);
console.log(`User ${message.username} has logged in!`);
callback({ ok: true });
}
);
for (const key in keys<ServerInboundMessageMap>()) {
const type = key as keyof ServerInboundMessageMap;
this.connection.socket.on(key, (message: any, callback: Function) => {
// Game > Room > User 순으로 전달
if (
connection?.user?.room &&
connection.user.room.handler.handle(
type,
connection,
connection.user,
message,
callback
)
......@@ -31,11 +41,14 @@ export class MessageHandlerChain {
if (
connection?.user &&
connection.user.handler.handle(type, connection, message, callback)
connection.user.handler.handle(
type,
connection.user,
message,
callback
)
)
return;
this.handler.handle(type, connection, message, callback);
});
}
}
......
import { ServerInboundMessageMap, ServerResponse } from "../../../common";
import { Connection } from "../../connection/Connection";
import { RoomManager } from "../../room/RoomManager";
import { User } from "../../user/User";
export function loginHandler(
connection: Connection,
message: ServerInboundMessageMap["login"],
scope: undefined
): ServerResponse<"login"> {
connection.user = new User(message.username, connection);
console.log(`User ${message.username} has logged in!`);
return { ok: true };
}
import { ServerInboundMessage, ServerResponse } from "../../../common";
import { Connection } from "../../connection/Connection";
import { Room } from "../../room/Room";
import { RoomManager } from "../../room/RoomManager";
import { User } from "../../user/User";
export function chatHandler(
connection: Connection,
message: ServerInboundMessage<"chat">,
scope: Room
): ServerResponse<"chat"> {
scope.sendChat(user, message.message);
return { ok: true };
}
import { ServerInboundMessage, ServerResponse } from "../../../common";
import { Connection } from "../../connection/Connection";
import { Room } from "../../room/Room";
import { RoomManager } from "../../room/RoomManager";
import { User } from "../../user/User";
export function roomJoinHandler(
connection: Connection,
message: ServerInboundMessage<"joinRoom">,
scope: Room
): ServerResponse<"joinRoom"> {
const room = RoomManager.instance().get(message.uuid);
if (room !== undefined) {
room.connect(user);
return { ok: user.room !== undefined, result: user.room?.getInfo() };
}
return { ok: false };
}
import { ServerInboundMessage, ServerResponse } from "../../../common";
import { Connection } from "../../connection/Connection";
import { Room } from "../../room/Room";
import { RoomManager } from "../../room/RoomManager";
import { User } from "../../user/User";
export function roomLeaveHandler(
connection: Connection,
message: ServerInboundMessage<"leaveRoom">,
scope: Room
): ServerResponse<"leaveRoom"> {
user.room?.disconnect(user);
return { ok: true };
}
import {
ServerInboundMessage,
ServerInboundMessageMap,
ServerResponse,
} from "../../../common";
import { RoomDescription } from "../../../common/dataType";
import { Connection } from "../../connection/Connection";
import { RoomManager } from "../../room/RoomManager";
import { User } from "../../user/User";
export function roomListRequestHandler(
connection: Connection,
message: ServerInboundMessage<"roomList">,
scope: User
): ServerResponse<"roomList"> {
return { ok: true, result: RoomManager.instance().list() };
}
......@@ -2,10 +2,13 @@ import { Connection } from "../connection/Connection";
import { v4 as uuidv4 } from "uuid";
import { User } from "../user/User";
import { MessageHandlerChain } from "../message/MessageHandlerChain";
import { HandlerMap } from "../message/MessageHandler";
import { roomJoinHandler } from "../message/handler/roomJoinHandler";
import { roomLeaveHandler } from "../message/handler/roomLeaveHandler";
import { chatHandler } from "../message/handler/roomChatHandler";
import { MessageHandler } from "../message/MessageHandler";
import {
ServerInboundMessage,
ServerOutboundMessage,
ServerOutboundMessageMap,
} from "../../common";
import { RoomDescription, RoomInfo, UserData } from "../../common/dataType";
export class Room {
public readonly uuid: string;
......@@ -17,16 +20,22 @@ export class Room {
private closed: boolean = false;
public handler: HandlerMap<Room>;
public handler: MessageHandler;
constructor(name: string, maxUsers: number = 8) {
this.uuid = uuidv4();
this.name = name;
this.maxUsers = maxUsers;
this.handler = new HandlerMap<Room>(this, {
joinRoom: roomJoinHandler,
leaveRoom: roomLeaveHandler,
chat: chatHandler,
this.handler = new MessageHandler({
chat: (user, message) => {
this.sendChat(user, message.message);
return { ok: true };
},
leaveRoom: (user, message) => {
this.disconnect(user);
return { ok: true };
},
});
}
......@@ -35,10 +44,10 @@ export class Room {
return;
}
this.broadcast(new RoomUserUpdateMessage("added", user.getData()));
this.broadcast("updateRoomUser", { state: "added", user: user.getData() });
this.users.push(user);
user.room = this; // TODO: 더 나은 관리
user.room = this;
}
public disconnect(user: User): void {
......@@ -47,12 +56,15 @@ export class Room {
this.users.splice(index, 1);
user.room = undefined;
this.broadcast(new RoomUserUpdateMessage("removed", user.getData()));
this.broadcast("updateRoomUser", {
state: "removed",
user: user.getData(),
});
}
}
public sendChat(user: User, message: string): void {
this.broadcast(new RoomChatMessage(message, user.username), user);
this.broadcast("chat", { sender: user.username, message: message });
}
public getDescription(): RoomDescription {
......@@ -74,10 +86,14 @@ export class Room {
};
}
public broadcast(message: Message, except?: User): void {
public broadcast<T extends keyof ServerOutboundMessageMap>(
type: T,
message: ServerOutboundMessage<T>,
except?: User
): void {
this.users.forEach((u) => {
if (u !== except) {
u.connection.send(message);
u.connection.send(type, message);
}
});
}
......
import { UserData } from "../../common/dataType";
import { Connection } from "../connection/Connection";
import { roomListRequestHandler } from "../message/handler/roomListRequestHandler";
import { HandlerMap } from "../message/MessageHandler";
import { MessageHandler } from "../message/MessageHandler";
import { Room } from "../room/Room";
import { RoomManager } from "../room/RoomManager";
export class User {
public readonly username: string;
......@@ -11,13 +11,27 @@ export class User {
public room?: Room;
public handler: HandlerMap<User>;
public handler: MessageHandler;
constructor(username: string, connection: Connection) {
this.username = username;
this.connection = connection;
this.handler = new HandlerMap<User>(this, {
roomList: roomListRequestHandler,
this.handler = new MessageHandler({
roomList: (user, message) => {
return { ok: true, result: RoomManager.instance().list() };
},
joinRoom: (user, message) => {
const room = RoomManager.instance().get(message.uuid);
if (user.room || !room) {
return { ok: false };
}
// TODO: 방 접속 실패 처리
room.connect(user);
if (user.room === undefined) {
return { ok: false };
}
return { ok: true, result: room.getInfo() };
},
});
}
......