feat: new mjs scanner

Signed-off-by: MiaoWoo <admin@yumc.pw>
This commit is contained in:
MiaoWoo 2022-04-01 17:18:32 +08:00
parent 30fd065bc5
commit 143c960da5
27 changed files with 327 additions and 96 deletions

View File

@ -44,12 +44,37 @@ export namespace command {
protected setExecutor(plugin: plugin.Plugin, command: any, executor: Function) { protected setExecutor(plugin: plugin.Plugin, command: any, executor: Function) {
return (sender: any, _: any, command: string, args: string[]) => { return (sender: any, _: any, command: string, args: string[]) => {
try { 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) { } 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) console.ex(ex)
if (sender.name != 'CONSOLE') { 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 return true
} }
@ -58,13 +83,42 @@ export namespace command {
protected setTabCompleter(plugin: plugin.Plugin, command: any, tabCompleter: Function) { protected setTabCompleter(plugin: plugin.Plugin, command: any, tabCompleter: Function) {
return (sender: any, _: any, command: string, args: string[]) => { return (sender: any, _: any, command: string, args: string[]) => {
try { try {
let time = Date.now()
var token = args[args.length - 1] var token = args[args.length - 1]
var complete = tabCompleter(sender, command, Java.from(args)) || [] 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) { } 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.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 [] return []
} }
} }

View File

@ -100,7 +100,7 @@ export namespace event {
let time = Date.now() let time = Date.now()
exec(event) exec(event)
let cost = Date.now() - time 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 }) console.i18n("ms.api.event.execute.slow", { name, event: this.class2Name(eventCls), cost })
} }
} }

View File

