mm_os 4.3.6 → 5.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.
Files changed (234) hide show
  1. package/README.md +101 -504
  2. package/index.js +26 -19
  3. package/lib/loader.js +787 -0
  4. package/lib/os.js +369 -0
  5. package/package.json +55 -80
  6. package/LICENSE +0 -21
  7. package/README_EN.md +0 -505
  8. package/adapter/adapter.js +0 -484
  9. package/adapter/custom_persistence.js +0 -661
  10. package/adapter/mqtt.js +0 -254
  11. package/adapter/socket.js +0 -244
  12. package/adapter/web.js +0 -92
  13. package/adapter/websocket.js +0 -43
  14. package/ai.js +0 -130
  15. package/com/api/README.md +0 -3
  16. package/com/api/com.json +0 -5
  17. package/com/api/config.tpl.json +0 -41
  18. package/com/api/drive.js +0 -732
  19. package/com/api/index.js +0 -201
  20. package/com/api/oauth.js +0 -200
  21. package/com/api/script.tpl.js +0 -32
  22. package/com/cmd/README.md +0 -11
  23. package/com/cmd/com.json +0 -5
  24. package/com/cmd/config.tpl.json +0 -48
  25. package/com/cmd/drive.js +0 -176
  26. package/com/cmd/index.js +0 -287
  27. package/com/cmd/job/config.json +0 -66
  28. package/com/cmd/job/index.js +0 -241
  29. package/com/cmd/job/test.js +0 -188
  30. package/com/cmd/msg.json +0 -48
  31. package/com/cmd/script.tpl.js +0 -12
  32. package/com/db/README.md +0 -3
  33. package/com/db/com.json +0 -5
  34. package/com/db/drive.js +0 -2206
  35. package/com/db/index.js +0 -241
  36. package/com/event/README.md +0 -4
  37. package/com/event/com.json +0 -5
  38. package/com/event/config.tpl.json +0 -20
  39. package/com/event/drive.js +0 -61
  40. package/com/event/index.js +0 -438
  41. package/com/event/script.tpl.js +0 -23
  42. package/com/mqtt/README.md +0 -47
  43. package/com/mqtt/com.json +0 -5
  44. package/com/mqtt/config.tpl.json +0 -42
  45. package/com/mqtt/drive.js +0 -696
  46. package/com/mqtt/index.js +0 -1017
  47. package/com/mqtt/index.test.js +0 -503
  48. package/com/mqtt/mm_mqtt.js +0 -446
  49. package/com/mqtt/script.tpl.js +0 -719
  50. package/com/nav/README.md +0 -2
  51. package/com/nav/com.json +0 -5
  52. package/com/nav/config.tpl.json +0 -84
  53. package/com/nav/drive.js +0 -702
  54. package/com/nav/index.js +0 -228
  55. package/com/nav/tpl/admin_pc/page_channel.vue +0 -305
  56. package/com/nav/tpl/admin_pc/page_config.vue +0 -280
  57. package/com/nav/tpl/admin_pc/page_config_form.vue +0 -194
  58. package/com/nav/tpl/admin_pc/page_default.vue +0 -247
  59. package/com/nav/tpl/admin_pc/page_form.vue +0 -180
  60. package/com/nav/tpl/admin_pc/page_lang.vue +0 -264
  61. package/com/nav/tpl/admin_pc/page_nav.vue +0 -321
  62. package/com/nav/tpl/admin_pc/page_table.vue +0 -296
  63. package/com/nav/tpl/admin_pc/page_type.vue +0 -321
  64. package/com/nav/tpl/admin_pc/page_view.vue +0 -124
  65. package/com/nav/tpl/dev_pc/page_channel.vue +0 -253
  66. package/com/nav/tpl/dev_pc/page_config.vue +0 -253
  67. package/com/nav/tpl/dev_pc/page_default.vue +0 -247
  68. package/com/nav/tpl/dev_pc/page_form.vue +0 -162
  69. package/com/nav/tpl/dev_pc/page_nav.vue +0 -253
  70. package/com/nav/tpl/dev_pc/page_table.vue +0 -247
  71. package/com/nav/tpl/dev_pc/page_type.vue +0 -313
  72. package/com/nav/tpl/home_pc/page_channel.vue +0 -234
  73. package/com/nav/tpl/home_pc/page_default.vue +0 -234
  74. package/com/nav/tpl/home_pc/page_form.vue +0 -137
  75. package/com/nav/tpl/home_pc/page_list.vue +0 -234
  76. package/com/nav/tpl/home_pc/page_nav.vue +0 -221
  77. package/com/nav/tpl/home_pc/page_type.vue +0 -234
  78. package/com/nav/tpl/home_pc/page_view.vue +0 -125
  79. package/com/nav/tpl/home_phone/page_channel.vue +0 -234
  80. package/com/nav/tpl/home_phone/page_default.vue +0 -234
  81. package/com/nav/tpl/home_phone/page_form.vue +0 -137
  82. package/com/nav/tpl/home_phone/page_list.vue +0 -234
  83. package/com/nav/tpl/home_phone/page_nav.vue +0 -237
  84. package/com/nav/tpl/home_phone/page_type.vue +0 -234
  85. package/com/nav/tpl/home_phone/page_view.vue +0 -125
  86. package/com/nav/viewmodel.js +0 -446
  87. package/com/param/README.md +0 -2
  88. package/com/param/com.json +0 -5
  89. package/com/param/config.tpl.json +0 -136
  90. package/com/param/drive.js +0 -502
  91. package/com/param/index.js +0 -158
  92. package/com/param/script.tpl.js +0 -12
  93. package/com/pendant/README.md +0 -3
  94. package/com/pendant/com.json +0 -5
  95. package/com/pendant/config.tpl.json +0 -32
  96. package/com/pendant/drive.js +0 -204
  97. package/com/pendant/index.js +0 -467
  98. package/com/pendant/pendant.html +0 -16
  99. package/com/pendant/script.tpl.js +0 -18
  100. package/com/socket/README.md +0 -47
  101. package/com/socket/com.json +0 -5
  102. package/com/socket/config.tpl.json +0 -12
  103. package/com/socket/drive.js +0 -679
  104. package/com/socket/index.js +0 -402
  105. package/com/socket/script.tpl.js +0 -41
  106. package/com/sql/README.md +0 -3
  107. package/com/sql/com.json +0 -5
  108. package/com/sql/config.tpl.json +0 -74
  109. package/com/sql/drive.js +0 -1283
  110. package/com/sql/index.js +0 -152
  111. package/com/sql/script.tpl.js +0 -47
  112. package/com/static/README.md +0 -2
  113. package/com/static/com.json +0 -5
  114. package/com/static/config.tpl.json +0 -24
  115. package/com/static/drive.js +0 -194
  116. package/com/static/index.js +0 -237
  117. package/com/static/script.tpl.js +0 -28
  118. package/com/task/README.md +0 -3
  119. package/com/task/com.json +0 -5
  120. package/com/task/config.tpl.json +0 -24
  121. package/com/task/drive.js +0 -428
  122. package/com/task/index.js +0 -159
  123. package/com/task/script.tpl.js +0 -40
  124. package/com/template/com.json +0 -5
  125. package/com/template/config.tpl.json +0 -16
  126. package/com/template/drive.js +0 -81
  127. package/com/template/index.js +0 -141
  128. package/com.js +0 -237
  129. package/common/README.md +0 -2
  130. package/common/middleware/web_after/index.js +0 -26
  131. package/common/middleware/web_after/middleware.json +0 -9
  132. package/common/middleware/web_base/index.js +0 -113
  133. package/common/middleware/web_base/middleware.json +0 -19
  134. package/common/middleware/web_before/index.js +0 -33
  135. package/common/middleware/web_before/middleware.json +0 -9
  136. package/common/middleware/web_cors/index.js +0 -87
  137. package/common/middleware/web_cors/middleware.json +0 -24
  138. package/common/middleware/web_error/index.js +0 -131
  139. package/common/middleware/web_error/middleware.json +0 -19
  140. package/common/middleware/web_ip/index.js +0 -15
  141. package/common/middleware/web_ip/middleware.json +0 -14
  142. package/common/middleware/web_logger/index.js +0 -156
  143. package/common/middleware/web_logger/middleware.json +0 -14
  144. package/common/middleware/web_main/index.js +0 -26
  145. package/common/middleware/web_main/middleware.json +0 -9
  146. package/common/middleware/web_static/index.js +0 -73
  147. package/common/middleware/web_static/middleware.json +0 -54
  148. package/common/middleware/web_waf/index.js +0 -385
  149. package/common/middleware/web_waf/middleware.json +0 -13
  150. package/core/app/config.tpl.json +0 -67
  151. package/core/app/index.js +0 -710
  152. package/core/app/script.tpl.js +0 -52
  153. package/core/channel/index.js +0 -899
  154. package/core/channel/matcher.js +0 -585
  155. package/core/com/config.tpl.json +0 -16
  156. package/core/com/index.js +0 -77
  157. package/core/com/script.tpl.js +0 -5
  158. package/core/component/component.js +0 -42
  159. package/core/component/config.tpl.json +0 -63
  160. package/core/component/index.js +0 -273
  161. package/core/component/script.tpl.js +0 -19
  162. package/core/controller/config.tpl.json +0 -14
  163. package/core/controller/index.js +0 -371
  164. package/core/controller/script.tpl.js +0 -27
  165. package/core/factory/config.tpl.json +0 -14
  166. package/core/factory/entity.js +0 -275
  167. package/core/factory/index.js +0 -241
  168. package/core/factory/script.tpl.js +0 -16
  169. package/core/game/bat/index.js +0 -137
  170. package/core/game/bat/world.js +0 -622
  171. package/core/game/config.tpl.json +0 -16
  172. package/core/game/entity_admin.js +0 -230
  173. package/core/game/index.js +0 -186
  174. package/core/handler/config.tpl.json +0 -22
  175. package/core/handler/index.js +0 -181
  176. package/core/handler/script.tpl.js +0 -23
  177. package/core/logic/config.tpl.json +0 -14
  178. package/core/logic/index.js +0 -59
  179. package/core/logic/script.tpl.js +0 -19
  180. package/core/middleware/config.tpl.json +0 -16
  181. package/core/middleware/index.js +0 -117
  182. package/core/middleware/script.tpl.js +0 -37
  183. package/core/mod/config.tpl.json +0 -22
  184. package/core/mod/index.js +0 -136
  185. package/core/mod/script.tpl.js +0 -34
  186. package/core/model/config.tpl.json +0 -219
  187. package/core/model/index.js +0 -272
  188. package/core/model/model.js +0 -27
  189. package/core/model/script.tpl.js +0 -20
  190. package/core/notifier/config.tpl.json +0 -14
  191. package/core/notifier/index.js +0 -77
  192. package/core/notifier/script.tpl.js +0 -20
  193. package/core/plugin/config.tpl.json +0 -36
  194. package/core/plugin/index.js +0 -297
  195. package/core/plugin/script.tpl.js +0 -51
  196. package/core/pusher/config.tpl.json +0 -14
  197. package/core/pusher/index.js +0 -158
  198. package/core/pusher/script.tpl.js +0 -20
  199. package/core/room/bat/index.js +0 -170
  200. package/core/room/bat/room.js +0 -524
  201. package/core/room/config.tpl.json +0 -20
  202. package/core/room/index.js +0 -249
  203. package/core/room/room.js +0 -61
  204. package/core/scene/config.tpl.json +0 -14
  205. package/core/scene/index.js +0 -467
  206. package/core/scene/loop.js +0 -1306
  207. package/core/scene/map.js +0 -28
  208. package/core/scene/script.tpl.js +0 -22
  209. package/core/sender/config.tpl.json +0 -14
  210. package/core/sender/index.js +0 -79
  211. package/core/sender/script.tpl.js +0 -20
  212. package/core/service/config.tpl.json +0 -14
  213. package/core/service/index.js +0 -100
  214. package/core/service/script.tpl.js +0 -25
  215. package/core/store/config.tpl.json +0 -26
  216. package/core/store/index.js +0 -1794
  217. package/core/store/script.tpl.js +0 -22
  218. package/core/store/sql.js +0 -1488
  219. package/core/system/config.tpl.json +0 -18
  220. package/core/system/index.js +0 -312
  221. package/core/system/script.tpl.js +0 -77
  222. package/core/view/config.tpl.json +0 -14
  223. package/core/view/index.js +0 -91
  224. package/core/view/script.tpl.js +0 -20
  225. package/core/zone/bat/index.js +0 -744
  226. package/core/zone/config.tpl.json +0 -54
  227. package/core/zone/index.js +0 -655
  228. package/core/zone/script.tpl.js +0 -10
  229. package/core/zone/zone_bat.js +0 -136
  230. package/core//345/237/272/347/261/273/346/250/241/345/235/227/346/270/205/345/215/225.md +0 -24
  231. package/os.js +0 -53
  232. package/server.js +0 -595
  233. package/ulits/id_gen.js +0 -134
  234. 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;