diff --git a/packages/api/src/command.ts b/packages/api/src/command.ts index 9c58a5c7..0f2eb3f3 100644 --- a/packages/api/src/command.ts +++ b/packages/api/src/command.ts @@ -44,12 +44,37 @@ export namespace command { protected setExecutor(plugin: plugin.Plugin, command: any, executor: Function) { return (sender: any, _: any, command: string, args: string[]) => { try { - return executor(sender, command, Java.from(args)) + let time = Date.now() + let result = executor(sender, command, Java.from(args)) + let cost = Date.now() - time + if (cost > global.ScriptSlowExecuteTime) { + console.i18n("ms.api.command.execute.slow", { + player: sender.name, + plugin: plugin.description.name, + command, + args: Java.from(args).join(' '), + cost + }) + } + return result } catch (ex: any) { - console.i18n("ms.api.command.execute.error", { player: sender.name, plugin: plugin.description.name, command, args: Java.from(args).join(' '), ex }) + console.i18n("ms.api.command.execute.error", { + player: sender.name, + plugin: plugin.description.name, + command, + args: Java.from(args).join(' '), + ex + }) console.ex(ex) if (sender.name != 'CONSOLE') { - console.sender(sender, [i18n.translate("ms.api.command.execute.error", { player: sender.name, plugin: plugin.description.name, command, args: Java.from(args).join(' '), ex }), ...console.stack(ex)]) + console.sender(sender, [i18n.translate("ms.api.command.execute.error", { + player: sender.name, + plugin: plugin.description.name, + command, + args: Java.from(args).join(' '), + ex + }), + ...console.stack(ex)]) } return true } @@ -58,13 +83,42 @@ export namespace command { protected setTabCompleter(plugin: plugin.Plugin, command: any, tabCompleter: Function) { return (sender: any, _: any, command: string, args: string[]) => { try { + let time = Date.now() var token = args[args.length - 1] var complete = tabCompleter(sender, command, Java.from(args)) || [] - return this.copyPartialMatches(complete, token) + let result = this.copyPartialMatches(complete, token) + let cost = Date.now() - time + if (cost > global.ScriptSlowExecuteTime) { + console.i18n("ms.api.command.tab.completer.slow", { + player: sender.name, + plugin: plugin.description.name, + command, + args: Java.from(args).join(' '), + cost + }) + } + return result } catch (ex: any) { - console.i18n("ms.api.command.tab.completer.error", { player: sender.name, plugin: plugin.description.name, command, args: Java.from(args).join(' '), ex }) + console.i18n("ms.api.command.tab.completer.error", { + player: sender.name, + plugin: plugin.description.name, + command, + args: Java.from(args).join(' '), + ex + }) console.ex(ex) - console.sender(sender, [i18n.translate("ms.api.command.tab.completer.error", { player: sender.name, plugin: plugin.description.name, command, args: Java.from(args).join(' '), ex }), ...console.stack(ex)]) + if (sender.name != 'CONSOLE') { + console.sender(sender, [ + i18n.translate("ms.api.command.tab.completer.error", { + player: sender.name, + plugin: plugin.description.name, + command, + args: Java.from(args).join(' '), + ex + }), + ...console.stack(ex) + ]) + } return [] } } diff --git a/packages/api/src/event.ts b/packages/api/src/event.ts index ad1c7e44..9cf01d6d 100644 --- a/packages/api/src/event.ts +++ b/packages/api/src/event.ts @@ -100,7 +100,7 @@ export namespace event { let time = Date.now() exec(event) let cost = Date.now() - time - if (cost > 20) { + if (cost > global.ScriptSlowExecuteTime) { console.i18n("ms.api.event.execute.slow", { name, event: this.class2Name(eventCls), cost }) } } diff --git a/packages/api/src/server.ts b/packages/api/src/server.ts index a3b3a128..60025b3f 100644 --- a/packages/api/src/server.ts +++ b/packages/api/src/server.ts @@ -40,13 +40,13 @@ export namespace server { get(name: string): NativePlugin { throw new Error("Method not implemented.") } - load(name: string): boolean { + enable(name: string): NativePlugin { throw new Error("Method not implemented.") } - unload(name: string): boolean { + disable(name: string): NativePlugin { throw new Error("Method not implemented.") } - reload(name: string): boolean { + reload(name: string): NativePlugin { throw new Error("Method not implemented.") } delete(name: string): boolean { diff --git a/packages/bukkit/src/channel.ts b/packages/bukkit/src/channel.ts index f5752656..dcb055d1 100644 --- a/packages/bukkit/src/channel.ts +++ b/packages/bukkit/src/channel.ts @@ -1,7 +1,7 @@ import { channel, plugin } from '@ccms/api' import { inject, provideSingleton } from '@ccms/container' -const Bukkit = org.bukkit.Bukkit +const Bukkit: typeof org.bukkit.Bukkit = Java.type('org.bukkit.Bukkit') const PluginMessageListener = Java.type("org.bukkit.plugin.messaging.PluginMessageListener") const Messenger = Bukkit.getMessenger() diff --git a/packages/bukkit/src/chat.ts b/packages/bukkit/src/chat.ts index 3ca19d11..0a278e7c 100644 --- a/packages/bukkit/src/chat.ts +++ b/packages/bukkit/src/chat.ts @@ -1,9 +1,12 @@ import { chat } from '@ccms/api' import { provideSingleton } from '@ccms/container' -import bukkitChat from './enhance/chat' +import bukkitChat from './internal/chat' @provideSingleton(chat.Chat) export class BukkitChat extends chat.Chat { + get handle(): any { + return bukkitChat + } sendJson(sender: any, json: string | object, type = 0) { bukkitChat.send(sender, typeof json === "string" ? json : JSON.stringify(json), type) } diff --git a/packages/bukkit/src/command.ts b/packages/bukkit/src/command.ts index 7a6d018f..c928eb28 100644 --- a/packages/bukkit/src/command.ts +++ b/packages/bukkit/src/command.ts @@ -1,35 +1,38 @@ import '@ccms/nashorn' import { command, plugin } from '@ccms/api' -import * as reflect from '@ccms/common/dist/reflect' import { provideSingleton, postConstruct, inject } from '@ccms/container' +import * as reflect from '@ccms/common/dist/reflect' -let Bukkit = org.bukkit.Bukkit -let TabCompleter = Java.type('org.bukkit.command.TabCompleter') -let PluginCommand = Java.type('org.bukkit.command.PluginCommand') -let CommandExecutor = Java.type('org.bukkit.command.CommandExecutor') +const Bukkit: typeof org.bukkit.Bukkit = Java.type('org.bukkit.Bukkit') +const TabCompleter = Java.type('org.bukkit.command.TabCompleter') +const PluginCommand = Java.type('org.bukkit.command.PluginCommand') +const CommandExecutor = Java.type('org.bukkit.command.CommandExecutor') @provideSingleton(command.Command) export class BukkitCommand extends command.Command { @inject(plugin.PluginInstance) private pluginInstance: any private commandMap: any + private knownCommands: any @postConstruct() init() { this.commandMap = reflect.on(Bukkit.getPluginManager()).get('commandMap').get() + this.knownCommands = reflect.on(this.commandMap).get('knownCommands').get() } create(plugin: any, command: string) { var cmd = this.commandMap.getCommand(command) - if (cmd && cmd instanceof PluginCommand) { return cmd }; + if (cmd instanceof PluginCommand) { return cmd }; cmd = reflect.on(PluginCommand).create(command, this.pluginInstance).get() this.commandMap.register(plugin.description.name, cmd) return cmd } remove(plugin: any, command: string) { var cmd = this.commandMap.getCommand(command) - if (cmd && cmd instanceof PluginCommand) { + if (cmd instanceof PluginCommand) { cmd.unregister(this.commandMap) + this.knownCommands.remove(command) } } tabComplete(sender: any, input: string, index?: number): string[] { diff --git a/packages/bukkit/src/console.ts b/packages/bukkit/src/console.ts index 1196a19e..11bc1ca2 100644 --- a/packages/bukkit/src/console.ts +++ b/packages/bukkit/src/console.ts @@ -1,20 +1,20 @@ import { MiaoScriptConsole } from '@ccms/api' -let Bukkit = org.bukkit.Bukkit; +const Bukkit: typeof org.bukkit.Bukkit = Java.type('org.bukkit.Bukkit') export class BukkitConsole extends MiaoScriptConsole { sender(sender, ...args) { if (!(sender instanceof Java.type('org.bukkit.command.CommandSender'))) { this.error(`First parameter ${sender} not instanceof org.bukkit.command.CommandSender can't send message!`) - return; + return } if (Object.prototype.toString.call(args[0]) === "[object Array]") { args[0].forEach(line => sender.sendMessage(this.prefix + line)) } else { - sender.sendMessage(this.prefix + args.join(' ')); + sender.sendMessage(this.prefix + args.join(' ')) } } console(...args: string[]): void { - this.sender(Bukkit.getConsoleSender(), args.join(' ')); + this.sender(Bukkit.getConsoleSender(), args.join(' ')) } } diff --git a/packages/bukkit/src/event.ts b/packages/bukkit/src/event.ts index fc7f420c..40e1daae 100644 --- a/packages/bukkit/src/event.ts +++ b/packages/bukkit/src/event.ts @@ -1,13 +1,13 @@ import { event, plugin } from '@ccms/api' -import { inject, provideSingleton } from '@ccms/container'; +import { inject, provideSingleton } from '@ccms/container' import * as reflect from '@ccms/common/dist/reflect' -const Bukkit = Java.type("org.bukkit.Bukkit"); -const Event = Java.type("org.bukkit.event.Event"); -const Modifier = Java.type("java.lang.reflect.Modifier"); -const Listener = Java.type("org.bukkit.event.Listener"); -const EventPriority = Java.type("org.bukkit.event.EventPriority"); -const EventExecutor = Java.type("org.bukkit.plugin.EventExecutor"); +const Bukkit = Java.type("org.bukkit.Bukkit") +const Event = Java.type("org.bukkit.event.Event") +const Modifier = Java.type("java.lang.reflect.Modifier") +const Listener = Java.type("org.bukkit.event.Listener") +const EventPriority = Java.type("org.bukkit.event.EventPriority") +const EventExecutor = Java.type("org.bukkit.plugin.EventExecutor") @provideSingleton(event.Event) export class BukkitEvent extends event.Event { @@ -15,7 +15,7 @@ export class BukkitEvent extends event.Event { private pluginInstance: any constructor() { - super('org/bukkit/event'); + super('org/bukkit/event') } getJarFile(resource: string) { @@ -27,10 +27,10 @@ export class BukkitEvent extends event.Event { // 访问符为Public Modifier.isPublic(clazz.getModifiers()) && // 不是抽象类 - !Modifier.isAbstract(clazz.getModifiers()); + !Modifier.isAbstract(clazz.getModifiers()) } register(eventCls: any, exec: Function, priority: event.EventPriority, ignoreCancel: boolean) { - let listener = new Listener({}); + let listener = new Listener({}) Bukkit.pluginManager.registerEvent( eventCls, listener, @@ -39,10 +39,10 @@ export class BukkitEvent extends event.Event { execute: exec }), this.pluginInstance, - ignoreCancel); - return listener; + ignoreCancel) + return listener } unregister(event: any, listener: any): void { - reflect.on(event).call('getHandlerList').get().unregister(listener); + reflect.on(event).call('getHandlerList').get().unregister(listener) } } diff --git a/packages/bukkit/src/enhance/chat.ts b/packages/bukkit/src/internal/chat.ts similarity index 90% rename from packages/bukkit/src/enhance/chat.ts rename to packages/bukkit/src/internal/chat.ts index 4f971ffe..23cde23f 100644 --- a/packages/bukkit/src/enhance/chat.ts +++ b/packages/bukkit/src/internal/chat.ts @@ -29,17 +29,16 @@ abstract class BukkitChatInvoke { this.ChatSerializer = Java.type(nmsChatSerializerClass.getName()) let packetTypeClass = this.getPacketPlayOutChatClass() this.PacketPlayOutChat = Java.type(packetTypeClass.getName()) - let packetTypeConstructor: { parameterTypes: any[] } let constructors = packetTypeClass.constructors - Java.from(constructors).forEach(function (c) { - if (c.parameterTypes.length === 2 || c.parameterTypes.length === 3) { - packetTypeConstructor = c + for (const constructor of Java.from(constructors)) { + let parameterTypes = constructor.parameterTypes + if (parameterTypes.length === 2 || parameterTypes.length === 3) { + let nmsChatMessageTypeClass = parameterTypes[1] + if (nmsChatMessageTypeClass.isEnum()) { + this.chatMessageTypes = nmsChatMessageTypeClass.getEnumConstants() + break + } } - }) - let parameterTypes = packetTypeConstructor.parameterTypes - let nmsChatMessageTypeClass = parameterTypes[1] - if (nmsChatMessageTypeClass.isEnum()) { - this.chatMessageTypes = nmsChatMessageTypeClass.getEnumConstants() } let playerConnectionField = this.getPlayerConnectionField() this.playerConnectionFieldName = playerConnectionField.getName() @@ -151,8 +150,9 @@ class BukkitChatInvoke_1_17_1 extends BukkitChatInvoke_1_16_5 { } try { - //@ts-ignore - let nmsVersion = org.bukkit.Bukkit.server.class.name.split('.')[3] + let Bukkit: typeof org.bukkit.Bukkit = Java.type('org.bukkit.Bukkit') + // @ts-ignore + let nmsVersion = Bukkit.getServer().class.name.split('.')[3] let nmsSubVersion = nmsVersion.split("_")[1] if (nmsSubVersion >= 8) { bukkitChatInvoke = new BukkitChatInvoke_1_8(nmsVersion) @@ -168,6 +168,7 @@ try { } let chat = { + invoke: bukkitChatInvoke, json: bukkitChatInvoke.json.bind(bukkitChatInvoke), send: bukkitChatInvoke.send.bind(bukkitChatInvoke) } diff --git a/packages/bukkit/src/particle.ts b/packages/bukkit/src/particle.ts index 1f92c17a..85f2b193 100644 --- a/packages/bukkit/src/particle.ts +++ b/packages/bukkit/src/particle.ts @@ -1,5 +1,5 @@ import { provideSingleton } from '@ccms/container' -import { particle, plugin } from '@ccms/api' +import { particle } from '@ccms/api' @provideSingleton(particle.ParticleManager) export class BukkitParticleManager extends particle.ParticleManager { diff --git a/packages/bukkit/src/plugin.ts b/packages/bukkit/src/plugin.ts index ae9be698..5bb43b11 100644 --- a/packages/bukkit/src/plugin.ts +++ b/packages/bukkit/src/plugin.ts @@ -19,6 +19,22 @@ export class BukkitNativePluginManager extends server.NativePluginManager { get(name: string): server.NativePlugin { return this.convert(this.bukkitPluginManager.getPlugin(name)) } + enable(name: string): server.NativePlugin { + let origin = this.bukkitPluginManager.getPlugin(name) + if (!origin) { throw new Error(`Native Plugin ${name} not found.`) } + if (!origin.isEnabled()) { + this.bukkitPluginManager.enablePlugin(origin) + } + return this.convert(origin) + } + disable(name: string): server.NativePlugin { + let origin = this.bukkitPluginManager.getPlugin(name) + if (!origin) { throw new Error(`Native Plugin ${name} not found.`) } + if (origin.isEnabled()) { + this.bukkitPluginManager.disablePlugin(origin) + } + return this.convert(origin) + } private convert(plugin: org.bukkit.plugin.Plugin): server.NativePlugin { if (!plugin) return plugin as any diff --git a/packages/bukkit/src/server.ts b/packages/bukkit/src/server.ts index f889652c..e731c6bb 100644 --- a/packages/bukkit/src/server.ts +++ b/packages/bukkit/src/server.ts @@ -1,10 +1,9 @@ -import { server, constants } from '@ccms/api' +import { server } from '@ccms/api' import { provideSingleton } from '@ccms/container' import * as reflect from '@ccms/common/dist/reflect' -import chat from './enhance/chat' -let Bukkit: typeof org.bukkit.Bukkit = org.bukkit.Bukkit +const Bukkit: typeof org.bukkit.Bukkit = Java.type('org.bukkit.Bukkit') @provideSingleton(server.Server) export class BukkitServer extends server.ReflectServer { diff --git a/packages/bukkit/src/task.ts b/packages/bukkit/src/task.ts index 0673fd2b..608189b1 100644 --- a/packages/bukkit/src/task.ts +++ b/packages/bukkit/src/task.ts @@ -1,5 +1,5 @@ import { task, plugin } from '@ccms/api' -import { inject, provideSingleton } from '@ccms/container' +import { provideSingleton } from '@ccms/container' const Bukkit = Java.type('org.bukkit.Bukkit') const BukkitRunnable = Java.type('org.bukkit.scheduler.BukkitRunnable') diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 455c9fc3..64a27529 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -43,7 +43,11 @@ class MiaoScriptCore { this.pluginManager.disable(this.pluginManager.getPlugins()) this.taskManager.disable() process.exit(0) - console.i18n("ms.core.engine.disable.finish", { version: 'v' + global.ScriptEngineVersion, time: (new Date().getTime() - disableStartTime) / 1000 }) + console.i18n("ms.core.engine.disable.finish", { + loader: base.version, + version: 'v' + global.ScriptEngineVersion, + time: (new Date().getTime() - disableStartTime) / 1000 + }) } } @@ -77,8 +81,16 @@ function detectServer(): constants.ServerType { } function initialize() { + global.ScriptSlowExecuteTime = 30 global.ScriptEngineVersion = require('../package.json').version - try { engineLoad({ script: http.get("http://ms.yumc.pw/api/plugin/download/name/initialize"), name: 'core/initialize.js' }) } catch (error: any) { console.debug(error) } + try { + engineLoad({ + script: http.get("https://ms.yumc.pw/api/plugin/download/name/initialize"), + name: 'core/initialize.js' + }) + } catch (error: any) { + console.debug(error) + } try { let corePackageStartTime = new Date().getTime() container.bind(ContainerInstance).toConstantValue(container) @@ -95,7 +107,11 @@ function initialize() { container.load(buildProviderModule()) console.i18n("ms.core.package.completed", { scope: global.scope, type, time: (Date.now() - corePackageStartTime) / 1000 }) let disable = container.get(MiaoScriptCore).enable() - console.i18n("ms.core.engine.completed", { version: 'v' + global.ScriptEngineVersion, time: (Date.now() - global.ScriptEngineStartTime) / 1000 }) + console.i18n("ms.core.engine.completed", { + loader: base.version, + version: 'v' + global.ScriptEngineVersion, + time: (Date.now() - global.ScriptEngineStartTime) / 1000 + }) return disable } catch (error: any) { console.i18n("ms.core.initialize.error", { error }) diff --git a/packages/core/src/initialize.js b/packages/core/src/initialize.js index 7b9a3a2d..12671021 100644 --- a/packages/core/src/initialize.js +++ b/packages/core/src/initialize.js @@ -1,8 +1,22 @@ +var fs = require('@ccms/common/dist/fs') +function updateJar() { + if (!base.version) { + var pluginFolder = fs.file(fs.concat(root, '..')) + var updateFolder = fs.concat(pluginFolder, 'update') + } +} +function upgradeModules() { + var core = http.get('https://registry.npmmirror.com/@ccms/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!') + } +} function initialize() { - var mspmc = 'http://ms.yumc.pw/api/plugin/download/name/' + var mspmc = 'https://ms.yumc.pw/api/plugin/download/name/' var http = require('@ccms/common/dist/http').default - var fs = require('@ccms/common/dist/fs') var pluginFolder = fs.concat(root, 'plugins') var updateFolder = fs.concat(pluginFolder, 'update') @@ -27,12 +41,12 @@ function initialize() { } } }) - var core = http.get('https://registry.npmmirror.com/@ccms/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!') + try { + Java.type("org.bukkit.Bukkit") + updateJar() + } catch (ignore) { } + upgradeModules() console.debug('initialize finish!') } initialize() diff --git a/packages/i18n/languages/zh_cn.yml b/packages/i18n/languages/zh_cn.yml index 38c64ada..094fcd1b 100644 --- a/packages/i18n/languages/zh_cn.yml +++ b/packages/i18n/languages/zh_cn.yml @@ -10,9 +10,9 @@ ms.core.package.initialize: "初始化 MiaoScript 扩展 {scope}/core {scope}/{t ms.core.package.completed: "MiaoScript 扩展 {scope}/core {scope}/{type} {scope}/plugin 加载完成 耗时({time}s)" ms.core.plugin.initialize: "MiaoScript 开始引导插件系统. 请稍候..." ms.core.plugin.completed: "MiaoScript 插件加载完毕 耗时({time}s)!" -ms.core.engine.completed: "MiaoScript 脚本引擎 {version} 加载完毕... 耗时({time}s)!" +ms.core.engine.completed: "MiaoScript 框架 {loader} 引擎 {version} 加载完毕... 耗时({time}s)!" ms.core.engine.disable: "MiaoScript 关闭脚本引擎 请稍候..." -ms.core.engine.disable.finish: "MiaoScript 脚本引擎 {version} 关闭完成... 耗时({time}s)!" +ms.core.engine.disable.finish: "MiaoScript 框架 {loader} 引擎 {version} 关闭完成... 耗时({time}s)!" ms.core.engine.disable.abnormal: "引擎异常启动或初始化未完成 跳过关闭流程..." ms.api.event.resource.not.found: "无法映射事件 未找到资源文件 {resource}!" @@ -27,7 +27,9 @@ 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}..." +ms.api.command.execute.slow: "§c注意! §6玩家 §a{player} §6执行 §b{plugin} §6插件 §d{command} {args} §6命令 §c耗时 §4{cost}ms !" ms.api.command.execute.error: "§6玩家 §a{player} §6执行 §b{plugin} §6插件 §d{command} {args} §6命令时发生异常 §4{ex}" +ms.api.command.tab.completer.slow: "§c注意! §6玩家 §a{player} §6执行 §b{plugin} §6插件 §d{command} {args} §6补全 §c耗时 §4{cost}ms !" ms.api.command.tab.completer.error: "§6玩家 §a{player} §6执行 §b{plugin} §6插件 §d{command} {args} §6补全时发生异常 §4{ex}" ms.plugin.initialize: "初始化 MiaoScript 插件系统: 实例: {plugin} 加载器: {loader}..." diff --git a/packages/nashorn/src/index.ts b/packages/nashorn/src/index.ts index 0f9db70d..f5891357 100644 --- a/packages/nashorn/src/index.ts +++ b/packages/nashorn/src/index.ts @@ -50,6 +50,7 @@ declare global { debug: boolean level: string ScriptEngineVersion: string + ScriptSlowExecuteTime: number ScriptEngineStartTime: number setGlobal: (key: string, value: any, config?: PropertyDescriptor & ThisType) => void noop: () => void @@ -65,6 +66,7 @@ declare global { const ScriptEngineContextHolder: any function engineLoad(str: string | { script: string, name: string }): any interface Core { + version: string getClass(name: String): any getProxyClass(): any getJavaScriptTaskClass(): any diff --git a/packages/plugin/package.json b/packages/plugin/package.json index cb0ac802..295ffc81 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -29,6 +29,7 @@ "@ccms/common": "^0.18.0", "@ccms/container": "^0.18.0", "@ccms/i18n": "^0.18.0", + "crypto-js": "^4.1.1", "js-yaml": "^4.1.0", "yaml": "^1.10.2" } diff --git a/packages/plugin/src/command.ts b/packages/plugin/src/command.ts index 50a77c9d..a9808671 100644 --- a/packages/plugin/src/command.ts +++ b/packages/plugin/src/command.ts @@ -35,7 +35,15 @@ export class PluginCommandManager { private unregistryCommand(pluginInstance: plugin.Plugin) { let cmds = getPluginCommandMetadata(pluginInstance) - cmds.forEach(cmd => this.CommandManager.off(pluginInstance, cmd.name)) + for (const cmd of cmds) { + if (!this.ServerChecker.check(cmd.servers)) { + console.debug(`[${pluginInstance.description.name}] ${cmd.target.constructor.name} incompatible command ${cmd.name} server(${cmd.servers}) ignore.`) + continue + } + for (let command of [cmd.name, ...cmd.alias]) { + this.CommandManager.off(pluginInstance, command) + } + } } private generateAutoMainCommand(pluginInstance: plugin.Plugin, cmd: interfaces.CommandMetadata, tab: interfaces.CommandMetadata) { @@ -46,17 +54,41 @@ export class PluginCommandManager { cmdExecutor = (sender: any, command: string, args: string[]) => { let subcommand = args[0] let cmdKey = 'cmd' + subcommand - if (pluginInstance[cmdKey]) { + let subcommandexec = pluginInstance[cmdKey] + if (!subcommandexec) { args.shift() - return pluginInstance[cmdKey].apply(pluginInstance, [sender, ...args]) - } else if (pluginInstance['cmdmain']) { - return pluginInstance['cmdmain'].apply(pluginInstance, [sender, ...args]) + subcommandexec = pluginInstance['cmdmain'] } - pluginInstance.logger.sender(sender, '§4未知的子命令: §c' + subcommand) - pluginInstance['cmdhelp'] && pluginInstance.logger.sender(sender, `§6请执行 §b/${command} §ahelp §6查看帮助!`) + if (!subcommandexec) { + subcommand && pluginInstance.logger.sender(sender, '§4未知的子命令: §c' + subcommand) + pluginInstance.logger.sender( + sender, + pluginInstance['cmdhelp'] ? + `§6请执行 §b/${command} §ahelp §6查看帮助!` : + `§b版本: §a ${pluginInstance.description.version}` + ) + return + } + let permission: string + if (typeof cmd.permission == "string") { + permission = cmd.permission as string + } else if (cmd.permission) { + permission = `${pluginInstance.description.name.toLocaleLowerCase()}.${command}.${subcommand}` + } + if (sender.hasPermission && !sender.hasPermission(permission)) { + return pluginInstance.logger.sender(sender, `§c你需要 ${permission} 权限 才可执行此命令.`) + } + return subcommandexec.apply(pluginInstance, [sender, ...args]) } let originCompleter = cmdCompleter cmdCompleter = (sender: any, command: string, args: string[]) => { + let permission: string + if (typeof cmd.permission == "string") { + permission = cmd.permission as string + } else if (cmd.permission) { + permission = `${pluginInstance.description.name.toLocaleLowerCase()}.${command}` + } + if (sender.hasPermission && !sender.hasPermission(permission)) { return [] } return (args.length == 1 ? cmdSubCache : []).concat(originCompleter?.apply(pluginInstance, [sender, command, args]) || []) } } diff --git a/packages/plugin/src/config.ts b/packages/plugin/src/config.ts index 235629ec..d2f23761 100644 --- a/packages/plugin/src/config.ts +++ b/packages/plugin/src/config.ts @@ -78,7 +78,10 @@ export class PluginConfigManager { 'save': { value: () => this.saveConfig0(plugin, metadata) }, 'reload': { value: () => this.loadConfig0(plugin, metadata) } }) - Object.defineProperty(plugin, metadata.variable, { value }) + Object.defineProperty(plugin, metadata.variable, { + value, + configurable: true + }) } private loadConfig0(plugin: plugin.Plugin, metadata: interfaces.ConfigMetadata) { @@ -86,37 +89,50 @@ export class PluginConfigManager { let defaultValue = metadata.default ?? plugin[metadata.variable] let configValue = defaultValue || {} if (defaultValue) { - metadata.file = fs.concat(fs.file(plugin.description.loadMetadata.file).parent, plugin.description.name, metadata.filename) + metadata.file = fs.concat( + fs.file(plugin.description.loadMetadata.file).parent, + plugin.description.name, + metadata.filename + ) let configLoader = this.getConfigLoader(metadata.format) if (!fs.exists(metadata.file)) { base.save(metadata.file, configLoader.dump(defaultValue)) - console.i18n("ms.plugin.manager.config.save.default", { plugin: plugin.description.name, name: metadata.name, format: metadata.format }) + console.i18n("ms.plugin.manager.config.save.default", { + plugin: plugin.description.name, + name: metadata.name, + format: metadata.format + }) } else { configValue = configLoader.load(base.read(metadata.file)) || {} - if (defaultValue && this.setDefaultValue(configValue, defaultValue)) { + if (defaultValue && this.setDefaultValue(configValue, defaultValue, !!metadata.default)) { base.save(metadata.file, configLoader.dump(configValue)) } - console.debug(`[${plugin.description.name}] Load Config ${metadata.variable} from file ${metadata.file} =>\n${JSON.stringify(configValue, undefined, 4).substr(0, 500)}`) + console.debug(`[${plugin.description.name}] Load Config ${metadata.variable} from file ${metadata.file} => +${JSON.stringify(configValue, undefined, 4).substring(0, 500)}`) } } this.defienConfigProp(plugin, metadata, configValue) } catch (error: any) { - console.i18n("ms.plugin.manager.config.load.error", { plugin: plugin.description.name, name: metadata.name, format: metadata.format, error }) + console.i18n("ms.plugin.manager.config.load.error", { + plugin: plugin.description.name, + name: metadata.name, + format: metadata.format, + error + }) console.ex(error) } } - private setDefaultValue(configValue, defaultValue) { + private setDefaultValue(configValue, defaultValue, deepCopy) { let needSave = false for (const key of Object.keys(defaultValue)) { // 当配置文件不存在当前属性时才进行赋值 - if (!Object.prototype.hasOwnProperty.call(configValue, key) && key != '____deep_copy____') { + if (!Object.prototype.hasOwnProperty.call(configValue, key)) { configValue[key] = defaultValue[key] needSave = true - } else if (Object.prototype.toString.call(configValue[key]) == "[object Object]" - && Object.prototype.hasOwnProperty.call(defaultValue[key], '____deep_copy____')) { - // 对象需要递归检测 如果对象内存在 ____deep_copy____ 那就忽略设置 - needSave ||= this.setDefaultValue(configValue[key], defaultValue[key]) + } else if (Object.prototype.toString.call(configValue[key]) == "[object Object]" && deepCopy) { + // 对象需要递归检测 + needSave ||= this.setDefaultValue(configValue[key], defaultValue[key], deepCopy) } } return needSave @@ -127,10 +143,16 @@ export class PluginConfigManager { metadata.file = fs.concat(fs.file(plugin.description.loadMetadata.file).parent, plugin.description.name, metadata.filename) let result = this.getConfigLoader(metadata.format).dump(plugin[metadata.variable]) base.save(metadata.file, result) - console.debug(`[${plugin.description.name}] Save Config ${metadata.variable} to file ${metadata.file} =>\n${result.substr(0, 500)}`) + console.debug(`[${plugin.description.name}] Save Config ${metadata.variable} to file ${metadata.file} => +${result.substring(0, 500)}`) return true } catch (error: any) { - console.i18n("ms.plugin.manager.config.save.error", { plugin: plugin.description.name, name: metadata.name, format: metadata.format, error }) + console.i18n("ms.plugin.manager.config.save.error", { + plugin: plugin.description.name, + name: metadata.name, + format: metadata.format, + error + }) console.ex(error) return false } diff --git a/packages/plugin/src/constants.ts b/packages/plugin/src/constants.ts index 9ec39df9..518ac7df 100644 --- a/packages/plugin/src/constants.ts +++ b/packages/plugin/src/constants.ts @@ -5,6 +5,7 @@ export const METADATA_KEY = { tab: Symbol.for("@ccms/plugin:tab"), listener: Symbol.for("@ccms/plugin:listener"), config: Symbol.for("@ccms/plugin:config"), + playerdata: Symbol.for("@ccms/plugin:playerdata"), stage: { load: Symbol.for("@ccms/plugin:stage:load"), enable: Symbol.for("@ccms/plugin:stage:enable"), diff --git a/packages/plugin/src/decorators.ts b/packages/plugin/src/decorators.ts index dd7e6907..fc87e444 100644 --- a/packages/plugin/src/decorators.ts +++ b/packages/plugin/src/decorators.ts @@ -2,7 +2,7 @@ import { plugin as pluginApi } from "@ccms/api" import { injectable, decorate } from "@ccms/container" import { interfaces } from './interfaces' import { METADATA_KEY } from './constants' -import { getPluginMetadatas, getPluginCommandMetadata, getPluginListenerMetadata, getPluginTabCompleterMetadata, getPluginConfigMetadata, getPluginStageMetadata, getPluginSources } from './utils' +import { getPluginMetadatas, getPluginCommandMetadata, getPluginListenerMetadata, getPluginTabCompleterMetadata, getPluginConfigMetadata, getPluginStageMetadata, getPluginSources, getPluginPlayerDataMetadata } from './utils' /** * MiaoScript plugin @@ -84,6 +84,21 @@ export function config(metadata: interfaces.ConfigMetadata = {}) { } } +export function playerdata(metadata: interfaces.PlayerDataMetadata = {}) { + return function (target: any, key: string) { + metadata.name = metadata.name || key + metadata.variable = key + metadata.version = metadata.version ?? 1 + metadata.format = metadata.format ?? 'yml' + metadata.autosave = metadata.autosave ?? false + metadata.filename = metadata.filename ?? "username" + metadata.dir = metadata.dir ?? "playerdata" + let previousMetadata = getPluginPlayerDataMetadata(target) + previousMetadata.set(metadata.name, metadata) + Reflect.defineMetadata(METADATA_KEY.playerdata, previousMetadata, target.constructor) + } +} + function stage(stage: string) { return (metadata: interfaces.ExecMetadata = {}) => { return function (target: any, key: string, value: any) { diff --git a/packages/plugin/src/index.ts b/packages/plugin/src/index.ts index 7f5d7bab..cfb272b5 100644 --- a/packages/plugin/src/index.ts +++ b/packages/plugin/src/index.ts @@ -1,4 +1,6 @@ -import './scanner/file-scanner' +import './scanner/js-scanner' +import './scanner/mjs-scanner' + import './loader/ioc-loader' import './loader/basic-loader' @@ -12,5 +14,6 @@ export { cmd as Cmd, tab as Tab, listener as Listener, - config as Config + config as Config, + playerdata as PlayerData } from './decorators' diff --git a/packages/plugin/src/interfaces.ts b/packages/plugin/src/interfaces.ts index cb82edb2..d7561b3a 100644 --- a/packages/plugin/src/interfaces.ts +++ b/packages/plugin/src/interfaces.ts @@ -59,6 +59,10 @@ export namespace interfaces { * 自动化主命令 */ autoMain?: boolean + /** + * 子命令权限效验 + */ + permission?: boolean | string } export interface ListenerMetadata extends ExecMetadata { /** @@ -100,4 +104,38 @@ export namespace interfaces { */ file?: any } + export interface PlayerDataMetadata extends plugin.BaseMetadata { + /** + * 配置文件版本号 + */ + version?: number + /** + * 默认配置 + */ + default?: any + /** + * 实体变量名称 + */ + variable?: string + /** + * 配置文件格式 默认 yml + */ + format?: string + /** + * 自动保存 默认为 false + */ + autosave?: boolean + /** + * 配置文件名称 默认玩家名称 + */ + filename?: "username" | "uuid" + /** + * 配置文件目录 默认 playerdata + */ + dir?: string + /** + * 配置文件子目录 默认为空 + */ + subdir?: string + } } diff --git a/packages/plugin/src/manager.ts b/packages/plugin/src/manager.ts index deaa8213..88294edb 100644 --- a/packages/plugin/src/manager.ts +++ b/packages/plugin/src/manager.ts @@ -169,6 +169,7 @@ export class PluginManagerImpl implements plugin.PluginManager { private loadAndRequirePlugin(loadMetadata: plugin.PluginLoadMetadata) { let startTime = Date.now() let metadata = this.loadPlugin(loadMetadata.scanner.load(loadMetadata)) + if (!metadata) { throw new Error('load plugin metadata failed.') } console.i18n('ms.plugin.manager.build', { name: loadMetadata.metadata.name, version: loadMetadata.metadata.version, @@ -184,9 +185,10 @@ export class PluginManagerImpl implements plugin.PluginManager { * 从文件加载插件 * @param file java.io.File */ - loadFromFile(file: string, scanner = this.sacnnerMap.get('file')): plugin.Plugin { - if (!file) { throw new Error('plugin file can\'t be undefiend!') } - if (!scanner) { throw new Error('plugin scanner can\'t be undefiend!') } + loadFromFile(file: string, ext: any = 'js'): plugin.Plugin { + if (!file) { throw new Error('plugin file can\'t be undefiend.') } + 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.load(plugin) @@ -213,7 +215,7 @@ export class PluginManagerImpl implements plugin.PluginManager { reload(...args: any[]): void { this.checkAndGet(args[0]).forEach((pl: plugin.Plugin) => { this.disable(pl) - this.loadFromFile(pl.description.loadMetadata.file, pl.description.loadMetadata.scanner) + this.loadFromFile(pl.description.loadMetadata.file, pl.description.loadMetadata.scanner.type) }) } @@ -235,12 +237,12 @@ export class PluginManagerImpl implements plugin.PluginManager { } private checkAndGet(name: string | plugin.Plugin | undefined | any): Map | plugin.Plugin[] { - if (name === undefined) throw new Error(`checkAndGet Plugin can't be undefiend!`) + if (name === undefined) throw new Error(`checkAndGet Plugin can't be undefiend.`) if (name == this.instanceMap) { return this.instanceMap } if (typeof name == 'string' && this.instanceMap.has(name)) { return [this.instanceMap.get(name)] } if (name instanceof interfaces.Plugin) { return [name as plugin.Plugin] } if (name.description?.name) { return [name as plugin.Plugin] } - throw new Error(`Plugin ${JSON.stringify(name)} not exist!`) + throw new Error(`Plugin ${JSON.stringify(name)} not exist.`) } private buildPlugins() { diff --git a/packages/plugin/src/scanner/file-scanner.ts b/packages/plugin/src/scanner/js-scanner.ts similarity index 98% rename from packages/plugin/src/scanner/file-scanner.ts rename to packages/plugin/src/scanner/js-scanner.ts index 97630a4e..c3e9a9fa 100644 --- a/packages/plugin/src/scanner/file-scanner.ts +++ b/packages/plugin/src/scanner/js-scanner.ts @@ -2,7 +2,7 @@ import { plugin } from "@ccms/api" import * as fs from '@ccms/common/dist/fs' import { provideSingletonNamed } from "@ccms/container" -const SCANNER_TYPE_NAME = 'file' +const SCANNER_TYPE_NAME = 'js' @provideSingletonNamed(plugin.PluginScanner, SCANNER_TYPE_NAME) export class JSFileScanner implements plugin.PluginScanner { diff --git a/packages/plugin/src/utils.ts b/packages/plugin/src/utils.ts index 154ac238..f503c4d1 100644 --- a/packages/plugin/src/utils.ts +++ b/packages/plugin/src/utils.ts @@ -67,7 +67,13 @@ function getPluginConfigMetadata(target: any) { ) || new Map() return configMetadata } - +function getPluginPlayerDataMetadata(target: any) { + let playerdataMetadata: Map = Reflect.getMetadata( + METADATA_KEY.playerdata, + target.constructor + ) || new Map() + return playerdataMetadata +} function getPluginStageMetadata(target: any, stage: string) { let stageMetadata: interfaces.ExecMetadata[] = Reflect.getMetadata( METADATA_KEY.stage[stage], @@ -86,5 +92,6 @@ export { getPluginTabCompleterMetadata, getPluginListenerMetadata, getPluginConfigMetadata, + getPluginPlayerDataMetadata, getPluginStageMetadata }