@ -40,13 +40,13 @@ export namespace server {
get(name: string): NativePlugin { get(name: string): NativePlugin {
throw new Error("Method not implemented.") throw new Error("Method not implemented.")
} }
load(name: string): boolean { enable(name: string): NativePlugin {
throw new Error("Method not implemented.") throw new Error("Method not implemented.")
} }
unload(name: string): boolean { disable(name: string): NativePlugin {
throw new Error("Method not implemented.") throw new Error("Method not implemented.")
} }
reload(name: string): boolean { reload(name: string): NativePlugin {
throw new Error("Method not implemented.") throw new Error("Method not implemented.")
} }
delete(name: string): boolean { delete(name: string): boolean {

View File

@ -1,7 +1,7 @@
import { channel, plugin } from '@ccms/api' import { channel, plugin } from '@ccms/api'
import { inject, provideSingleton } from '@ccms/container' 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 PluginMessageListener = Java.type("org.bukkit.plugin.messaging.PluginMessageListener")
const Messenger = Bukkit.getMessenger() const Messenger = Bukkit.getMessenger()

View File

@ -1,9 +1,12 @@
import { chat } from '@ccms/api' import { chat } from '@ccms/api'
import { provideSingleton } from '@ccms/container' import { provideSingleton } from '@ccms/container'
import bukkitChat from './enhance/chat' import bukkitChat from './internal/chat'
@provideSingleton(chat.Chat) @provideSingleton(chat.Chat)
export class BukkitChat extends chat.Chat { export class BukkitChat extends chat.Chat {
get handle(): any {
return bukkitChat
}
sendJson(sender: any, json: string | object, type = 0) { sendJson(sender: any, json: string | object, type = 0) {
bukkitChat.send(sender, typeof json === "string" ? json : JSON.stringify(json), type) bukkitChat.send(sender, typeof json === "string" ? json : JSON.stringify(json), type)
} }

View File

@ -1,35 +1,38 @@
import '@ccms/nashorn' import '@ccms/nashorn'
import { command, plugin } from '@ccms/api' import { command, plugin } from '@ccms/api'
import * as reflect from '@ccms/common/dist/reflect'
import { provideSingleton, postConstruct, inject } from '@ccms/container' import { provideSingleton, postConstruct, inject } from '@ccms/container'
import * as reflect from '@ccms/common/dist/reflect'
let Bukkit = org.bukkit.Bukkit const Bukkit: typeof org.bukkit.Bukkit = Java.type('org.bukkit.Bukkit')
let TabCompleter = Java.type('org.bukkit.command.TabCompleter') const TabCompleter = Java.type('org.bukkit.command.TabCompleter')
let PluginCommand = Java.type('org.bukkit.command.PluginCommand') const PluginCommand = Java.type('org.bukkit.command.PluginCommand')
let CommandExecutor = Java.type('org.bukkit.command.CommandExecutor') const CommandExecutor = Java.type('org.bukkit.command.CommandExecutor')
@provideSingleton(command.Command) @provideSingleton(command.Command)
export class BukkitCommand extends command.Command { export class BukkitCommand extends command.Command {
@inject(plugin.PluginInstance) @inject(plugin.PluginInstance)
private pluginInstance: any private pluginInstance: any
private commandMap: any private commandMap: any
private knownCommands: any
@postConstruct() @postConstruct()
init() { init() {
this.commandMap = reflect.on(Bukkit.getPluginManager()).get('commandMap').get() this.commandMap = reflect.on(Bukkit.getPluginManager()).get('commandMap').get()
this.knownCommands = reflect.on(this.commandMap).get('knownCommands').get()
} }
create(plugin: any, command: string) { create(plugin: any, command: string) {
var cmd = this.commandMap.getCommand(command) 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() cmd = reflect.on(PluginCommand).create(command, this.pluginInstance).get()
this.commandMap.register(plugin.description.name, cmd) this.commandMap.register(plugin.description.name, cmd)
return cmd return cmd
} }
remove(plugin: any, command: string) { remove(plugin: any, command: string) {
var cmd = this.commandMap.getCommand(command) var cmd = this.commandMap.getCommand(command)
if (cmd && cmd instanceof PluginCommand) { if (cmd instanceof PluginCommand) {
cmd.unregister(this.commandMap) cmd.unregister(this.commandMap)
this.knownCommands.remove(command)
} }
} }
tabComplete(sender: any, input: string, index?: number): string[] { tabComplete(sender: any, input: string, index?: number): string[] {

View File

@ -1,20 +1,20 @@
import { MiaoScriptConsole } from '@ccms/api' 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 { export class BukkitConsole extends MiaoScriptConsole {
sender(sender, ...args) { sender(sender, ...args) {
if (!(sender instanceof Java.type('org.bukkit.command.CommandSender'))) { if (!(sender instanceof Java.type('org.bukkit.command.CommandSender'))) {
this.error(`First parameter ${sender} not instanceof org.bukkit.command.CommandSender can't send message!`) 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]") { if (Object.prototype.toString.call(args[0]) === "[object Array]") {
args[0].forEach(line => sender.sendMessage(this.prefix + line)) args[0].forEach(line => sender.sendMessage(this.prefix + line))
} else { } else {
sender.sendMessage(this.prefix + args.join(' ')); sender.sendMessage(this.prefix + args.join(' '))
} }
} }
console(...args: string[]): void { console(...args: string[]): void {
this.sender(Bukkit.getConsoleSender(), args.join(' ')); this.sender(Bukkit.getConsoleSender(), args.join(' '))
} }
} }

View File

@ -1,13 +1,13 @@
import { event, plugin } from '@ccms/api' 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' import * as reflect from '@ccms/common/dist/reflect'
const Bukkit = Java.type("org.bukkit.Bukkit"); const Bukkit = Java.type("org.bukkit.Bukkit")
const Event = Java.type("org.bukkit.event.Event"); const Event = Java.type("org.bukkit.event.Event")
const Modifier = Java.type("java.lang.reflect.Modifier"); const Modifier = Java.type("java.lang.reflect.Modifier")
const Listener = Java.type("org.bukkit.event.Listener"); const Listener = Java.type("org.bukkit.event.Listener")
const EventPriority = Java.type("org.bukkit.event.EventPriority"); const EventPriority = Java.type("org.bukkit.event.EventPriority")
const EventExecutor = Java.type("org.bukkit.plugin.EventExecutor"); const EventExecutor = Java.type("org.bukkit.plugin.EventExecutor")
@provideSingleton(event.Event) @provideSingleton(event.Event)
export class BukkitEvent extends event.Event { export class BukkitEvent extends event.Event {
@ -15,7 +15,7 @@ export class BukkitEvent extends event.Event {
private pluginInstance: any private pluginInstance: any
constructor() { constructor() {
super('org/bukkit/event'); super('org/bukkit/event')
} }
getJarFile(resource: string) { getJarFile(resource: string) {
@ -27,10 +27,10 @@ export class BukkitEvent extends event.Event {
// 访问符为Public // 访问符为Public
Modifier.isPublic(clazz.getModifiers()) && Modifier.isPublic(clazz.getModifiers()) &&
// 不是抽象类 // 不是抽象类
!Modifier.isAbstract(clazz.getModifiers()); !Modifier.isAbstract(clazz.getModifiers())
} }
register(eventCls: any, exec: Function, priority: event.EventPriority, ignoreCancel: boolean) { register(eventCls: any, exec: Function, priority: event.EventPriority, ignoreCancel: boolean) {
let listener = new Listener({}); let listener = new Listener({})
Bukkit.pluginManager.registerEvent( Bukkit.pluginManager.registerEvent(
eventCls, eventCls,
listener, listener,
@ -39,10 +39,10 @@ export class BukkitEvent extends event.Event {
execute: exec execute: exec
}), }),
this.pluginInstance, this.pluginInstance,
ignoreCancel); ignoreCancel)
return listener; return listener
} }
unregister(event: any, listener: any): void { unregister(event: any, listener: any): void {
reflect.on(event).call('getHandlerList').get().unregister(listener); reflect.on(event).call('getHandlerList').get().unregister(listener)
} }
} }

View File

@ -29,17 +29,16 @@ abstract class BukkitChatInvoke {
this.ChatSerializer = Java.type(nmsChatSerializerClass.getName()) this.ChatSerializer = Java.type(nmsChatSerializerClass.getName())
let packetTypeClass = this.getPacketPlayOutChatClass() let packetTypeClass = this.getPacketPlayOutChatClass()
this.PacketPlayOutChat = Java.type(packetTypeClass.getName()) this.PacketPlayOutChat = Java.type(packetTypeClass.getName())
let packetTypeConstructor: { parameterTypes: any[] }
let constructors = packetTypeClass.constructors let constructors = packetTypeClass.constructors
Java.from(constructors).forEach(function (c) { for (const constructor of Java.from(constructors)) {
if (c.parameterTypes.length === 2 || c.parameterTypes.length === 3) { let parameterTypes = constructor.parameterTypes
packetTypeConstructor = c if (parameterTypes.length === 2 || parameterTypes.length === 3) {
}
})
let parameterTypes = packetTypeConstructor.parameterTypes
let nmsChatMessageTypeClass = parameterTypes[1] let nmsChatMessageTypeClass = parameterTypes[1]
if (nmsChatMessageTypeClass.isEnum()) { if (nmsChatMessageTypeClass.isEnum()) {
this.chatMessageTypes = nmsChatMessageTypeClass.getEnumConstants() this.chatMessageTypes = nmsChatMessageTypeClass.getEnumConstants()
break
}
}
} }
let playerConnectionField = this.getPlayerConnectionField() let playerConnectionField = this.getPlayerConnectionField()
this.playerConnectionFieldName = playerConnectionField.getName() this.playerConnectionFieldName = playerConnectionField.getName()
@ -151,8 +150,9 @@ class BukkitChatInvoke_1_17_1 extends BukkitChatInvoke_1_16_5 {
} }
try { try {
//@ts-ignore let Bukkit: typeof org.bukkit.Bukkit = Java.type('org.bukkit.Bukkit')
let nmsVersion = org.bukkit.Bukkit.server.class.name.split('.')[3] // @ts-ignore
let nmsVersion = Bukkit.getServer().class.name.split('.')[3]
let nmsSubVersion = nmsVersion.split("_")[1] let nmsSubVersion = nmsVersion.split("_")[1]
if (nmsSubVersion >= 8) { if (nmsSubVersion >= 8) {
bukkitChatInvoke = new BukkitChatInvoke_1_8(nmsVersion) bukkitChatInvoke = new BukkitChatInvoke_1_8(nmsVersion)
@ -168,6 +168,7 @@ try {
} }
let chat = { let chat = {
invoke: bukkitChatInvoke,
json: bukkitChatInvoke.json.bind(bukkitChatInvoke), json: bukkitChatInvoke.json.bind(bukkitChatInvoke),
send: bukkitChatInvoke.send.bind(bukkitChatInvoke) send: bukkitChatInvoke.send.bind(bukkitChatInvoke)
} }

View File

@ -1,5 +1,5 @@
import { provideSingleton } from '@ccms/container' import { provideSingleton } from '@ccms/container'
import { particle, plugin } from '@ccms/api' import { particle } from '@ccms/api'
@provideSingleton(particle.ParticleManager) @provideSingleton(particle.ParticleManager)
export class BukkitParticleManager extends particle.ParticleManager { export class BukkitParticleManager extends particle.ParticleManager {

View File

@ -19,6 +19,22 @@ export class BukkitNativePluginManager extends server.NativePluginManager {
get(name: string): server.NativePlugin { get(name: string): server.NativePlugin {
return this.convert(this.bukkitPluginManager.getPlugin(name)) 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 { private convert(plugin: org.bukkit.plugin.Plugin): server.NativePlugin {
if (!plugin) return plugin as any if (!plugin) return plugin as any

View File

@ -1,10 +1,9 @@
import { server, constants } from '@ccms/api' import { server } from '@ccms/api'
import { provideSingleton } from '@ccms/container' import { provideSingleton } from '@ccms/container'
import * as reflect from '@ccms/common/dist/reflect' 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) @provideSingleton(server.Server)
export class BukkitServer extends server.ReflectServer { export class BukkitServer extends server.ReflectServer {

View File

@ -1,5 +1,5 @@
import { task, plugin } from '@ccms/api' 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 Bukkit = Java.type('org.bukkit.Bukkit')
const BukkitRunnable = Java.type('org.bukkit.scheduler.BukkitRunnable') const BukkitRunnable = Java.type('org.bukkit.scheduler.BukkitRunnable')

View File

@ -43,7 +43,11 @@ class MiaoScriptCore {
this.pluginManager.disable(this.pluginManager.getPlugins()) this.pluginManager.disable(this.pluginManager.getPlugins())
this.taskManager.disable() this.taskManager.disable()
process.exit(0) 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() { function initialize() {
global.ScriptSlowExecuteTime = 30
global.ScriptEngineVersion = require('../package.json').version 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 { try {
let corePackageStartTime = new Date().getTime() let corePackageStartTime = new Date().getTime()
container.bind(ContainerInstance).toConstantValue(container) container.bind(ContainerInstance).toConstantValue(container)
@ -95,7 +107,11 @@ function initialize() {
container.load(buildProviderModule()) container.load(buildProviderModule())
console.i18n("ms.core.package.completed", { scope: global.scope, type, time: (Date.now() - corePackageStartTime) / 1000 }) console.i18n("ms.core.package.completed", { scope: global.scope, type, time: (Date.now() - corePackageStartTime) / 1000 })
let disable = container.get<MiaoScriptCore>(MiaoScriptCore).enable() let disable = container.get<MiaoScriptCore>(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 return disable
} catch (error: any) { } catch (error: any) {
console.i18n("ms.core.initialize.error", { error }) console.i18n("ms.core.initialize.error", { error })

View File

@ -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() { 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 http = require('@ccms/common/dist/http').default
var fs = require('@ccms/common/dist/fs')
var pluginFolder = fs.concat(root, 'plugins') var pluginFolder = fs.concat(root, 'plugins')
var updateFolder = fs.concat(pluginFolder, 'update') var updateFolder = fs.concat(pluginFolder, 'update')
@ -27,12 +41,12 @@ function initialize() {
} }
} }
}) })
var core = http.get('https://registry.npmmirror.com/@ccms/core') try {
if (base.VERSION && global.ScriptEngineVersion != core['dist-tags']['latest']) { Java.type("org.bukkit.Bukkit")
var Paths = Java.type('java.nio.file.Paths') updateJar()
base.save(Paths.get(root, "upgrade"), core['dist-tags']['latest']) } catch (ignore) {
console.info('@ccms/core found new version ' + core['dist-tags']['latest'] + ' will upgrade after reboot!')
} }
upgradeModules()
console.debug('initialize finish!') console.debug('initialize finish!')
} }
initialize() initialize()

View File

@ -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.package.completed: "MiaoScript 扩展 {scope}/core {scope}/{type} {scope}/plugin 加载完成 耗时({time}s)"
ms.core.plugin.initialize: "MiaoScript 开始引导插件系统. 请稍候..." ms.core.plugin.initialize: "MiaoScript 开始引导插件系统. 请稍候..."
ms.core.plugin.completed: "MiaoScript 插件加载完毕 耗时({time}s)!" 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: "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.core.engine.disable.abnormal: "引擎异常启动或初始化未完成 跳过关闭流程..."
ms.api.event.resource.not.found: "无法映射事件 未找到资源文件 {resource}!" 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.input.error: "CommandExec 必须为一个函数... 输入: {exec}"
ms.api.command.register: "[{plugin}] 注册命令 {name}({cmd})..." ms.api.command.register: "[{plugin}] 注册命令 {name}({cmd})..."
ms.api.command.unregister: "[{plugin}] 注销命令 {name}..." 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.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.api.command.tab.completer.error: "§6玩家 §a{player} §6执行 §b{plugin} §6插件 §d{command} {args} §6补全时发生异常 §4{ex}"
ms.plugin.initialize: "初始化 MiaoScript 插件系统: 实例: {plugin} 加载器: {loader}..." ms.plugin.initialize: "初始化 MiaoScript 插件系统: 实例: {plugin} 加载器: {loader}..."

View File

@ -50,6 +50,7 @@ declare global {
debug: boolean debug: boolean
level: string level: string
ScriptEngineVersion: string ScriptEngineVersion: string
ScriptSlowExecuteTime: number
ScriptEngineStartTime: number ScriptEngineStartTime: number
setGlobal: (key: string, value: any, config?: PropertyDescriptor & ThisType<any>) => void setGlobal: (key: string, value: any, config?: PropertyDescriptor & ThisType<any>) => void
noop: () => void noop: () => void
@ -65,6 +66,7 @@ declare global {
const ScriptEngineContextHolder: any const ScriptEngineContextHolder: any
function engineLoad(str: string | { script: string, name: string }): any function engineLoad(str: string | { script: string, name: string }): any
interface Core { interface Core {
version: string
getClass(name: String): any getClass(name: String): any
getProxyClass(): any getProxyClass(): any
getJavaScriptTaskClass(): any getJavaScriptTaskClass(): any

View File

@ -29,6 +29,7 @@
"@ccms/common": "^0.18.0", "@ccms/common": "^0.18.0",
"@ccms/container": "^0.18.0", "@ccms/container": "^0.18.0",
"@ccms/i18n": "^0.18.0", "@ccms/i18n": "^0.18.0",
"crypto-js": "^4.1.1",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"yaml": "^1.10.2" "yaml": "^1.10.2"
} }

View File

@ -35,7 +35,15 @@ export class PluginCommandManager {
private unregistryCommand(pluginInstance: plugin.Plugin) { private unregistryCommand(pluginInstance: plugin.Plugin) {
let cmds = getPluginCommandMetadata(pluginInstance) 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) { 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[]) => { cmdExecutor = (sender: any, command: string, args: string[]) => {
let subcommand = args[0] let subcommand = args[0]
let cmdKey = 'cmd' + subcommand let cmdKey = 'cmd' + subcommand
if (pluginInstance[cmdKey]) { let subcommandexec = pluginInstance[cmdKey]
if (!subcommandexec) {
args.shift() args.shift()
return pluginInstance[cmdKey].apply(pluginInstance, [sender, ...args]) subcommandexec = pluginInstance['cmdmain']
} else if (pluginInstance['cmdmain']) {
return pluginInstance['cmdmain'].apply(pluginInstance, [sender, ...args])
} }
pluginInstance.logger.sender(sender, '§4未知的子命令: §c' + subcommand) if (!subcommandexec) {
pluginInstance['cmdhelp'] && pluginInstance.logger.sender(sender, `§6请执行 §b/${command} §ahelp §6查看帮助!`) 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 let originCompleter = cmdCompleter
cmdCompleter = (sender: any, command: string, args: string[]) => { 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]) || []) return (args.length == 1 ? cmdSubCache : []).concat(originCompleter?.apply(pluginInstance, [sender, command, args]) || [])
} }
} }

View File

@ -78,7 +78,10 @@ export class PluginConfigManager {
'save': { value: () => this.saveConfig0(plugin, metadata) }, 'save': { value: () => this.saveConfig0(plugin, metadata) },
'reload': { value: () => this.loadConfig0(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) { private loadConfig0(plugin: plugin.Plugin, metadata: interfaces.ConfigMetadata) {
@ -86,37 +89,50 @@ export class PluginConfigManager {
let defaultValue = metadata.default ?? plugin[metadata.variable] let defaultValue = metadata.default ?? plugin[metadata.variable]
let configValue = defaultValue || {} let configValue = defaultValue || {}
if (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) let configLoader = this.getConfigLoader(metadata.format)
if (!fs.exists(metadata.file)) { if (!fs.exists(metadata.file)) {
base.save(metadata.file, configLoader.dump(defaultValue)) 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 { } else {
configValue = configLoader.load(base.read(metadata.file)) || {} 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)) 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) this.defienConfigProp(plugin, metadata, configValue)
} catch (error: any) { } 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) console.ex(error)
} }
} }
private setDefaultValue(configValue, defaultValue) { private setDefaultValue(configValue, defaultValue, deepCopy) {
let needSave = false let needSave = false
for (const key of Object.keys(defaultValue)) { 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] configValue[key] = defaultValue[key]
needSave = true needSave = true
} else if (Object.prototype.toString.call(configValue[key]) == "[object Object]" } else if (Object.prototype.toString.call(configValue[key]) == "[object Object]" && deepCopy) {
&& Object.prototype.hasOwnProperty.call(defaultValue[key], '____deep_copy____')) { // 对象需要递归检测
// 对象需要递归检测 如果对象内存在 ____deep_copy____ 那就忽略设置 needSave ||= this.setDefaultValue(configValue[key], defaultValue[key], deepCopy)
needSave ||= this.setDefaultValue(configValue[key], defaultValue[key])
} }
} }
return needSave 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) 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]) let result = this.getConfigLoader(metadata.format).dump(plugin[metadata.variable])
base.save(metadata.file, result) 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 return true
} catch (error: any) { } 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) console.ex(error)
return false return false
} }

View File

@ -5,6 +5,7 @@ export const METADATA_KEY = {
tab: Symbol.for("@ccms/plugin:tab"), tab: Symbol.for("@ccms/plugin:tab"),
listener: Symbol.for("@ccms/plugin:listener"), listener: Symbol.for("@ccms/plugin:listener"),
config: Symbol.for("@ccms/plugin:config"), config: Symbol.for("@ccms/plugin:config"),
playerdata: Symbol.for("@ccms/plugin:playerdata"),
stage: { stage: {
load: Symbol.for("@ccms/plugin:stage:load"), load: Symbol.for("@ccms/plugin:stage:load"),
enable: Symbol.for("@ccms/plugin:stage:enable"), enable: Symbol.for("@ccms/plugin:stage:enable"),

View File

@ -2,7 +2,7 @@ import { plugin as pluginApi } from "@ccms/api"
import { injectable, decorate } from "@ccms/container" import { injectable, decorate } from "@ccms/container"
import { interfaces } from './interfaces' import { interfaces } from './interfaces'
import { METADATA_KEY } from './constants' 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 * 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) { function stage(stage: string) {
return (metadata: interfaces.ExecMetadata = {}) => { return (metadata: interfaces.ExecMetadata = {}) => {
return function (target: any, key: string, value: any) { return function (target: any, key: string, value: any) {

View File

@ -1,4 +1,6 @@
import './scanner/file-scanner' import './scanner/js-scanner'
import './scanner/mjs-scanner'
import './loader/ioc-loader' import './loader/ioc-loader'
import './loader/basic-loader' import './loader/basic-loader'
@ -12,5 +14,6 @@ export {
cmd as Cmd, cmd as Cmd,
tab as Tab, tab as Tab,
listener as Listener, listener as Listener,
config as Config config as Config,
playerdata as PlayerData
} from './decorators' } from './decorators'

View File

@ -59,6 +59,10 @@ export namespace interfaces {
* *
*/ */
autoMain?: boolean autoMain?: boolean
/**
*
*/
permission?: boolean | string
} }
export interface ListenerMetadata extends ExecMetadata { export interface ListenerMetadata extends ExecMetadata {
/** /**
@ -100,4 +104,38 @@ export namespace interfaces {
*/ */
file?: any 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
}
} }

View File

@ -169,6 +169,7 @@ export class PluginManagerImpl implements plugin.PluginManager {
private loadAndRequirePlugin(loadMetadata: plugin.PluginLoadMetadata) { private loadAndRequirePlugin(loadMetadata: plugin.PluginLoadMetadata) {
let startTime = Date.now() let startTime = Date.now()
let metadata = this.loadPlugin(loadMetadata.scanner.load(loadMetadata)) let metadata = this.loadPlugin(loadMetadata.scanner.load(loadMetadata))
if (!metadata) { throw new Error('load plugin metadata failed.') }
console.i18n('ms.plugin.manager.build', { console.i18n('ms.plugin.manager.build', {
name: loadMetadata.metadata.name, name: loadMetadata.metadata.name,
version: loadMetadata.metadata.version, version: loadMetadata.metadata.version,
@ -184,9 +185,10 @@ export class PluginManagerImpl implements plugin.PluginManager {
* *
* @param file java.io.File * @param file java.io.File
*/ */
loadFromFile(file: string, scanner = this.sacnnerMap.get('file')): plugin.Plugin { loadFromFile(file: string, ext: any = 'js'): plugin.Plugin {
if (!file) { throw new Error('plugin file can\'t be undefiend!') } if (!file) { throw new Error('plugin file can\'t be undefiend.') }
if (!scanner) { throw new Error('plugin scanner 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 metadata = this.loadAndRequirePlugin(scanner.read(file))
let plugin = this.buildPlugin(metadata) let plugin = this.buildPlugin(metadata)
this.load(plugin) this.load(plugin)
@ -213,7 +215,7 @@ export class PluginManagerImpl implements plugin.PluginManager {
reload(...args: any[]): void { reload(...args: any[]): void {
this.checkAndGet(args[0]).forEach((pl: plugin.Plugin) => { this.checkAndGet(args[0]).forEach((pl: plugin.Plugin) => {
this.disable(pl) 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<string, plugin.Plugin> | plugin.Plugin[] { private checkAndGet(name: string | plugin.Plugin | undefined | any): Map<string, plugin.Plugin> | 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 (name == this.instanceMap) { return this.instanceMap }
if (typeof name == 'string' && this.instanceMap.has(name)) { return [this.instanceMap.get(name)] } 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 instanceof interfaces.Plugin) { return [name as plugin.Plugin] }
if (name.description?.name) { 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() { private buildPlugins() {

View File

@ -2,7 +2,7 @@ import { plugin } from "@ccms/api"
import * as fs from '@ccms/common/dist/fs' import * as fs from '@ccms/common/dist/fs'
import { provideSingletonNamed } from "@ccms/container" import { provideSingletonNamed } from "@ccms/container"
const SCANNER_TYPE_NAME = 'file' const SCANNER_TYPE_NAME = 'js'
@provideSingletonNamed(plugin.PluginScanner, SCANNER_TYPE_NAME) @provideSingletonNamed(plugin.PluginScanner, SCANNER_TYPE_NAME)
export class JSFileScanner implements plugin.PluginScanner { export class JSFileScanner implements plugin.PluginScanner {

View File

@ -67,7 +67,13 @@ function getPluginConfigMetadata(target: any) {
) || new Map<string, interfaces.ConfigMetadata>() ) || new Map<string, interfaces.ConfigMetadata>()
return configMetadata return configMetadata
} }
function getPluginPlayerDataMetadata(target: any) {
let playerdataMetadata: Map<string, interfaces.ConfigMetadata> = Reflect.getMetadata(
METADATA_KEY.playerdata,
target.constructor
) || new Map<string, interfaces.ConfigMetadata>()
return playerdataMetadata
}
function getPluginStageMetadata(target: any, stage: string) { function getPluginStageMetadata(target: any, stage: string) {
let stageMetadata: interfaces.ExecMetadata[] = Reflect.getMetadata( let stageMetadata: interfaces.ExecMetadata[] = Reflect.getMetadata(
METADATA_KEY.stage[stage], METADATA_KEY.stage[stage],
@ -86,5 +92,6 @@ export {
getPluginTabCompleterMetadata, getPluginTabCompleterMetadata,
getPluginListenerMetadata, getPluginListenerMetadata,
getPluginConfigMetadata, getPluginConfigMetadata,
getPluginPlayerDataMetadata,
getPluginStageMetadata getPluginStageMetadata
} }