mm_os 4.0.8 → 4.1.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/com/api/drive.js +4 -4
- package/com/api/oauth.js +1 -1
- package/com/cmd/drive.js +1 -1
- package/com/db/drive.js +1 -1
- package/com/event/drive.js +1 -1
- package/com/event/index.js +4 -2
- package/com/mqtt/drive.js +1 -1
- package/com/nav/drive.js +1 -1
- package/com/param/drive.js +1 -1
- package/com/pendant/drive.js +4 -4
- package/com/socket/drive.js +1 -1
- package/com/sql/drive.js +1 -1
- package/com/static/drive.js +1 -1
- package/com/task/drive.js +1 -1
- package/com/template/drive.js +2 -2
- package/com/template/index.js +5 -5
- package/common/middleware/web_after/index.js +4 -1
- package/common/middleware/web_static/index.js +1 -1
- package/core/app/bat.js +657 -0
- package/core/app/config.tpl.json +5 -5
- package/core/app/index.js +24 -45
- package/core/channel/index.js +13 -12
- package/core/com/index.js +3 -0
- package/core/game/bat/world.js +2 -2
- package/core/game/index.js +3 -3
- package/core/mod/index.js +6 -0
- package/core/plugin/index.js +1 -1
- package/core/zone/bat/index.js +9 -8
- package/core/zone/index.js +4 -3
- package/package.json +5 -5
- package/server.js +109 -277
package/core/app/bat.js
ADDED
|
@@ -0,0 +1,657 @@
|
|
|
1
|
+
const compressing = require('compressing');
|
|
2
|
+
const {
|
|
3
|
+
Manager,
|
|
4
|
+
Drive
|
|
5
|
+
} = require('mm_machine');
|
|
6
|
+
const {
|
|
7
|
+
Controller
|
|
8
|
+
} = require('../controller/index.js');
|
|
9
|
+
const {
|
|
10
|
+
View
|
|
11
|
+
} = require('../view/index.js');
|
|
12
|
+
const {
|
|
13
|
+
Handler
|
|
14
|
+
} = require('../handler/index.js');
|
|
15
|
+
const {
|
|
16
|
+
Service
|
|
17
|
+
} = require('../service/index.js');
|
|
18
|
+
const {
|
|
19
|
+
Model
|
|
20
|
+
} = require('../model/index.js');
|
|
21
|
+
const {
|
|
22
|
+
Store
|
|
23
|
+
} = require('../store/index.js');
|
|
24
|
+
const {
|
|
25
|
+
Plugin
|
|
26
|
+
} = require('../plugin/index.js');
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 应用
|
|
30
|
+
*/
|
|
31
|
+
class App extends Drive {
|
|
32
|
+
static config = {
|
|
33
|
+
'name': 'default',
|
|
34
|
+
'version': '1.0.0',
|
|
35
|
+
'title': '应用',
|
|
36
|
+
'description': '应用',
|
|
37
|
+
'author': 'qww',
|
|
38
|
+
'scope': 'server',
|
|
39
|
+
'dir': '',
|
|
40
|
+
// 游戏配置,非游戏模块不需要配置
|
|
41
|
+
'game': {
|
|
42
|
+
// 游戏类型
|
|
43
|
+
'type': 'card_game'
|
|
44
|
+
},
|
|
45
|
+
'dependencies': [],
|
|
46
|
+
// // 压缩目录
|
|
47
|
+
// 'zip_dir': './static/file/zip',
|
|
48
|
+
// // 解压目录
|
|
49
|
+
// 'unzip_dir': './static/file/unzip',
|
|
50
|
+
// 自定义数据库 - 1 开启 0 关闭
|
|
51
|
+
'diy_sql': 0,
|
|
52
|
+
// 自定义缓存 - 1 开启 0 关闭
|
|
53
|
+
'diy_cache': 0,
|
|
54
|
+
// sql 配置 - 可以没有,没有则使用默认数据库
|
|
55
|
+
'sql': {
|
|
56
|
+
// 数据库方式
|
|
57
|
+
'way': 'mysql',
|
|
58
|
+
'mysql': {
|
|
59
|
+
// 数据库主机
|
|
60
|
+
'host': '127.0.0.1',
|
|
61
|
+
// 数据库端口
|
|
62
|
+
'port': 3306,
|
|
63
|
+
// 数据库用户名
|
|
64
|
+
'username': 'root',
|
|
65
|
+
// 数据库密码
|
|
66
|
+
'password': 'Asd159357',
|
|
67
|
+
// 数据库名称
|
|
68
|
+
'database': 'mos'
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
// 缓存配置 - 可以没有,没有则使用默认缓存
|
|
72
|
+
'cache': {
|
|
73
|
+
// 缓存方式方式
|
|
74
|
+
'way': 'redis',
|
|
75
|
+
'redis': {
|
|
76
|
+
// 缓存主机
|
|
77
|
+
'host': '127.0.0.1',
|
|
78
|
+
// 缓存端口
|
|
79
|
+
'port': 6379,
|
|
80
|
+
// 缓存密码
|
|
81
|
+
'password': 'asd159357',
|
|
82
|
+
// 缓存数据库
|
|
83
|
+
'database': 0
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* 构造函数
|
|
89
|
+
* @param {object} config 配置参数
|
|
90
|
+
* @param {object} parent 父对象
|
|
91
|
+
*/
|
|
92
|
+
constructor(config, parent) {
|
|
93
|
+
super({ ...App.config, ...config || {} }, parent);
|
|
94
|
+
// 管理器集合
|
|
95
|
+
this.manager = {};
|
|
96
|
+
|
|
97
|
+
/** == 业务类 == */
|
|
98
|
+
// MVC 架构
|
|
99
|
+
// 控制器集合
|
|
100
|
+
this.controller = {};
|
|
101
|
+
// 视图集合
|
|
102
|
+
this.view = {};
|
|
103
|
+
// 操作器集合
|
|
104
|
+
this.handler = {};
|
|
105
|
+
// 领域服务
|
|
106
|
+
this.service = {};
|
|
107
|
+
// 富血模型
|
|
108
|
+
this.model = {};
|
|
109
|
+
// 仓储
|
|
110
|
+
this.store = {};
|
|
111
|
+
|
|
112
|
+
// 插件,用于功能扩展
|
|
113
|
+
this.plugin = {};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 预置
|
|
119
|
+
*/
|
|
120
|
+
App.prototype._preset = function () {
|
|
121
|
+
// 数据库,调用统一数据库接口,也允许重定义数据库
|
|
122
|
+
this.sql = null;
|
|
123
|
+
// 缓存,调用统一缓存接口,也允许重定义缓存
|
|
124
|
+
this.cache = null;
|
|
125
|
+
// 文件管理,调用统一文件管理接口,也允许重定义文件管理
|
|
126
|
+
this.filer = null;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 获取服务器实例
|
|
130
|
+
* @returns {object} 服务器实例对象
|
|
131
|
+
*/
|
|
132
|
+
this.getServer = function () {
|
|
133
|
+
return $.server;
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* 创建管理器实例
|
|
139
|
+
* @param {string} name 管理器名称
|
|
140
|
+
* @param {string} title 管理器标题
|
|
141
|
+
* @param {Function} cls 管理器类
|
|
142
|
+
* @returns {object} 管理器实例
|
|
143
|
+
*/
|
|
144
|
+
App.prototype._createManager = function (name, title, cls) {
|
|
145
|
+
let dir = this.getDir();
|
|
146
|
+
var manager = new Manager({
|
|
147
|
+
name: name,
|
|
148
|
+
title: title,
|
|
149
|
+
filename: name + '.json',
|
|
150
|
+
tpl_dir: `../${name}/`.fullname(__dirname),
|
|
151
|
+
base_dir: '',
|
|
152
|
+
dir: `./${name}`.fullname(dir)
|
|
153
|
+
}, this, this[name], cls);
|
|
154
|
+
this.manager[name] = manager;
|
|
155
|
+
return manager;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* 初始化管理器集合
|
|
160
|
+
* @returns {Promise<void>} 初始化完成
|
|
161
|
+
*/
|
|
162
|
+
App.prototype._initManager = async function () {
|
|
163
|
+
// console.time('[TIMING] App._initManager');
|
|
164
|
+
// 管理器配置列表
|
|
165
|
+
var mgr_configs = [
|
|
166
|
+
{ name: 'store', title: '仓储', Module: Store },
|
|
167
|
+
{ name: 'model', title: '模型', Module: Model },
|
|
168
|
+
{ name: 'service', title: '服务', Module: Service },
|
|
169
|
+
{ name: 'handler', title: '操作器', Module: Handler },
|
|
170
|
+
{ name: 'view', title: '视图', Module: View },
|
|
171
|
+
{ name: 'controller', title: '控制器', Module: Controller },
|
|
172
|
+
{ name: 'plugin', title: '插件', Module: Plugin }
|
|
173
|
+
];
|
|
174
|
+
|
|
175
|
+
// 管理器实例集合
|
|
176
|
+
var managers = [];
|
|
177
|
+
|
|
178
|
+
// 使用for循环初始化所有管理器
|
|
179
|
+
for (var i = 0; i < mgr_configs.length; i++) {
|
|
180
|
+
var config = mgr_configs[i];
|
|
181
|
+
var manager = this._createManager(config.name, config.title, config.Module);
|
|
182
|
+
managers.push(manager);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// 并行执行所有管理器的初始化操作
|
|
186
|
+
var init_promises = [];
|
|
187
|
+
for (var j = 0; j < managers.length; j++) {
|
|
188
|
+
init_promises.push(managers[j].do('init'));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
await Promise.all(init_promises);
|
|
192
|
+
// console.timeEnd('[TIMING] App._initManager');
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* 加载资源
|
|
197
|
+
* @returns {Promise<void>} 加载完成
|
|
198
|
+
*/
|
|
199
|
+
App.prototype._loadSources = async function () {
|
|
200
|
+
// console.time('[TIMING] App._loadSources');
|
|
201
|
+
// 加载无需依赖关系,所以可以同时加载,内部并行执行
|
|
202
|
+
let loadPromises = [
|
|
203
|
+
this.manager.store.runAll('load'),
|
|
204
|
+
this.manager.model.runAll('load'),
|
|
205
|
+
this.manager.service.runAll('load'),
|
|
206
|
+
this.manager.handler.runAll('load'),
|
|
207
|
+
this.manager.view.runAll('load'),
|
|
208
|
+
this.manager.controller.runAll('load'),
|
|
209
|
+
this.manager.plugin.runAll('load')
|
|
210
|
+
];
|
|
211
|
+
await Promise.all(loadPromises);
|
|
212
|
+
// console.timeEnd('[TIMING] App._loadSources');
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* 初始化路由相关模块
|
|
217
|
+
*/
|
|
218
|
+
App.prototype._initRouteMod = async function () {
|
|
219
|
+
// console.time('[TIMING] App._initRouteMod');
|
|
220
|
+
|
|
221
|
+
// console.time('[TIMING] App._initRouteMod/store+view+model');
|
|
222
|
+
await Promise.all([
|
|
223
|
+
this.manager.store.runAll('init', this.sql, this.cache, this.getLogger()),
|
|
224
|
+
this.manager.view.runAll('init', this.getLogger()),
|
|
225
|
+
this.manager.model.runAll('init', this.getEventer(), this.getLogger())
|
|
226
|
+
]);
|
|
227
|
+
// console.timeEnd('[TIMING] App._initRouteMod/store+view+model');
|
|
228
|
+
|
|
229
|
+
// console.time('[TIMING] App._initRouteMod/service');
|
|
230
|
+
await this.manager.service.runAll('init', this.model, this.getEventer(), this.getLogger());
|
|
231
|
+
// console.timeEnd('[TIMING] App._initRouteMod/service');
|
|
232
|
+
|
|
233
|
+
// console.time('[TIMING] App._initRouteMod/controller+handler');
|
|
234
|
+
|
|
235
|
+
await Promise.all([
|
|
236
|
+
this.manager.controller.runAll('init', this.model, this.service, this.view, this.getEventer(), this.getLogger()),
|
|
237
|
+
this.manager.handler.runAll('init', this, this.getEventer(), this.getLogger())
|
|
238
|
+
]);
|
|
239
|
+
// console.timeEnd('[TIMING] App._initRouteMod/controller+handler');
|
|
240
|
+
|
|
241
|
+
// console.timeEnd('[TIMING] App._initRouteMod');
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* 初始化资源
|
|
246
|
+
* @returns {Promise<void>} 初始化完成
|
|
247
|
+
*/
|
|
248
|
+
App.prototype._initSources = async function () {
|
|
249
|
+
// console.time('[TIMING] App._initSources');
|
|
250
|
+
|
|
251
|
+
// console.time('[TIMING] App._initSources/plugin');
|
|
252
|
+
// await this.manager.plugin.runWait('init', this, this.getEventer(), this.getLogger());
|
|
253
|
+
await this.manager.plugin.runAll('init', this, this.getEventer(), this.getLogger());
|
|
254
|
+
// console.timeEnd('[TIMING] App._initSources/plugin');
|
|
255
|
+
|
|
256
|
+
await this._initRouteMod();
|
|
257
|
+
// console.timeEnd('[TIMING] App._initSources');
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* 初始化核心
|
|
262
|
+
* @param {object} server 服务器
|
|
263
|
+
* @param {object} eventer 事件总线
|
|
264
|
+
* @param {object} logger 日志管理器
|
|
265
|
+
* @private
|
|
266
|
+
*/
|
|
267
|
+
App.prototype._initCore = async function (server, eventer, logger) {
|
|
268
|
+
if (logger) {
|
|
269
|
+
this.setLogger(logger);
|
|
270
|
+
}
|
|
271
|
+
if (eventer) {
|
|
272
|
+
this.getEventer = function () {
|
|
273
|
+
return eventer;
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
if (server) {
|
|
277
|
+
this.getServer = function () {
|
|
278
|
+
return server;
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
// 初始化基础设施
|
|
282
|
+
await this._initBase();
|
|
283
|
+
// 初始化管理
|
|
284
|
+
await this._initManager();
|
|
285
|
+
// 加载资源
|
|
286
|
+
await this._loadSources();
|
|
287
|
+
// 验证依赖关系
|
|
288
|
+
this._validateDep();
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* 初始化基础设施
|
|
293
|
+
* @private
|
|
294
|
+
*/
|
|
295
|
+
App.prototype._initBase = async function () {
|
|
296
|
+
// 初始化文件处理器
|
|
297
|
+
this.filer = this.getServer().filer;
|
|
298
|
+
|
|
299
|
+
// 初始化数据库
|
|
300
|
+
if (this.config.diy_sql) {
|
|
301
|
+
var cg = this.config.sql || $.config.get('sql');
|
|
302
|
+
this.sql = sqlAdmin(this.config.name, cg);
|
|
303
|
+
await this.sql.open();
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
this.sql = this.getServer().sql;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// 初始化缓存
|
|
310
|
+
if (this.config.diy_cache) {
|
|
311
|
+
var cg = this.config.cache || $.config.get('cache');
|
|
312
|
+
this.cache = cacheAdmin(this.config.name, cg);
|
|
313
|
+
await this.cache.connect();
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
this.cache = this.getServer().cache;
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* 启动资源
|
|
322
|
+
*/
|
|
323
|
+
App.prototype._startSources = async function () {
|
|
324
|
+
// 启动无顺序的管理器(保持并行执行)
|
|
325
|
+
let startPromises = [
|
|
326
|
+
this.manager.handler.runAll('start'),
|
|
327
|
+
this.manager.service.runAll('start'),
|
|
328
|
+
this.manager.store.runAll('start'),
|
|
329
|
+
this.manager.plugin.runAll('start')
|
|
330
|
+
];
|
|
331
|
+
|
|
332
|
+
// 等待其他管理器启动完成
|
|
333
|
+
await Promise.all(startPromises);
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* 启动核心
|
|
338
|
+
* @private
|
|
339
|
+
*/
|
|
340
|
+
App.prototype._startCore = async function () {
|
|
341
|
+
// 初始化资源
|
|
342
|
+
await this._initSources();
|
|
343
|
+
// 启动资源
|
|
344
|
+
await this._startSources();
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* 验证依赖关系
|
|
349
|
+
* @private
|
|
350
|
+
*/
|
|
351
|
+
App.prototype._validateDep = function () {
|
|
352
|
+
var required_managers = ['store', 'model', 'service', 'handler', 'view', 'controller', 'plugin'];
|
|
353
|
+
|
|
354
|
+
for (var name of required_managers) {
|
|
355
|
+
if (!this.manager[name]) {
|
|
356
|
+
throw new Error(`缺少必要的管理器: ${name}`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* 记录初始化结果
|
|
363
|
+
* @param {Array} results 初始化结果数组
|
|
364
|
+
* @private
|
|
365
|
+
*/
|
|
366
|
+
App.prototype._logInitResults = function (results) {
|
|
367
|
+
var success = 0;
|
|
368
|
+
var error = 0;
|
|
369
|
+
var error_modules = [];
|
|
370
|
+
|
|
371
|
+
for (var result of results) {
|
|
372
|
+
if (result.result && result.result.error_count > 0) {
|
|
373
|
+
error += result.result.error_count;
|
|
374
|
+
error_modules.push(result.name);
|
|
375
|
+
} else {
|
|
376
|
+
success++;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (error > 0) {
|
|
381
|
+
this.log('warn', `资源初始化完成,成功: ${success}, 失败: ${error}, 失败模块: ${error_modules.join(', ')}`);
|
|
382
|
+
} else {
|
|
383
|
+
this.log('info', `所有资源初始化成功,共 ${success} 个模块`);
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* 压缩插件
|
|
389
|
+
* @param {string} name 插件名称
|
|
390
|
+
* @returns {string} 打包成功返回压缩包文件地址
|
|
391
|
+
*/
|
|
392
|
+
App.prototype.zipPlugin = async function (name) {
|
|
393
|
+
if (!name || typeof name !== 'string') {
|
|
394
|
+
throw new TypeError('无效的插件名称');
|
|
395
|
+
}
|
|
396
|
+
var plugin = this.plugin[name];
|
|
397
|
+
if (!plugin) {
|
|
398
|
+
throw new Error(`插件 ${name} 不存在`);
|
|
399
|
+
}
|
|
400
|
+
var dir = plugin.dir;
|
|
401
|
+
var file = ('./' + name + '.zip').fullname('/static/file/zip/');
|
|
402
|
+
return await this.zip(file, dir);
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* 压缩插件
|
|
407
|
+
* @param {string} file 插件压缩文件
|
|
408
|
+
* @param {string} dir 要压缩的目录
|
|
409
|
+
* @returns {string} 打包成功返回压缩包文件地址
|
|
410
|
+
*/
|
|
411
|
+
App.prototype.zip = async function (file, dir = '/static/file/zip/') {
|
|
412
|
+
file.addDir();
|
|
413
|
+
await compressing.zip.compressDir(dir, file);
|
|
414
|
+
if (file.hasFile()) {
|
|
415
|
+
return file;
|
|
416
|
+
}
|
|
417
|
+
return null;
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* 解压插件
|
|
422
|
+
* @param {object} file 插件压缩文件
|
|
423
|
+
* @param {string} unzip_dir 要解压的目录
|
|
424
|
+
* @returns {object} 返回解压结果
|
|
425
|
+
*/
|
|
426
|
+
App.prototype.unzip = async function (file, unzip_dir = '/cache/unzip/') {
|
|
427
|
+
if (!file) {
|
|
428
|
+
throw new TypeError('无效的文件路径');
|
|
429
|
+
}
|
|
430
|
+
var name = file.basename().left('.');
|
|
431
|
+
var dir = name.fullname(unzip_dir);
|
|
432
|
+
|
|
433
|
+
try {
|
|
434
|
+
// 确保目标目录存在
|
|
435
|
+
dir.addDir();
|
|
436
|
+
// 解压文件到目标目录
|
|
437
|
+
await compressing.zip.uncompress(file, dir);
|
|
438
|
+
// 查找目标目录
|
|
439
|
+
var target_dir = this._findTargetDir(dir, name);
|
|
440
|
+
// 如果找到目标目录,直接使用它作为解压结果
|
|
441
|
+
if (target_dir) {
|
|
442
|
+
var config_file = './plugin.json'.fullname(target_dir);
|
|
443
|
+
if (config_file.hasFile()) {
|
|
444
|
+
return { file, config_file, dir: target_dir, unzip_dir: dir };
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return { file, dir, unzip_dir: dir };
|
|
448
|
+
} catch (err) {
|
|
449
|
+
this.log('error', '插件解压失败!', err);
|
|
450
|
+
// 清理失败的解压目录
|
|
451
|
+
if (dir.hasDir()) {
|
|
452
|
+
dir.delDir();
|
|
453
|
+
}
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* 查找目标目录
|
|
460
|
+
* @param {string} dir 解压目录
|
|
461
|
+
* @param {string} name 插件名称
|
|
462
|
+
* @returns {string|null} 目标目录路径
|
|
463
|
+
* @private
|
|
464
|
+
*/
|
|
465
|
+
App.prototype._findTargetDir = function (dir, name) {
|
|
466
|
+
var dir_files = $.dir.get(dir);
|
|
467
|
+
if (!dir_files || dir_files.length === 0) {
|
|
468
|
+
return null;
|
|
469
|
+
}
|
|
470
|
+
for (var i = 0; i < dir_files.length; i++) {
|
|
471
|
+
var item = dir_files[i];
|
|
472
|
+
if (item.hasDir() && item.basename() === name) {
|
|
473
|
+
var plugin_json = './plugin.json'.fullname(item);
|
|
474
|
+
if (plugin_json.hasFile()) {
|
|
475
|
+
return item;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
return null;
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* 卸载插件
|
|
484
|
+
* @param {string} name 插件名称
|
|
485
|
+
* @returns {string} 返回卸载路径
|
|
486
|
+
*/
|
|
487
|
+
App.prototype.uninstallPlugin = async function (name) {
|
|
488
|
+
if (!name || typeof name !== 'string') {
|
|
489
|
+
this.log('error', '卸载失败:无效的插件名称');
|
|
490
|
+
return null;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
var plugin = this.manager.plugin.get(name);
|
|
494
|
+
if (!plugin) {
|
|
495
|
+
this.log('error', '卸载失败:插件不存在');
|
|
496
|
+
return null;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
var dir = plugin.dir;
|
|
500
|
+
|
|
501
|
+
// 执行卸载脚本
|
|
502
|
+
if (plugin.call) {
|
|
503
|
+
await plugin.call('uninstall');
|
|
504
|
+
}
|
|
505
|
+
try {
|
|
506
|
+
this.manager.plugin.del(name);
|
|
507
|
+
dir.delDir();
|
|
508
|
+
} catch (err) {
|
|
509
|
+
this.log('error', '卸载失败:', err);
|
|
510
|
+
return null;
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* 复制目录
|
|
516
|
+
* @param {string} dir_src 原路径
|
|
517
|
+
* @param {string} dir 新路径
|
|
518
|
+
* @returns {object} 返回复制结果
|
|
519
|
+
*/
|
|
520
|
+
App.prototype.copyDir = function (dir_src, dir) {
|
|
521
|
+
return new Promise((resolve, reject) => {
|
|
522
|
+
if (!dir_src || !dir) {
|
|
523
|
+
this.log('error', '复制目录失败:无效的目录路径');
|
|
524
|
+
reject(new Error('无效的目录路径'));
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if (!dir_src.hasDir()) {
|
|
529
|
+
this.log('error', '复制目录失败:源目录不存在');
|
|
530
|
+
reject(new Error('源目录不存在'));
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
try {
|
|
535
|
+
// 确保目标目录存在
|
|
536
|
+
dir.addDir();
|
|
537
|
+
|
|
538
|
+
$.dir.copy(dir_src, dir, (err) => {
|
|
539
|
+
if (err) {
|
|
540
|
+
this.log('error', '复制目录失败:', err);
|
|
541
|
+
reject(err);
|
|
542
|
+
} else {
|
|
543
|
+
resolve({
|
|
544
|
+
dir_src,
|
|
545
|
+
dir
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
});
|
|
549
|
+
} catch (err) {
|
|
550
|
+
this.log('error', '复制目录失败:', err);
|
|
551
|
+
reject(err);
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* 解压插件
|
|
558
|
+
* @param {object} file 插件压缩文件
|
|
559
|
+
* @returns {object} 返回解压结果
|
|
560
|
+
*/
|
|
561
|
+
App.prototype._unzipPlugin = async function (file) {
|
|
562
|
+
var ret = await this.unzip(file);
|
|
563
|
+
if (!ret) {
|
|
564
|
+
return null;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// 直接使用解压目录作为 dir_src
|
|
568
|
+
var dir_src = ret.dir;
|
|
569
|
+
|
|
570
|
+
var config_file = './plugin.json'.fullname(dir_src);
|
|
571
|
+
|
|
572
|
+
if (!config_file.hasFile()) {
|
|
573
|
+
throw new Error('安装失败:找不到配置文件');
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
var config = config_file.loadJson();
|
|
577
|
+
if (!config) {
|
|
578
|
+
throw new Error('安装失败:配置文件无效');
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
var app = config.app || this.config.name;
|
|
582
|
+
var name = config.name;
|
|
583
|
+
|
|
584
|
+
if (!name) {
|
|
585
|
+
throw new Error('安装失败:配置文件中缺少名称字段');
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
var dir = `/app/${app}/plugin/${name}`;
|
|
589
|
+
|
|
590
|
+
await this.copyDir(dir_src, dir);
|
|
591
|
+
ret.app = app;
|
|
592
|
+
ret.name = name;
|
|
593
|
+
ret.dir = dir;
|
|
594
|
+
return ret;
|
|
595
|
+
};
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* 安装插件
|
|
599
|
+
* @param {object} info 插件信息
|
|
600
|
+
* @returns {object} 返回安装结果
|
|
601
|
+
*/
|
|
602
|
+
App.prototype._installPlugin = async function (info) {
|
|
603
|
+
if (!info) {
|
|
604
|
+
return null;
|
|
605
|
+
}
|
|
606
|
+
if (!info.config_file) {
|
|
607
|
+
throw new Error('安装失败:找不到配置文件');
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
var manager = this.manager.plugin;
|
|
611
|
+
if (info.app !== this.config.name) {
|
|
612
|
+
var app = this.getServer().app[info.app];
|
|
613
|
+
if (!app) {
|
|
614
|
+
throw new Error(`安装失败:应用 ${info.app} 不存在`);
|
|
615
|
+
}
|
|
616
|
+
manager = app.manager.plugin;
|
|
617
|
+
}
|
|
618
|
+
// 只加载当前插件,而不是重载所有插件
|
|
619
|
+
await manager.load(info.config_file);
|
|
620
|
+
|
|
621
|
+
// 执行安装方法
|
|
622
|
+
var plugin = manager.getMod(info.name);
|
|
623
|
+
if (plugin && plugin.call) {
|
|
624
|
+
await plugin.call('install');
|
|
625
|
+
}
|
|
626
|
+
return true;
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* 安装插件
|
|
631
|
+
* @param {string} file 插件压缩文件路径
|
|
632
|
+
* @returns {object} 返回安装结果
|
|
633
|
+
*/
|
|
634
|
+
App.prototype.installPlugin = async function (file) {
|
|
635
|
+
if (!file) {
|
|
636
|
+
throw new TypeError('无效的文件路径');
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// 解压插件
|
|
640
|
+
var info = await this._unzipPlugin(file);
|
|
641
|
+
if (!info) {
|
|
642
|
+
return null;
|
|
643
|
+
}
|
|
644
|
+
// 安装插件
|
|
645
|
+
await this._installPlugin(info);
|
|
646
|
+
|
|
647
|
+
// 清理临时目录
|
|
648
|
+
setTimeout(() => {
|
|
649
|
+
if (info.unzip_dir && info.unzip_dir.hasDir()) {
|
|
650
|
+
info.unzip_dir.delDir();
|
|
651
|
+
}
|
|
652
|
+
}, 3000);
|
|
653
|
+
info.zip_file = file;
|
|
654
|
+
return info;
|
|
655
|
+
};
|
|
656
|
+
|
|
657
|
+
exports.App = App;
|
package/core/app/config.tpl.json
CHANGED
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
"dir": "${dir || ''}",
|
|
24
24
|
// 是否为游戏应用
|
|
25
25
|
"is_game": false,
|
|
26
|
-
// 游戏配置,非游戏模块不需要配置
|
|
27
|
-
"game": {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
},
|
|
26
|
+
// // 游戏配置,非游戏模块不需要配置
|
|
27
|
+
// "game": {
|
|
28
|
+
// // 游戏类型
|
|
29
|
+
// "type": "card_game"
|
|
30
|
+
// },
|
|
31
31
|
// 自定义数据库 - 1 开启 0 关闭
|
|
32
32
|
"diy_sql": 0,
|
|
33
33
|
// 自定义缓存 - 1 开启 0 关闭
|