@esengine/server 4.1.0 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/index.d.ts +3 -3
- package/dist/auth/index.js +8 -5
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/testing/index.d.ts +2 -2
- package/dist/auth/testing/index.js.map +1 -1
- package/dist/{chunk-O3VN2QVN.js → chunk-FACTBKJ3.js} +2 -2
- package/dist/chunk-FACTBKJ3.js.map +1 -0
- package/dist/chunk-I4QQSQ72.js +12 -0
- package/dist/chunk-I4QQSQ72.js.map +1 -0
- package/dist/{chunk-BIAOJF7P.js → chunk-M7VONMZJ.js} +267 -58
- package/dist/chunk-M7VONMZJ.js.map +1 -0
- package/dist/ecs/index.js +2 -2
- package/dist/ecs/index.js.map +1 -1
- package/dist/{index-DyMTy67Q.d.ts → index-lcuKuQsL.d.ts} +106 -9
- package/dist/index.d.ts +28 -6
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/ratelimit/index.d.ts +11 -1
- package/dist/ratelimit/index.js +11 -2
- package/dist/ratelimit/index.js.map +1 -1
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.js +13 -11
- package/dist/testing/index.js.map +1 -1
- package/dist/{types-AbAXO9Vw.d.ts → types-C7sS8Sfi.d.ts} +1 -1
- package/package.json +5 -9
- package/dist/chunk-BIAOJF7P.js.map +0 -1
- package/dist/chunk-O3VN2QVN.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/testing/TestClient.ts","../../src/testing/TestServer.ts","../../src/testing/MockRoom.ts"],"names":["PacketType","ApiRequest","ApiResponse","ApiError","Message","TestClient","port","options","_port","_codec","_timeout","_connectTimeout","_ws","_callIdCounter","_connected","_currentRoomId","_currentPlayerId","_pendingCalls","Map","_msgHandlers","_receivedMessages","codec","json","timeout","connectTimeout","isConnected","roomId","playerId","receivedMessages","connect","Promise","resolve","reject","url","WebSocket","setTimeout","close","Error","on","clearTimeout","_rejectAllPending","err","data","_handleMessage","disconnect","readyState","CLOSED","once","joinRoom","roomType","result","call","joinRoomById","leaveRoom","sendToRoom","type","send","name","input","id","timer","delete","set","packet","encode","handler","handlers","get","Set","add","off","waitForMessage","timeoutMs","waitForRoomMessage","msg","hasReceivedMessage","some","m","getMessagesOfType","filter","map","getLastMessage","i","length","undefined","clearMessages","getMessageCount","raw","decode","_handleApiResponse","_handleApiError","_handleMsg","console","error","pending","code","message","push","timestamp","Date","now","reason","clear","getRandomPort","net","server","createServer","listen","address","createTestServer","silent","originalLog","log","tickRate","apiDir","msgDir","start","cleanup","stop","createTestEnv","serverCleanup","clients","createClient","clientOptions","client","createClients","count","newClients","all","c","catch","MockRoom","Room","state","messages","joinCount","leaveCount","onCreate","onJoin","player","broadcast","onLeave","handleAnyMessage","from","handleEcho","handleBroadcast","_player","handlePing","_data","EchoRoom","BroadcastRoom"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @zh 测试客户端\n * @en Test client for server testing\n */\n\nimport WebSocket from 'ws'\nimport { json } from '@esengine/rpc/codec'\nimport type { Codec } from '@esengine/rpc/codec'\n\n// ============================================================================\n// Types | 类型定义\n// ============================================================================\n\n/**\n * @zh 测试客户端配置\n * @en Test client options\n */\nexport interface TestClientOptions {\n /**\n * @zh 编解码器\n * @en Codec\n * @defaultValue json()\n */\n codec?: Codec\n\n /**\n * @zh API 调用超时(毫秒)\n * @en API call timeout in milliseconds\n * @defaultValue 5000\n */\n timeout?: number\n\n /**\n * @zh 连接超时(毫秒)\n * @en Connection timeout in milliseconds\n * @defaultValue 5000\n */\n connectTimeout?: number\n}\n\n/**\n * @zh 房间加入结果\n * @en Room join result\n */\nexport interface JoinRoomResult {\n roomId: string\n playerId: string\n}\n\n/**\n * @zh 收到的消息记录\n * @en Received message record\n */\nexport interface ReceivedMessage {\n type: string\n data: unknown\n timestamp: number\n}\n\n// ============================================================================\n// Constants | 常量\n// ============================================================================\n\nconst PacketType = {\n ApiRequest: 0,\n ApiResponse: 1,\n ApiError: 2,\n Message: 3,\n} as const\n\n// ============================================================================\n// TestClient Class | 测试客户端类\n// ============================================================================\n\ninterface PendingCall {\n resolve: (value: unknown) => void\n reject: (error: Error) => void\n timer: ReturnType<typeof setTimeout>\n}\n\n/**\n * @zh 测试客户端\n * @en Test client for server integration testing\n *\n * @zh 专为测试设计的客户端,提供便捷的断言方法和消息记录功能\n * @en Client designed for testing, with convenient assertion methods and message recording\n *\n * @example\n * ```typescript\n * const client = new TestClient(3000)\n * await client.connect()\n *\n * // 加入房间\n * const { roomId } = await client.joinRoom('game')\n *\n * // 发送消息\n * client.sendToRoom('Move', { x: 10, y: 20 })\n *\n * // 等待收到特定消息\n * const msg = await client.waitForMessage('PlayerMoved')\n *\n * // 断言收到消息\n * expect(client.hasReceivedMessage('PlayerMoved')).toBe(true)\n *\n * await client.disconnect()\n * ```\n */\nexport class TestClient {\n private readonly _port: number\n private readonly _codec: Codec\n private readonly _timeout: number\n private readonly _connectTimeout: number\n\n private _ws: WebSocket | null = null\n private _callIdCounter = 0\n private _connected = false\n private _currentRoomId: string | null = null\n private _currentPlayerId: string | null = null\n\n private readonly _pendingCalls = new Map<number, PendingCall>()\n private readonly _msgHandlers = new Map<string, Set<(data: unknown) => void>>()\n private readonly _receivedMessages: ReceivedMessage[] = []\n\n constructor(port: number, options: TestClientOptions = {}) {\n this._port = port\n this._codec = options.codec ?? json()\n this._timeout = options.timeout ?? 5000\n this._connectTimeout = options.connectTimeout ?? 5000\n }\n\n // ========================================================================\n // Properties | 属性\n // ========================================================================\n\n /**\n * @zh 是否已连接\n * @en Whether connected\n */\n get isConnected(): boolean {\n return this._connected\n }\n\n /**\n * @zh 当前房间 ID\n * @en Current room ID\n */\n get roomId(): string | null {\n return this._currentRoomId\n }\n\n /**\n * @zh 当前玩家 ID\n * @en Current player ID\n */\n get playerId(): string | null {\n return this._currentPlayerId\n }\n\n /**\n * @zh 收到的所有消息\n * @en All received messages\n */\n get receivedMessages(): ReadonlyArray<ReceivedMessage> {\n return this._receivedMessages\n }\n\n // ========================================================================\n // Connection | 连接管理\n // ========================================================================\n\n /**\n * @zh 连接到服务器\n * @en Connect to server\n */\n connect(): Promise<this> {\n return new Promise((resolve, reject) => {\n const url = `ws://localhost:${this._port}`\n this._ws = new WebSocket(url)\n\n const timeout = setTimeout(() => {\n this._ws?.close()\n reject(new Error(`Connection timeout after ${this._connectTimeout}ms`))\n }, this._connectTimeout)\n\n this._ws.on('open', () => {\n clearTimeout(timeout)\n this._connected = true\n resolve(this)\n })\n\n this._ws.on('close', () => {\n this._connected = false\n this._rejectAllPending('Connection closed')\n })\n\n this._ws.on('error', (err) => {\n clearTimeout(timeout)\n if (!this._connected) {\n reject(err)\n }\n })\n\n this._ws.on('message', (data: Buffer) => {\n this._handleMessage(data)\n })\n })\n }\n\n /**\n * @zh 断开连接\n * @en Disconnect from server\n */\n async disconnect(): Promise<void> {\n return new Promise((resolve) => {\n if (!this._ws || this._ws.readyState === WebSocket.CLOSED) {\n resolve()\n return\n }\n\n this._ws.once('close', () => {\n this._connected = false\n this._ws = null\n resolve()\n })\n\n this._ws.close()\n })\n }\n\n // ========================================================================\n // Room Operations | 房间操作\n // ========================================================================\n\n /**\n * @zh 加入房间\n * @en Join a room\n */\n async joinRoom(roomType: string, options?: Record<string, unknown>): Promise<JoinRoomResult> {\n const result = await this.call<JoinRoomResult>('JoinRoom', { roomType, options })\n this._currentRoomId = result.roomId\n this._currentPlayerId = result.playerId\n return result\n }\n\n /**\n * @zh 通过 ID 加入房间\n * @en Join a room by ID\n */\n async joinRoomById(roomId: string): Promise<JoinRoomResult> {\n const result = await this.call<JoinRoomResult>('JoinRoom', { roomId })\n this._currentRoomId = result.roomId\n this._currentPlayerId = result.playerId\n return result\n }\n\n /**\n * @zh 离开房间\n * @en Leave room\n */\n async leaveRoom(): Promise<void> {\n await this.call('LeaveRoom', {})\n this._currentRoomId = null\n this._currentPlayerId = null\n }\n\n /**\n * @zh 发送消息到房间\n * @en Send message to room\n */\n sendToRoom(type: string, data: unknown): void {\n this.send('RoomMessage', { type, data })\n }\n\n // ========================================================================\n // API Calls | API 调用\n // ========================================================================\n\n /**\n * @zh 调用 API\n * @en Call API\n */\n call<T = unknown>(name: string, input: unknown): Promise<T> {\n return new Promise((resolve, reject) => {\n if (!this._connected || !this._ws) {\n reject(new Error('Not connected'))\n return\n }\n\n const id = ++this._callIdCounter\n const timer = setTimeout(() => {\n this._pendingCalls.delete(id)\n reject(new Error(`API call '${name}' timeout after ${this._timeout}ms`))\n }, this._timeout)\n\n this._pendingCalls.set(id, {\n resolve: resolve as (v: unknown) => void,\n reject,\n timer,\n })\n\n const packet = [PacketType.ApiRequest, id, name, input]\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this._ws.send(this._codec.encode(packet as any) as Buffer)\n })\n }\n\n /**\n * @zh 发送消息\n * @en Send message\n */\n send(name: string, data: unknown): void {\n if (!this._connected || !this._ws) return\n const packet = [PacketType.Message, name, data]\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this._ws.send(this._codec.encode(packet as any) as Buffer)\n }\n\n // ========================================================================\n // Message Handling | 消息处理\n // ========================================================================\n\n /**\n * @zh 监听消息\n * @en Listen for message\n */\n on(name: string, handler: (data: unknown) => void): this {\n let handlers = this._msgHandlers.get(name)\n if (!handlers) {\n handlers = new Set()\n this._msgHandlers.set(name, handlers)\n }\n handlers.add(handler)\n return this\n }\n\n /**\n * @zh 取消监听消息\n * @en Remove message listener\n */\n off(name: string, handler?: (data: unknown) => void): this {\n if (handler) {\n this._msgHandlers.get(name)?.delete(handler)\n } else {\n this._msgHandlers.delete(name)\n }\n return this\n }\n\n /**\n * @zh 等待收到指定消息\n * @en Wait for a specific message\n */\n waitForMessage<T = unknown>(type: string, timeout?: number): Promise<T> {\n return new Promise((resolve, reject) => {\n const timeoutMs = timeout ?? this._timeout\n\n const timer = setTimeout(() => {\n this.off(type, handler)\n reject(new Error(`Timeout waiting for message '${type}' after ${timeoutMs}ms`))\n }, timeoutMs)\n\n const handler = (data: unknown) => {\n clearTimeout(timer)\n this.off(type, handler)\n resolve(data as T)\n }\n\n this.on(type, handler)\n })\n }\n\n /**\n * @zh 等待收到指定房间消息\n * @en Wait for a specific room message\n */\n waitForRoomMessage<T = unknown>(type: string, timeout?: number): Promise<T> {\n return new Promise((resolve, reject) => {\n const timeoutMs = timeout ?? this._timeout\n\n const timer = setTimeout(() => {\n this.off('RoomMessage', handler)\n reject(new Error(`Timeout waiting for room message '${type}' after ${timeoutMs}ms`))\n }, timeoutMs)\n\n const handler = (data: unknown) => {\n const msg = data as { type: string; data: unknown }\n if (msg.type === type) {\n clearTimeout(timer)\n this.off('RoomMessage', handler)\n resolve(msg.data as T)\n }\n }\n\n this.on('RoomMessage', handler)\n })\n }\n\n // ========================================================================\n // Assertions | 断言辅助\n // ========================================================================\n\n /**\n * @zh 是否收到过指定消息\n * @en Whether received a specific message\n */\n hasReceivedMessage(type: string): boolean {\n return this._receivedMessages.some((m) => m.type === type)\n }\n\n /**\n * @zh 获取指定类型的所有消息\n * @en Get all messages of a specific type\n */\n getMessagesOfType<T = unknown>(type: string): T[] {\n return this._receivedMessages\n .filter((m) => m.type === type)\n .map((m) => m.data as T)\n }\n\n /**\n * @zh 获取最后收到的指定类型消息\n * @en Get the last received message of a specific type\n */\n getLastMessage<T = unknown>(type: string): T | undefined {\n for (let i = this._receivedMessages.length - 1; i >= 0; i--) {\n if (this._receivedMessages[i].type === type) {\n return this._receivedMessages[i].data as T\n }\n }\n return undefined\n }\n\n /**\n * @zh 清空消息记录\n * @en Clear message records\n */\n clearMessages(): void {\n this._receivedMessages.length = 0\n }\n\n /**\n * @zh 获取收到的消息数量\n * @en Get received message count\n */\n getMessageCount(type?: string): number {\n if (type) {\n return this._receivedMessages.filter((m) => m.type === type).length\n }\n return this._receivedMessages.length\n }\n\n // ========================================================================\n // Private Methods | 私有方法\n // ========================================================================\n\n private _handleMessage(raw: Buffer): void {\n try {\n const packet = this._codec.decode(raw) as unknown[]\n const type = packet[0] as number\n\n switch (type) {\n case PacketType.ApiResponse:\n this._handleApiResponse([packet[0], packet[1], packet[2]] as [number, number, unknown])\n break\n case PacketType.ApiError:\n this._handleApiError([packet[0], packet[1], packet[2], packet[3]] as [number, number, string, string])\n break\n case PacketType.Message:\n this._handleMsg([packet[0], packet[1], packet[2]] as [number, string, unknown])\n break\n }\n } catch (err) {\n console.error('[TestClient] Failed to handle message:', err)\n }\n }\n\n private _handleApiResponse([, id, result]: [number, number, unknown]): void {\n const pending = this._pendingCalls.get(id)\n if (pending) {\n clearTimeout(pending.timer)\n this._pendingCalls.delete(id)\n pending.resolve(result)\n }\n }\n\n private _handleApiError([, id, code, message]: [number, number, string, string]): void {\n const pending = this._pendingCalls.get(id)\n if (pending) {\n clearTimeout(pending.timer)\n this._pendingCalls.delete(id)\n pending.reject(new Error(`[${code}] ${message}`))\n }\n }\n\n private _handleMsg([, name, data]: [number, string, unknown]): void {\n // 记录消息\n this._receivedMessages.push({\n type: name,\n data,\n timestamp: Date.now(),\n })\n\n // 触发处理器\n const handlers = this._msgHandlers.get(name)\n if (handlers) {\n for (const handler of handlers) {\n try {\n handler(data)\n } catch (err) {\n console.error('[TestClient] Handler error:', err)\n }\n }\n }\n }\n\n private _rejectAllPending(reason: string): void {\n for (const [, pending] of this._pendingCalls) {\n clearTimeout(pending.timer)\n pending.reject(new Error(reason))\n }\n this._pendingCalls.clear()\n }\n}\n","/**\n * @zh 测试服务器工具\n * @en Test server utilities\n */\n\nimport { createServer } from '../core/server.js'\nimport type { GameServer } from '../types/index.js'\nimport { TestClient, type TestClientOptions } from './TestClient.js'\n\n// ============================================================================\n// Types | 类型定义\n// ============================================================================\n\n/**\n * @zh 测试服务器配置\n * @en Test server options\n */\nexport interface TestServerOptions {\n /**\n * @zh 端口号,0 表示随机端口\n * @en Port number, 0 for random port\n * @defaultValue 0\n */\n port?: number\n\n /**\n * @zh Tick 速率\n * @en Tick rate\n * @defaultValue 0\n */\n tickRate?: number\n\n /**\n * @zh 是否禁用控制台日志\n * @en Whether to suppress console logs\n * @defaultValue true\n */\n silent?: boolean\n}\n\n/**\n * @zh 测试环境\n * @en Test environment\n */\nexport interface TestEnvironment {\n /**\n * @zh 服务器实例\n * @en Server instance\n */\n server: GameServer\n\n /**\n * @zh 服务器端口\n * @en Server port\n */\n port: number\n\n /**\n * @zh 创建测试客户端\n * @en Create test client\n */\n createClient(options?: TestClientOptions): Promise<TestClient>\n\n /**\n * @zh 创建多个测试客户端\n * @en Create multiple test clients\n */\n createClients(count: number, options?: TestClientOptions): Promise<TestClient[]>\n\n /**\n * @zh 清理测试环境\n * @en Cleanup test environment\n */\n cleanup(): Promise<void>\n\n /**\n * @zh 所有已创建的客户端\n * @en All created clients\n */\n readonly clients: ReadonlyArray<TestClient>\n}\n\n// ============================================================================\n// Helper Functions | 辅助函数\n// ============================================================================\n\n/**\n * @zh 获取随机可用端口\n * @en Get a random available port\n */\nasync function getRandomPort(): Promise<number> {\n const net = await import('node:net')\n return new Promise((resolve, reject) => {\n const server = net.createServer()\n server.listen(0, () => {\n const address = server.address()\n if (address && typeof address === 'object') {\n const port = address.port\n server.close(() => resolve(port))\n } else {\n server.close(() => reject(new Error('Failed to get port')))\n }\n })\n server.on('error', reject)\n })\n}\n\n/**\n * @zh 等待指定毫秒\n * @en Wait for specified milliseconds\n */\nexport function wait(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\n// ============================================================================\n// Factory Functions | 工厂函数\n// ============================================================================\n\n/**\n * @zh 创建测试服务器\n * @en Create test server\n *\n * @example\n * ```typescript\n * const { server, port, cleanup } = await createTestServer()\n * server.define('game', GameRoom)\n *\n * const client = new TestClient(port)\n * await client.connect()\n *\n * // ... run tests ...\n *\n * await cleanup()\n * ```\n */\nexport async function createTestServer(\n options: TestServerOptions = {}\n): Promise<{ server: GameServer; port: number; cleanup: () => Promise<void> }> {\n const port = options.port || (await getRandomPort())\n const silent = options.silent ?? true\n\n // 临时禁用 console.log\n const originalLog = console.log\n if (silent) {\n console.log = () => {}\n }\n\n const server = await createServer({\n port,\n tickRate: options.tickRate ?? 0,\n apiDir: '__non_existent_api__',\n msgDir: '__non_existent_msg__',\n })\n\n await server.start()\n\n // 恢复 console.log\n if (silent) {\n console.log = originalLog\n }\n\n return {\n server,\n port,\n cleanup: async () => {\n await server.stop()\n },\n }\n}\n\n/**\n * @zh 创建完整测试环境\n * @en Create complete test environment\n *\n * @zh 包含服务器、客户端创建和清理功能的完整测试环境\n * @en Complete test environment with server, client creation and cleanup\n *\n * @example\n * ```typescript\n * describe('GameRoom', () => {\n * let env: TestEnvironment\n *\n * beforeEach(async () => {\n * env = await createTestEnv()\n * env.server.define('game', GameRoom)\n * })\n *\n * afterEach(async () => {\n * await env.cleanup()\n * })\n *\n * it('should handle player join', async () => {\n * const client = await env.createClient()\n * const result = await client.joinRoom('game')\n * expect(result.roomId).toBeDefined()\n * })\n *\n * it('should broadcast to all players', async () => {\n * const [client1, client2] = await env.createClients(2)\n *\n * await client1.joinRoom('game')\n * const joinPromise = client1.waitForRoomMessage('PlayerJoined')\n *\n * await client2.joinRoom('game')\n * const msg = await joinPromise\n *\n * expect(msg).toBeDefined()\n * })\n * })\n * ```\n */\nexport async function createTestEnv(options: TestServerOptions = {}): Promise<TestEnvironment> {\n const { server, port, cleanup: serverCleanup } = await createTestServer(options)\n const clients: TestClient[] = []\n\n return {\n server,\n port,\n clients,\n\n async createClient(clientOptions?: TestClientOptions): Promise<TestClient> {\n const client = new TestClient(port, clientOptions)\n await client.connect()\n clients.push(client)\n return client\n },\n\n async createClients(count: number, clientOptions?: TestClientOptions): Promise<TestClient[]> {\n const newClients: TestClient[] = []\n for (let i = 0; i < count; i++) {\n const client = new TestClient(port, clientOptions)\n await client.connect()\n clients.push(client)\n newClients.push(client)\n }\n return newClients\n },\n\n async cleanup(): Promise<void> {\n // 断开所有客户端\n await Promise.all(clients.map((c) => c.disconnect().catch(() => {})))\n clients.length = 0\n\n // 停止服务器\n await serverCleanup()\n },\n }\n}\n","/**\n * @zh 模拟房间\n * @en Mock room for testing\n */\n\nimport { Room, onMessage, type Player } from '../room/index.js'\n\n/**\n * @zh 模拟房间状态\n * @en Mock room state\n */\nexport interface MockRoomState {\n messages: Array<{ type: string; data: unknown; playerId: string }>\n joinCount: number\n leaveCount: number\n}\n\n/**\n * @zh 模拟房间\n * @en Mock room for testing\n *\n * @zh 记录所有事件和消息,用于测试断言\n * @en Records all events and messages for test assertions\n *\n * @example\n * ```typescript\n * const env = await createTestEnv()\n * env.server.define('mock', MockRoom)\n *\n * const client = await env.createClient()\n * await client.joinRoom('mock')\n *\n * client.sendToRoom('Test', { value: 123 })\n * await wait(50)\n *\n * // MockRoom 会广播收到的消息\n * const msg = client.getLastMessage('RoomMessage')\n * ```\n */\nexport class MockRoom extends Room<MockRoomState> {\n state: MockRoomState = {\n messages: [],\n joinCount: 0,\n leaveCount: 0,\n }\n\n onCreate(): void {\n // 房间创建\n }\n\n onJoin(player: Player): void {\n this.state.joinCount++\n this.broadcast('PlayerJoined', {\n playerId: player.id,\n joinCount: this.state.joinCount,\n })\n }\n\n onLeave(player: Player): void {\n this.state.leaveCount++\n this.broadcast('PlayerLeft', {\n playerId: player.id,\n leaveCount: this.state.leaveCount,\n })\n }\n\n @onMessage('*')\n handleAnyMessage(data: unknown, player: Player, type: string): void {\n this.state.messages.push({\n type,\n data,\n playerId: player.id,\n })\n\n // 回显消息给所有玩家\n this.broadcast('MessageReceived', {\n type,\n data,\n from: player.id,\n })\n }\n\n @onMessage('Echo')\n handleEcho(data: unknown, player: Player): void {\n // 只回复给发送者\n player.send('EchoReply', data)\n }\n\n @onMessage('Broadcast')\n handleBroadcast(data: unknown, _player: Player): void {\n this.broadcast('BroadcastMessage', data)\n }\n\n @onMessage('Ping')\n handlePing(_data: unknown, player: Player): void {\n player.send('Pong', { timestamp: Date.now() })\n }\n}\n\n/**\n * @zh 简单回显房间\n * @en Simple echo room\n *\n * @zh 将收到的任何消息回显给发送者\n * @en Echoes any received message back to sender\n */\nexport class EchoRoom extends Room {\n @onMessage('*')\n handleAnyMessage(data: unknown, player: Player, type: string): void {\n player.send(type, data)\n }\n}\n\n/**\n * @zh 广播房间\n * @en Broadcast room\n *\n * @zh 将收到的任何消息广播给所有玩家\n * @en Broadcasts any received message to all players\n */\nexport class BroadcastRoom extends Room {\n onJoin(player: Player): void {\n this.broadcast('PlayerJoined', { id: player.id })\n }\n\n onLeave(player: Player): void {\n this.broadcast('PlayerLeft', { id: player.id })\n }\n\n @onMessage('*')\n handleAnyMessage(data: unknown, player: Player, type: string): void {\n this.broadcast(type, { from: player.id, data })\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/testing/TestClient.ts","../../src/testing/TestServer.ts","../../src/testing/MockRoom.ts"],"names":["logger","createLogger","PacketType","ApiRequest","ApiResponse","ApiError","Message","TestClient","port","options","_port","_codec","_timeout","_connectTimeout","_ws","_callIdCounter","_connected","_currentRoomId","_currentPlayerId","_pendingCalls","Map","_msgHandlers","_receivedMessages","codec","json","timeout","connectTimeout","isConnected","roomId","playerId","receivedMessages","connect","Promise","resolve","reject","url","WebSocket","setTimeout","close","Error","on","clearTimeout","_rejectAllPending","err","data","_handleMessage","disconnect","readyState","CLOSED","once","joinRoom","roomType","result","call","joinRoomById","leaveRoom","sendToRoom","type","send","name","input","id","timer","delete","set","packet","encode","handler","handlers","get","Set","add","off","waitForMessage","timeoutMs","waitForRoomMessage","msg","hasReceivedMessage","some","m","getMessagesOfType","filter","map","getLastMessage","i","length","undefined","clearMessages","getMessageCount","raw","decode","_handleApiResponse","_handleApiError","_handleMsg","error","pending","code","message","push","timestamp","Date","now","reason","clear","getRandomPort","net","server","createServer","listen","address","createTestServer","silent","loggerManager","LoggerManager","getInstance","originalLevel","LogLevel","Info","setGlobalLevel","None","tickRate","apiDir","msgDir","start","cleanup","stop","createTestEnv","serverCleanup","clients","createClient","clientOptions","client","createClients","count","newClients","all","c","catch","MockRoom","Room","state","messages","joinCount","leaveCount","onCreate","onJoin","player","broadcast","onLeave","handleAnyMessage","from","handleEcho","handleBroadcast","_player","handlePing","_data","EchoRoom","BroadcastRoom"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @zh 测试客户端\n * @en Test client for server testing\n */\n\nimport WebSocket from 'ws';\nimport { json } from '@esengine/rpc/codec';\nimport type { Codec } from '@esengine/rpc/codec';\nimport { createLogger } from '../logger.js';\n\nconst logger = createLogger('TestClient');\n\n// ============================================================================\n// Types | 类型定义\n// ============================================================================\n\n/**\n * @zh 测试客户端配置\n * @en Test client options\n */\nexport interface TestClientOptions {\n /**\n * @zh 编解码器\n * @en Codec\n * @defaultValue json()\n */\n codec?: Codec\n\n /**\n * @zh API 调用超时(毫秒)\n * @en API call timeout in milliseconds\n * @defaultValue 5000\n */\n timeout?: number\n\n /**\n * @zh 连接超时(毫秒)\n * @en Connection timeout in milliseconds\n * @defaultValue 5000\n */\n connectTimeout?: number\n}\n\n/**\n * @zh 房间加入结果\n * @en Room join result\n */\nexport interface JoinRoomResult {\n roomId: string\n playerId: string\n}\n\n/**\n * @zh 收到的消息记录\n * @en Received message record\n */\nexport interface ReceivedMessage {\n type: string\n data: unknown\n timestamp: number\n}\n\n// ============================================================================\n// Constants | 常量\n// ============================================================================\n\nconst PacketType = {\n ApiRequest: 0,\n ApiResponse: 1,\n ApiError: 2,\n Message: 3\n} as const;\n\n// ============================================================================\n// TestClient Class | 测试客户端类\n// ============================================================================\n\ninterface PendingCall {\n resolve: (value: unknown) => void\n reject: (error: Error) => void\n timer: ReturnType<typeof setTimeout>\n}\n\n/**\n * @zh 测试客户端\n * @en Test client for server integration testing\n *\n * @zh 专为测试设计的客户端,提供便捷的断言方法和消息记录功能\n * @en Client designed for testing, with convenient assertion methods and message recording\n *\n * @example\n * ```typescript\n * const client = new TestClient(3000)\n * await client.connect()\n *\n * // 加入房间\n * const { roomId } = await client.joinRoom('game')\n *\n * // 发送消息\n * client.sendToRoom('Move', { x: 10, y: 20 })\n *\n * // 等待收到特定消息\n * const msg = await client.waitForMessage('PlayerMoved')\n *\n * // 断言收到消息\n * expect(client.hasReceivedMessage('PlayerMoved')).toBe(true)\n *\n * await client.disconnect()\n * ```\n */\nexport class TestClient {\n private readonly _port: number;\n private readonly _codec: Codec;\n private readonly _timeout: number;\n private readonly _connectTimeout: number;\n\n private _ws: WebSocket | null = null;\n private _callIdCounter = 0;\n private _connected = false;\n private _currentRoomId: string | null = null;\n private _currentPlayerId: string | null = null;\n\n private readonly _pendingCalls = new Map<number, PendingCall>();\n private readonly _msgHandlers = new Map<string, Set<(data: unknown) => void>>();\n private readonly _receivedMessages: ReceivedMessage[] = [];\n\n constructor(port: number, options: TestClientOptions = {}) {\n this._port = port;\n this._codec = options.codec ?? json();\n this._timeout = options.timeout ?? 5000;\n this._connectTimeout = options.connectTimeout ?? 5000;\n }\n\n // ========================================================================\n // Properties | 属性\n // ========================================================================\n\n /**\n * @zh 是否已连接\n * @en Whether connected\n */\n get isConnected(): boolean {\n return this._connected;\n }\n\n /**\n * @zh 当前房间 ID\n * @en Current room ID\n */\n get roomId(): string | null {\n return this._currentRoomId;\n }\n\n /**\n * @zh 当前玩家 ID\n * @en Current player ID\n */\n get playerId(): string | null {\n return this._currentPlayerId;\n }\n\n /**\n * @zh 收到的所有消息\n * @en All received messages\n */\n get receivedMessages(): ReadonlyArray<ReceivedMessage> {\n return this._receivedMessages;\n }\n\n // ========================================================================\n // Connection | 连接管理\n // ========================================================================\n\n /**\n * @zh 连接到服务器\n * @en Connect to server\n */\n connect(): Promise<this> {\n return new Promise((resolve, reject) => {\n const url = `ws://localhost:${this._port}`;\n this._ws = new WebSocket(url);\n\n const timeout = setTimeout(() => {\n this._ws?.close();\n reject(new Error(`Connection timeout after ${this._connectTimeout}ms`));\n }, this._connectTimeout);\n\n this._ws.on('open', () => {\n clearTimeout(timeout);\n this._connected = true;\n resolve(this);\n });\n\n this._ws.on('close', () => {\n this._connected = false;\n this._rejectAllPending('Connection closed');\n });\n\n this._ws.on('error', (err) => {\n clearTimeout(timeout);\n if (!this._connected) {\n reject(err);\n }\n });\n\n this._ws.on('message', (data: Buffer) => {\n this._handleMessage(data);\n });\n });\n }\n\n /**\n * @zh 断开连接\n * @en Disconnect from server\n */\n async disconnect(): Promise<void> {\n return new Promise((resolve) => {\n if (!this._ws || this._ws.readyState === WebSocket.CLOSED) {\n resolve();\n return;\n }\n\n this._ws.once('close', () => {\n this._connected = false;\n this._ws = null;\n resolve();\n });\n\n this._ws.close();\n });\n }\n\n // ========================================================================\n // Room Operations | 房间操作\n // ========================================================================\n\n /**\n * @zh 加入房间\n * @en Join a room\n */\n async joinRoom(roomType: string, options?: Record<string, unknown>): Promise<JoinRoomResult> {\n const result = await this.call<JoinRoomResult>('JoinRoom', { roomType, options });\n this._currentRoomId = result.roomId;\n this._currentPlayerId = result.playerId;\n return result;\n }\n\n /**\n * @zh 通过 ID 加入房间\n * @en Join a room by ID\n */\n async joinRoomById(roomId: string): Promise<JoinRoomResult> {\n const result = await this.call<JoinRoomResult>('JoinRoom', { roomId });\n this._currentRoomId = result.roomId;\n this._currentPlayerId = result.playerId;\n return result;\n }\n\n /**\n * @zh 离开房间\n * @en Leave room\n */\n async leaveRoom(): Promise<void> {\n await this.call('LeaveRoom', {});\n this._currentRoomId = null;\n this._currentPlayerId = null;\n }\n\n /**\n * @zh 发送消息到房间\n * @en Send message to room\n */\n sendToRoom(type: string, data: unknown): void {\n this.send('RoomMessage', { type, data });\n }\n\n // ========================================================================\n // API Calls | API 调用\n // ========================================================================\n\n /**\n * @zh 调用 API\n * @en Call API\n */\n call<T = unknown>(name: string, input: unknown): Promise<T> {\n return new Promise((resolve, reject) => {\n if (!this._connected || !this._ws) {\n reject(new Error('Not connected'));\n return;\n }\n\n const id = ++this._callIdCounter;\n const timer = setTimeout(() => {\n this._pendingCalls.delete(id);\n reject(new Error(`API call '${name}' timeout after ${this._timeout}ms`));\n }, this._timeout);\n\n this._pendingCalls.set(id, {\n resolve: resolve as (v: unknown) => void,\n reject,\n timer\n });\n\n const packet = [PacketType.ApiRequest, id, name, input];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this._ws.send(this._codec.encode(packet as any) as Buffer);\n });\n }\n\n /**\n * @zh 发送消息\n * @en Send message\n */\n send(name: string, data: unknown): void {\n if (!this._connected || !this._ws) return;\n const packet = [PacketType.Message, name, data];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this._ws.send(this._codec.encode(packet as any) as Buffer);\n }\n\n // ========================================================================\n // Message Handling | 消息处理\n // ========================================================================\n\n /**\n * @zh 监听消息\n * @en Listen for message\n */\n on(name: string, handler: (data: unknown) => void): this {\n let handlers = this._msgHandlers.get(name);\n if (!handlers) {\n handlers = new Set();\n this._msgHandlers.set(name, handlers);\n }\n handlers.add(handler);\n return this;\n }\n\n /**\n * @zh 取消监听消息\n * @en Remove message listener\n */\n off(name: string, handler?: (data: unknown) => void): this {\n if (handler) {\n this._msgHandlers.get(name)?.delete(handler);\n } else {\n this._msgHandlers.delete(name);\n }\n return this;\n }\n\n /**\n * @zh 等待收到指定消息\n * @en Wait for a specific message\n */\n waitForMessage<T = unknown>(type: string, timeout?: number): Promise<T> {\n return new Promise((resolve, reject) => {\n const timeoutMs = timeout ?? this._timeout;\n\n const timer = setTimeout(() => {\n this.off(type, handler);\n reject(new Error(`Timeout waiting for message '${type}' after ${timeoutMs}ms`));\n }, timeoutMs);\n\n const handler = (data: unknown) => {\n clearTimeout(timer);\n this.off(type, handler);\n resolve(data as T);\n };\n\n this.on(type, handler);\n });\n }\n\n /**\n * @zh 等待收到指定房间消息\n * @en Wait for a specific room message\n */\n waitForRoomMessage<T = unknown>(type: string, timeout?: number): Promise<T> {\n return new Promise((resolve, reject) => {\n const timeoutMs = timeout ?? this._timeout;\n\n const timer = setTimeout(() => {\n this.off('RoomMessage', handler);\n reject(new Error(`Timeout waiting for room message '${type}' after ${timeoutMs}ms`));\n }, timeoutMs);\n\n const handler = (data: unknown) => {\n const msg = data as { type: string; data: unknown };\n if (msg.type === type) {\n clearTimeout(timer);\n this.off('RoomMessage', handler);\n resolve(msg.data as T);\n }\n };\n\n this.on('RoomMessage', handler);\n });\n }\n\n // ========================================================================\n // Assertions | 断言辅助\n // ========================================================================\n\n /**\n * @zh 是否收到过指定消息\n * @en Whether received a specific message\n */\n hasReceivedMessage(type: string): boolean {\n return this._receivedMessages.some((m) => m.type === type);\n }\n\n /**\n * @zh 获取指定类型的所有消息\n * @en Get all messages of a specific type\n */\n getMessagesOfType<T = unknown>(type: string): T[] {\n return this._receivedMessages\n .filter((m) => m.type === type)\n .map((m) => m.data as T);\n }\n\n /**\n * @zh 获取最后收到的指定类型消息\n * @en Get the last received message of a specific type\n */\n getLastMessage<T = unknown>(type: string): T | undefined {\n for (let i = this._receivedMessages.length - 1; i >= 0; i--) {\n if (this._receivedMessages[i].type === type) {\n return this._receivedMessages[i].data as T;\n }\n }\n return undefined;\n }\n\n /**\n * @zh 清空消息记录\n * @en Clear message records\n */\n clearMessages(): void {\n this._receivedMessages.length = 0;\n }\n\n /**\n * @zh 获取收到的消息数量\n * @en Get received message count\n */\n getMessageCount(type?: string): number {\n if (type) {\n return this._receivedMessages.filter((m) => m.type === type).length;\n }\n return this._receivedMessages.length;\n }\n\n // ========================================================================\n // Private Methods | 私有方法\n // ========================================================================\n\n private _handleMessage(raw: Buffer): void {\n try {\n const packet = this._codec.decode(raw) as unknown[];\n const type = packet[0] as number;\n\n switch (type) {\n case PacketType.ApiResponse:\n this._handleApiResponse([packet[0], packet[1], packet[2]] as [number, number, unknown]);\n break;\n case PacketType.ApiError:\n this._handleApiError([packet[0], packet[1], packet[2], packet[3]] as [number, number, string, string]);\n break;\n case PacketType.Message:\n this._handleMsg([packet[0], packet[1], packet[2]] as [number, string, unknown]);\n break;\n }\n } catch (err) {\n logger.error('Failed to handle message:', err);\n }\n }\n\n private _handleApiResponse([, id, result]: [number, number, unknown]): void {\n const pending = this._pendingCalls.get(id);\n if (pending) {\n clearTimeout(pending.timer);\n this._pendingCalls.delete(id);\n pending.resolve(result);\n }\n }\n\n private _handleApiError([, id, code, message]: [number, number, string, string]): void {\n const pending = this._pendingCalls.get(id);\n if (pending) {\n clearTimeout(pending.timer);\n this._pendingCalls.delete(id);\n pending.reject(new Error(`[${code}] ${message}`));\n }\n }\n\n private _handleMsg([, name, data]: [number, string, unknown]): void {\n // 记录消息\n this._receivedMessages.push({\n type: name,\n data,\n timestamp: Date.now()\n });\n\n // 触发处理器\n const handlers = this._msgHandlers.get(name);\n if (handlers) {\n for (const handler of handlers) {\n try {\n handler(data);\n } catch (err) {\n logger.error('Handler error:', err);\n }\n }\n }\n }\n\n private _rejectAllPending(reason: string): void {\n for (const [, pending] of this._pendingCalls) {\n clearTimeout(pending.timer);\n pending.reject(new Error(reason));\n }\n this._pendingCalls.clear();\n }\n}\n","/**\n * @zh 测试服务器工具\n * @en Test server utilities\n */\n\nimport { createServer } from '../core/server.js';\nimport type { GameServer } from '../types/index.js';\nimport { TestClient, type TestClientOptions } from './TestClient.js';\nimport { LoggerManager, LogLevel } from '@esengine/ecs-framework';\n\n// ============================================================================\n// Types | 类型定义\n// ============================================================================\n\n/**\n * @zh 测试服务器配置\n * @en Test server options\n */\nexport interface TestServerOptions {\n /**\n * @zh 端口号,0 表示随机端口\n * @en Port number, 0 for random port\n * @defaultValue 0\n */\n port?: number\n\n /**\n * @zh Tick 速率\n * @en Tick rate\n * @defaultValue 0\n */\n tickRate?: number\n\n /**\n * @zh 是否禁用控制台日志\n * @en Whether to suppress console logs\n * @defaultValue true\n */\n silent?: boolean\n}\n\n/**\n * @zh 测试环境\n * @en Test environment\n */\nexport interface TestEnvironment {\n /**\n * @zh 服务器实例\n * @en Server instance\n */\n server: GameServer\n\n /**\n * @zh 服务器端口\n * @en Server port\n */\n port: number\n\n /**\n * @zh 创建测试客户端\n * @en Create test client\n */\n createClient(options?: TestClientOptions): Promise<TestClient>\n\n /**\n * @zh 创建多个测试客户端\n * @en Create multiple test clients\n */\n createClients(count: number, options?: TestClientOptions): Promise<TestClient[]>\n\n /**\n * @zh 清理测试环境\n * @en Cleanup test environment\n */\n cleanup(): Promise<void>\n\n /**\n * @zh 所有已创建的客户端\n * @en All created clients\n */\n readonly clients: ReadonlyArray<TestClient>\n}\n\n// ============================================================================\n// Helper Functions | 辅助函数\n// ============================================================================\n\n/**\n * @zh 获取随机可用端口\n * @en Get a random available port\n */\nasync function getRandomPort(): Promise<number> {\n const net = await import('node:net');\n return new Promise((resolve, reject) => {\n const server = net.createServer();\n server.listen(0, () => {\n const address = server.address();\n if (address && typeof address === 'object') {\n const port = address.port;\n server.close(() => resolve(port));\n } else {\n server.close(() => reject(new Error('Failed to get port')));\n }\n });\n server.on('error', reject);\n });\n}\n\n/**\n * @zh 等待指定毫秒\n * @en Wait for specified milliseconds\n */\nexport function wait(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// ============================================================================\n// Factory Functions | 工厂函数\n// ============================================================================\n\n/**\n * @zh 创建测试服务器\n * @en Create test server\n *\n * @example\n * ```typescript\n * const { server, port, cleanup } = await createTestServer()\n * server.define('game', GameRoom)\n *\n * const client = new TestClient(port)\n * await client.connect()\n *\n * // ... run tests ...\n *\n * await cleanup()\n * ```\n */\nexport async function createTestServer(\n options: TestServerOptions = {}\n): Promise<{ server: GameServer; port: number; cleanup: () => Promise<void> }> {\n const port = options.port || (await getRandomPort());\n const silent = options.silent ?? true;\n\n // 临时设置日志级别为 None(禁用所有日志)\n const loggerManager = LoggerManager.getInstance();\n let originalLevel: LogLevel | undefined;\n if (silent) {\n originalLevel = LogLevel.Info;\n loggerManager.setGlobalLevel(LogLevel.None);\n }\n\n const server = await createServer({\n port,\n tickRate: options.tickRate ?? 0,\n apiDir: '__non_existent_api__',\n msgDir: '__non_existent_msg__'\n });\n\n await server.start();\n\n // 恢复日志级别\n if (silent && originalLevel !== undefined) {\n loggerManager.setGlobalLevel(originalLevel);\n }\n\n return {\n server,\n port,\n cleanup: async () => {\n await server.stop();\n }\n };\n}\n\n/**\n * @zh 创建完整测试环境\n * @en Create complete test environment\n *\n * @zh 包含服务器、客户端创建和清理功能的完整测试环境\n * @en Complete test environment with server, client creation and cleanup\n *\n * @example\n * ```typescript\n * describe('GameRoom', () => {\n * let env: TestEnvironment\n *\n * beforeEach(async () => {\n * env = await createTestEnv()\n * env.server.define('game', GameRoom)\n * })\n *\n * afterEach(async () => {\n * await env.cleanup()\n * })\n *\n * it('should handle player join', async () => {\n * const client = await env.createClient()\n * const result = await client.joinRoom('game')\n * expect(result.roomId).toBeDefined()\n * })\n *\n * it('should broadcast to all players', async () => {\n * const [client1, client2] = await env.createClients(2)\n *\n * await client1.joinRoom('game')\n * const joinPromise = client1.waitForRoomMessage('PlayerJoined')\n *\n * await client2.joinRoom('game')\n * const msg = await joinPromise\n *\n * expect(msg).toBeDefined()\n * })\n * })\n * ```\n */\nexport async function createTestEnv(options: TestServerOptions = {}): Promise<TestEnvironment> {\n const { server, port, cleanup: serverCleanup } = await createTestServer(options);\n const clients: TestClient[] = [];\n\n return {\n server,\n port,\n clients,\n\n async createClient(clientOptions?: TestClientOptions): Promise<TestClient> {\n const client = new TestClient(port, clientOptions);\n await client.connect();\n clients.push(client);\n return client;\n },\n\n async createClients(count: number, clientOptions?: TestClientOptions): Promise<TestClient[]> {\n const newClients: TestClient[] = [];\n for (let i = 0; i < count; i++) {\n const client = new TestClient(port, clientOptions);\n await client.connect();\n clients.push(client);\n newClients.push(client);\n }\n return newClients;\n },\n\n async cleanup(): Promise<void> {\n // 断开所有客户端\n await Promise.all(clients.map((c) => c.disconnect().catch(() => {})));\n clients.length = 0;\n\n // 停止服务器\n await serverCleanup();\n }\n };\n}\n","/**\n * @zh 模拟房间\n * @en Mock room for testing\n */\n\nimport { Room, onMessage, type Player } from '../room/index.js';\n\n/**\n * @zh 模拟房间状态\n * @en Mock room state\n */\nexport interface MockRoomState {\n messages: Array<{ type: string; data: unknown; playerId: string }>\n joinCount: number\n leaveCount: number\n}\n\n/**\n * @zh 模拟房间\n * @en Mock room for testing\n *\n * @zh 记录所有事件和消息,用于测试断言\n * @en Records all events and messages for test assertions\n *\n * @example\n * ```typescript\n * const env = await createTestEnv()\n * env.server.define('mock', MockRoom)\n *\n * const client = await env.createClient()\n * await client.joinRoom('mock')\n *\n * client.sendToRoom('Test', { value: 123 })\n * await wait(50)\n *\n * // MockRoom 会广播收到的消息\n * const msg = client.getLastMessage('RoomMessage')\n * ```\n */\nexport class MockRoom extends Room<MockRoomState> {\n state: MockRoomState = {\n messages: [],\n joinCount: 0,\n leaveCount: 0\n };\n\n onCreate(): void {\n // 房间创建\n }\n\n onJoin(player: Player): void {\n this.state.joinCount++;\n this.broadcast('PlayerJoined', {\n playerId: player.id,\n joinCount: this.state.joinCount\n });\n }\n\n onLeave(player: Player): void {\n this.state.leaveCount++;\n this.broadcast('PlayerLeft', {\n playerId: player.id,\n leaveCount: this.state.leaveCount\n });\n }\n\n @onMessage('*')\n handleAnyMessage(data: unknown, player: Player, type: string): void {\n this.state.messages.push({\n type,\n data,\n playerId: player.id\n });\n\n // 回显消息给所有玩家\n this.broadcast('MessageReceived', {\n type,\n data,\n from: player.id\n });\n }\n\n @onMessage('Echo')\n handleEcho(data: unknown, player: Player): void {\n // 只回复给发送者\n player.send('EchoReply', data);\n }\n\n @onMessage('Broadcast')\n handleBroadcast(data: unknown, _player: Player): void {\n this.broadcast('BroadcastMessage', data);\n }\n\n @onMessage('Ping')\n handlePing(_data: unknown, player: Player): void {\n player.send('Pong', { timestamp: Date.now() });\n }\n}\n\n/**\n * @zh 简单回显房间\n * @en Simple echo room\n *\n * @zh 将收到的任何消息回显给发送者\n * @en Echoes any received message back to sender\n */\nexport class EchoRoom extends Room {\n @onMessage('*')\n handleAnyMessage(data: unknown, player: Player, type: string): void {\n player.send(type, data);\n }\n}\n\n/**\n * @zh 广播房间\n * @en Broadcast room\n *\n * @zh 将收到的任何消息广播给所有玩家\n * @en Broadcasts any received message to all players\n */\nexport class BroadcastRoom extends Room {\n onJoin(player: Player): void {\n this.broadcast('PlayerJoined', { id: player.id });\n }\n\n onLeave(player: Player): void {\n this.broadcast('PlayerLeft', { id: player.id });\n }\n\n @onMessage('*')\n handleAnyMessage(data: unknown, player: Player, type: string): void {\n this.broadcast(type, { from: player.id, data });\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@esengine/server",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.0",
|
|
4
4
|
"description": "Game server framework for ESEngine with file-based routing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -37,19 +37,16 @@
|
|
|
37
37
|
"module.json"
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@esengine/rpc": "1.1.
|
|
40
|
+
"@esengine/rpc": "1.1.3",
|
|
41
|
+
"@esengine/ecs-framework": "2.7.1"
|
|
41
42
|
},
|
|
42
43
|
"peerDependencies": {
|
|
43
44
|
"ws": ">=8.0.0",
|
|
44
|
-
"jsonwebtoken": ">=9.0.0"
|
|
45
|
-
"@esengine/ecs-framework": ">=2.7.1"
|
|
45
|
+
"jsonwebtoken": ">=9.0.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependenciesMeta": {
|
|
48
48
|
"jsonwebtoken": {
|
|
49
49
|
"optional": true
|
|
50
|
-
},
|
|
51
|
-
"@esengine/ecs-framework": {
|
|
52
|
-
"optional": true
|
|
53
50
|
}
|
|
54
51
|
},
|
|
55
52
|
"devDependencies": {
|
|
@@ -61,8 +58,7 @@
|
|
|
61
58
|
"tsup": "^8.0.0",
|
|
62
59
|
"typescript": "^5.7.0",
|
|
63
60
|
"vitest": "^2.0.0",
|
|
64
|
-
"ws": "^8.18.0"
|
|
65
|
-
"@esengine/ecs-framework": "2.7.1"
|
|
61
|
+
"ws": "^8.18.0"
|
|
66
62
|
},
|
|
67
63
|
"publishConfig": {
|
|
68
64
|
"access": "public"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/http/router.ts","../src/router/loader.ts","../src/room/RoomManager.ts","../src/core/server.ts"],"names":["createRequest","req","url","URL","headers","host","query","searchParams","forEach","value","key","body","method","parseBody","ip","split","trim","socket","remoteAddress","raw","path","pathname","Promise","resolve","chunks","on","chunk","push","rawBody","Buffer","concat","toString","contentType","includes","JSON","parse","params","URLSearchParams","result","createResponse","res","statusCode","response","status","code","header","name","setHeader","json","data","end","stringify","text","error","message","applyCors","cors","origin","Array","isArray","methods","join","allowedHeaders","credentials","maxAge","String","createHttpRouter","routes","parsedRoutes","handlerOrMethods","Object","entries","handler","undefined","corsOptions","handleRequest","route","find","r","httpReq","httpRes","console","fileNameToHandlerName","fileName","baseName","replace","map","part","charAt","toUpperCase","slice","scanDirectory","dir","existsSync","files","readdirSync","withFileTypes","entry","isFile","test","startsWith","loadApiHandlers","apiDir","handlers","filePath","fileUrl","pathToFileURL","href","module","definition","default","basename","err","warn","loadMsgHandlers","msgDir","scanDirectoryRecursive","baseDir","fullPath","isDirectory","relativePath","relative","filePathToRoute","prefix","fullRoute","endsWith","loadHttpHandlers","httpDir","RoomManager","sendFn","_definitions","Map","_rooms","_playerToRoom","_nextRoomId","_sendFn","define","roomClass","set","create","options","def","get","roomId","_generateRoomId","room","_init","id","broadcastFn","type","player","players","send","disposeFn","delete","_create","log","joinOrCreate","playerId","conn","_findAvailableRoom","_addPlayer","joinById","leave","reason","_removePlayer","handleMessage","_handleMessage","getRoom","getPlayerRoom","getRooms","from","values","getRoomsByType","filter","isFull","isLocked","isDisposed","DEFAULT_CONFIG","port","httpPrefix","tickRate","createServer","config","opts","cwd","process","apiHandlers","msgHandlers","httpHandlers","length","mergedHttpRoutes","existingRoute","http","existing","assign","hasHttpRoutes","keys","apiDefs","JoinRoom","rpc","api","LeaveRoom","msgDefs","RoomMessage","msg","protocol","currentTick","tickInterval","rpcServer","httpServer","roomManager","apiMap","msgMap","gameServer","connections","tick","rooms","start","apiHandlersObj","input","roomType","Error","_input","success","ctx","server","msgHandlersObj","payload","httpRouter","createHttpServer","handled","serve","createConnData","onStart","onConnect","onDisconnect","listen","p","setInterval","stop","clearInterval","reject","close","broadcast"],"mappings":"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","file":"chunk-BIAOJF7P.js","sourcesContent":["/**\n * @zh HTTP 路由器\n * @en HTTP Router\n *\n * @zh 简洁的 HTTP 路由实现,支持与 WebSocket 共用端口\n * @en Simple HTTP router implementation, supports sharing port with WebSocket\n */\n\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport type {\n HttpRequest,\n HttpResponse,\n HttpHandler,\n HttpRoutes,\n CorsOptions,\n} from './types.js';\n\n/**\n * @zh 创建 HTTP 请求对象\n * @en Create HTTP request object\n */\nasync function createRequest(req: IncomingMessage): Promise<HttpRequest> {\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);\n\n // 解析查询参数\n const query: Record<string, string> = {};\n url.searchParams.forEach((value, key) => {\n query[key] = value;\n });\n\n // 解析请求体\n let body: unknown = null;\n if (req.method === 'POST' || req.method === 'PUT' || req.method === 'PATCH') {\n body = await parseBody(req);\n }\n\n // 获取客户端 IP\n const ip =\n (req.headers['x-forwarded-for'] as string)?.split(',')[0]?.trim() ||\n req.socket?.remoteAddress ||\n 'unknown';\n\n return {\n raw: req,\n method: req.method ?? 'GET',\n path: url.pathname,\n query,\n headers: req.headers as Record<string, string | string[] | undefined>,\n body,\n ip,\n };\n}\n\n/**\n * @zh 解析请求体\n * @en Parse request body\n */\nfunction parseBody(req: IncomingMessage): Promise<unknown> {\n return new Promise((resolve) => {\n const chunks: Buffer[] = [];\n\n req.on('data', (chunk: Buffer) => {\n chunks.push(chunk);\n });\n\n req.on('end', () => {\n const rawBody = Buffer.concat(chunks).toString('utf-8');\n\n if (!rawBody) {\n resolve(null);\n return;\n }\n\n const contentType = req.headers['content-type'] ?? '';\n\n if (contentType.includes('application/json')) {\n try {\n resolve(JSON.parse(rawBody));\n } catch {\n resolve(rawBody);\n }\n } else if (contentType.includes('application/x-www-form-urlencoded')) {\n const params = new URLSearchParams(rawBody);\n const result: Record<string, string> = {};\n params.forEach((value, key) => {\n result[key] = value;\n });\n resolve(result);\n } else {\n resolve(rawBody);\n }\n });\n\n req.on('error', () => {\n resolve(null);\n });\n });\n}\n\n/**\n * @zh 创建 HTTP 响应对象\n * @en Create HTTP response object\n */\nfunction createResponse(res: ServerResponse): HttpResponse {\n let statusCode = 200;\n\n const response: HttpResponse = {\n raw: res,\n\n status(code: number) {\n statusCode = code;\n return response;\n },\n\n header(name: string, value: string) {\n res.setHeader(name, value);\n return response;\n },\n\n json(data: unknown) {\n res.setHeader('Content-Type', 'application/json; charset=utf-8');\n res.statusCode = statusCode;\n res.end(JSON.stringify(data));\n },\n\n text(data: string) {\n res.setHeader('Content-Type', 'text/plain; charset=utf-8');\n res.statusCode = statusCode;\n res.end(data);\n },\n\n error(code: number, message: string) {\n res.setHeader('Content-Type', 'application/json; charset=utf-8');\n res.statusCode = code;\n res.end(JSON.stringify({ error: message }));\n },\n };\n\n return response;\n}\n\n/**\n * @zh 应用 CORS 头\n * @en Apply CORS headers\n */\nfunction applyCors(res: ServerResponse, req: IncomingMessage, cors: CorsOptions): void {\n const origin = req.headers.origin;\n\n // 处理 origin\n if (cors.origin === true || cors.origin === '*') {\n res.setHeader('Access-Control-Allow-Origin', origin ?? '*');\n } else if (typeof cors.origin === 'string') {\n res.setHeader('Access-Control-Allow-Origin', cors.origin);\n } else if (Array.isArray(cors.origin) && origin && cors.origin.includes(origin)) {\n res.setHeader('Access-Control-Allow-Origin', origin);\n }\n\n // 允许的方法\n if (cors.methods) {\n res.setHeader('Access-Control-Allow-Methods', cors.methods.join(', '));\n } else {\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');\n }\n\n // 允许的头\n if (cors.allowedHeaders) {\n res.setHeader('Access-Control-Allow-Headers', cors.allowedHeaders.join(', '));\n } else {\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');\n }\n\n // 凭证\n if (cors.credentials) {\n res.setHeader('Access-Control-Allow-Credentials', 'true');\n }\n\n // 缓存\n if (cors.maxAge) {\n res.setHeader('Access-Control-Max-Age', String(cors.maxAge));\n }\n}\n\n/**\n * @zh 创建 HTTP 路由器\n * @en Create HTTP router\n */\nexport function createHttpRouter(routes: HttpRoutes, cors?: CorsOptions | boolean) {\n // 解析路由\n const parsedRoutes: Array<{\n method: string;\n path: string;\n handler: HttpHandler;\n }> = [];\n\n for (const [path, handlerOrMethods] of Object.entries(routes)) {\n if (typeof handlerOrMethods === 'function') {\n // 简单形式:路径 -> 处理器(接受所有方法)\n parsedRoutes.push({ method: '*', path, handler: handlerOrMethods });\n } else {\n // 对象形式:路径 -> { GET, POST, ... }\n for (const [method, handler] of Object.entries(handlerOrMethods)) {\n if (handler !== undefined) {\n parsedRoutes.push({ method, path, handler });\n }\n }\n }\n }\n\n // 默认 CORS 配置\n const corsOptions: CorsOptions | null =\n cors === true\n ? { origin: true, credentials: true }\n : cors === false\n ? null\n : cors ?? null;\n\n /**\n * @zh 处理 HTTP 请求\n * @en Handle HTTP request\n */\n return async function handleRequest(\n req: IncomingMessage,\n res: ServerResponse\n ): Promise<boolean> {\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);\n const path = url.pathname;\n const method = req.method ?? 'GET';\n\n // 应用 CORS\n if (corsOptions) {\n applyCors(res, req, corsOptions);\n\n // 处理预检请求\n if (method === 'OPTIONS') {\n res.statusCode = 204;\n res.end();\n return true;\n }\n }\n\n // 查找匹配的路由\n const route = parsedRoutes.find(\n (r) => r.path === path && (r.method === '*' || r.method === method)\n );\n\n if (!route) {\n return false; // 未找到路由,让其他处理器处理\n }\n\n try {\n const httpReq = await createRequest(req);\n const httpRes = createResponse(res);\n await route.handler(httpReq, httpRes);\n return true;\n } catch (error) {\n console.error('[HTTP] Route handler error:', error);\n res.statusCode = 500;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: 'Internal Server Error' }));\n return true;\n }\n };\n}\n","/**\n * @zh 文件路由加载器\n * @en File-based router loader\n */\n\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport type {\n ApiDefinition,\n MsgDefinition,\n HttpDefinition,\n LoadedApiHandler,\n LoadedMsgHandler,\n LoadedHttpHandler,\n HttpMethod,\n} from '../types/index.js'\n\n/**\n * @zh 将文件名转换为 API/消息名称\n * @en Convert filename to API/message name\n *\n * @example\n * 'join.ts' -> 'Join'\n * 'spawn-agent.ts' -> 'SpawnAgent'\n * 'save_blueprint.ts' -> 'SaveBlueprint'\n */\nfunction fileNameToHandlerName(fileName: string): string {\n const baseName = fileName.replace(/\\.(ts|js|mts|mjs)$/, '')\n\n return baseName\n .split(/[-_]/)\n .map(part => part.charAt(0).toUpperCase() + part.slice(1))\n .join('')\n}\n\n/**\n * @zh 扫描目录获取所有处理器文件\n * @en Scan directory for all handler files\n */\nfunction scanDirectory(dir: string): string[] {\n if (!fs.existsSync(dir)) {\n return []\n }\n\n const files: string[] = []\n const entries = fs.readdirSync(dir, { withFileTypes: true })\n\n for (const entry of entries) {\n if (entry.isFile() && /\\.(ts|js|mts|mjs)$/.test(entry.name)) {\n // 跳过 index 和下划线开头的文件\n if (entry.name.startsWith('_') || entry.name.startsWith('index.')) {\n continue\n }\n files.push(path.join(dir, entry.name))\n }\n }\n\n return files\n}\n\n/**\n * @zh 加载 API 处理器\n * @en Load API handlers\n */\nexport async function loadApiHandlers(apiDir: string): Promise<LoadedApiHandler[]> {\n const files = scanDirectory(apiDir)\n const handlers: LoadedApiHandler[] = []\n\n for (const filePath of files) {\n try {\n const fileUrl = pathToFileURL(filePath).href\n const module = await import(fileUrl)\n const definition = module.default as ApiDefinition<unknown, unknown, unknown>\n\n if (definition && typeof definition.handler === 'function') {\n const name = fileNameToHandlerName(path.basename(filePath))\n handlers.push({\n name,\n path: filePath,\n definition,\n })\n }\n } catch (err) {\n console.warn(`[Server] Failed to load API handler: ${filePath}`, err)\n }\n }\n\n return handlers\n}\n\n/**\n * @zh 加载消息处理器\n * @en Load message handlers\n */\nexport async function loadMsgHandlers(msgDir: string): Promise<LoadedMsgHandler[]> {\n const files = scanDirectory(msgDir)\n const handlers: LoadedMsgHandler[] = []\n\n for (const filePath of files) {\n try {\n const fileUrl = pathToFileURL(filePath).href\n const module = await import(fileUrl)\n const definition = module.default as MsgDefinition<unknown, unknown>\n\n if (definition && typeof definition.handler === 'function') {\n const name = fileNameToHandlerName(path.basename(filePath))\n handlers.push({\n name,\n path: filePath,\n definition,\n })\n }\n } catch (err) {\n console.warn(`[Server] Failed to load msg handler: ${filePath}`, err)\n }\n }\n\n return handlers\n}\n\n/**\n * @zh 递归扫描目录获取所有处理器文件\n * @en Recursively scan directory for all handler files\n */\nfunction scanDirectoryRecursive(dir: string, baseDir: string = dir): Array<{ filePath: string; relativePath: string }> {\n if (!fs.existsSync(dir)) {\n return []\n }\n\n const files: Array<{ filePath: string; relativePath: string }> = []\n const entries = fs.readdirSync(dir, { withFileTypes: true })\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name)\n\n if (entry.isDirectory()) {\n files.push(...scanDirectoryRecursive(fullPath, baseDir))\n } else if (entry.isFile() && /\\.(ts|js|mts|mjs)$/.test(entry.name)) {\n if (entry.name.startsWith('_') || entry.name.startsWith('index.')) {\n continue\n }\n const relativePath = path.relative(baseDir, fullPath)\n files.push({ filePath: fullPath, relativePath })\n }\n }\n\n return files\n}\n\n/**\n * @zh 将文件路径转换为路由路径\n * @en Convert file path to route path\n *\n * @example\n * 'login.ts' -> '/login'\n * 'users/profile.ts' -> '/users/profile'\n * 'users/[id].ts' -> '/users/:id'\n */\nfunction filePathToRoute(relativePath: string, prefix: string): string {\n let route = relativePath\n .replace(/\\.(ts|js|mts|mjs)$/, '')\n .replace(/\\\\/g, '/')\n .replace(/\\[([^\\]]+)\\]/g, ':$1')\n\n if (!route.startsWith('/')) {\n route = '/' + route\n }\n\n const fullRoute = prefix.endsWith('/')\n ? prefix.slice(0, -1) + route\n : prefix + route\n\n return fullRoute\n}\n\n/**\n * @zh 加载 HTTP 路由处理器\n * @en Load HTTP route handlers\n *\n * @example\n * ```typescript\n * // Directory structure:\n * // src/http/\n * // login.ts -> POST /api/login\n * // register.ts -> POST /api/register\n * // users/\n * // [id].ts -> GET /api/users/:id\n *\n * const handlers = await loadHttpHandlers('src/http', '/api')\n * ```\n */\nexport async function loadHttpHandlers(\n httpDir: string,\n prefix: string = '/api'\n): Promise<LoadedHttpHandler[]> {\n const files = scanDirectoryRecursive(httpDir)\n const handlers: LoadedHttpHandler[] = []\n\n for (const { filePath, relativePath } of files) {\n try {\n const fileUrl = pathToFileURL(filePath).href\n const module = await import(fileUrl)\n const definition = module.default as HttpDefinition<unknown>\n\n if (definition && typeof definition.handler === 'function') {\n const route = filePathToRoute(relativePath, prefix)\n const method: HttpMethod = definition.method ?? 'POST'\n\n handlers.push({\n route,\n method,\n path: filePath,\n definition,\n })\n }\n } catch (err) {\n console.warn(`[Server] Failed to load HTTP handler: ${filePath}`, err)\n }\n }\n\n return handlers\n}\n","/**\n * @zh 房间管理器\n * @en Room manager\n */\n\nimport { Room, type RoomOptions } from './Room.js'\nimport type { Player } from './Player.js'\n\n/**\n * @zh 房间类型\n * @en Room class type\n */\nexport type RoomClass<T extends Room = Room> = new () => T\n\n/**\n * @zh 房间定义\n * @en Room definition\n */\ninterface RoomDefinition {\n roomClass: RoomClass\n}\n\n/**\n * @zh 房间管理器\n * @en Room manager\n */\nexport class RoomManager {\n private _definitions: Map<string, RoomDefinition> = new Map()\n private _rooms: Map<string, Room> = new Map()\n private _playerToRoom: Map<string, string> = new Map()\n private _nextRoomId = 1\n\n private _sendFn: (conn: any, type: string, data: unknown) => void\n\n constructor(sendFn: (conn: any, type: string, data: unknown) => void) {\n this._sendFn = sendFn\n }\n\n /**\n * @zh 注册房间类型\n * @en Define room type\n */\n define<T extends Room>(name: string, roomClass: RoomClass<T>): void {\n this._definitions.set(name, { roomClass })\n }\n\n /**\n * @zh 创建房间\n * @en Create room\n */\n async create(name: string, options?: RoomOptions): Promise<Room | null> {\n const def = this._definitions.get(name)\n if (!def) {\n console.warn(`[RoomManager] Room type not found: ${name}`)\n return null\n }\n\n const roomId = this._generateRoomId()\n const room = new def.roomClass()\n\n room._init({\n id: roomId,\n sendFn: this._sendFn,\n broadcastFn: (type, data) => {\n for (const player of room.players) {\n player.send(type, data)\n }\n },\n disposeFn: () => {\n this._rooms.delete(roomId)\n },\n })\n\n this._rooms.set(roomId, room)\n await room._create(options)\n\n console.log(`[Room] Created: ${name} (${roomId})`)\n return room\n }\n\n /**\n * @zh 加入或创建房间\n * @en Join or create room\n */\n async joinOrCreate(\n name: string,\n playerId: string,\n conn: any,\n options?: RoomOptions\n ): Promise<{ room: Room; player: Player } | null> {\n // 查找可加入的房间\n let room = this._findAvailableRoom(name)\n\n // 没有则创建\n if (!room) {\n room = await this.create(name, options)\n if (!room) return null\n }\n\n // 加入房间\n const player = await room._addPlayer(playerId, conn)\n if (!player) return null\n\n this._playerToRoom.set(playerId, room.id)\n\n console.log(`[Room] Player ${playerId} joined ${room.id}`)\n return { room, player }\n }\n\n /**\n * @zh 加入指定房间\n * @en Join specific room\n */\n async joinById(\n roomId: string,\n playerId: string,\n conn: any\n ): Promise<{ room: Room; player: Player } | null> {\n const room = this._rooms.get(roomId)\n if (!room) return null\n\n const player = await room._addPlayer(playerId, conn)\n if (!player) return null\n\n this._playerToRoom.set(playerId, room.id)\n\n console.log(`[Room] Player ${playerId} joined ${room.id}`)\n return { room, player }\n }\n\n /**\n * @zh 玩家离开\n * @en Player leave\n */\n async leave(playerId: string, reason?: string): Promise<void> {\n const roomId = this._playerToRoom.get(playerId)\n if (!roomId) return\n\n const room = this._rooms.get(roomId)\n if (room) {\n await room._removePlayer(playerId, reason)\n }\n\n this._playerToRoom.delete(playerId)\n console.log(`[Room] Player ${playerId} left ${roomId}`)\n }\n\n /**\n * @zh 处理消息\n * @en Handle message\n */\n handleMessage(playerId: string, type: string, data: unknown): void {\n const roomId = this._playerToRoom.get(playerId)\n if (!roomId) return\n\n const room = this._rooms.get(roomId)\n if (room) {\n room._handleMessage(type, data, playerId)\n }\n }\n\n /**\n * @zh 获取房间\n * @en Get room\n */\n getRoom(roomId: string): Room | undefined {\n return this._rooms.get(roomId)\n }\n\n /**\n * @zh 获取玩家所在房间\n * @en Get player's room\n */\n getPlayerRoom(playerId: string): Room | undefined {\n const roomId = this._playerToRoom.get(playerId)\n return roomId ? this._rooms.get(roomId) : undefined\n }\n\n /**\n * @zh 获取所有房间\n * @en Get all rooms\n */\n getRooms(): ReadonlyArray<Room> {\n return Array.from(this._rooms.values())\n }\n\n /**\n * @zh 获取指定类型的所有房间\n * @en Get all rooms of a type\n */\n getRoomsByType(name: string): Room[] {\n const def = this._definitions.get(name)\n if (!def) return []\n\n return Array.from(this._rooms.values()).filter(\n room => room instanceof def.roomClass\n )\n }\n\n private _findAvailableRoom(name: string): Room | null {\n const def = this._definitions.get(name)\n if (!def) return null\n\n for (const room of this._rooms.values()) {\n if (\n room instanceof def.roomClass &&\n !room.isFull &&\n !room.isLocked &&\n !room.isDisposed\n ) {\n return room\n }\n }\n\n return null\n }\n\n private _generateRoomId(): string {\n return `room_${this._nextRoomId++}`\n }\n}\n","/**\n * @zh 游戏服务器核心\n * @en Game server core\n */\n\nimport * as path from 'node:path'\nimport { createServer as createHttpServer, type Server as HttpServer } from 'node:http'\nimport { serve, type RpcServer } from '@esengine/rpc/server'\nimport { rpc } from '@esengine/rpc'\nimport type {\n ServerConfig,\n ServerConnection,\n GameServer,\n ApiContext,\n MsgContext,\n LoadedApiHandler,\n LoadedMsgHandler,\n LoadedHttpHandler,\n} from '../types/index.js'\nimport type { HttpRoutes, HttpHandler } from '../http/types.js'\nimport { loadApiHandlers, loadMsgHandlers, loadHttpHandlers } from '../router/loader.js'\nimport { RoomManager, type RoomClass, type Room } from '../room/index.js'\nimport { createHttpRouter } from '../http/router.js'\n\n/**\n * @zh 默认配置\n * @en Default configuration\n */\nconst DEFAULT_CONFIG: Required<Omit<ServerConfig, 'onStart' | 'onConnect' | 'onDisconnect' | 'http' | 'cors' | 'httpDir' | 'httpPrefix'>> & { httpDir: string; httpPrefix: string } = {\n port: 3000,\n apiDir: 'src/api',\n msgDir: 'src/msg',\n httpDir: 'src/http',\n httpPrefix: '/api',\n tickRate: 20,\n}\n\n/**\n * @zh 创建游戏服务器\n * @en Create game server\n *\n * @example\n * ```typescript\n * import { createServer, Room, onMessage } from '@esengine/server'\n *\n * class GameRoom extends Room {\n * onJoin(player) {\n * this.broadcast('Joined', { id: player.id })\n * }\n * }\n *\n * const server = await createServer({ port: 3000 })\n * server.define('game', GameRoom)\n * await server.start()\n * ```\n */\nexport async function createServer(config: ServerConfig = {}): Promise<GameServer> {\n const opts = { ...DEFAULT_CONFIG, ...config }\n const cwd = process.cwd()\n\n // 加载文件路由处理器\n const apiHandlers = await loadApiHandlers(path.resolve(cwd, opts.apiDir))\n const msgHandlers = await loadMsgHandlers(path.resolve(cwd, opts.msgDir))\n\n // 加载 HTTP 文件路由\n const httpDir = config.httpDir ?? opts.httpDir\n const httpPrefix = config.httpPrefix ?? opts.httpPrefix\n const httpHandlers = await loadHttpHandlers(path.resolve(cwd, httpDir), httpPrefix)\n\n if (apiHandlers.length > 0) {\n console.log(`[Server] Loaded ${apiHandlers.length} API handlers`)\n }\n if (msgHandlers.length > 0) {\n console.log(`[Server] Loaded ${msgHandlers.length} message handlers`)\n }\n if (httpHandlers.length > 0) {\n console.log(`[Server] Loaded ${httpHandlers.length} HTTP handlers`)\n }\n\n // 合并 HTTP 路由(文件路由 + 内联路由)\n const mergedHttpRoutes: HttpRoutes = {}\n\n // 先添加文件路由\n for (const handler of httpHandlers) {\n const existingRoute = mergedHttpRoutes[handler.route]\n if (existingRoute && typeof existingRoute !== 'function') {\n (existingRoute as Record<string, HttpHandler>)[handler.method] = handler.definition.handler\n } else {\n mergedHttpRoutes[handler.route] = {\n [handler.method]: handler.definition.handler,\n }\n }\n }\n\n // 再添加内联路由(覆盖文件路由)\n if (config.http) {\n for (const [route, handlerOrMethods] of Object.entries(config.http)) {\n if (typeof handlerOrMethods === 'function') {\n mergedHttpRoutes[route] = handlerOrMethods\n } else {\n const existing = mergedHttpRoutes[route]\n if (existing && typeof existing !== 'function') {\n Object.assign(existing, handlerOrMethods)\n } else {\n mergedHttpRoutes[route] = handlerOrMethods\n }\n }\n }\n }\n\n const hasHttpRoutes = Object.keys(mergedHttpRoutes).length > 0\n\n // 动态构建协议\n const apiDefs: Record<string, ReturnType<typeof rpc.api>> = {\n // 内置 API\n JoinRoom: rpc.api(),\n LeaveRoom: rpc.api(),\n }\n const msgDefs: Record<string, ReturnType<typeof rpc.msg>> = {\n // 内置消息(房间消息透传)\n RoomMessage: rpc.msg(),\n }\n\n for (const handler of apiHandlers) {\n apiDefs[handler.name] = rpc.api()\n }\n for (const handler of msgHandlers) {\n msgDefs[handler.name] = rpc.msg()\n }\n\n const protocol = rpc.define({\n api: apiDefs,\n msg: msgDefs,\n })\n\n // 服务器状态\n let currentTick = 0\n let tickInterval: ReturnType<typeof setInterval> | null = null\n let rpcServer: RpcServer<typeof protocol, Record<string, unknown>> | null = null\n let httpServer: HttpServer | null = null\n\n // 房间管理器(立即初始化,以便 define() 可在 start() 前调用)\n const roomManager = new RoomManager((conn, type, data) => {\n rpcServer?.send(conn, 'RoomMessage' as any, { type, data } as any)\n })\n\n // 构建 API 处理器映射\n const apiMap: Record<string, LoadedApiHandler> = {}\n for (const handler of apiHandlers) {\n apiMap[handler.name] = handler\n }\n\n // 构建消息处理器映射\n const msgMap: Record<string, LoadedMsgHandler> = {}\n for (const handler of msgHandlers) {\n msgMap[handler.name] = handler\n }\n\n // 游戏服务器实例\n const gameServer: GameServer & {\n rooms: RoomManager\n } = {\n get connections() {\n return (rpcServer?.connections ?? []) as ReadonlyArray<ServerConnection>\n },\n\n get tick() {\n return currentTick\n },\n\n get rooms() {\n return roomManager\n },\n\n /**\n * @zh 注册房间类型\n * @en Define room type\n */\n define(name: string, roomClass: new () => unknown): void {\n roomManager.define(name, roomClass as RoomClass)\n },\n\n async start() {\n // 构建 API handlers\n const apiHandlersObj: Record<string, (input: unknown, conn: any) => Promise<unknown>> = {}\n\n // 内置 JoinRoom API\n apiHandlersObj['JoinRoom'] = async (input: any, conn) => {\n const { roomType, roomId, options } = input as {\n roomType?: string\n roomId?: string\n options?: Record<string, unknown>\n }\n\n if (roomId) {\n const result = await roomManager.joinById(roomId, conn.id, conn)\n if (!result) {\n throw new Error('Failed to join room')\n }\n return { roomId: result.room.id, playerId: result.player.id }\n }\n\n if (roomType) {\n const result = await roomManager.joinOrCreate(roomType, conn.id, conn, options)\n if (!result) {\n throw new Error('Failed to join or create room')\n }\n return { roomId: result.room.id, playerId: result.player.id }\n }\n\n throw new Error('roomType or roomId required')\n }\n\n // 内置 LeaveRoom API\n apiHandlersObj['LeaveRoom'] = async (_input, conn) => {\n await roomManager.leave(conn.id)\n return { success: true }\n }\n\n // 文件路由 API\n for (const [name, handler] of Object.entries(apiMap)) {\n apiHandlersObj[name] = async (input, conn) => {\n const ctx: ApiContext = {\n conn: conn as ServerConnection,\n server: gameServer,\n }\n return handler.definition.handler(input, ctx)\n }\n }\n\n // 构建消息 handlers\n const msgHandlersObj: Record<string, (data: unknown, conn: any) => void | Promise<void>> = {}\n\n // 内置 RoomMessage 处理\n msgHandlersObj['RoomMessage'] = async (data: any, conn) => {\n const { type, data: payload } = data as { type: string; data: unknown }\n roomManager.handleMessage(conn.id, type, payload)\n }\n\n // 文件路由消息\n for (const [name, handler] of Object.entries(msgMap)) {\n msgHandlersObj[name] = async (data, conn) => {\n const ctx: MsgContext = {\n conn: conn as ServerConnection,\n server: gameServer,\n }\n await handler.definition.handler(data, ctx)\n }\n }\n\n // 如果有 HTTP 路由,创建 HTTP 服务器\n if (hasHttpRoutes) {\n const httpRouter = createHttpRouter(mergedHttpRoutes, config.cors ?? true)\n\n httpServer = createHttpServer(async (req, res) => {\n // 先尝试 HTTP 路由\n const handled = await httpRouter(req, res)\n if (!handled) {\n // 未匹配的请求返回 404\n res.statusCode = 404\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify({ error: 'Not Found' }))\n }\n })\n\n // 使用 HTTP 服务器创建 RPC\n rpcServer = serve(protocol, {\n server: httpServer,\n createConnData: () => ({}),\n onStart: () => {\n console.log(`[Server] Started on http://localhost:${opts.port}`)\n opts.onStart?.(opts.port)\n },\n onConnect: async (conn) => {\n await config.onConnect?.(conn as ServerConnection)\n },\n onDisconnect: async (conn) => {\n await roomManager?.leave(conn.id, 'disconnected')\n await config.onDisconnect?.(conn as ServerConnection)\n },\n api: apiHandlersObj as any,\n msg: msgHandlersObj as any,\n })\n\n await rpcServer.start()\n\n // 启动 HTTP 服务器\n await new Promise<void>((resolve) => {\n httpServer!.listen(opts.port, () => resolve())\n })\n } else {\n // 仅 WebSocket 模式\n rpcServer = serve(protocol, {\n port: opts.port,\n createConnData: () => ({}),\n onStart: (p) => {\n console.log(`[Server] Started on ws://localhost:${p}`)\n opts.onStart?.(p)\n },\n onConnect: async (conn) => {\n await config.onConnect?.(conn as ServerConnection)\n },\n onDisconnect: async (conn) => {\n await roomManager?.leave(conn.id, 'disconnected')\n await config.onDisconnect?.(conn as ServerConnection)\n },\n api: apiHandlersObj as any,\n msg: msgHandlersObj as any,\n })\n\n await rpcServer.start()\n }\n\n // 启动 tick 循环\n if (opts.tickRate > 0) {\n tickInterval = setInterval(() => {\n currentTick++\n }, 1000 / opts.tickRate)\n }\n },\n\n async stop() {\n if (tickInterval) {\n clearInterval(tickInterval)\n tickInterval = null\n }\n if (rpcServer) {\n await rpcServer.stop()\n rpcServer = null\n }\n if (httpServer) {\n await new Promise<void>((resolve, reject) => {\n httpServer!.close((err) => {\n if (err) reject(err)\n else resolve()\n })\n })\n httpServer = null\n }\n },\n\n broadcast(name, data) {\n rpcServer?.broadcast(name as any, data as any)\n },\n\n send(conn, name, data) {\n rpcServer?.send(conn as any, name as any, data as any)\n },\n }\n\n return gameServer as GameServer\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/room/Player.ts","../src/room/Room.ts","../src/room/decorators.ts"],"names":["Player","options","id","roomId","data","_conn","_sendFn","_leaveFn","conn","sendFn","leaveFn","initialData","connection","send","type","leave","reason","MESSAGE_HANDLERS","Room","maxPlayers","tickRate","autoDispose","state","_id","_players","Map","_locked","_disposed","_tickInterval","_lastTickTime","_broadcastFn","_disposeFn","players","Array","from","values","playerCount","size","isFull","isLocked","isDisposed","onCreate","onJoin","player","onLeave","onTick","dt","onDispose","broadcast","broadcastExcept","except","getPlayer","get","kick","lock","unlock","dispose","_stopTick","clear","_init","broadcastFn","disposeFn","_create","_startTick","_addPlayer","p","_removePlayer","set","delete","_handleMessage","playerId","handlers","handler","method","call","performance","now","setInterval","clearInterval","registerMessageHandler","target","push","onMessage","propertyKey","_descriptor"],"mappings":";;;AAuBO,IAAMA,OAAAA,GAAN,MAAMA,OAAAA,CAAAA;AAST,EAAA,WAAA,CAAYC,OAAAA,EAOT;AAfMC,IAAAA,aAAAA,CAAAA,IAAAA,EAAAA,IAAAA,CAAAA;AACAC,IAAAA,aAAAA,CAAAA,IAAAA,EAAAA,QAAAA,CAAAA;AACTC,IAAAA,aAAAA,CAAAA,IAAAA,EAAAA,MAAAA,CAAAA;AAEQC,IAAAA,aAAAA,CAAAA,IAAAA,EAAAA,OAAAA,CAAAA;AACAC,IAAAA,aAAAA,CAAAA,IAAAA,EAAAA,SAAAA,CAAAA;AACAC,IAAAA,aAAAA,CAAAA,IAAAA,EAAAA,UAAAA,CAAAA;AAUJ,IAAA,IAAA,CAAKL,KAAKD,OAAAA,CAAQC,EAAAA;AAClB,IAAA,IAAA,CAAKC,SAASF,OAAAA,CAAQE,MAAAA;AACtB,IAAA,IAAA,CAAKE,QAAQJ,OAAAA,CAAQO,IAAAA;AACrB,IAAA,IAAA,CAAKF,UAAUL,OAAAA,CAAQQ,MAAAA;AACvB,IAAA,IAAA,CAAKF,WAAWN,OAAAA,CAAQS,OAAAA;AACxB,IAAA,IAAA,CAAKN,IAAAA,GAAOH,OAAAA,CAAQU,WAAAA,IAAgB,EAAC;AACzC,EAAA;;;;;AAMA,EAAA,IAAIC,UAAAA,GAA8B;AAC9B,IAAA,OAAO,IAAA,CAAKP,KAAAA;AAChB,EAAA;;;;;AAMAQ,EAAAA,IAAAA,CAAQC,MAAcV,IAAAA,EAAe;AACjC,IAAA,IAAA,CAAKE,OAAAA,CAAQ,IAAA,CAAKD,KAAAA,EAAOS,IAAAA,EAAMV,IAAAA,CAAAA;AACnC,EAAA;;;;;AAMAW,EAAAA,KAAAA,CAAMC,MAAAA,EAAuB;AACzB,IAAA,IAAA,CAAKT,QAAAA,CAAS,MAAMS,MAAAA,CAAAA;AACxB,EAAA;AACJ,CAAA;AAhDahB,MAAAA,CAAAA,OAAAA,EAAAA,QAAAA,CAAAA;AAAN,IAAMA,MAAAA,GAAN;;;ACKP,IAAMiB,gBAAAA,0BAA0B,iBAAA,CAAA;AAuBzB,IAAeC,KAAAA,GAAf,MAAeA,KAAAA,CAAAA;AAAf,EAAA,WAAA,GAAA;AASHC;;;;;;;AAAa,IAAA,aAAA,CAAA,IAAA,EAAA,YAAA,EAAA,EAAA,CAAA;AAMbC;;;;AAAW,IAAA,aAAA,CAAA,IAAA,EAAA,UAAA,EAAA,CAAA,CAAA;AAMXC;;;;AAAc,IAAA,aAAA,CAAA,IAAA,EAAA,aAAA,EAAA,IAAA,CAAA;AAUdC;;;;;;;iCAAgB,EAAC,CAAA;AAMTC;;;AAAc,IAAA,aAAA,CAAA,IAAA,EAAA,KAAA,EAAA,EAAA,CAAA;AACdC,IAAAA,aAAAA,CAAAA,IAAAA,EAAAA,UAAAA,sBAAiDC,GAAAA,EAAAA,CAAAA;AACjDC,IAAAA,aAAAA,CAAAA,IAAAA,EAAAA,SAAAA,EAAU,KAAA,CAAA;AACVC,IAAAA,aAAAA,CAAAA,IAAAA,EAAAA,WAAAA,EAAY,KAAA,CAAA;AACZC,IAAAA,aAAAA,CAAAA,IAAAA,EAAAA,eAAAA,EAAuD,IAAA,CAAA;AACvDC,IAAAA,aAAAA,CAAAA,IAAAA,EAAAA,eAAAA,EAAgB,CAAA,CAAA;AAChBC,IAAAA,aAAAA,CAAAA,IAAAA,EAAAA,cAAAA,EAA+D,IAAA,CAAA;AAC/DxB,IAAAA,aAAAA,CAAAA,IAAAA,EAAAA,SAAAA,EAAqE,IAAA,CAAA;AACrEyB,IAAAA,aAAAA,CAAAA,IAAAA,EAAAA,YAAAA,EAAkC,IAAA,CAAA;;;;;;;;;AAU1C,EAAA,IAAI7B,EAAAA,GAAa;AACb,IAAA,OAAO,IAAA,CAAKqB,GAAAA;AAChB,EAAA;;;;;AAMA,EAAA,IAAIS,OAAAA,GAA8C;AAC9C,IAAA,OAAOC,KAAAA,CAAMC,IAAAA,CAAK,IAAA,CAAKV,QAAAA,CAASW,QAAM,CAAA;AAC1C,EAAA;;;;;AAMA,EAAA,IAAIC,WAAAA,GAAsB;AACtB,IAAA,OAAO,KAAKZ,QAAAA,CAASa,IAAAA;AACzB,EAAA;;;;;AAMA,EAAA,IAAIC,MAAAA,GAAkB;AAClB,IAAA,OAAO,IAAA,CAAKd,QAAAA,CAASa,IAAAA,IAAQ,IAAA,CAAKlB,UAAAA;AACtC,EAAA;;;;;AAMA,EAAA,IAAIoB,QAAAA,GAAoB;AACpB,IAAA,OAAO,IAAA,CAAKb,OAAAA;AAChB,EAAA;;;;;AAMA,EAAA,IAAIc,UAAAA,GAAsB;AACtB,IAAA,OAAO,IAAA,CAAKb,SAAAA;AAChB,EAAA;;;;;;;;AAUAc,EAAAA,QAAAA,CAASxC,OAAAA,EAA6C;AAAC,EAAA;;;;;AAMvDyC,EAAAA,MAAAA,CAAOC,MAAAA,EAAmD;AAAC,EAAA;;;;;AAM3DC,EAAAA,OAAAA,CAAQD,QAA6B3B,MAAAA,EAAuC;AAAC,EAAA;;;;;AAM7E6B,EAAAA,MAAAA,CAAOC,EAAAA,EAAkB;AAAC,EAAA;;;;;EAM1BC,SAAAA,GAAkC;AAAC,EAAA;;;;;;;;AAUnCC,EAAAA,SAAAA,CAAalC,MAAcV,IAAAA,EAAe;AACtC,IAAA,KAAA,MAAWuC,MAAAA,IAAU,IAAA,CAAKnB,QAAAA,CAASW,MAAAA,EAAM,EAAI;AACzCQ,MAAAA,MAAAA,CAAO9B,IAAAA,CAAKC,MAAMV,IAAAA,CAAAA;AACtB,IAAA;AACJ,EAAA;;;;;EAMA6C,eAAAA,CAAmBC,MAAAA,EAA6BpC,MAAcV,IAAAA,EAAe;AACzE,IAAA,KAAA,MAAWuC,MAAAA,IAAU,IAAA,CAAKnB,QAAAA,CAASW,MAAAA,EAAM,EAAI;AACzC,MAAA,IAAIQ,MAAAA,CAAOzC,EAAAA,KAAOgD,MAAAA,CAAOhD,EAAAA,EAAI;AACzByC,QAAAA,MAAAA,CAAO9B,IAAAA,CAAKC,MAAMV,IAAAA,CAAAA;AACtB,MAAA;AACJ,IAAA;AACJ,EAAA;;;;;AAMA+C,EAAAA,SAAAA,CAAUjD,EAAAA,EAA6C;AACnD,IAAA,OAAO,IAAA,CAAKsB,QAAAA,CAAS4B,GAAAA,CAAIlD,EAAAA,CAAAA;AAC7B,EAAA;;;;;AAMAmD,EAAAA,IAAAA,CAAKV,QAA6B3B,MAAAA,EAAuB;AACrD2B,IAAAA,MAAAA,CAAO5B,KAAAA,CAAMC,UAAU,QAAA,CAAA;AAC3B,EAAA;;;;;EAMAsC,IAAAA,GAAa;AACT,IAAA,IAAA,CAAK5B,OAAAA,GAAU,IAAA;AACnB,EAAA;;;;;EAMA6B,MAAAA,GAAe;AACX,IAAA,IAAA,CAAK7B,OAAAA,GAAU,KAAA;AACnB,EAAA;;;;;EAMA8B,OAAAA,GAAgB;AACZ,IAAA,IAAI,KAAK7B,SAAAA,EAAW;AACpB,IAAA,IAAA,CAAKA,SAAAA,GAAY,IAAA;AAEjB,IAAA,IAAA,CAAK8B,SAAAA,EAAS;AAEd,IAAA,KAAA,MAAWd,MAAAA,IAAU,IAAA,CAAKnB,QAAAA,CAASW,MAAAA,EAAM,EAAI;AACzCQ,MAAAA,MAAAA,CAAO5B,MAAM,eAAA,CAAA;AACjB,IAAA;AACA,IAAA,IAAA,CAAKS,SAASkC,KAAAA,EAAK;AAEnB,IAAA,IAAA,CAAKX,SAAAA,EAAS;AACd,IAAA,IAAA,CAAKhB,UAAAA,IAAU;AACnB,EAAA;;;;;;;AASA4B,EAAAA,KAAAA,CAAM1D,OAAAA,EAKG;AACL,IAAA,IAAA,CAAKsB,MAAMtB,OAAAA,CAAQC,EAAAA;AACnB,IAAA,IAAA,CAAKI,UAAUL,OAAAA,CAAQQ,MAAAA;AACvB,IAAA,IAAA,CAAKqB,eAAe7B,OAAAA,CAAQ2D,WAAAA;AAC5B,IAAA,IAAA,CAAK7B,aAAa9B,OAAAA,CAAQ4D,SAAAA;AAC9B,EAAA;;;;AAKA,EAAA,MAAMC,QAAQ7D,OAAAA,EAAsC;AAChD,IAAA,MAAM,IAAA,CAAKwC,SAASxC,OAAAA,CAAAA;AACpB,IAAA,IAAA,CAAK8D,UAAAA,EAAU;AACnB,EAAA;;;;EAKA,MAAMC,UAAAA,CAAW9D,IAAYM,IAAAA,EAAgD;AACzE,IAAA,IAAI,IAAA,CAAKkB,OAAAA,IAAW,IAAA,CAAKY,MAAAA,IAAU,KAAKX,SAAAA,EAAW;AAC/C,MAAA,OAAO,IAAA;AACX,IAAA;AAEA,IAAA,MAAMgB,MAAAA,GAAS,IAAI3C,MAAAA,CAAoB;AACnCE,MAAAA,EAAAA;AACAC,MAAAA,MAAAA,EAAQ,IAAA,CAAKoB,GAAAA;AACbf,MAAAA,IAAAA;AACAC,MAAAA,MAAAA,EAAQ,IAAA,CAAKH,OAAAA;MACbI,OAAAA,kBAAS,MAAA,CAAA,CAACuD,GAAGjD,MAAAA,KAAW,IAAA,CAAKkD,cAAcD,CAAAA,CAAE/D,EAAAA,EAAIc,MAAAA,CAAAA,EAAxC,SAAA;KACb,CAAA;AAEA,IAAA,IAAA,CAAKQ,QAAAA,CAAS2C,GAAAA,CAAIjE,EAAAA,EAAIyC,MAAAA,CAAAA;AACtB,IAAA,MAAM,IAAA,CAAKD,OAAOC,MAAAA,CAAAA;AAElB,IAAA,OAAOA,MAAAA;AACX,EAAA;;;;EAKA,MAAMuB,aAAAA,CAAchE,IAAYc,MAAAA,EAAgC;AAC5D,IAAA,MAAM2B,MAAAA,GAAS,IAAA,CAAKnB,QAAAA,CAAS4B,GAAAA,CAAIlD,EAAAA,CAAAA;AACjC,IAAA,IAAI,CAACyC,MAAAA,EAAQ;AAEb,IAAA,IAAA,CAAKnB,QAAAA,CAAS4C,OAAOlE,EAAAA,CAAAA;AACrB,IAAA,MAAM,IAAA,CAAK0C,OAAAA,CAAQD,MAAAA,EAAQ3B,MAAAA,CAAAA;AAE3B,IAAA,IAAI,IAAA,CAAKK,WAAAA,IAAe,IAAA,CAAKG,QAAAA,CAASa,SAAS,CAAA,EAAG;AAC9C,MAAA,IAAA,CAAKmB,OAAAA,EAAO;AAChB,IAAA;AACJ,EAAA;;;;EAKAa,cAAAA,CAAevD,IAAAA,EAAcV,MAAekE,QAAAA,EAAwB;AAChE,IAAA,MAAM3B,MAAAA,GAAS,IAAA,CAAKnB,QAAAA,CAAS4B,GAAAA,CAAIkB,QAAAA,CAAAA;AACjC,IAAA,IAAI,CAAC3B,MAAAA,EAAQ;AAEb,IAAA,MAAM4B,QAAAA,GAAY,IAAA,CAAK,WAAA,CAAoBtD,gBAAAA,CAAAA;AAC3C,IAAA,IAAIsD,QAAAA,EAAU;AACV,MAAA,KAAA,MAAWC,WAAWD,QAAAA,EAAU;AAC5B,QAAA,IAAIC,OAAAA,CAAQ1D,SAASA,IAAAA,EAAM;AACvB,UAAA,MAAM2D,MAAAA,GAAU,IAAA,CAAaD,OAAAA,CAAQC,MAAM,CAAA;AAC3C,UAAA,IAAI,OAAOA,WAAW,UAAA,EAAY;AAC9BA,YAAAA,MAAAA,CAAOC,IAAAA,CAAK,IAAA,EAAMtE,IAAAA,EAAMuC,MAAAA,CAAAA;AAC5B,UAAA;AACJ,QAAA;AACJ,MAAA;AACJ,IAAA;AACJ,EAAA;EAEQoB,UAAAA,GAAmB;AACvB,IAAA,IAAI,IAAA,CAAK3C,YAAY,CAAA,EAAG;AAExB,IAAA,IAAA,CAAKS,aAAAA,GAAgB8C,YAAYC,GAAAA,EAAG;AACpC,IAAA,IAAA,CAAKhD,aAAAA,GAAgBiD,YAAY,MAAA;AAC7B,MAAA,MAAMD,GAAAA,GAAMD,YAAYC,GAAAA,EAAG;AAC3B,MAAA,MAAM9B,EAAAA,GAAAA,CAAM8B,GAAAA,GAAM,IAAA,CAAK/C,aAAAA,IAAiB,GAAA;AACxC,MAAA,IAAA,CAAKA,aAAAA,GAAgB+C,GAAAA;AACrB,MAAA,IAAA,CAAK/B,OAAOC,EAAAA,CAAAA;IAChB,CAAA,EAAG,GAAA,GAAO,KAAK1B,QAAQ,CAAA;AAC3B,EAAA;EAEQqC,SAAAA,GAAkB;AACtB,IAAA,IAAI,KAAK7B,aAAAA,EAAe;AACpBkD,MAAAA,aAAAA,CAAc,KAAKlD,aAAa,CAAA;AAChC,MAAA,IAAA,CAAKA,aAAAA,GAAgB,IAAA;AACzB,IAAA;AACJ,EAAA;AACJ,CAAA;AAxTsBV,MAAAA,CAAAA,KAAAA,EAAAA,MAAAA,CAAAA;AAAf,IAAeA,IAAAA,GAAf;AAsUA,SAAS6D,sBAAAA,CAAuBC,MAAAA,EAAalE,IAAAA,EAAc2D,MAAAA,EAAc;AAC5E,EAAA,IAAI,CAACO,MAAAA,CAAO/D,gBAAAA,CAAAA,EAAmB;AAC3B+D,IAAAA,MAAAA,CAAO/D,gBAAAA,IAAoB,EAAA;AAC/B,EAAA;AACA+D,EAAAA,MAAAA,CAAO/D,gBAAAA,EAAkBgE,IAAAA,CAAK;AAAEnE,IAAAA,IAAAA;AAAM2D,IAAAA;GAAO,CAAA;AACjD;AALgBM,MAAAA,CAAAA,sBAAAA,EAAAA,wBAAAA,CAAAA;;;AC/VT,SAASG,UAAUpE,IAAAA,EAAY;AAClC,EAAA,OAAO,SACHkE,MAAAA,EACAG,WAAAA,EACAC,WAAAA,EAA+B;AAE/BL,IAAAA,sBAAAA,CAAuBC,MAAAA,CAAO,WAAA,EAAalE,IAAAA,EAAMqE,WAAAA,CAAAA;AACrD,EAAA,CAAA;AACJ;AARgBD,MAAAA,CAAAA,SAAAA,EAAAA,WAAAA,CAAAA","file":"chunk-O3VN2QVN.js","sourcesContent":["/**\n * @zh 玩家类\n * @en Player class\n */\n\nimport type { Connection } from '@esengine/rpc'\n\n/**\n * @zh 玩家接口\n * @en Player interface\n */\nexport interface IPlayer<TData = Record<string, unknown>> {\n readonly id: string\n readonly roomId: string\n data: TData\n send<T>(type: string, data: T): void\n leave(reason?: string): void\n}\n\n/**\n * @zh 玩家实现\n * @en Player implementation\n */\nexport class Player<TData = Record<string, unknown>> implements IPlayer<TData> {\n readonly id: string\n readonly roomId: string\n data: TData\n\n private _conn: Connection<any>\n private _sendFn: (conn: Connection<any>, type: string, data: unknown) => void\n private _leaveFn: (player: Player<TData>, reason?: string) => void\n\n constructor(options: {\n id: string\n roomId: string\n conn: Connection<any>\n sendFn: (conn: Connection<any>, type: string, data: unknown) => void\n leaveFn: (player: Player<TData>, reason?: string) => void\n initialData?: TData\n }) {\n this.id = options.id\n this.roomId = options.roomId\n this._conn = options.conn\n this._sendFn = options.sendFn\n this._leaveFn = options.leaveFn\n this.data = options.initialData ?? ({} as TData)\n }\n\n /**\n * @zh 获取底层连接\n * @en Get underlying connection\n */\n get connection(): Connection<any> {\n return this._conn\n }\n\n /**\n * @zh 发送消息给玩家\n * @en Send message to player\n */\n send<T>(type: string, data: T): void {\n this._sendFn(this._conn, type, data)\n }\n\n /**\n * @zh 让玩家离开房间\n * @en Make player leave the room\n */\n leave(reason?: string): void {\n this._leaveFn(this, reason)\n }\n}\n","/**\n * @zh 房间基类\n * @en Room base class\n */\n\nimport { Player } from './Player.js'\n\n/**\n * @zh 房间配置\n * @en Room options\n */\nexport interface RoomOptions {\n [key: string]: unknown\n}\n\n/**\n * @zh 消息处理器元数据\n * @en Message handler metadata\n */\ninterface MessageHandlerMeta {\n type: string\n method: string\n}\n\n/**\n * @zh 消息处理器存储 key\n * @en Message handler storage key\n */\nconst MESSAGE_HANDLERS = Symbol('messageHandlers')\n\n/**\n * @zh 房间基类\n * @en Room base class\n *\n * @example\n * ```typescript\n * class GameRoom extends Room {\n * maxPlayers = 4\n * tickRate = 20\n *\n * onJoin(player: Player) {\n * this.broadcast('Joined', { id: player.id })\n * }\n *\n * @onMessage('Move')\n * handleMove(data: { x: number, y: number }, player: Player) {\n * // handle move\n * }\n * }\n * ```\n */\nexport abstract class Room<TState = any, TPlayerData = Record<string, unknown>> {\n // ========================================================================\n // 配置 | Configuration\n // ========================================================================\n\n /**\n * @zh 最大玩家数\n * @en Maximum players\n */\n maxPlayers = 16\n\n /**\n * @zh Tick 速率(每秒),0 = 不自动 tick\n * @en Tick rate (per second), 0 = no auto tick\n */\n tickRate = 0\n\n /**\n * @zh 空房间自动销毁\n * @en Auto dispose when empty\n */\n autoDispose = true\n\n // ========================================================================\n // 状态 | State\n // ========================================================================\n\n /**\n * @zh 房间状态\n * @en Room state\n */\n state: TState = {} as TState\n\n // ========================================================================\n // 内部属性 | Internal properties\n // ========================================================================\n\n private _id: string = ''\n private _players: Map<string, Player<TPlayerData>> = new Map()\n private _locked = false\n private _disposed = false\n private _tickInterval: ReturnType<typeof setInterval> | null = null\n private _lastTickTime = 0\n private _broadcastFn: ((type: string, data: unknown) => void) | null = null\n private _sendFn: ((conn: any, type: string, data: unknown) => void) | null = null\n private _disposeFn: (() => void) | null = null\n\n // ========================================================================\n // 只读属性 | Readonly properties\n // ========================================================================\n\n /**\n * @zh 房间 ID\n * @en Room ID\n */\n get id(): string {\n return this._id\n }\n\n /**\n * @zh 所有玩家\n * @en All players\n */\n get players(): ReadonlyArray<Player<TPlayerData>> {\n return Array.from(this._players.values())\n }\n\n /**\n * @zh 玩家数量\n * @en Player count\n */\n get playerCount(): number {\n return this._players.size\n }\n\n /**\n * @zh 是否已满\n * @en Is full\n */\n get isFull(): boolean {\n return this._players.size >= this.maxPlayers\n }\n\n /**\n * @zh 是否已锁定\n * @en Is locked\n */\n get isLocked(): boolean {\n return this._locked\n }\n\n /**\n * @zh 是否已销毁\n * @en Is disposed\n */\n get isDisposed(): boolean {\n return this._disposed\n }\n\n // ========================================================================\n // 生命周期 | Lifecycle\n // ========================================================================\n\n /**\n * @zh 房间创建时调用\n * @en Called when room is created\n */\n onCreate(options?: RoomOptions): void | Promise<void> {}\n\n /**\n * @zh 玩家加入时调用\n * @en Called when player joins\n */\n onJoin(player: Player<TPlayerData>): void | Promise<void> {}\n\n /**\n * @zh 玩家离开时调用\n * @en Called when player leaves\n */\n onLeave(player: Player<TPlayerData>, reason?: string): void | Promise<void> {}\n\n /**\n * @zh 游戏循环\n * @en Game tick\n */\n onTick(dt: number): void {}\n\n /**\n * @zh 房间销毁时调用\n * @en Called when room is disposed\n */\n onDispose(): void | Promise<void> {}\n\n // ========================================================================\n // 公共方法 | Public methods\n // ========================================================================\n\n /**\n * @zh 广播消息给所有玩家\n * @en Broadcast message to all players\n */\n broadcast<T>(type: string, data: T): void {\n for (const player of this._players.values()) {\n player.send(type, data)\n }\n }\n\n /**\n * @zh 广播消息给除指定玩家外的所有玩家\n * @en Broadcast message to all players except one\n */\n broadcastExcept<T>(except: Player<TPlayerData>, type: string, data: T): void {\n for (const player of this._players.values()) {\n if (player.id !== except.id) {\n player.send(type, data)\n }\n }\n }\n\n /**\n * @zh 获取玩家\n * @en Get player by id\n */\n getPlayer(id: string): Player<TPlayerData> | undefined {\n return this._players.get(id)\n }\n\n /**\n * @zh 踢出玩家\n * @en Kick player\n */\n kick(player: Player<TPlayerData>, reason?: string): void {\n player.leave(reason ?? 'kicked')\n }\n\n /**\n * @zh 锁定房间\n * @en Lock room\n */\n lock(): void {\n this._locked = true\n }\n\n /**\n * @zh 解锁房间\n * @en Unlock room\n */\n unlock(): void {\n this._locked = false\n }\n\n /**\n * @zh 手动销毁房间\n * @en Manually dispose room\n */\n dispose(): void {\n if (this._disposed) return\n this._disposed = true\n\n this._stopTick()\n\n for (const player of this._players.values()) {\n player.leave('room_disposed')\n }\n this._players.clear()\n\n this.onDispose()\n this._disposeFn?.()\n }\n\n // ========================================================================\n // 内部方法 | Internal methods\n // ========================================================================\n\n /**\n * @internal\n */\n _init(options: {\n id: string\n sendFn: (conn: any, type: string, data: unknown) => void\n broadcastFn: (type: string, data: unknown) => void\n disposeFn: () => void\n }): void {\n this._id = options.id\n this._sendFn = options.sendFn\n this._broadcastFn = options.broadcastFn\n this._disposeFn = options.disposeFn\n }\n\n /**\n * @internal\n */\n async _create(options?: RoomOptions): Promise<void> {\n await this.onCreate(options)\n this._startTick()\n }\n\n /**\n * @internal\n */\n async _addPlayer(id: string, conn: any): Promise<Player<TPlayerData> | null> {\n if (this._locked || this.isFull || this._disposed) {\n return null\n }\n\n const player = new Player<TPlayerData>({\n id,\n roomId: this._id,\n conn,\n sendFn: this._sendFn!,\n leaveFn: (p, reason) => this._removePlayer(p.id, reason),\n })\n\n this._players.set(id, player)\n await this.onJoin(player)\n\n return player\n }\n\n /**\n * @internal\n */\n async _removePlayer(id: string, reason?: string): Promise<void> {\n const player = this._players.get(id)\n if (!player) return\n\n this._players.delete(id)\n await this.onLeave(player, reason)\n\n if (this.autoDispose && this._players.size === 0) {\n this.dispose()\n }\n }\n\n /**\n * @internal\n */\n _handleMessage(type: string, data: unknown, playerId: string): void {\n const player = this._players.get(playerId)\n if (!player) return\n\n const handlers = (this.constructor as any)[MESSAGE_HANDLERS] as MessageHandlerMeta[] | undefined\n if (handlers) {\n for (const handler of handlers) {\n if (handler.type === type) {\n const method = (this as any)[handler.method]\n if (typeof method === 'function') {\n method.call(this, data, player)\n }\n }\n }\n }\n }\n\n private _startTick(): void {\n if (this.tickRate <= 0) return\n\n this._lastTickTime = performance.now()\n this._tickInterval = setInterval(() => {\n const now = performance.now()\n const dt = (now - this._lastTickTime) / 1000\n this._lastTickTime = now\n this.onTick(dt)\n }, 1000 / this.tickRate)\n }\n\n private _stopTick(): void {\n if (this._tickInterval) {\n clearInterval(this._tickInterval)\n this._tickInterval = null\n }\n }\n}\n\n/**\n * @zh 获取消息处理器元数据\n * @en Get message handler metadata\n */\nexport function getMessageHandlers(target: any): MessageHandlerMeta[] {\n return target[MESSAGE_HANDLERS] || []\n}\n\n/**\n * @zh 注册消息处理器元数据\n * @en Register message handler metadata\n */\nexport function registerMessageHandler(target: any, type: string, method: string): void {\n if (!target[MESSAGE_HANDLERS]) {\n target[MESSAGE_HANDLERS] = []\n }\n target[MESSAGE_HANDLERS].push({ type, method })\n}\n","/**\n * @zh 房间装饰器\n * @en Room decorators\n */\n\nimport { registerMessageHandler } from './Room.js'\n\n/**\n * @zh 消息处理器装饰器\n * @en Message handler decorator\n *\n * @example\n * ```typescript\n * class GameRoom extends Room {\n * @onMessage('Move')\n * handleMove(data: { x: number, y: number }, player: Player) {\n * // handle move\n * }\n *\n * @onMessage('Chat')\n * handleChat(data: { text: string }, player: Player) {\n * this.broadcast('Chat', { from: player.id, text: data.text })\n * }\n * }\n * ```\n */\nexport function onMessage(type: string): MethodDecorator {\n return function (\n target: any,\n propertyKey: string | symbol,\n _descriptor: PropertyDescriptor\n ) {\n registerMessageHandler(target.constructor, type, propertyKey as string)\n }\n}\n"]}
|