From 46729b9cf078c46e7cf4885fd3c544577cbe9d95 Mon Sep 17 00:00:00 2001 From: MiaoWoo Date: Tue, 19 Apr 2022 09:29:57 +0800 Subject: [PATCH] feat: optimize config loader Signed-off-by: MiaoWoo --- .npmignore | 1 + packages/api/src/event.ts | 21 +++++- packages/core/src/exit.js | 13 ---- packages/core/src/index.ts | 40 ++++++----- packages/core/src/initialize.js | 68 ------------------- packages/i18n/languages/en.yml | 7 +- packages/i18n/languages/zh_cn.yml | 4 +- packages/plugin/package.json | 4 +- packages/plugin/src/command.ts | 35 +++++----- packages/plugin/src/config.ts | 67 +++++++----------- packages/plugin/src/config/file-config.ts | 65 ++++++++++++++++++ packages/plugin/src/config/interfaces.ts | 18 +++++ .../plugin/src/config/loader/json-loader.ts | 18 +++++ .../plugin/src/config/loader/yaml-loader.ts | 19 ++++++ packages/plugin/src/index.ts | 2 +- packages/plugin/src/manager.ts | 61 ++++++++++------- packages/websocket/src/engine.io/index.ts | 3 +- packages/websocket/src/index.ts | 17 ++++- packages/websocket/src/server/index.ts | 7 +- .../websocket/src/server/netty/httprequest.ts | 62 +++++++++-------- 20 files changed, 304 insertions(+), 228 deletions(-) delete mode 100644 packages/core/src/exit.js delete mode 100644 packages/core/src/initialize.js create mode 100644 packages/plugin/src/config/file-config.ts create mode 100644 packages/plugin/src/config/interfaces.ts create mode 100644 packages/plugin/src/config/loader/json-loader.ts create mode 100644 packages/plugin/src/config/loader/yaml-loader.ts diff --git a/.npmignore b/.npmignore index 2995d956..aa498cd6 100644 --- a/.npmignore +++ b/.npmignore @@ -1,3 +1,4 @@ +src test typings bundled diff --git a/packages/api/src/event.ts b/packages/api/src/event.ts index 9cf01d6d..c2a0d4ca 100644 --- a/packages/api/src/event.ts +++ b/packages/api/src/event.ts @@ -133,7 +133,12 @@ export namespace event { // @ts-ignore let executor = exec.name || exec.executor || '[anonymous]' // noinspection JSUnusedGlobalSymbols - var listener = this.register(eventCls, this.execute(name, exec, eventCls), priority, ignoreCancel) + var listener = this.register( + eventCls, + this.execute(name, exec, eventCls), + priority, + ignoreCancel + ) var listenerMap = this.listenerMap // add to cache Be used for close plugin to close event if (!listenerMap[name]) listenerMap[name] = [] @@ -141,11 +146,21 @@ export namespace event { if (off['offed']) return off['offed'] = true this.unregister(eventCls, listener) - console.debug(i18n.translate("ms.api.event.unregister", { name, event: this.class2Name(eventCls), exec: executor })) + console.debug(i18n.translate("ms.api.event.unregister", { + name, + event: this.class2Name(eventCls), + exec: executor + })) } listenerMap[name].push(off) // noinspection JSUnresolvedVariable - console.debug(i18n.translate("ms.api.event.register", { name, event: this.class2Name(eventCls), exec: executor })) + console.debug(i18n.translate("ms.api.event.register", { + name, + event: this.class2Name(eventCls), + exec: executor, + priority, + ignore: ignoreCancel + })) return off } diff --git a/packages/core/src/exit.js b/packages/core/src/exit.js deleted file mode 100644 index af68ca02..00000000 --- a/packages/core/src/exit.js +++ /dev/null @@ -1,13 +0,0 @@ -function exit() { - var http = require('@ccms/common/dist/http').default - function upgradeModules(core) { - if (base.version && global.ScriptEngineVersion != core['dist-tags']['latest']) { - var Paths = Java.type('java.nio.file.Paths') - base.save(Paths.get(root, "upgrade"), core['dist-tags']['latest']) - console.info('@ccms/core found new version ' + core['dist-tags']['latest'] + ' will upgrade after reboot!') - } - } - upgradeModules(http.get('https://registry.npmmirror.com/@ccms/core')) - console.debug('exit finish!') -} -exit() diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index af9d78da..889ae65c 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -50,14 +50,7 @@ class MiaoScriptCore { this.pluginManager.disable(this.pluginManager.getPlugins()) this.taskManager.disable() process.emit('core.after.disable') - try { - engineLoad({ - script: http.get("https://ms.yumc.pw/api/plugin/download/name/exit"), - name: 'core/exit.js' - }) - } catch (error: any) { - console.debug(error) - } + loadCoreScript('exit') process.emit('core.before.exit') process.exit(0) console.i18n("ms.core.engine.disable.finish", { @@ -98,18 +91,27 @@ function detectServer(): constants.ServerType { throw Error('Unknow Server Type...') } -function initialize() { - process.emit('core.before.initialize') - global.ScriptSlowExecuteTime = 30 - global.ScriptEngineVersion = require('../package.json').version +function loadCoreScript(name) { try { + let scriptname = name + (global.debug ? '-debug' : '') engineLoad({ - script: http.get("https://ms.yumc.pw/api/plugin/download/name/initialize"), - name: 'core/initialize.js' + script: http.get(`https://ms.yumc.pw/api/plugin/download/name/${scriptname}`), + name: `core/${scriptname}.js` }) } catch (error: any) { - console.debug(error) + if (global.debug) { + console.debug(error) + console.ex(error) + } } +} + +function initialize() { + process.emit('core.before.initialize') + global.ScriptSlowExecuteTime = 50 + global.ScriptEngineVersion = require('../package.json').version + global.setGlobal('loadCoreScript', loadCoreScript) + loadCoreScript('initialize') try { let corePackageStartTime = new Date().getTime() container.bind(ContainerInstance).toConstantValue(container) @@ -141,8 +143,12 @@ function initialize() { process.emit('core.after.initialize') return disable } catch (error: any) { - console.i18n("ms.core.initialize.error", { error }) - console.ex(error) + if (console.console) { + console.i18n("ms.core.initialize.error", { error }) + console.ex(error) + } else { + error.printStackTrace() + } return () => console.i18n('ms.core.engine.disable.abnormal') } } diff --git a/packages/core/src/initialize.js b/packages/core/src/initialize.js deleted file mode 100644 index ab825ee4..00000000 --- a/packages/core/src/initialize.js +++ /dev/null @@ -1,68 +0,0 @@ -global.initialize = function () { - var mspmc = 'https://ms.yumc.pw/api/plugin/download/name/' - var artifact = 'https://ci.yumc.pw/job/Minecraft/job/MiaoScript/lastSuccessfulBuild/artifact' - - var fs = require('@ccms/common/dist/fs') - var http = require('@ccms/common/dist/http').default - - function updateJar() { - var DocumentBuilderFactory = Java.type('javax.xml.parsers.DocumentBuilderFactory') - var URLDecoder = Java.type('java.net.URLDecoder') - var pom = DocumentBuilderFactory.newInstance().newDocumentBuilder() - .parse(artifact + "/pom.xml") - var latestVersion = pom.getElementsByTagName("version").item(0).getTextContent() - if (base.version != latestVersion) { - var pluginFolder = fs.file(fs.concat(root, '..')) - var updateFolder = fs.concat(pluginFolder, 'update') - fs.mkdirs(updateFolder) - var filePath = base.getInstance().class.classLoader.getURLs()[0] - var pluginFile = fs.file(URLDecoder.decode(filePath.getFile(), "UTF-8")) - var updateFile = fs.file(updateFolder, pluginFile.getName()) - http.download(artifact + "/target/MiaoScript.jar", updateFile.getAbsolutePath()) - console.info('MiaoScript found new version ' + latestVersion + ' will upgrade after reboot!') - } - } - function upgradeModules(core) { - if (base.version && global.ScriptEngineVersion != core['dist-tags']['latest']) { - var Paths = Java.type('java.nio.file.Paths') - base.save(Paths.get(root, "upgrade"), core['dist-tags']['latest']) - console.info('@ccms/core found new version ' + core['dist-tags']['latest'] + ' will upgrade after reboot!') - } - } - var pluginFolder = fs.concat(root, 'plugins') - var updateFolder = fs.concat(pluginFolder, 'update') - var pluginFile = fs.concat(pluginFolder, 'MiaoScriptPackageManager.js') - if (!fs.exists(pluginFile)) { - fs.mkdirs(pluginFile) - base.save(fs.concat(updateFolder, 'MiaoScriptPackageManager.auto.install'), '.') - } - fs.list(updateFolder).forEach(function (path) { - var file = path.toFile() - if (file.exists()) { - var filename = file.getName() - if (filename.endsWith(".auto.install")) { - var pluginName = filename.replace('.auto.install', '') - var pluginFile = fs.concat(pluginFolder, pluginName + '.js') - if (!fs.exists(pluginFile)) { - var pluginTemp = pluginFile + '.tmp' - http.download(mspmc + pluginName, pluginTemp) - fs.move(pluginTemp, pluginFile, true) - } - base.delete(file) - } - } - }) - try { - Java.type("org.bukkit.Bukkit") - updateJar() - } catch (error) { - console.debug(error) - if (global.debug) { - console.ex(error) - } - } - upgradeModules(http.get('https://registry.npmmirror.com/@ccms/core')) - console.debug('initialize finish!') -} -global.initialize() -delete global.initialize diff --git a/packages/i18n/languages/en.yml b/packages/i18n/languages/en.yml index 4b046e28..33dc8a2f 100644 --- a/packages/i18n/languages/en.yml +++ b/packages/i18n/languages/en.yml @@ -12,6 +12,7 @@ ms.core.plugin.initialize: "Initialization MiaoScript Plugin System. Please wait ms.core.plugin.completed: "MiaoScript Plugin System loading completed({time}s)!" ms.core.engine.completed: "MiaoScript ScriptEngine loading completed... Done({time}s)!" ms.core.engine.disable: "Disable MiaoScript Engine..." +ms.core.engine.disable.finish: "MiaoScript framework {loader} engine {version} 关闭完成... 耗时({time}s)!" ms.core.engine.disable.abnormal: "abnormal Initialization MiaoScript Engine. Skip disable step..." ms.api.event.resource.not.found: "Can't Mapping Event Because not found Resources {resource}!" @@ -21,16 +22,20 @@ ms.api.event.not.found: "§6Plugin §b{name} §6register {event} error. event no ms.api.event.execute.slow: "§cWARN! §6Plugin §b{name} §6execute §d{event} §6evnet §ccost §4{cost}ms !" ms.api.event.execute.error: "§6Plugin §b{name} §6execute §d{event} §6event error §4{ex}" ms.api.event.listen.plugin.name.empty: "Plugin name can't be empty!" -ms.api.event.register: "[{name}] register event {event}" +ms.api.event.register: "[{name}] register event {event} priority {priority} ignoreCancelled {ignore}" ms.api.event.unregister: "[{name}] unregister event {event}" + ms.api.command.register.input.error: "CommandExec Must be a function... Input: {exec}" ms.api.command.register: "[{plugin}] register command {name}({cmd})..." ms.api.command.unregister: "[{plugin}] unregister command {name}..." +ms.api.command.execute.slow: "§cWarn. §6Player §a{player} §6exec §b{plugin} §6Plugin §d{command} {args} §6Command §cCost §4{cost}ms !" ms.api.command.execute.error: "§6Player {player} §6exec §b{plugin} §6Plugin Command §d{command} {args} §6error §4{ex}" +ms.api.command.tab.completer.slow: "§cWarn. §6Player §a{player} §6exec §b{plugin} §6Plugin §d{command} {args} §6TabComplete §cCost §4{cost}ms !" ms.api.command.tab.completer.error: "§6Player {player} §6exec §b{plugin} §6Plugin TabComplete §d{command} {args} §6error §4{ex}" ms.plugin.initialize: "Initialization MiaoScript Plugin System: Plugin: {plugin} Loader: {loader}..." ms.plugin.event.map: "Total {count} {type} Event Mapping Complate..." +ms.plugin.event.map.error: "Mapping {type} Event Failed. Error: {error}" ms.plugin.manager.scan: "Scanner {scanner} Scanning Plugins in {folder} ..." ms.plugin.manager.scan.finish: "Scanner {scanner} Found {size} Plugins in {folder} Start Build..." ms.plugin.manager.initialize.error: "§6Plugin §b{name} §6initialize error §4{ex}" diff --git a/packages/i18n/languages/zh_cn.yml b/packages/i18n/languages/zh_cn.yml index 094fcd1b..cb6c5002 100644 --- a/packages/i18n/languages/zh_cn.yml +++ b/packages/i18n/languages/zh_cn.yml @@ -22,8 +22,9 @@ ms.api.event.not.found: "§6插件 §b{name} §6注册事件 §c{event} §6失 ms.api.event.execute.slow: "§c注意! §6插件 §b{name} §6处理 §d{event} §6事件 §c耗时 §4{cost}ms !" ms.api.event.execute.error: "§6插件 §b{name} §6处理 §d{event} §6事件时发生异常 §4{ex}" ms.api.event.listen.plugin.name.empty: "插件名称为空 请检查传入参数!" -ms.api.event.register: "[{name}] 注册事件 {event} => 执行器 {exec}" +ms.api.event.register: "[{name}] 注册事件 {event} => 执行器 {exec} 优先级 {priority} 忽略取消 {ignore}" ms.api.event.unregister: "[{name}] 注销事件 {event} => 执行器 {exec}" + ms.api.command.register.input.error: "CommandExec 必须为一个函数... 输入: {exec}" ms.api.command.register: "[{plugin}] 注册命令 {name}({cmd})..." ms.api.command.unregister: "[{plugin}] 注销命令 {name}..." @@ -34,6 +35,7 @@ ms.api.command.tab.completer.error: "§6玩家 §a{player} §6执行 §b{plugin} ms.plugin.initialize: "初始化 MiaoScript 插件系统: 实例: {plugin} 加载器: {loader}..." ms.plugin.event.map: "总计 {count} 个 {type} 事件 映射完成..." +ms.plugin.event.map.error: "映射 {type} 事件 异常 将无法使用事件简称. Error: {error}" ms.plugin.manager.scan: "扫描器 {scanner} 扫描 {folder} 中的插件..." ms.plugin.manager.scan.finish: "扫描器 {scanner} 在 {folder} 中 发现 {size} 个插件 开始构建..." ms.plugin.manager.initialize.error: "§6插件 §b{name} §6初始化错误 §4{ex}" diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 1349df36..b7929c17 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -19,6 +19,7 @@ "test": "echo \"Error: run tests from root\" && exit 1" }, "devDependencies": { + "@types/crypto-js": "^4.1.1", "@types/js-yaml": "^4.0.5", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", @@ -30,7 +31,6 @@ "@ccms/container": "^0.19.0", "@ccms/i18n": "^0.19.0", "crypto-js": "^4.1.1", - "js-yaml": "^4.1.0", - "yaml": "^1.10.2" + "js-yaml": "^4.1.0" } } diff --git a/packages/plugin/src/command.ts b/packages/plugin/src/command.ts index 95ec03d5..e2cd3b23 100644 --- a/packages/plugin/src/command.ts +++ b/packages/plugin/src/command.ts @@ -54,37 +54,34 @@ export class PluginCommandManager { cmdExecutor = (sender: any, command: string, args: string[]) => { let subcommand = args[0] let cmdKey = 'cmd' + subcommand - let subcommandexec = pluginInstance[cmdKey] - if (!subcommandexec) { - subcommandexec = pluginInstance['cmdmain'] - subcommand = 'main' - } else { - args.shift() - } - if (!subcommandexec) { - subcommand && pluginInstance.logger.sender(sender, '§4未知的子命令: §c' + subcommand) - pluginInstance.logger.sender( - sender, - pluginInstance['cmdhelp'] ? - `§6请执行 §b/${command} §ahelp §6查看帮助!` : - [ - `§6插件: §b${pluginInstance.description.name}`, - `§6版本: §a${pluginInstance.description.version}` - ] - ) + if (!cmdSubCache.includes(subcommand)) { + if (!pluginInstance[cmd.executor].apply(pluginInstance, [sender, command, args])) { + subcommand && pluginInstance.logger.sender(sender, '§4未知的子命令: §c' + subcommand) + pluginInstance.logger.sender( + sender, + pluginInstance['cmdhelp'] ? + `§6请执行 §b/${command} §ahelp §6查看帮助!` : + [ + `§6插件: §b${pluginInstance.description.name}`, + `§6版本: §a${pluginInstance.description.version}` + ] + ) + } return } + let subcommandexec = pluginInstance[cmdKey] let permission: string if (cmd.permission && sender.hasPermission) { if (typeof cmd.permission == "string") { permission = cmd.permission as string } else { - permission = `${pluginInstance.description.name.toLocaleLowerCase()}.${command}.${subcommand}` + permission = `${pluginInstance.description.name.toLocaleLowerCase()}.${command}.${subcommand || 'main'}` } if (!sender.hasPermission(permission)) { return pluginInstance.logger.sender(sender, `§c你需要 ${permission} 权限 才可执行此命令.`) } } + args.shift() return subcommandexec.apply(pluginInstance, [sender, ...args]) } let originCompleter = cmdCompleter diff --git a/packages/plugin/src/config.ts b/packages/plugin/src/config.ts index d2f23761..e888993c 100644 --- a/packages/plugin/src/config.ts +++ b/packages/plugin/src/config.ts @@ -1,59 +1,36 @@ -import * as yaml from 'js-yaml' -import * as fs from '@ccms/common/dist/fs' import { plugin } from '@ccms/api' -import { provideSingleton } from '@ccms/container' +import { Autowired, Container, ContainerInstance, postConstruct, provideSingleton } from '@ccms/container' + +import * as fs from '@ccms/common/dist/fs' import { interfaces } from './interfaces' import { getPluginConfigMetadata } from './utils' -export interface PluginConfigLoader { - load(content: string): any - dump(variable: any): string -} - -export class YamlPluginConfig implements PluginConfigLoader { - load(content: string) { - return yaml.load(content) - } - dump(variable: any): string { - return yaml.dump(variable, { skipInvalid: true, lineWidth: 120 }) - } -} - -export class JsonPluginConfig implements PluginConfigLoader { - load(content: string) { - return JSON.parse(content) - } - dump(variable: any): string { - return JSON.stringify(variable, undefined, 4) - } -} - -export interface PluginConfig { - /** - * Save Config to File - */ - readonly save?: () => void - /** - * Reload Config from File - */ - readonly reload?: () => void - [key: string]: any -} +import { PluginConfigLoader } from './config/interfaces' +import './config/loader/json-loader' +import './config/loader/yaml-loader' @provideSingleton(PluginConfigManager) export class PluginConfigManager { + @Autowired(ContainerInstance) + private container: Container + private configLoaderMap = new Map() constructor() { - this.configLoaderMap.set("json", new JsonPluginConfig()) - let yaml = new YamlPluginConfig() - this.configLoaderMap.set("yml", yaml) - this.configLoaderMap.set("yaml", yaml) process.on('plugin.before.load', this.loadConfig.bind(this)) process.on('plugin.after.disable', this.saveConfig.bind(this)) } + @postConstruct() + initialize() { + let configLoader = this.container.getAll(PluginConfigLoader) + configLoader.forEach((scanner) => { + console.debug(`loading config loader ${scanner.type}...`) + this.configLoaderMap.set(scanner.type, scanner) + }) + } + getConfigLoader(format: string) { if (!this.configLoaderMap.has(format)) { throw new Error(`Unsupport config format ${format} !`) } return this.configLoaderMap.get(format) @@ -73,13 +50,17 @@ export class PluginConfigManager { } } - private defienConfigProp(plugin: plugin.Plugin, metadata: interfaces.ConfigMetadata, value: any) { + createConfig(plugin: plugin.Plugin, metadata: interfaces.ConfigMetadata, value: any) { Object.defineProperties(value, { 'save': { value: () => this.saveConfig0(plugin, metadata) }, 'reload': { value: () => this.loadConfig0(plugin, metadata) } }) + return value + } + + private defienConfigProp(plugin: plugin.Plugin, metadata: interfaces.ConfigMetadata, value: any) { Object.defineProperty(plugin, metadata.variable, { - value, + value: this.createConfig(plugin, metadata, value), configurable: true }) } diff --git a/packages/plugin/src/config/file-config.ts b/packages/plugin/src/config/file-config.ts new file mode 100644 index 00000000..0ada5844 --- /dev/null +++ b/packages/plugin/src/config/file-config.ts @@ -0,0 +1,65 @@ +import * as fs from '@ccms/common/dist/fs' + +import { PluginConfig, PluginConfigLoader } from './interfaces' + +export class PluginFileConfig implements PluginConfig { + private loader: PluginConfigLoader + private file: string + constructor(loader: PluginConfigLoader, file: string, def = {}) { + this.loader = loader + this.file = file + if (fs.exists(file)) { + this.reload() + } else { + Object.assign(this, def) + } + this.initialize() + } + + initialize() { + } + + save() { + base.save(this.file, this.loader.dump(this)) + } + + reload() { + Object.assign(this, this.loader.load(base.read(this.file))) + } +} + +export class PluginConfigFolder { + private loader: PluginConfigLoader + private folder: string + + private configCache = new Map() + + constructor(loader: PluginConfigLoader, folder: string) { + this.loader = loader + this.folder = folder + } + + createConfig(path: string, def = {}) { + return new PluginFileConfig(this.loader, path, def) + } + + getConfig(name: string, def = {}) { + let path = fs.concat(this.folder, name) + if (!this.configCache.has(path)) { + this.configCache.set(path, this.createConfig(path, def)) + } + return this.configCache.get(path) + } + + clear() { + this.configCache.clear() + } + + save() { + this.configCache.forEach((config) => config.save()) + } + + reload() { + this.configCache.forEach((config) => config.reload()) + } +} diff --git a/packages/plugin/src/config/interfaces.ts b/packages/plugin/src/config/interfaces.ts new file mode 100644 index 00000000..960b1e06 --- /dev/null +++ b/packages/plugin/src/config/interfaces.ts @@ -0,0 +1,18 @@ +export const PluginConfigLoader = Symbol.for('PluginConfigLoader') +export interface PluginConfigLoader { + type: string + load(content: string): any + dump(variable: any): string +} + +export interface PluginConfig { + /** + * Save Config to File + */ + readonly save?: () => void + /** + * Reload Config from File + */ + readonly reload?: () => void + [key: string]: any +} diff --git a/packages/plugin/src/config/loader/json-loader.ts b/packages/plugin/src/config/loader/json-loader.ts new file mode 100644 index 00000000..305d9434 --- /dev/null +++ b/packages/plugin/src/config/loader/json-loader.ts @@ -0,0 +1,18 @@ +import { provideSingletonNamed } from '@ccms/container' + +import { PluginConfigLoader } from '../interfaces' + +const LOADER_TYPE_NAME = 'json' + +@provideSingletonNamed(PluginConfigLoader, LOADER_TYPE_NAME) +export class JsonPluginConfig implements PluginConfigLoader { + type: string = LOADER_TYPE_NAME + + load(content: string) { + return JSON.parse(content) + } + + dump(variable: any): string { + return JSON.stringify(variable, undefined, 4) + } +} diff --git a/packages/plugin/src/config/loader/yaml-loader.ts b/packages/plugin/src/config/loader/yaml-loader.ts new file mode 100644 index 00000000..2e52bb22 --- /dev/null +++ b/packages/plugin/src/config/loader/yaml-loader.ts @@ -0,0 +1,19 @@ +import * as yaml from 'js-yaml' +import { provideSingletonNamed } from '@ccms/container' + +import { PluginConfigLoader } from '../interfaces' + +const LOADER_TYPE_NAME = 'yml' + +@provideSingletonNamed(PluginConfigLoader, LOADER_TYPE_NAME) +export class YamlPluginConfig implements PluginConfigLoader { + type: string = LOADER_TYPE_NAME + + load(content: string) { + return yaml.load(content) + } + + dump(variable: any): string { + return yaml.dump(variable, { skipInvalid: true, lineWidth: 120 }) + } +} diff --git a/packages/plugin/src/index.ts b/packages/plugin/src/index.ts index cfb272b5..4642b024 100644 --- a/packages/plugin/src/index.ts +++ b/packages/plugin/src/index.ts @@ -1,5 +1,5 @@ +import './scanner/ms-scanner' import './scanner/js-scanner' -import './scanner/mjs-scanner' import './loader/ioc-loader' import './loader/basic-loader' diff --git a/packages/plugin/src/manager.ts b/packages/plugin/src/manager.ts index 79e24a47..03bdff3f 100644 --- a/packages/plugin/src/manager.ts +++ b/packages/plugin/src/manager.ts @@ -64,9 +64,13 @@ export class PluginManagerImpl implements plugin.PluginManager { initialize() { if (this.pluginInstance === undefined) { throw new Error("Can't found Plugin Instance!") } if (this.initialized !== true) { - process.emit('plugin.manager.before.initialize') + process.emit('plugin.manager.before.initialize', this) console.i18n('ms.plugin.initialize', { plugin: this.pluginInstance, loader: Thread.currentThread().contextClassLoader }) - console.i18n('ms.plugin.event.map', { count: this.eventManager.mapEventName(), type: this.serverType }) + try { + console.i18n('ms.plugin.event.map', { count: this.eventManager.mapEventName(), type: this.serverType }) + } catch (error) { + console.i18n('ms.plugin.event.map.error', { error }) + } let pluginScanner = this.container.getAll(plugin.PluginScanner) pluginScanner.forEach((scanner) => { console.debug(`loading plugin sacnner ${scanner.type}...`) @@ -78,14 +82,14 @@ export class PluginManagerImpl implements plugin.PluginManager { this.loaderMap.set(loader.type, loader) }) this.initialized = true - process.emit('plugin.manager.after.initialize') + process.emit('plugin.manager.after.initialize', this) } } scan(folder: string): void { if (!folder) { throw new Error('plugin scan folder can\'t be empty!') } this.initialize() - process.emit('plugin.manager.before.scan', folder) + process.emit('plugin.manager.before.scan', folder, this) for (const [, scanner] of this.sacnnerMap) { try { console.i18n('ms.plugin.manager.scan', { scanner: scanner.type, folder }) @@ -104,13 +108,13 @@ export class PluginManagerImpl implements plugin.PluginManager { console.ex(error) } } - process.emit('plugin.manager.after.scan', folder) + process.emit('plugin.manager.after.scan', folder, this) } build(): void { - process.emit('plugin.manager.before.build') + process.emit('plugin.manager.before.build', this) this.buildPlugins() - process.emit('plugin.manager.after.build') + process.emit('plugin.manager.after.build', this) } private logStage(plugin: plugin.Plugin, stage: string) { @@ -144,7 +148,6 @@ export class PluginManagerImpl implements plugin.PluginManager { console.i18n("ms.plugin.manager.initialize.error", { name: loadMetadata.file, ex: error }) console.ex(error) } - console.console(`§6scanner: §b${loadMetadata.scanner.type} §ccan\'t load §6file §b${loadMetadata.file}. §eskip!`) } private loaderRequirePlugin(loadMetadata: plugin.PluginLoadMetadata, loader: plugin.PluginLoader) { @@ -195,7 +198,9 @@ export class PluginManagerImpl implements plugin.PluginManager { let scanner = this.sacnnerMap.get(ext) if (!scanner) { throw new Error(`plugin scanner ${ext} can't found in sacnnerMap.`) } let metadata = this.loadAndRequirePlugin(scanner.read(file)) - let plugin = this.buildPlugin(metadata) + this.buildPlugin(metadata) + let plugin = metadata.target + if (!plugin) { throw new Error(`plugin scanner ${ext} can't found in sacnnerMap.`) } this.load(plugin) this.enable(plugin) return plugin @@ -255,10 +260,10 @@ export class PluginManagerImpl implements plugin.PluginManager { if (metadata?.depends?.length) { this.lazyMetadataMap.set(key, metadata) } else { - this.buildPlugin(metadata) + this.tryBuildPlugin(metadata) } }) - this.lazyMetadataMap.forEach((metadata, key) => this.buildPlugin(metadata)) + this.lazyMetadataMap.forEach((metadata, key) => this.tryBuildPlugin(metadata)) } private checkDepends(depends: string | string[]) { @@ -273,24 +278,30 @@ export class PluginManagerImpl implements plugin.PluginManager { for (const depend of depends) { if (!this.nativePluginManager.has(depend)) loseDepends.push(depend) } return loseDepends } - private buildPlugin(metadata: plugin.PluginMetadata) { - process.emit(`plugin.before.build`, metadata) + + private tryBuildPlugin(metadata: plugin.PluginMetadata) { try { - if (this.instanceMap.has(metadata.name)) { throw new Error(`Plugin ${metadata.name} is already load from ${metadata.source}...`) } - if (!this.loaderMap.has(metadata.type)) { throw new Error(`§4无法加载插件 §b${metadata.name} §4请检查 §c${metadata.type} §4加载器是否正常启用!`) } - if (!this.serverChecker.check(metadata.servers)) { throw new Error(`§6插件 §b${metadata.name} §c服务器类型不兼容(${metadata.servers.join(',')}) §6忽略加载...`) } - let loseDepends = this.checkDepends(metadata.depends) || [] - if (loseDepends.length) { throw new Error(`§4无法加载插件 §b${metadata.name} §4请检查脚本依赖 §3[${loseDepends.join(',')}] §4是否安装完整!`) } - let loseNativeDepends = this.checkNativeDepends(metadata.nativeDepends) || [] - if (loseNativeDepends.length) { throw new Error(`§4无法加载插件 §b${metadata.name} §4请检查插件依赖 §3[${loseNativeDepends.join(',')}] §4是否安装完整!`) } - let pluginInstance = this.loaderMap.get(metadata.type).build(metadata) - if (!pluginInstance) { throw new Error(`§4加载器 §c${metadata.type} §4加载插件 §c${metadata.name} §4失败!`) } - this.instanceMap.set(metadata.name, pluginInstance) - process.emit(`plugin.after.build`, metadata, pluginInstance) - return pluginInstance + return this.buildPlugin(metadata) } catch (error: any) { console.console(`§4无法加载插件 §b${metadata.name} §4构建插件失败!`) console.ex(error) } } + + private buildPlugin(metadata: plugin.PluginMetadata) { + process.emit(`plugin.before.build`, metadata) + if (this.instanceMap.has(metadata.name)) { throw new Error(`Plugin ${metadata.name} is already load from ${metadata.source}...`) } + if (!this.loaderMap.has(metadata.type)) { throw new Error(`§4无法加载插件 §b${metadata.name} §4请检查 §c${metadata.type} §4加载器是否正常启用!`) } + if (!this.serverChecker.check(metadata.servers)) { throw new Error(`§6插件 §b${metadata.name} §c服务器类型不兼容(${metadata.servers.join(',')}) §6忽略加载...`) } + let loseDepends = this.checkDepends(metadata.depends) || [] + if (loseDepends.length) { throw new Error(`§4无法加载插件 §b${metadata.name} §4请检查脚本依赖 §3[${loseDepends.join(',')}] §4是否安装完整!`) } + let loseNativeDepends = this.checkNativeDepends(metadata.nativeDepends) || [] + if (loseNativeDepends.length) { throw new Error(`§4无法加载插件 §b${metadata.name} §4请检查插件依赖 §3[${loseNativeDepends.join(',')}] §4是否安装完整!`) } + let pluginInstance = this.loaderMap.get(metadata.type).build(metadata) + if (!pluginInstance) { throw new Error(`§4加载器 §c${metadata.type} §4加载插件 §c${metadata.name} §4失败!`) } + metadata.target = pluginInstance + this.instanceMap.set(metadata.name, pluginInstance) + process.emit(`plugin.after.build`, metadata, pluginInstance) + return pluginInstance + } } diff --git a/packages/websocket/src/engine.io/index.ts b/packages/websocket/src/engine.io/index.ts index 7cffba16..4be68afd 100644 --- a/packages/websocket/src/engine.io/index.ts +++ b/packages/websocket/src/engine.io/index.ts @@ -2,7 +2,6 @@ * Module dependencies. */ -import * as server from "../server" // const http = require("http") // const Server = require("./server") import { Server } from './server' @@ -18,7 +17,7 @@ import { Server } from './server' function attach(srv, options) { const engine = new Server(options) - engine.attach(server.attach(srv, options), options) + engine.attach(srv, options) return engine } diff --git a/packages/websocket/src/index.ts b/packages/websocket/src/index.ts index 9b39efee..d401b77c 100644 --- a/packages/websocket/src/index.ts +++ b/packages/websocket/src/index.ts @@ -1,6 +1,7 @@ /// /// +import * as server from './server' import { Server, ServerOptions } from './socket.io' interface SocketIOStatic { @@ -38,9 +39,21 @@ interface SocketIOStatic { type SocketStatic = SocketIOStatic & { Instance?: symbol } +let singletonServer: Server + // @ts-ignore -let io: SocketStatic = function (pipeline: any, options: Partial) { - return new Server(pipeline, options) +let io: SocketStatic = function io(pipeline: any, options: Partial, singleton = true) { + if (singleton) { + if (!singletonServer) { + singletonServer = new Server(server.attach(pipeline, options), options) + process.emit('websocket.create', singletonServer) + process.on('exit', () => { + singletonServer.close() + }) + } + return singletonServer + } + return new Server(server.attach(pipeline, options), options) } io.Instance = Symbol("@ccms/websocket") export default io diff --git a/packages/websocket/src/server/index.ts b/packages/websocket/src/server/index.ts index b56fb19b..ad56a9da 100644 --- a/packages/websocket/src/server/index.ts +++ b/packages/websocket/src/server/index.ts @@ -19,6 +19,7 @@ export enum ServerEvent { export interface JavaServerOptions extends ServerOptions { event?: EventEmitter root?: string + httpRequestHandler?: (ctx, request) => void } export abstract class WebSocketServer extends EventEmitter { @@ -30,7 +31,6 @@ export abstract class WebSocketServer extends EventEmitter { this.instance = instance this.options = options this.clients = new Map() - console.debug('create websocket server from ' + this.constructor.name) this.initialize() } protected onconnect(handler: any) { @@ -56,7 +56,7 @@ export abstract class WebSocketServer extends EventEmitter { if (this.clients.has(id)) { this.clients.has(id) && callback(this.clients.get(id)) } else { - console.debug('ignore execute', handler, 'callback', callback) + console.trace('ignore execute', handler, 'callback', callback) } } public close() { @@ -75,7 +75,7 @@ export const attach = (instance, options) => { options = Object.assign({ event: new EventEmitter(), path: '/ws', - root: root + '/wwwroot', + root: root + Java.type("java.io.File").separatorChar + 'wwwroot', }, options) let WebSocketServerImpl = undefined if (instance.class.name.startsWith('io.netty.channel')) { @@ -83,5 +83,6 @@ export const attach = (instance, options) => { } else { WebSocketServerImpl = require("./tomcat").TomcatWebSocketServer } + console.debug('create websocket server from ' + WebSocketServerImpl.name) return new WebSocketServerImpl(instance, options) } diff --git a/packages/websocket/src/server/netty/httprequest.ts b/packages/websocket/src/server/netty/httprequest.ts index 211e34c8..d6c7cb18 100644 --- a/packages/websocket/src/server/netty/httprequest.ts +++ b/packages/websocket/src/server/netty/httprequest.ts @@ -23,40 +23,46 @@ export class HttpRequestHandler extends HttpRequestHandlerAdapter { super() this.root = options.root this.ws = options.path + if (options.httpRequestHandler) { + this.httpRequestHandler = options.httpRequestHandler + } } channelRead0(ctx: any, request: any) { if (request.getUri().startsWith(this.ws)) { ctx.channel().attr(AttributeKeys.Request).set(request) ctx.fireChannelRead(request.retain()) } else { - ctx.executor().execute(new Runnable({ - run: () => { - if (HttpHeaders.is100ContinueExpected(request)) { - ctx.writeAndFlush(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE)) - } - let filename = request.getUri().split('?')[0].substr(1) - let file = new File(this.root, filename || 'index.html') - if (!file.exists() || !file.isFile()) { - ctx.write(new DefaultHttpResponse(request.getProtocolVersion(), HttpResponseStatus.NOT_FOUND)) - ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT).addListener(ChannelFutureListener.CLOSE) - return - } - let response = new DefaultHttpResponse(request.getProtocolVersion(), HttpResponseStatus.OK) - response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/html charset=UTF-8") - let raf = new RandomAccessFile(file, 'r') - let keepAlive = HttpHeaders.isKeepAlive(request) - if (keepAlive) { - response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, file.length()) - response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE) - } - ctx.write(response) - ctx.write(new DefaultFileRegion(raf.getChannel(), 0, raf.length())) - let future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT) - if (!keepAlive) { - future.addListener(ChannelFutureListener.CLOSE) - } - } - })) + this.httpRequestHandler(ctx, request) } } + httpRequestHandler(ctx: any, request: any) { + ctx.executor().execute(new Runnable({ + run: () => { + if (HttpHeaders.is100ContinueExpected(request)) { + ctx.writeAndFlush(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE)) + } + let filename = request.getUri().split('?')[0].substr(1) + let file = new File(this.root, filename || 'index.html') + if (!file.exists() || !file.isFile()) { + ctx.write(new DefaultHttpResponse(request.getProtocolVersion(), HttpResponseStatus.NOT_FOUND)) + ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT).addListener(ChannelFutureListener.CLOSE) + return + } + let response = new DefaultHttpResponse(request.getProtocolVersion(), HttpResponseStatus.OK) + response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/html charset=UTF-8") + let raf = new RandomAccessFile(file, 'r') + let keepAlive = HttpHeaders.isKeepAlive(request) + if (keepAlive) { + response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, file.length()) + response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE) + } + ctx.write(response) + ctx.write(new DefaultFileRegion(raf.getChannel(), 0, raf.length())) + let future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT) + if (!keepAlive) { + future.addListener(ChannelFutureListener.CLOSE) + } + } + })) + } }