@@ -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 []
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -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 })
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 
 | 
			
		||||
@@ -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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -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[] {
 | 
			
		||||
 
 | 
			
		||||
@@ -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(' '))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
}
 | 
			
		||||
@@ -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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -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')
 | 
			
		||||
 
 | 
			
		||||
@@ -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 })
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
 
 | 
			
		||||
@@ -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}..."
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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"
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -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]) || [])
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -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"),
 | 
			
		||||
 
 | 
			
		||||
@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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'
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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() {
 | 
			
		||||
 
 | 
			
		||||
@@ -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 {
 | 
			
		||||
							
								
								
									
										131
									
								
								packages/plugin/src/scanner/mjs-scanner.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								packages/plugin/src/scanner/mjs-scanner.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,131 @@
 | 
			
		||||
import { plugin } from "@ccms/api"
 | 
			
		||||
import http from '@ccms/common/dist/http'
 | 
			
		||||
import { provideSingletonNamed } from "@ccms/container"
 | 
			
		||||
 | 
			
		||||
import * as fs from '@ccms/common/dist/fs'
 | 
			
		||||
 | 
			
		||||
import * as CryptoJS from "crypto-js"
 | 
			
		||||
 | 
			
		||||
const SCANNER_TYPE_NAME = 'mjs'
 | 
			
		||||
 | 
			
		||||
@provideSingletonNamed(plugin.PluginScanner, SCANNER_TYPE_NAME)
 | 
			
		||||
export class SecretJSFileScanner implements plugin.PluginScanner {
 | 
			
		||||
    type: string = SCANNER_TYPE_NAME
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        global.setGlobal('MiaoScriptPackageCenterTokenCache', {})
 | 
			
		||||
        global.setGlobal('MiaoScriptPackageCenterQQCache', {})
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    scan(target: any): plugin.PluginLoadMetadata[] {
 | 
			
		||||
        return this.scanFolder(fs.concat(root, target)).map((file) => this.read(file))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    read(file: any): plugin.PluginLoadMetadata {
 | 
			
		||||
        return { file, type: this.type, scanner: this, loaded: false }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    load(metadata: plugin.PluginLoadMetadata): plugin.PluginLoadMetadata {
 | 
			
		||||
        if (metadata.type !== this.type) { return }
 | 
			
		||||
        this.updatePlugin(metadata.file)
 | 
			
		||||
        //@ts-ignore load plugin not use cache
 | 
			
		||||
        metadata.instance = require(metadata.file.toString(), {
 | 
			
		||||
            cache: false,
 | 
			
		||||
            hook: (origin) => {
 | 
			
		||||
                let info = JSON.parse(origin)
 | 
			
		||||
                if (!info || !info.encrypt) {
 | 
			
		||||
                    return this.exportErrorPlugin(
 | 
			
		||||
                        metadata.file,
 | 
			
		||||
                        `console.console('§4无效的加密插件: §c${metadata.file}')`
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
                let qq = info.qq
 | 
			
		||||
                let encrypt = info.encrypt
 | 
			
		||||
                let token = this.decrypt(info.token, qq, info.timestamp)
 | 
			
		||||
                global.MiaoScriptPackageCenterQQCache[info.name] = qq
 | 
			
		||||
                global.MiaoScriptPackageCenterTokenCache[info.name] = token
 | 
			
		||||
                try {
 | 
			
		||||
                    let result = http.post('https://ms.yumc.pw/api/plugin/check', `{
 | 
			
		||||
                        "pid": ${info.pid},
 | 
			
		||||
                        "qq": ${qq},
 | 
			
		||||
                        "token": "${token}",
 | 
			
		||||
                        "source": true
 | 
			
		||||
                    }`)
 | 
			
		||||
                    if (result.code != 200) {
 | 
			
		||||
                        return this.exportErrorPlugin(
 | 
			
		||||
                            info.name,
 | 
			
		||||
                            `console.console('§6[§b圈云授权系统§6] §6插件: §b${info.name} §c授权效验失败: §4${result.msg}')`
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                    if (!result.data) {
 | 
			
		||||
                        return this.exportErrorPlugin(
 | 
			
		||||
                            info.name,
 | 
			
		||||
                            `console.console('§6[§b圈云授权系统§6] §6插件: §b${info.name} §c服务器返回异常数据.')`
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                    base.save(metadata.file, result.data.encrypt)
 | 
			
		||||
                    return result.data.source
 | 
			
		||||
                } catch (error) {
 | 
			
		||||
                }
 | 
			
		||||
                if (Date.now() / 1000 - info.timestamp > 7 * 24 * 60 * 60) {
 | 
			
		||||
                    return this.exportErrorPlugin(
 | 
			
		||||
                        info.name,
 | 
			
		||||
                        `console.console('§6[§b圈云授权系统§6] §6插件: §b${info.name} §c离线授权已过期.')`
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
                return this.decrypt(encrypt, qq, token)
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        return metadata
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private decrypt(encrypt, key, iv) {
 | 
			
		||||
        return CryptoJS.AES.decrypt(encrypt,
 | 
			
		||||
            CryptoJS.enc.Utf8.parse(`${key}`.padEnd(16, "\0").substring(0, 16)), {
 | 
			
		||||
            iv: CryptoJS.enc.Utf8.parse(`${iv}`.padEnd(16, "\0").substring(0, 16))
 | 
			
		||||
        }).toString(CryptoJS.enc.Utf8)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private exportErrorPlugin(name, src) {
 | 
			
		||||
        return `module.exports = {
 | 
			
		||||
            description: {
 | 
			
		||||
                name: "${name}",
 | 
			
		||||
                version: "未授权",
 | 
			
		||||
                author: "未授权",
 | 
			
		||||
                type: "basic",
 | 
			
		||||
                source: __filename,
 | 
			
		||||
                target: this
 | 
			
		||||
            },
 | 
			
		||||
            load: function () { ${src} }
 | 
			
		||||
        }`
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private scanFolder(folder: any): string[] {
 | 
			
		||||
        var files = []
 | 
			
		||||
        this.checkUpdateFolder(folder)
 | 
			
		||||
        // must check file is exist maybe is a illegal symbolic link file
 | 
			
		||||
        fs.list(folder).forEach((path: any) => {
 | 
			
		||||
            let file = path.toFile()
 | 
			
		||||
            if (file.exists() && (file.getName().endsWith(".mjs.json") || file.getName().endsWith(".mjs"))) {
 | 
			
		||||
                files.push(file)
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        return files
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private checkUpdateFolder(path: any) {
 | 
			
		||||
        var update = fs.file(path, "update")
 | 
			
		||||
        if (!update.exists()) {
 | 
			
		||||
            update.mkdirs()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private updatePlugin(file: any) {
 | 
			
		||||
        var update = fs.file(fs.file(fs.file(file).parentFile, 'update'), file.name)
 | 
			
		||||
        if (update.exists()) {
 | 
			
		||||
            console.i18n("ms.plugin.manager.build.update", { name: file.name })
 | 
			
		||||
            fs.move(update, file, true)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user