mm_os 4.3.7 → 5.0.1

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.
Files changed (231) hide show
  1. package/README.md +102 -504
  2. package/index.js +26 -19
  3. package/lib/os.js +536 -0
  4. package/package.json +60 -84
  5. package/LICENSE +0 -21
  6. package/README_EN.md +0 -505
  7. package/adapter/adapter.js +0 -484
  8. package/adapter/custom_persistence.js +0 -661
  9. package/adapter/mqtt.js +0 -254
  10. package/adapter/socket.js +0 -244
  11. package/adapter/web.js +0 -92
  12. package/adapter/websocket.js +0 -43
  13. package/ai.js +0 -130
  14. package/com/api/README.md +0 -3
  15. package/com/api/com.json +0 -5
  16. package/com/api/config.tpl.json +0 -41
  17. package/com/api/drive.js +0 -732
  18. package/com/api/index.js +0 -201
  19. package/com/api/oauth.js +0 -200
  20. package/com/api/script.tpl.js +0 -32
  21. package/com/cmd/README.md +0 -11
  22. package/com/cmd/com.json +0 -5
  23. package/com/cmd/config.tpl.json +0 -48
  24. package/com/cmd/drive.js +0 -176
  25. package/com/cmd/index.js +0 -287
  26. package/com/cmd/job/config.json +0 -66
  27. package/com/cmd/job/index.js +0 -241
  28. package/com/cmd/job/test.js +0 -188
  29. package/com/cmd/msg.json +0 -48
  30. package/com/cmd/script.tpl.js +0 -12
  31. package/com/db/README.md +0 -3
  32. package/com/db/com.json +0 -5
  33. package/com/db/drive.js +0 -2206
  34. package/com/db/index.js +0 -241
  35. package/com/event/README.md +0 -4
  36. package/com/event/com.json +0 -5
  37. package/com/event/config.tpl.json +0 -20
  38. package/com/event/drive.js +0 -61
  39. package/com/event/index.js +0 -438
  40. package/com/event/script.tpl.js +0 -23
  41. package/com/mqtt/README.md +0 -47
  42. package/com/mqtt/com.json +0 -5
  43. package/com/mqtt/config.tpl.json +0 -42
  44. package/com/mqtt/drive.js +0 -704
  45. package/com/mqtt/index.js +0 -1052
  46. package/com/mqtt/index.test.js +0 -503
  47. package/com/mqtt/script.tpl.js +0 -719
  48. package/com/nav/README.md +0 -2
  49. package/com/nav/com.json +0 -5
  50. package/com/nav/config.tpl.json +0 -84
  51. package/com/nav/drive.js +0 -702
  52. package/com/nav/index.js +0 -228
  53. package/com/nav/tpl/admin_pc/page_channel.vue +0 -305
  54. package/com/nav/tpl/admin_pc/page_config.vue +0 -280
  55. package/com/nav/tpl/admin_pc/page_config_form.vue +0 -194
  56. package/com/nav/tpl/admin_pc/page_default.vue +0 -247
  57. package/com/nav/tpl/admin_pc/page_form.vue +0 -180
  58. package/com/nav/tpl/admin_pc/page_lang.vue +0 -264
  59. package/com/nav/tpl/admin_pc/page_nav.vue +0 -321
  60. package/com/nav/tpl/admin_pc/page_table.vue +0 -296
  61. package/com/nav/tpl/admin_pc/page_type.vue +0 -321
  62. package/com/nav/tpl/admin_pc/page_view.vue +0 -124
  63. package/com/nav/tpl/dev_pc/page_channel.vue +0 -253
  64. package/com/nav/tpl/dev_pc/page_config.vue +0 -253
  65. package/com/nav/tpl/dev_pc/page_default.vue +0 -247
  66. package/com/nav/tpl/dev_pc/page_form.vue +0 -162
  67. package/com/nav/tpl/dev_pc/page_nav.vue +0 -253
  68. package/com/nav/tpl/dev_pc/page_table.vue +0 -247
  69. package/com/nav/tpl/dev_pc/page_type.vue +0 -313
  70. package/com/nav/tpl/home_pc/page_channel.vue +0 -234
  71. package/com/nav/tpl/home_pc/page_default.vue +0 -234
  72. package/com/nav/tpl/home_pc/page_form.vue +0 -137
  73. package/com/nav/tpl/home_pc/page_list.vue +0 -234
  74. package/com/nav/tpl/home_pc/page_nav.vue +0 -221
  75. package/com/nav/tpl/home_pc/page_type.vue +0 -234
  76. package/com/nav/tpl/home_pc/page_view.vue +0 -125
  77. package/com/nav/tpl/home_phone/page_channel.vue +0 -234
  78. package/com/nav/tpl/home_phone/page_default.vue +0 -234
  79. package/com/nav/tpl/home_phone/page_form.vue +0 -137
  80. package/com/nav/tpl/home_phone/page_list.vue +0 -234
  81. package/com/nav/tpl/home_phone/page_nav.vue +0 -237
  82. package/com/nav/tpl/home_phone/page_type.vue +0 -234
  83. package/com/nav/tpl/home_phone/page_view.vue +0 -125
  84. package/com/nav/viewmodel.js +0 -446
  85. package/com/param/README.md +0 -2
  86. package/com/param/com.json +0 -5
  87. package/com/param/config.tpl.json +0 -136
  88. package/com/param/drive.js +0 -502
  89. package/com/param/index.js +0 -158
  90. package/com/param/script.tpl.js +0 -12
  91. package/com/pendant/README.md +0 -3
  92. package/com/pendant/com.json +0 -5
  93. package/com/pendant/config.tpl.json +0 -32
  94. package/com/pendant/drive.js +0 -204
  95. package/com/pendant/index.js +0 -467
  96. package/com/pendant/pendant.html +0 -16
  97. package/com/pendant/script.tpl.js +0 -18
  98. package/com/socket/README.md +0 -47
  99. package/com/socket/com.json +0 -5
  100. package/com/socket/config.tpl.json +0 -12
  101. package/com/socket/drive.js +0 -679
  102. package/com/socket/index.js +0 -402
  103. package/com/socket/script.tpl.js +0 -41
  104. package/com/sql/README.md +0 -3
  105. package/com/sql/com.json +0 -5
  106. package/com/sql/config.tpl.json +0 -74
  107. package/com/sql/drive.js +0 -1328
  108. package/com/sql/index.js +0 -152
  109. package/com/sql/script.tpl.js +0 -47
  110. package/com/static/README.md +0 -2
  111. package/com/static/com.json +0 -5
  112. package/com/static/config.tpl.json +0 -24
  113. package/com/static/drive.js +0 -194
  114. package/com/static/index.js +0 -237
  115. package/com/static/script.tpl.js +0 -28
  116. package/com/task/README.md +0 -3
  117. package/com/task/com.json +0 -5
  118. package/com/task/config.tpl.json +0 -24
  119. package/com/task/drive.js +0 -428
  120. package/com/task/index.js +0 -159
  121. package/com/task/script.tpl.js +0 -40
  122. package/com/template/com.json +0 -5
  123. package/com/template/config.tpl.json +0 -16
  124. package/com/template/drive.js +0 -81
  125. package/com/template/index.js +0 -141
  126. package/com.js +0 -237
  127. package/common/README.md +0 -2
  128. package/common/middleware/web_after/index.js +0 -26
  129. package/common/middleware/web_after/middleware.json +0 -9
  130. package/common/middleware/web_base/index.js +0 -113
  131. package/common/middleware/web_base/middleware.json +0 -19
  132. package/common/middleware/web_before/index.js +0 -33
  133. package/common/middleware/web_before/middleware.json +0 -9
  134. package/common/middleware/web_cors/index.js +0 -87
  135. package/common/middleware/web_cors/middleware.json +0 -24
  136. package/common/middleware/web_error/index.js +0 -131
  137. package/common/middleware/web_error/middleware.json +0 -19
  138. package/common/middleware/web_ip/index.js +0 -15
  139. package/common/middleware/web_ip/middleware.json +0 -14
  140. package/common/middleware/web_logger/index.js +0 -156
  141. package/common/middleware/web_logger/middleware.json +0 -14
  142. package/common/middleware/web_main/index.js +0 -26
  143. package/common/middleware/web_main/middleware.json +0 -9
  144. package/common/middleware/web_static/index.js +0 -73
  145. package/common/middleware/web_static/middleware.json +0 -54
  146. package/common/middleware/web_waf/index.js +0 -395
  147. package/common/middleware/web_waf/middleware.json +0 -13
  148. package/core/app/config.tpl.json +0 -67
  149. package/core/app/index.js +0 -711
  150. package/core/app/script.tpl.js +0 -52
  151. package/core/channel/index.js +0 -899
  152. package/core/channel/matcher.js +0 -585
  153. package/core/com/config.tpl.json +0 -16
  154. package/core/com/index.js +0 -77
  155. package/core/com/script.tpl.js +0 -5
  156. package/core/component/component.js +0 -42
  157. package/core/component/config.tpl.json +0 -63
  158. package/core/component/index.js +0 -273
  159. package/core/component/script.tpl.js +0 -19
  160. package/core/controller/config.tpl.json +0 -14
  161. package/core/controller/index.js +0 -371
  162. package/core/controller/script.tpl.js +0 -27
  163. package/core/factory/config.tpl.json +0 -14
  164. package/core/factory/entity.js +0 -275
  165. package/core/factory/index.js +0 -241
  166. package/core/factory/script.tpl.js +0 -16
  167. package/core/game/bat/index.js +0 -137
  168. package/core/game/bat/world.js +0 -622
  169. package/core/game/config.tpl.json +0 -16
  170. package/core/game/entity_admin.js +0 -230
  171. package/core/game/index.js +0 -186
  172. package/core/handler/config.tpl.json +0 -22
  173. package/core/handler/index.js +0 -181
  174. package/core/handler/script.tpl.js +0 -23
  175. package/core/logic/config.tpl.json +0 -14
  176. package/core/logic/index.js +0 -59
  177. package/core/logic/script.tpl.js +0 -19
  178. package/core/middleware/config.tpl.json +0 -16
  179. package/core/middleware/index.js +0 -117
  180. package/core/middleware/script.tpl.js +0 -37
  181. package/core/mod/config.tpl.json +0 -22
  182. package/core/mod/index.js +0 -136
  183. package/core/mod/script.tpl.js +0 -34
  184. package/core/model/config.tpl.json +0 -219
  185. package/core/model/index.js +0 -272
  186. package/core/model/model.js +0 -27
  187. package/core/model/script.tpl.js +0 -20
  188. package/core/notifier/config.tpl.json +0 -14
  189. package/core/notifier/index.js +0 -77
  190. package/core/notifier/script.tpl.js +0 -20
  191. package/core/plugin/config.tpl.json +0 -36
  192. package/core/plugin/index.js +0 -267
  193. package/core/plugin/script.tpl.js +0 -51
  194. package/core/pusher/config.tpl.json +0 -14
  195. package/core/pusher/index.js +0 -158
  196. package/core/pusher/script.tpl.js +0 -20
  197. package/core/room/bat/index.js +0 -170
  198. package/core/room/bat/room.js +0 -524
  199. package/core/room/config.tpl.json +0 -20
  200. package/core/room/index.js +0 -249
  201. package/core/room/room.js +0 -61
  202. package/core/scene/config.tpl.json +0 -14
  203. package/core/scene/index.js +0 -467
  204. package/core/scene/loop.js +0 -1306
  205. package/core/scene/map.js +0 -28
  206. package/core/scene/script.tpl.js +0 -22
  207. package/core/sender/config.tpl.json +0 -14
  208. package/core/sender/index.js +0 -79
  209. package/core/sender/script.tpl.js +0 -20
  210. package/core/service/config.tpl.json +0 -14
  211. package/core/service/index.js +0 -100
  212. package/core/service/script.tpl.js +0 -25
  213. package/core/store/config.tpl.json +0 -26
  214. package/core/store/index.js +0 -1794
  215. package/core/store/script.tpl.js +0 -22
  216. package/core/system/config.tpl.json +0 -18
  217. package/core/system/index.js +0 -312
  218. package/core/system/script.tpl.js +0 -77
  219. package/core/view/config.tpl.json +0 -14
  220. package/core/view/index.js +0 -91
  221. package/core/view/script.tpl.js +0 -20
  222. package/core/zone/bat/index.js +0 -744
  223. package/core/zone/config.tpl.json +0 -54
  224. package/core/zone/index.js +0 -655
  225. package/core/zone/script.tpl.js +0 -10
  226. package/core/zone/zone_bat.js +0 -136
  227. package/core//345/237/272/347/261/273/346/250/241/345/235/227/346/270/205/345/215/225.md +0 -24
  228. package/os.js +0 -53
  229. package/server.js +0 -602
  230. package/ulits/id_gen.js +0 -134
  231. package/ulits/word.js +0 -206
