import { ApiMsgEnum, EntityTypeEnum, IClientInput, IMsgClientSync, InputTypeEnum, IState, toFixed } from "../Common"; import { Connection } from "../Core"; import type Player from "./Player"; import PlayerManager from "./PlayerManager"; import RoomManager from "./RoomManager"; export default class Room { id: number; players: Set = new Set(); private lastTime?: number; private timers: NodeJS.Timer[] = []; private pendingInput: Array = []; private lastPlayerFrameIdMap: Map = new Map(); constructor(rid: number) { this.id = rid; } join(uid: number) { const player = PlayerManager.Instance.getPlayerById(uid); if (player) { player.rid = this.id; this.players.add(player); } } leave(uid: number) { const player = PlayerManager.Instance.getPlayerById(uid); if (player) { player.rid = -1; player.connection.unlistenMsg(ApiMsgEnum.MsgClientSync, this.getClientMsg, this); this.players.delete(player); if (!this.players.size) { RoomManager.Instance.closeRoom(this.id); } } } close() { this.timers.forEach((t) => clearInterval(t)); for (const player of this.players) { player.rid = -1; player.connection.sendMsg(ApiMsgEnum.MsgGameEnd, {}); player.connection.unlistenMsg(ApiMsgEnum.MsgClientSync, this.getClientMsg, this); } this.players.clear(); } sync() { for (const player of this.players) { player.connection.sendMsg(ApiMsgEnum.MsgRoom, { room: RoomManager.Instance.getRoomView(this), }); } } start() { const state: IState = { actors: [...this.players].map((player, index) => ({ id: player.id, nickname: player.nickname, position: { x: -150 + index * 300, y: -150 + index * 300, }, direction: { x: 1, y: 0, }, hp: 100, type: index === 0 ? EntityTypeEnum.Actor1 : EntityTypeEnum.Actor2, weaponType: index === 0 ? EntityTypeEnum.Weapon1 : EntityTypeEnum.Weapon2, bulletType: index === 0 ? EntityTypeEnum.Bullet1 : EntityTypeEnum.Bullet2, })), bullets: [], nextBulletId: 1, }; for (const player of this.players) { player.connection.sendMsg(ApiMsgEnum.MsgGameStart, { state, }); player.connection.listenMsg(ApiMsgEnum.MsgClientSync, this.getClientMsg, this); } let t1 = setInterval(() => { this.sendServerMsg(); }, 100); let t2 = setInterval(() => { this.timePast(); }, 16); this.timers = [t1, t2]; } getClientMsg(connection: Connection, { frameId, input }: IMsgClientSync) { this.lastPlayerFrameIdMap.set(connection.playerId, frameId); this.pendingInput.push(input); } sendServerMsg() { const pendingInput = this.pendingInput; this.pendingInput = []; for (const player of this.players) { player.connection.sendMsg(ApiMsgEnum.MsgServerSync, { lastFrameId: this.lastPlayerFrameIdMap.get(player.id) ?? 0, inputs: pendingInput, }); } } timePast() { let now = process.uptime(); const dt = now - (this.lastTime ?? now); this.pendingInput.push({ type: InputTypeEnum.TimePast, dt: toFixed(dt), }); this.lastTime = now; } }