강동현

리팩토링

import express from "express";
import socketIo, { Server as IoServer } from "socket.io";
import { createServer } from "http";
import { SocketHandler } from "./SocketHandler";
import { RoomManager } from "./room/RoomManager";
import { Connection } from "./connection/Connection";
import { SocketIoWrapper } from "./connection/SocketWrapper";
export class Server {
public readonly port: number;
......@@ -15,7 +16,6 @@ export class Server {
const server = createServer(app);
this.io = new socketIo.Server(server);
const handler = new SocketHandler();
const roomManager = new RoomManager();
roomManager.create("테스트 방 #1", 8);
......@@ -23,7 +23,7 @@ export class Server {
roomManager.create("테스트 방 #3", 2);
this.io.on("connection", (socket) => {
handler.connected(socket);
new Connection(new SocketIoWrapper(socket), roomManager);
});
server.listen(port, () => console.log(`Listening on ${port}`));
......
import { Socket } from "socket.io";
import { ConnectionMapper } from "./connection/ConnectionMapper";
export class SocketHandler {
private connectionMapper: ConnectionMapper;
constructor() {
this.connectionMapper = new ConnectionMapper();
}
public connected(socket: Socket) {
const connection = this.connectionMapper.get(socket);
}
}
import { Socket } from "socket.io";
import { ServerOutboundMessage, ServerOutboundMessageKey } from "../../common";
import { MessageHandlerChain } from "../message/MessageHandlerChain";
import {
RawMessage,
ServerInboundMessage,
ServerInboundMessageKey,
ServerOutboundMessage,
ServerOutboundMessageKey,
ServerResponse,
} from "../../common";
import { Room } from "../room/Room";
import { RoomManager } from "../room/RoomManager";
import { User } from "../user/User";
import { SocketWrapper } from "./SocketWrapper";
export class Connection {
public readonly socket: Socket;
public readonly socket: SocketWrapper;
public readonly roomManager: RoomManager;
public user?: User;
private messageHandlerChain: MessageHandlerChain;
constructor(socket: Socket) {
constructor(socket: SocketWrapper, roomManager: RoomManager) {
this.socket = socket;
this.messageHandlerChain = new MessageHandlerChain(this);
this.roomManager = roomManager;
socket.setHandler((raw) => this.handleRaw(raw));
}
public send<T extends ServerOutboundMessageKey>(
type: T,
message: ServerOutboundMessage<T>
) {
this.socket.emit("msg", {
this.socket.send({
type: type as string,
message: message,
});
}
public handleRaw(raw: RawMessage): ServerResponse<any> {
const type = raw.type as ServerInboundMessageKey;
const message = raw.message;
// 유저 정보가 없으므로 로그인은 따로 핸들링
if (type === "login") {
return this.handleLogin(message);
}
// Game > Room > User 순으로 전달
if (this.user?.room) {
const response = this.user.room.handler.handle(type, this.user, message);
if (response) return response;
}
if (this.user) {
const response = this.user.handler.handle(type, this.user, message);
if (response) return response;
}
return { ok: false };
}
private handleLogin(
message: ServerInboundMessage<"login">
): ServerResponse<"login"> {
this.user = new User(message.username, this);
console.log(`User ${message.username} has logged in!`);
return { ok: true };
}
}
......
import { Socket } from "socket.io";
import { Connection } from "./Connection";
export class ConnectionMapper {
private map: Map<Socket, Connection>;
constructor() {
this.map = new Map<Socket, Connection>();
}
public get(socket: Socket): Connection {
var value = this.map.get(socket);
if (value) {
return value;
}
value = new Connection(socket);
// FIXME: Register connection to the map
return value;
}
public close(socket: Socket): void {
this.map.delete(socket);
}
}
import { Socket } from "socket.io";
import { RawMessage, ServerResponse } from "../../common";
export interface SocketWrapper {
setHandler: (listener: (raw: RawMessage) => ServerResponse<any>) => void;
send: (raw: RawMessage) => void;
}
export class SocketIoWrapper implements SocketWrapper {
private socketIo: Socket;
constructor(socketIo: Socket) {
this.socketIo = socketIo;
}
public setHandler(listener: (raw: RawMessage) => ServerResponse<any>): void {
this.socketIo.on("msg", (raw: RawMessage, callback: Function) => {
callback(listener(raw));
});
}
public send(raw: RawMessage) {
this.socketIo.emit("msg", raw);
}
}
import { Connection } from "../connection/Connection";
import {
ServerInboundMessage,
ServerInboundMessageKey,
......@@ -23,13 +22,10 @@ export class MessageHandler {
public handle(
type: ServerInboundMessageKey,
user: User,
message: any,
callback: Function
): boolean {
message: any
): ServerResponse<any> | undefined {
const handler = this.handlers[type];
if (!handler) return false;
const response = handler(user, message);
callback(response);
return true;
if (!handler) return undefined;
return handler(user, message);
}
}
......
import { Connection } from "../connection/Connection";
import {
RawMessage,
ServerInboundMessage,
ServerInboundMessageKey,
ServerResponse,
} from "../../common/index";
import { User } from "../user/User";
export class MessageHandlerChain {
connection: Connection;
constructor(connection: Connection) {
this.connection = connection;
this.connection.socket.on("msg", (raw: RawMessage, callback: Function) => {
this.handleRaw(connection, raw, callback);
});
}
private handleRaw(
connection: Connection,
raw: RawMessage,
callback: Function
) {
const type = raw.type as ServerInboundMessageKey;
const message = raw.message;
// 유저 정보가 없으므로 로그인은 따로 핸들링
if (type === "login") {
this.handleLogin(connection, message, callback);
return;
}
// Game > Room > User 순으로 전달
if (
connection?.user?.room &&
connection.user.room.handler.handle(
type,
connection.user,
message,
callback
)
)
return;
if (
connection?.user &&
connection.user.handler.handle(type, connection.user, message, callback)
)
return;
}
private handleLogin(
connection: Connection,
message: ServerInboundMessage<"login">,
callback: Function
) {
connection.user = new User(message.username, connection);
console.log(`User ${message.username} has logged in!`);
callback({ ok: true });
}
}
import { Connection } from "../connection/Connection";
import { v4 as uuidv4 } from "uuid";
import { User } from "../user/User";
import { MessageHandlerChain } from "../message/MessageHandlerChain";
import { MessageHandler } from "../message/MessageHandler";
import {
ServerInboundMessage,
......
......@@ -2,19 +2,12 @@ import { RoomDescription } from "../../common/dataType";
import { Room } from "./Room";
export class RoomManager {
private static _instance: RoomManager;
private rooms: Map<string, Room>;
constructor() {
RoomManager._instance = this;
this.rooms = new Map<string, Room>();
}
public static instance(): RoomManager {
return RoomManager._instance;
}
public create(name: string, maxConnections: number): Room {
const room = new Room(name, maxConnections);
this.rooms.set(room.uuid, room);
......
......@@ -18,10 +18,10 @@ export class User {
this.connection = connection;
this.handler = new MessageHandler({
roomList: (user, message) => {
return { ok: true, result: RoomManager.instance().list() };
return { ok: true, result: connection.roomManager.list() };
},
joinRoom: (user, message) => {
const room = RoomManager.instance().get(message.uuid);
const room = connection.roomManager.get(message.uuid);
if (user.room || !room) {
return { ok: false };
}
......