@@ -1,899 +0,0 @@
1
- const {
2
- Drive
3
- } = require('mm_machine');
4
- const {
5
- RoomAdmin
6
- } = require('../room/index.js');
7
- const {
8
- Matcher
9
- } = require('./matcher.js');
10
-
11
- /**
12
- * 游戏频道 - 平行世界管理器
13
- * 负责管理频道的创建、销毁、玩家隔离和频道间通信
14
- */
15
- class Channel extends Drive {
16
- static config = {
17
- 'name': 'default',
18
- 'title': '默认频道',
19
- 'min_level': 0,
20
- 'max_level': 1000,
21
- 'max_players': 5000,
22
- 'type': 'normal',
23
- 'load_threshold': 0.8,
24
- 'main': '',
25
- 'match': {
26
- 'wait_time': 120000,
27
- 'lv_range_init': 5,
28
- 'lv_range_max': 20,
29
- 'lv_expand_int': 10000,
30
- 'interval': 1000
31
- }
32
- };
33
-
34
- /**
35
- * 构造函数
36
- * @param {object} config 配置参数
37
- */
38
- constructor(config) {
39
- super({ ...Channel.config, ...config });
40
-
41
- // 游戏世界集合
42
- this.worlds = new Map();
43
- }
44
- }
45
-
46
- /**
47
- * 预置
48
- */
49
- Channel.prototype._preset = function () {
50
- this.channel_id = 0;
51
- this.status = 1;
52
- this.stats = {
53
- player: 0,
54
- room: 0,
55
- world: 0,
56
- message: 0,
57
- last_update: Date.now()
58
- };
59
-
60
- this._monitorTimer = null;
61
-
62
- this.roomAdmin = null;
63
- this.matcher = null;
64
-
65
- // 主世界ID
66
- this._main_world_id = '';
67
- };
68
-
69
- /**
70
- * 获取模板目录
71
- * @returns {string} 模板目录
72
- */
73
- Channel.prototype.getTplDir = function () {
74
- return __dirname;
75
- };
76
-
77
- /**
78
- * 初始化核心
79
- * @param {object} zone 分区对象
80
- * @param {object} eventer 事件管理器
81
- * @param {object} logger 日志管理器
82
- */
83
- Channel.prototype._initCore = async function (zone, eventer, logger) {
84
- if (!zone) {
85
- throw new TypeError('分区对象不能为空');
86
- }
87
- else {
88
- this.getZone = function () {
89
- return zone;
90
- };
91
- }
92
-
93
- if (logger) {
94
- this.setLogger(logger);
95
- }
96
- if (eventer) {
97
- this.getEventer = function () {
98
- return eventer;
99
- };
100
- }
101
-
102
- await this._initManager();
103
- await this._loadSources();
104
- };
105
-
106
- /**
107
- * 加载世界
108
- * @returns {Promise<void>}
109
- */
110
- Channel.prototype._loadWorlds = async function () {
111
- // 通过事件方式加载已创建的世界数据,避免直接操作数据库
112
- var worlds = await this.emitWait('world_load', {
113
- zone_name: this.getZone().config.name,
114
- channel_id: this.channel_id
115
- });
116
- if (worlds && worlds.length > 0) {
117
- // 遍历世界列表,创建世界对象
118
- for (var i = 0; i < worlds.length; i++) {
119
- var info = worlds[i];
120
- await this.createWorld(info, info.world_id);
121
- }
122
- this.log('info', `加载世界成功,共 ${worlds.length} 个`);
123
- }
124
-
125
- // 加载所有游戏世界
126
- var promises = [];
127
- for (var o of this.worlds.values()) {
128
- promises.push(o.do('load'));
129
- }
130
- await Promise.all(promises);
131
- };
132
-
133
- /**
134
- * 初始化房间管理器
135
- */
136
- Channel.prototype._initRoomAdmin = async function () {
137
- this.roomAdmin = new RoomAdmin();
138
- await this.roomAdmin.do('init', this);
139
- };
140
-
141
- /**
142
- * 初始化匹配器
143
- */
144
- Channel.prototype._initMatcher = async function () {
145
- this.matcher = new Matcher(this.config.match, this);
146
- await this.matcher.do('init', this);
147
- };
148
-
149
- /**
150
- * 初始化管理器
151
- */
152
- Channel.prototype._initManager = async function () {
153
- // var dir = this.getDir().dirname().dirname();
154
- // var game = new Manager({
155
- // name: 'game',
156
- // title: '游戏',
157
- // filename: 'game.json',
158
- // tpl_dir: `../game/`.fullname(__dirname),
159
- // base_dir: '',
160
- // // base_dir: `../../common/${name}/`.fullname(__dirname),
161
- // dir: `./game`.fullname(dir)
162
- // }, this, this.game, Game);
163
- // await game.do('init');
164
- // this.manager.game = game;
165
- };
166
-
167
- /**
168
- * 获取游戏管理器目录
169
- * @returns {string} 游戏管理器目录
170
- */
171
- Channel.prototype.getDir = function () {
172
- return this.getZone().getDir();
173
- };
174
-
175
- /**
176
- * 加载资源
177
- */
178
- Channel.prototype._loadSources = async function () {
179
- await this.manager.game.runAll('load');
180
- };
181
-
182
- /**
183
- * 设置主世界ID
184
- * @param {string} world_id 主世界ID
185
- */
186
- Channel.prototype.setMainWorld = function (world_id) {
187
- this._main_world_id = world_id;
188
- };
189
-
190
- /**
191
- * 初始化资源
192
- */
193
- Channel.prototype._initSources = async function () {
194
- await this.manager.game.runAll('init', this, this.getEventer(), this.getLogger());
195
- };
196
-
197
- /**
198
- * 加载游戏房间
199
- * @returns {Promise<void>}
200
- */
201
- Channel.prototype._loadRooms = async function () {
202
- // 通过事件方式加载已创建的房间数据,避免直接操作数据库
203
- var rooms = await this.emitWait('room_load', {
204
- zone_name: this.getZone().config.name,
205
- channel_id: this.channel_id
206
- });
207
- if (rooms && rooms.length > 0) {
208
- // 遍历房间列表,创建房间对象
209
- for (var i = 0; i < rooms.length; i++) {
210
- var info = rooms[i];
211
- var room = await this.roomAdmin.create(info, info.owner);
212
- // 恢复房间原有ID
213
- if (info.room_id) {
214
- this.roomAdmin._rooms.delete(room.room_id);
215
- room.room_id = info.room_id;
216
- this.roomAdmin._rooms.set(room.room_id, room);
217
- }
218
- // 恢复房间状态
219
- if (info.state) {
220
- room.state = info.state;
221
- }
222
- }
223
- this.log('info', `加载房间成功,共 ${rooms.length} 个`);
224
- }
225
-
226
- // 加载所有游戏房间
227
- var promises = [];
228
- for (var o of this.rooms.values()) {
229
- promises.push(o.do('load'));
230
- }
231
- await Promise.all(promises);
232
- };
233
-
234
- /**
235
- * 启动核心
236
- */
237
- Channel.prototype._startCore = async function () {
238
- await this._initSources();
239
-
240
- await this._loadWorlds();
241
-
242
- await this._initRoomAdmin();
243
-
244
- await this._loadRooms();
245
-
246
- await this._initMatcher();
247
-
248
- await this.manager.game.runAll('start');
249
- await this.matcher.do('start');
250
- this._startMonitor();
251
- await this._startWorlds();
252
- };
253
-
254
- /**
255
- * 启动游戏世界
256
- */
257
- Channel.prototype._startWorlds = async function () {
258
- var promises = [];
259
- for (let o of this.worlds.values()) {
260
- promises.push(o.do('start'));
261
- }
262
- await Promise.all(promises);
263
- };
264
-
265
- /**
266
- * 初始化默认房间
267
- */
268
- Channel.prototype._initRooms = async function () {
269
- var promises = [];
270
- for (let o of this.rooms.values()) {
271
- promises.push(o.do('init', this, this.getEventer(), this.getLogger()));
272
- }
273
- await Promise.all(promises);
274
- };
275
-
276
- /**
277
- * 启动频道监控
278
- */
279
- Channel.prototype._startMonitor = function () {
280
- this._monitorTimer = setInterval(() => {
281
- this._updateStatus();
282
- }, 30000);
283
- };
284
-
285
- /**
286
- * 更新频道状态
287
- */
288
- Channel.prototype._updateStatus = function () {
289
- var player = this.player_ids.size;
290
- var max_players = this.config.max_players;
291
- var load_ratio = player / max_players;
292
-
293
- if (load_ratio >= 1) {
294
- this.status = 3;
295
- } else if (load_ratio >= this.config.load_threshold) {
296
- this.status = 2;
297
- } else {
298
- this.status = 1;
299
- }
300
-
301
- this.stats.player = player;
302
- this.stats.room = this.rooms.size;
303
- this.stats.world = this.worlds.size;
304
- this.stats.last_update = Date.now();
305
- };
306
-
307
- /**
308
- * 玩家进入频道
309
- * @param {string} player_id 玩家ID
310
- * @returns {object} 世界对象
311
- */
312
- Channel.prototype.enter = function (player_id) {
313
- if (!player_id) {
314
- throw new TypeError('玩家ID不能为空');
315
- }
316
-
317
- if (this.player_ids.has(player_id)) {
318
- throw new Error('玩家已在当前频道中');
319
- }
320
-
321
- var player = this.getZone().getPlayer(player_id);
322
- if (!player) {
323
- throw new Error(`玩家 ${player_id} 不存在`);
324
- }
325
-
326
- var {
327
- min_level,
328
- max_level,
329
- status,
330
- max_players
331
- } = this.config;
332
-
333
- this._validateStatus(status);
334
- this._validateQualify(player, min_level, max_level, max_players);
335
-
336
- try {
337
- this.player_ids.set(player_id, true);
338
-
339
- this.emitEvent('player_enter_channel', {
340
- channel_id: this.config.name,
341
- player_id: player_id,
342
- player_level: player.level,
343
- timestamp: Date.now()
344
- });
345
-
346
- let world = this.enterMainWorld(player_id);
347
- return world;
348
- } catch (error) {
349
- this.player_ids.delete(player_id);
350
- throw error;
351
- }
352
- };
353
-
354
- /**
355
- * 验证频道状态
356
- * @param {number} status 频道状态
357
- */
358
- Channel.prototype._validateStatus = function (status) {
359
- if (status === 0) {
360
- throw new Error('频道已关闭,无法进入');
361
- }
362
- if (status === 2) {
363
- throw new Error('频道维护中,暂时无法进入');
364
- }
365
- if (status === 3) {
366
- throw new Error('频道爆满,请稍后再试');
367
- }
368
- };
369
-
370
- /**
371
- * 检查玩家是否在频道中
372
- * @param {string} player_id 玩家ID
373
- * @returns {boolean} 是否在频道中
374
- */
375
- Channel.prototype._hasPlayer = function (player_id) {
376
- return this.player_ids.has(player_id);
377
- };
378
-
379
- /**
380
- * 验证玩家资格
381
- * @param {object} player 玩家对象
382
- * @param {number} min_level 最低等级
383
- * @param {number} max_level 最高等级
384
- * @param {number} max_players 最大玩家数
385
- */
386
- Channel.prototype._validateQualify = function (player, min_level, max_level, max_players) {
387
- if (player.level < min_level || player.level > max_level) {
388
- var msg = `境界不符,无法进入此频道(要求等级:${min_level}-${max_level})`;
389
- throw new Error(msg);
390
- }
391
-
392
- if (this.player_ids.size >= max_players) {
393
- throw new Error('频道人数已达上限,无法进入');
394
- }
395
-
396
- if (this._hasPlayer(player.player_id)) {
397
- throw new Error('玩家已在当前频道中');
398
- }
399
- };
400
-
401
- /**
402
- * 获取默认房间
403
- * @param {string} player_id 玩家ID
404
- * @returns {object} 房间对象
405
- */
406
- Channel.prototype.getDefaultRoom = async function (player_id) {
407
- var player = this.getPlayer(player_id);
408
-
409
- var room_name = player.returning ? 'main_room' : 'cultivation_area';
410
- var room = this.rooms.get(room_name);
411
-
412
- if (!room) {
413
- throw new Error(`默认房间 ${room_name} 不存在`);
414
- }
415
-
416
- return room;
417
- };
418
-
419
- /**
420
- * 玩家退出频道
421
- * @param {string} player_id 玩家ID
422
- * @returns {boolean} 是否成功退出
423
- */
424
- Channel.prototype.leave = async function (player_id) {
425
- if (!player_id) {
426
- throw new TypeError('玩家ID不能为空');
427
- }
428
-
429
- if (!this.player_ids.has(player_id)) {
430
- throw new Error('玩家不在当前频道中');
431
- }
432
-
433
- this.player_ids.delete(player_id);
434
-
435
- this.emitEvent('player_leave_channel', {
436
- channel_id: this.config.name,
437
- player_id: player_id,
438
- timestamp: Date.now()
439
- });
440
- return true;
441
- };
442
-
443
- /**
444
- * 强制踢出玩家
445
- * @param {string} player_id 玩家ID
446
- * @param {string} reason 踢出原因
447
- * @returns {boolean} 是否成功踢出
448
- */
449
- Channel.prototype.kickPlayer = async function (player_id, reason = '系统维护') {
450
- if (!player_id) {
451
- throw new TypeError('玩家ID不能为空');
452
- }
453
-
454
- if (!this.player_ids.has(player_id)) {
455
- throw new Error('玩家不在当前频道中');
456
- }
457
-
458
- this.player_ids.delete(player_id);
459
-
460
- this.emitEvent('player_kicked_channel', {
461
- channel_id: this.config.name,
462
- player_id: player_id,
463
- reason: reason,
464
- timestamp: Date.now()
465
- });
466
- return true;
467
- };
468
-
469
- /**
470
- * 获取玩家对象
471
- * @param {string} player_id 玩家ID
472
- * @returns {object} 玩家对象
473
- */
474
- Channel.prototype.getPlayer = function (player_id) {
475
- if (!player_id) {
476
- throw new TypeError('玩家ID不能为空');
477
- }
478
-
479
- return this.getZone().getPlayer(player_id);
480
- };
481
-
482
- /**
483
- * 获取所有玩家对象
484
- * @returns {Array} 玩家对象列表
485
- */
486
- Channel.prototype.getAllPlayers = function () {
487
- var players = [];
488
- for (var player_id of this.player_ids.keys()) {
489
- var player = this.getPlayer(player_id);
490
- players.push(player);
491
- }
492
- return players;
493
- };
494
-
495
- /**
496
- * 创建房间
497
- * @param {string} name 房间名称
498
- * @param {string} player_id 玩家ID
499
- * @returns {object} 创建的房间对象
500
- */
501
- Channel.prototype.createRoom = async function (name, player_id) {
502
- if (!name) {
503
- throw new TypeError('房间名称不能为空');
504
- }
505
-
506
- if (!player_id) {
507
- throw new TypeError('玩家ID不能为空');
508
- }
509
-
510
- var player = this.getPlayer(player_id);
511
- if (!player) {
512
- throw new Error(`玩家 ${player_id} 不存在`);
513
- }
514
-
515
- let config = {
516
- name,
517
- 'min_level': player.level,
518
- 'max_level': player.level + 100
519
- };
520
-
521
- var room = await this.roomAdmin.create(config, player_id);
522
- return room;
523
- };
524
-
525
- /**
526
- * 销毁房间
527
- * @param {string} room_id 房间ID
528
- * @returns {boolean} 是否成功销毁
529
- */
530
- Channel.prototype.destroyRoom = async function (room_id) {
531
- if (!room_id) {
532
- throw new TypeError('房间ID不能为空');
533
- }
534
-
535
- this.roomAdmin.del(room_id);
536
- return true;
537
- };
538
-
539
- /**
540
- * 玩家进入房间
541
- * @param {string} room_id 房间ID
542
- * @param {string} player_id 玩家ID
543
- */
544
- Channel.prototype.enterRoom = function (room_id, player_id) {
545
- this.roomAdmin.enter(room_id, player_id);
546
- };
547
-
548
- /**
549
- * 玩家退出房间
550
- * @param {string} room_id 房间ID
551
- * @param {string} player_id 玩家ID
552
- */
553
- Channel.prototype.leaveRoom = function (room_id, player_id) {
554
- this.roomAdmin.leave(room_id, player_id);
555
- };
556
-
557
- /**
558
- * 获取房间列表
559
- * @returns {Array} 房间列表
560
- */
561
- Channel.prototype.getRoomList = function () {
562
- return this.roomAdmin.getList();
563
- };
564
-
565
- /**
566
- * 玩家开始实时匹配
567
- * @param {string} player_id 玩家ID
568
- * @param {object} options 匹配选项
569
- * @returns {object} 匹配状态
570
- */
571
- Channel.prototype.startQuickMatch = function (player_id, options = {}) {
572
- if (typeof player_id !== 'string') {
573
- throw new TypeError('玩家ID必须是字符串');
574
- }
575
-
576
- var mode = options.mode || 'rank';
577
- var format = options.format || '5v5';
578
- var team_members = options.team_members || [player_id];
579
- var team_size = team_members.length;
580
-
581
- if (team_size > this._getMaxTeamSize(format)) {
582
- throw new Error(`队伍人数超过限制: ${team_size}`);
583
- }
584
-
585
- for (var i = 0; i < team_members.length; i++) {
586
- if (this.matcher.has(team_members[i])) {
587
- throw new Error(`玩家 ${team_members[i]} 已在匹配池中`);
588
- }
589
- }
590
-
591
- this.matcher.join(player_id, {
592
- mode: mode,
593
- format: format,
594
- team_size: team_size,
595
- team_members: team_members
596
- });
597
-
598
- return this.matcher.getMatchStatus(player_id);
599
- };
600
-
601
- /**
602
- * 玩家取消实时匹配
603
- * @param {string} player_id 玩家ID
604
- * @returns {boolean} 是否成功取消
605
- */
606
- Channel.prototype.cancelQuickMatch = function (player_id) {
607
- if (typeof player_id !== 'string') {
608
- throw new TypeError('玩家ID必须是字符串');
609
- }
610
-
611
- return this.matcher.leave(player_id);
612
- };
613
-
614
- /**
615
- * 获取实时匹配状态
616
- * @param {string} player_id 玩家ID
617
- * @returns {object|null} 匹配状态
618
- */
619
- Channel.prototype.getQuickMatchStatus = function (player_id) {
620
- return this.matcher.getMatchStatus(player_id);
621
- };
622
-
623
- /**
624
- * 获取匹配统计信息
625
- * @returns {object} 统计信息
626
- */
627
- Channel.prototype.getMatchStats = function () {
628
- return {
629
- waiting_count: this.matcher.getTotalWaiting(),
630
- room_count: this.roomAdmin.getCount()
631
- };
632
- };
633
-
634
- /**
635
- * 获取最大队伍人数
636
- * @param {string} format 比赛格式
637
- * @returns {number} 最大队伍人数
638
- */
639
- Channel.prototype._getMaxTeamSize = function (format) {
640
- var sizes = {
641
- '1v1': 1,
642
- '3v3': 3,
643
- '5v5': 5
644
- };
645
- return sizes[format] || 5;
646
- };
647
-
648
- /**
649
- * 广播频道消息
650
- * @param {object} data 广播数据
651
- * @param {string} data_type 数据类型
652
- * @returns {boolean} 是否广播成功
653
- */
654
- Channel.prototype.broadcast = async function (data, data_type) {
655
- if (!data) {
656
- throw new TypeError('数据不能为空');
657
- }
658
-
659
- if (!data_type || typeof data_type !== 'string') {
660
- throw new TypeError('数据类型必须是有效字符串');
661
- }
662
-
663
- // 触发广播事件,由业务层处理具体的广播逻辑
664
- this.emitEvent('channel_broadcast', {
665
- channel_id: this.config.name,
666
- data: data,
667
- data_type: data_type,
668
- timestamp: Date.now()
669
- });
670
-
671
- return true;
672
- };
673
-
674
- /**
675
- * 发送频道消息
676
- * @param {string} player_id 发送者ID
677
- * @param {string} content 消息内容
678
- * @returns {boolean} 是否发送成功
679
- */
680
- Channel.prototype.sendMessage = async function (player_id, content) {
681
- if (!player_id) {
682
- throw new TypeError('发送者ID不能为空');
683
- }
684
-
685
- if (!content || typeof content !== 'string') {
686
- throw new TypeError('消息内容必须是有效字符串');
687
- }
688
-
689
- if (!this.player_ids.has(player_id)) {
690
- throw new Error('发送者不在当前频道中');
691
- }
692
-
693
- var player = this.getPlayer(player_id);
694
-
695
- var msg = {
696
- sender_id: player_id,
697
- sender_name: player.name || player_id,
698
- content: content,
699
- msg_type: 'text',
700
- msg_group: 'channel',
701
- timestamp: Date.now(),
702
- channel_id: this.config.name
703
- };
704
-
705
- // 触发消息发送事件,由业务层处理具体的发送逻辑
706
- this.emitEvent('channel_message_send', msg);
707
-
708
- return true;
709
- };
710
-
711
- /**
712
- * 公共数据传输方法
713
- * @param {string} sender_id 发送者ID
714
- * @param {object} data 传输数据
715
- * @param {string} data_type 数据类型
716
- * @returns {object} 传输结果
717
- */
718
- Channel.prototype.send = async function (sender_id, data, data_type) {
719
- // 触发数据发送事件,由业务层处理具体的发送逻辑
720
- this.emitEvent('channel_data_send', {
721
- channel_id: this.config.name,
722
- sender_id: sender_id,
723
- data: data,
724
- data_type: data_type,
725
- timestamp: Date.now()
726
- });
727
-
728
- // 返回默认结果
729
- return {
730
- players: this.player_ids.size,
731
- success: 0,
732
- fail: 0,
733
- failed_players: []
734
- };
735
- };
736
-
737
- /**
738
- * 获取频道统计信息
739
- * @returns {object} 统计信息
740
- */
741
- Channel.prototype.getStats = function () {
742
- return {
743
- ...this.stats,
744
- channel_name: this.config.name,
745
- channel_status: this.status,
746
- channel_type: this.config.type,
747
- current_time: Date.now()
748
- };
749
- };
750
-
751
- /**
752
- * 切换频道状态
753
- * @param {number} new_status 新状态
754
- * @returns {boolean} 是否切换成功
755
- */
756
- Channel.prototype.switchStatus = function (new_status) {
757
- if (typeof new_status !== 'number' || new_status < 0 || new_status > 3) {
758
- throw new TypeError('频道状态必须是0-3之间的数字');
759
- }
760
-
761
- var old_status = this.status;
762
- this.status = new_status;
763
-
764
- this.emitEvent('channel_status_changed', {
765
- channel_id: this.config.name,
766
- old_status: old_status,
767
- new_status: new_status,
768
- timestamp: Date.now()
769
- });
770
-
771
- this.log('info', `频道 ${this.channel_id} 状态切换: ${old_status} -> ${new_status}`);
772
- return true;
773
- };
774
-
775
- /**
776
- * 销毁频道
777
- * @returns {boolean} 是否销毁成功
778
- */
779
- Channel.prototype.destroy = async function () {
780
- if (this._monitorTimer) {
781
- clearInterval(this._monitorTimer);
782
- }
783
-
784
- for (var player_id of this.player_ids.keys()) {
785
- await this.kickPlayer(player_id, '频道关闭');
786
- }
787
-
788
- // 保存所有房间数据
789
- await this._saveRooms();
790
-
791
- for (var world_name of this.worlds.keys()) {
792
- await this.destroyWorld(world_name);
793
- }
794
-
795
- for (var room_name of this.rooms.keys()) {
796
- await this.destroyRoom(room_name);
797
- }
798
-
799
- if (this.matcher) {
800
- this.matcher.stop();
801
- }
802
-
803
- if (this.roomAdmin) {
804
- this.roomAdmin.destroy();
805
- }
806
-
807
- this.emitEvent('channel_destroyed', {
808
- channel_id: this.channel_id,
809
- timestamp: Date.now()
810
- });
811
- return true;
812
- };
813
-
814
- /**
815
- * 保存所有房间数据
816
- * @returns {Promise<void>}
817
- */
818
- Channel.prototype._saveRooms = async function () {
819
- try {
820
- var promises = [];
821
- for (var room of this.roomAdmin._rooms.values()) {
822
- promises.push(room.do('save'));
823
- }
824
- await Promise.all(promises);
825
- this.log('info', `保存所有房间数据成功,共 ${this.roomAdmin._rooms.size} 个`);
826
- } catch (error) {
827
- this.log('error', `保存房间数据失败: `, error);
828
- }
829
- };
830
-
831
- /**
832
- * 获取频道玩家数量
833
- * @returns {number} 玩家数量
834
- */
835
- Channel.prototype.getPlayerNum = function () {
836
- return this.player_ids.size;
837
- };
838
-
839
- /**
840
- * 进入主世界
841
- * @param {string} player_id 玩家ID
842
- */
843
- Channel.prototype.enterMainWorld = function (player_id) {
844
- this.enterWorld(player_id, this._main_world_id);
845
- };
846
-
847
- /**
848
- * 进入世界
849
- * @param {string} player_id 玩家ID
850
- * @param {string} world_id 世界ID
851
- * @returns {object} 世界对象
852
- */
853
- Channel.prototype.enterWorld = function (player_id, world_id) {
854
- let world = this.getWorldById(world_id);
855
- if (!world) {
856
- throw new Error(`世界 ${world_id} 不存在`);
857
- }
858
- world.enter(player_id);
859
- return world;
860
- };
861
-
862
- /**
863
- * 根据世界ID获取世界对象
864
- * @param {string} world_id 世界ID
865
- * @returns {object|null} 世界对象
866
- */
867
- Channel.prototype.getWorldById = function (world_id) {
868
- return this.worlds.get(world_id);
869
- };
870
-
871
- /**
872
- * 根据世界名称获取世界对象
873
- * @param {string} name 世界名称
874
- * @returns {object|null} 世界对象
875
- */
876
- Channel.prototype.getWorldByName = function (name) {
877
- for (let world of this.worlds.values()) {
878
- if (world.config.name === name) {
879
- return world;
880
- }
881
- }
882
- return null;
883
- };
884
-
885
- /**
886
- * 创建游戏世界
887
- * @param {object} config 世界配置
888
- * @param {string} world_id 世界ID
889
- * @returns {object} 世界对象
890
- */
891
- Channel.prototype.createWorld = async function (config, world_id) {
892
- // let game = this.game[config.type];
893
- // if (!game) {
894
- // throw new Error(`游戏类型 ${config.type} 不存在`);
895
- // }
896
- // return await game.createWorld(config, world_id);
897
- };
898
-
899
- exports.Channel = Channel;