강동현

방장 구현

......@@ -35,7 +35,12 @@ export const RoomInfoRecord = Record({
uuid: String,
name: String,
maxUsers: Number,
users: Array(UserDataRecord),
users: Array(
Record({
username: String,
admin: Boolean,
})
),
});
export type RoomInfo = Static<typeof RoomInfoRecord>;
......
......@@ -74,6 +74,7 @@ interface ServerOutboundMessageMap {
state: "added" | "updated" | "removed";
user: {
username: string;
admin: boolean;
};
};
......
......@@ -8,6 +8,7 @@ import {
ServerOutboundMessageKey,
} from "../../common";
import { RoomDescription, RoomInfo, UserData } from "../../common/dataType";
import { RoomManager } from "./RoomManager";
export class Room {
public readonly uuid: string;
......@@ -15,16 +16,26 @@ export class Room {
public name: string;
public readonly maxUsers: number;
public readonly roomManager: RoomManager;
public users: User[] = [];
public admin?: User;
private closed: boolean = false;
public closed: boolean = false;
public handler: MessageHandler;
constructor(name: string, maxUsers: number = 8) {
constructor(
roomManager: RoomManager,
name: string,
admin?: User,
maxUsers: number = 8
) {
this.uuid = uuidv4();
this.name = name;
this.maxUsers = maxUsers;
this.roomManager = roomManager;
this.admin = admin;
this.handler = new MessageHandler({
chat: (user, message) => {
......@@ -36,6 +47,10 @@ export class Room {
return { ok: true };
},
});
if (this.admin) {
this.connect(this.admin);
}
}
public connect(user: User): void {
......@@ -43,7 +58,13 @@ export class Room {
return;
}
this.broadcast("updateRoomUser", { state: "added", user: user.getData() });
this.broadcast("updateRoomUser", {
state: "added",
user: {
username: user.username,
admin: user === this.admin,
},
});
this.users.push(user);
user.room = this;
......@@ -57,11 +78,25 @@ export class Room {
this.broadcast("updateRoomUser", {
state: "removed",
user: user.getData(),
user: {
username: user.username,
admin: user === this.admin,
},
});
if (this.users.length === 0) {
this.close();
} else {
this.setNextAdmin();
}
}
}
private setNextAdmin(): void {
const nextAdmin = this.users[Math.floor(Math.random() * this.users.length)];
this.admin = nextAdmin;
}
public sendChat(user: User, message: string): void {
this.broadcast("chat", { sender: user.username, message: message });
}
......@@ -76,12 +111,16 @@ export class Room {
}
public getInfo(): RoomInfo {
var users: UserData[] = this.users.map((u) => u.getData());
return {
uuid: this.uuid,
name: this.name,
maxUsers: this.maxUsers,
users: users,
users: this.users.map((u) => {
return {
username: u.username,
admin: u === this.admin,
};
}),
};
}
......@@ -101,6 +140,7 @@ export class Room {
if (!this.closed) {
this.users.forEach((u) => this.disconnect(u));
this.closed = true;
this.roomManager.delete(this.uuid);
}
}
}
......
import { RoomDescription } from "../../common/dataType";
import { User } from "../user/User";
import { Room } from "./Room";
export class RoomManager {
......@@ -8,8 +9,8 @@ export class RoomManager {
this.rooms = new Map<string, Room>();
}
public create(name: string, maxConnections: number): Room {
const room = new Room(name, maxConnections);
public create(name: string, maxConnections: number, admin?: User): Room {
const room = new Room(this, name, admin, maxConnections);
this.rooms.set(room.uuid, room);
return room;
}
......
import { expect } from "chai";
import { RoomManager } from "../room/RoomManager";
import { SocketTester } from "./util/SocketTester";
describe("방장", () => {
const roomManager = new RoomManager();
it("방을 만든 유저가 방장이 됩니다", () => {
const socket = new SocketTester(roomManager);
socket.login("guest");
expect(socket.connection.user !== undefined).eq(true);
const room = roomManager.create("테스트", 2, socket.connection.user);
expect(room.admin).eq(socket.connection.user);
});
it("나중에 들어온 유저는 방장이 되지 않습니다", () => {
const socket1 = new SocketTester(roomManager);
const socket2 = new SocketTester(roomManager);
socket1.login("guest1");
socket2.login("guest2");
expect(socket1.connection.user !== undefined).eq(true);
expect(socket2.connection.user !== undefined).eq(true);
const room = roomManager.create("테스트", 2, socket1.connection.user);
const response = socket2.test("joinRoom", { uuid: room.uuid });
expect(response.ok).eq(true);
expect(room.admin).eq(socket1.connection.user);
expect(room.admin).not.eq(socket2.connection.user);
expect(response.result?.users[0]?.username).eq("guest1");
expect(response.result?.users[0]?.admin).eq(true);
});
it("방장이 나가면 방장이 인계됩니다", () => {
const socket1 = new SocketTester(roomManager);
const socket2 = new SocketTester(roomManager);
socket1.login("guest1");
socket2.login("guest2");
expect(socket1.connection.user !== undefined).eq(true);
expect(socket2.connection.user !== undefined).eq(true);
const room = roomManager.create("테스트", 2, socket1.connection.user);
expect(socket2.test("joinRoom", { uuid: room.uuid }).ok).eq(true);
expect(room.admin).eq(socket1.connection.user);
expect(room.admin).not.eq(socket2.connection.user);
expect(socket1.test("leaveRoom", {}).ok).eq(true);
expect(room.admin).eq(socket2.connection.user);
});
});
......@@ -52,4 +52,15 @@ describe("방 퇴장", () => {
expect(updated.state).eq("removed");
expect(updated.user.username).eq("guest2");
});
it("방에서 퇴장한 뒤 아무도 없으면 방이 닫힙니다", () => {
const socket = new SocketTester(roomManager);
socket.login("guest1");
const room = roomManager.create("테스트", 1);
expect(socket.test("joinRoom", { uuid: room.uuid }).ok).eq(true);
expect(room.closed).eq(false);
expect(socket.test("leaveRoom", {}).ok).eq(true);
expect(room.closed).eq(true);
});
});
......