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) {
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 []
}
}

View File

@ -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 })
}
}

View File

@ -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 {

View File

@ -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()

View File

@ -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)
}

View File

@ -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[] {

View File

@ -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(' '))
}
}

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -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 {

View File

@ -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

View File

@ -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 {

View File

@ -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')

View File

@ -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>(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 })

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() {
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()

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.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}..."

View File

@ -50,6 +50,7 @@ declare global {
debug: boolean
level: string
ScriptEngineVersion: string
ScriptSlowExecuteTime: number
ScriptEngineStartTime: number
setGlobal: (key: string, value: any, config?: PropertyDescriptor & ThisType<any>) => 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

View File

@ -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"
}

View File

@ -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]) || [])
}
}

View File

@ -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
}

View File

@ -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"),

View File

@ -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) {

View File

@ -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'

View File

@ -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
}
}

View File

@ -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<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 (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() {

View File

@ -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 {

View File

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