@esengine/server 1.3.0 → 2.0.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/chunk-O3VN2QVN.js +338 -0
- package/dist/chunk-O3VN2QVN.js.map +1 -0
- package/dist/{chunk-7C6JZO4O.js → chunk-QWEIP5QH.js} +3 -335
- package/dist/chunk-QWEIP5QH.js.map +1 -0
- package/dist/decorators-DY8nZ8Nh.d.ts +26 -0
- package/dist/ecs/index.d.ts +159 -0
- package/dist/ecs/index.js +247 -0
- package/dist/ecs/index.js.map +1 -0
- package/dist/index.d.ts +2 -26
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/testing/index.js +2 -1
- package/dist/testing/index.js.map +1 -1
- package/package.json +12 -3
- package/dist/chunk-7C6JZO4O.js.map +0 -1
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import { __name, __publicField } from './chunk-T626JPC7.js';
|
|
2
|
+
|
|
3
|
+
// src/room/Player.ts
|
|
4
|
+
var _Player = class _Player {
|
|
5
|
+
constructor(options) {
|
|
6
|
+
__publicField(this, "id");
|
|
7
|
+
__publicField(this, "roomId");
|
|
8
|
+
__publicField(this, "data");
|
|
9
|
+
__publicField(this, "_conn");
|
|
10
|
+
__publicField(this, "_sendFn");
|
|
11
|
+
__publicField(this, "_leaveFn");
|
|
12
|
+
this.id = options.id;
|
|
13
|
+
this.roomId = options.roomId;
|
|
14
|
+
this._conn = options.conn;
|
|
15
|
+
this._sendFn = options.sendFn;
|
|
16
|
+
this._leaveFn = options.leaveFn;
|
|
17
|
+
this.data = options.initialData ?? {};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* @zh 获取底层连接
|
|
21
|
+
* @en Get underlying connection
|
|
22
|
+
*/
|
|
23
|
+
get connection() {
|
|
24
|
+
return this._conn;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* @zh 发送消息给玩家
|
|
28
|
+
* @en Send message to player
|
|
29
|
+
*/
|
|
30
|
+
send(type, data) {
|
|
31
|
+
this._sendFn(this._conn, type, data);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* @zh 让玩家离开房间
|
|
35
|
+
* @en Make player leave the room
|
|
36
|
+
*/
|
|
37
|
+
leave(reason) {
|
|
38
|
+
this._leaveFn(this, reason);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
__name(_Player, "Player");
|
|
42
|
+
var Player = _Player;
|
|
43
|
+
|
|
44
|
+
// src/room/Room.ts
|
|
45
|
+
var MESSAGE_HANDLERS = /* @__PURE__ */ Symbol("messageHandlers");
|
|
46
|
+
var _Room = class _Room {
|
|
47
|
+
constructor() {
|
|
48
|
+
// ========================================================================
|
|
49
|
+
// 配置 | Configuration
|
|
50
|
+
// ========================================================================
|
|
51
|
+
/**
|
|
52
|
+
* @zh 最大玩家数
|
|
53
|
+
* @en Maximum players
|
|
54
|
+
*/
|
|
55
|
+
__publicField(this, "maxPlayers", 16);
|
|
56
|
+
/**
|
|
57
|
+
* @zh Tick 速率(每秒),0 = 不自动 tick
|
|
58
|
+
* @en Tick rate (per second), 0 = no auto tick
|
|
59
|
+
*/
|
|
60
|
+
__publicField(this, "tickRate", 0);
|
|
61
|
+
/**
|
|
62
|
+
* @zh 空房间自动销毁
|
|
63
|
+
* @en Auto dispose when empty
|
|
64
|
+
*/
|
|
65
|
+
__publicField(this, "autoDispose", true);
|
|
66
|
+
// ========================================================================
|
|
67
|
+
// 状态 | State
|
|
68
|
+
// ========================================================================
|
|
69
|
+
/**
|
|
70
|
+
* @zh 房间状态
|
|
71
|
+
* @en Room state
|
|
72
|
+
*/
|
|
73
|
+
__publicField(this, "state", {});
|
|
74
|
+
// ========================================================================
|
|
75
|
+
// 内部属性 | Internal properties
|
|
76
|
+
// ========================================================================
|
|
77
|
+
__publicField(this, "_id", "");
|
|
78
|
+
__publicField(this, "_players", /* @__PURE__ */ new Map());
|
|
79
|
+
__publicField(this, "_locked", false);
|
|
80
|
+
__publicField(this, "_disposed", false);
|
|
81
|
+
__publicField(this, "_tickInterval", null);
|
|
82
|
+
__publicField(this, "_lastTickTime", 0);
|
|
83
|
+
__publicField(this, "_broadcastFn", null);
|
|
84
|
+
__publicField(this, "_sendFn", null);
|
|
85
|
+
__publicField(this, "_disposeFn", null);
|
|
86
|
+
}
|
|
87
|
+
// ========================================================================
|
|
88
|
+
// 只读属性 | Readonly properties
|
|
89
|
+
// ========================================================================
|
|
90
|
+
/**
|
|
91
|
+
* @zh 房间 ID
|
|
92
|
+
* @en Room ID
|
|
93
|
+
*/
|
|
94
|
+
get id() {
|
|
95
|
+
return this._id;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* @zh 所有玩家
|
|
99
|
+
* @en All players
|
|
100
|
+
*/
|
|
101
|
+
get players() {
|
|
102
|
+
return Array.from(this._players.values());
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* @zh 玩家数量
|
|
106
|
+
* @en Player count
|
|
107
|
+
*/
|
|
108
|
+
get playerCount() {
|
|
109
|
+
return this._players.size;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* @zh 是否已满
|
|
113
|
+
* @en Is full
|
|
114
|
+
*/
|
|
115
|
+
get isFull() {
|
|
116
|
+
return this._players.size >= this.maxPlayers;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* @zh 是否已锁定
|
|
120
|
+
* @en Is locked
|
|
121
|
+
*/
|
|
122
|
+
get isLocked() {
|
|
123
|
+
return this._locked;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* @zh 是否已销毁
|
|
127
|
+
* @en Is disposed
|
|
128
|
+
*/
|
|
129
|
+
get isDisposed() {
|
|
130
|
+
return this._disposed;
|
|
131
|
+
}
|
|
132
|
+
// ========================================================================
|
|
133
|
+
// 生命周期 | Lifecycle
|
|
134
|
+
// ========================================================================
|
|
135
|
+
/**
|
|
136
|
+
* @zh 房间创建时调用
|
|
137
|
+
* @en Called when room is created
|
|
138
|
+
*/
|
|
139
|
+
onCreate(options) {
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* @zh 玩家加入时调用
|
|
143
|
+
* @en Called when player joins
|
|
144
|
+
*/
|
|
145
|
+
onJoin(player) {
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* @zh 玩家离开时调用
|
|
149
|
+
* @en Called when player leaves
|
|
150
|
+
*/
|
|
151
|
+
onLeave(player, reason) {
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* @zh 游戏循环
|
|
155
|
+
* @en Game tick
|
|
156
|
+
*/
|
|
157
|
+
onTick(dt) {
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* @zh 房间销毁时调用
|
|
161
|
+
* @en Called when room is disposed
|
|
162
|
+
*/
|
|
163
|
+
onDispose() {
|
|
164
|
+
}
|
|
165
|
+
// ========================================================================
|
|
166
|
+
// 公共方法 | Public methods
|
|
167
|
+
// ========================================================================
|
|
168
|
+
/**
|
|
169
|
+
* @zh 广播消息给所有玩家
|
|
170
|
+
* @en Broadcast message to all players
|
|
171
|
+
*/
|
|
172
|
+
broadcast(type, data) {
|
|
173
|
+
for (const player of this._players.values()) {
|
|
174
|
+
player.send(type, data);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* @zh 广播消息给除指定玩家外的所有玩家
|
|
179
|
+
* @en Broadcast message to all players except one
|
|
180
|
+
*/
|
|
181
|
+
broadcastExcept(except, type, data) {
|
|
182
|
+
for (const player of this._players.values()) {
|
|
183
|
+
if (player.id !== except.id) {
|
|
184
|
+
player.send(type, data);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* @zh 获取玩家
|
|
190
|
+
* @en Get player by id
|
|
191
|
+
*/
|
|
192
|
+
getPlayer(id) {
|
|
193
|
+
return this._players.get(id);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* @zh 踢出玩家
|
|
197
|
+
* @en Kick player
|
|
198
|
+
*/
|
|
199
|
+
kick(player, reason) {
|
|
200
|
+
player.leave(reason ?? "kicked");
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* @zh 锁定房间
|
|
204
|
+
* @en Lock room
|
|
205
|
+
*/
|
|
206
|
+
lock() {
|
|
207
|
+
this._locked = true;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* @zh 解锁房间
|
|
211
|
+
* @en Unlock room
|
|
212
|
+
*/
|
|
213
|
+
unlock() {
|
|
214
|
+
this._locked = false;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* @zh 手动销毁房间
|
|
218
|
+
* @en Manually dispose room
|
|
219
|
+
*/
|
|
220
|
+
dispose() {
|
|
221
|
+
if (this._disposed) return;
|
|
222
|
+
this._disposed = true;
|
|
223
|
+
this._stopTick();
|
|
224
|
+
for (const player of this._players.values()) {
|
|
225
|
+
player.leave("room_disposed");
|
|
226
|
+
}
|
|
227
|
+
this._players.clear();
|
|
228
|
+
this.onDispose();
|
|
229
|
+
this._disposeFn?.();
|
|
230
|
+
}
|
|
231
|
+
// ========================================================================
|
|
232
|
+
// 内部方法 | Internal methods
|
|
233
|
+
// ========================================================================
|
|
234
|
+
/**
|
|
235
|
+
* @internal
|
|
236
|
+
*/
|
|
237
|
+
_init(options) {
|
|
238
|
+
this._id = options.id;
|
|
239
|
+
this._sendFn = options.sendFn;
|
|
240
|
+
this._broadcastFn = options.broadcastFn;
|
|
241
|
+
this._disposeFn = options.disposeFn;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* @internal
|
|
245
|
+
*/
|
|
246
|
+
async _create(options) {
|
|
247
|
+
await this.onCreate(options);
|
|
248
|
+
this._startTick();
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* @internal
|
|
252
|
+
*/
|
|
253
|
+
async _addPlayer(id, conn) {
|
|
254
|
+
if (this._locked || this.isFull || this._disposed) {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
const player = new Player({
|
|
258
|
+
id,
|
|
259
|
+
roomId: this._id,
|
|
260
|
+
conn,
|
|
261
|
+
sendFn: this._sendFn,
|
|
262
|
+
leaveFn: /* @__PURE__ */ __name((p, reason) => this._removePlayer(p.id, reason), "leaveFn")
|
|
263
|
+
});
|
|
264
|
+
this._players.set(id, player);
|
|
265
|
+
await this.onJoin(player);
|
|
266
|
+
return player;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* @internal
|
|
270
|
+
*/
|
|
271
|
+
async _removePlayer(id, reason) {
|
|
272
|
+
const player = this._players.get(id);
|
|
273
|
+
if (!player) return;
|
|
274
|
+
this._players.delete(id);
|
|
275
|
+
await this.onLeave(player, reason);
|
|
276
|
+
if (this.autoDispose && this._players.size === 0) {
|
|
277
|
+
this.dispose();
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* @internal
|
|
282
|
+
*/
|
|
283
|
+
_handleMessage(type, data, playerId) {
|
|
284
|
+
const player = this._players.get(playerId);
|
|
285
|
+
if (!player) return;
|
|
286
|
+
const handlers = this.constructor[MESSAGE_HANDLERS];
|
|
287
|
+
if (handlers) {
|
|
288
|
+
for (const handler of handlers) {
|
|
289
|
+
if (handler.type === type) {
|
|
290
|
+
const method = this[handler.method];
|
|
291
|
+
if (typeof method === "function") {
|
|
292
|
+
method.call(this, data, player);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
_startTick() {
|
|
299
|
+
if (this.tickRate <= 0) return;
|
|
300
|
+
this._lastTickTime = performance.now();
|
|
301
|
+
this._tickInterval = setInterval(() => {
|
|
302
|
+
const now = performance.now();
|
|
303
|
+
const dt = (now - this._lastTickTime) / 1e3;
|
|
304
|
+
this._lastTickTime = now;
|
|
305
|
+
this.onTick(dt);
|
|
306
|
+
}, 1e3 / this.tickRate);
|
|
307
|
+
}
|
|
308
|
+
_stopTick() {
|
|
309
|
+
if (this._tickInterval) {
|
|
310
|
+
clearInterval(this._tickInterval);
|
|
311
|
+
this._tickInterval = null;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
__name(_Room, "Room");
|
|
316
|
+
var Room = _Room;
|
|
317
|
+
function registerMessageHandler(target, type, method) {
|
|
318
|
+
if (!target[MESSAGE_HANDLERS]) {
|
|
319
|
+
target[MESSAGE_HANDLERS] = [];
|
|
320
|
+
}
|
|
321
|
+
target[MESSAGE_HANDLERS].push({
|
|
322
|
+
type,
|
|
323
|
+
method
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
__name(registerMessageHandler, "registerMessageHandler");
|
|
327
|
+
|
|
328
|
+
// src/room/decorators.ts
|
|
329
|
+
function onMessage(type) {
|
|
330
|
+
return function(target, propertyKey, _descriptor) {
|
|
331
|
+
registerMessageHandler(target.constructor, type, propertyKey);
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
__name(onMessage, "onMessage");
|
|
335
|
+
|
|
336
|
+
export { Player, Room, onMessage };
|
|
337
|
+
//# sourceMappingURL=chunk-O3VN2QVN.js.map
|
|
338
|
+
//# sourceMappingURL=chunk-O3VN2QVN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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":"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","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"]}
|