feat: optimize plugin scan & load
Signed-off-by: MiaoWoo <admin@yumc.pw>
This commit is contained in:
		@@ -30,6 +30,44 @@ export namespace plugin {
 | 
			
		||||
        getPlugins(): Map<string, plugin.Plugin>
 | 
			
		||||
    }
 | 
			
		||||
    export const PluginScanner = Symbol("PluginScanner")
 | 
			
		||||
    /**
 | 
			
		||||
     * 插件加载元信息
 | 
			
		||||
     */
 | 
			
		||||
    export interface PluginLoadMetadata {
 | 
			
		||||
        /**
 | 
			
		||||
         * 插件加载类型
 | 
			
		||||
         */
 | 
			
		||||
        type: string
 | 
			
		||||
        /**
 | 
			
		||||
         * 插件名称
 | 
			
		||||
         */
 | 
			
		||||
        name?: string
 | 
			
		||||
        /**
 | 
			
		||||
         * 插件文件
 | 
			
		||||
         */
 | 
			
		||||
        file?: string
 | 
			
		||||
        /**
 | 
			
		||||
         * 插件实例
 | 
			
		||||
         */
 | 
			
		||||
        instance?: any
 | 
			
		||||
        /**
 | 
			
		||||
         * 插件元信息
 | 
			
		||||
         */
 | 
			
		||||
        metadata?: PluginMetadata
 | 
			
		||||
        /**
 | 
			
		||||
         * 插件扫描器
 | 
			
		||||
         */
 | 
			
		||||
        scanner: PluginScanner
 | 
			
		||||
        /**
 | 
			
		||||
         * 插件加载器
 | 
			
		||||
         */
 | 
			
		||||
        loader?: PluginLoader
 | 
			
		||||
        /**
 | 
			
		||||
         * 是否已加载
 | 
			
		||||
         */
 | 
			
		||||
        loaded?: boolean
 | 
			
		||||
        [key: string]: any
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * 插件扫描器
 | 
			
		||||
     */
 | 
			
		||||
@@ -39,15 +77,20 @@ export namespace plugin {
 | 
			
		||||
         */
 | 
			
		||||
        type: string
 | 
			
		||||
        /**
 | 
			
		||||
         * 扫描插件 返回插件加载列表
 | 
			
		||||
         * 扫描插件目录 返回插件加载元信息列表
 | 
			
		||||
         * @param target 扫描目标
 | 
			
		||||
         */
 | 
			
		||||
        scan(target: any): string[]
 | 
			
		||||
        scan(target: any): PluginLoadMetadata[]
 | 
			
		||||
        /**
 | 
			
		||||
         * 读取插件 返回插件加载元信息
 | 
			
		||||
         * @param target 
 | 
			
		||||
         */
 | 
			
		||||
        read(target: any): PluginLoadMetadata
 | 
			
		||||
        /**
 | 
			
		||||
         * 加载扫描到的目标
 | 
			
		||||
         * @param target 加载目标
 | 
			
		||||
         */
 | 
			
		||||
        load(target: any): any
 | 
			
		||||
        load(target: PluginLoadMetadata): PluginLoadMetadata
 | 
			
		||||
    }
 | 
			
		||||
    export const PluginLoader = Symbol("PluginLoader")
 | 
			
		||||
    /**
 | 
			
		||||
@@ -63,12 +106,12 @@ export namespace plugin {
 | 
			
		||||
         * @param target 加载目标
 | 
			
		||||
         * @param result 扫描器加载的结果
 | 
			
		||||
         */
 | 
			
		||||
        require(target: any, result: any): PluginMetadata
 | 
			
		||||
        require(loadMetadata: PluginLoadMetadata): PluginLoadMetadata
 | 
			
		||||
        /**
 | 
			
		||||
         * 构建插件
 | 
			
		||||
         * @param metadata 插件元信息
 | 
			
		||||
         */
 | 
			
		||||
        build(metadata: any): Plugin
 | 
			
		||||
        build(metadata: PluginMetadata): Plugin
 | 
			
		||||
        /**
 | 
			
		||||
         * Load 阶段
 | 
			
		||||
         * @param plugin 插件
 | 
			
		||||
@@ -133,13 +176,9 @@ export namespace plugin {
 | 
			
		||||
         */
 | 
			
		||||
        type?: string
 | 
			
		||||
        /**
 | 
			
		||||
         * 插件扫描器
 | 
			
		||||
         * 插件加载信息
 | 
			
		||||
         */
 | 
			
		||||
        scanner?: PluginScanner
 | 
			
		||||
        /**
 | 
			
		||||
         * 插件加载器
 | 
			
		||||
         */
 | 
			
		||||
        loader?: PluginLoader
 | 
			
		||||
        loadMetadata?: PluginLoadMetadata
 | 
			
		||||
        /**
 | 
			
		||||
         * 插件本体
 | 
			
		||||
         */
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@
 | 
			
		||||
    },
 | 
			
		||||
    "dependencies": {
 | 
			
		||||
        "@ccms/i18n": "^0.8.0",
 | 
			
		||||
        "@ccms/nodejs": "^0.8.0",
 | 
			
		||||
        "@ccms/nashorn": "^0.8.0",
 | 
			
		||||
        "core-js": "^3.6.5"
 | 
			
		||||
    },
 | 
			
		||||
@@ -23,4 +24,4 @@
 | 
			
		||||
        "rimraf": "^3.0.2",
 | 
			
		||||
        "typescript": "^3.9.2"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/// <reference types="@ccms/nashorn" />
 | 
			
		||||
 | 
			
		||||
import '@ccms/nodejs'
 | 
			
		||||
import i18n from '@ccms/i18n'
 | 
			
		||||
let ployfillStartTime = new Date().getTime()
 | 
			
		||||
i18n.initialize()
 | 
			
		||||
 
 | 
			
		||||
@@ -10,9 +10,14 @@ export class BasicLoader implements plugin.PluginLoader {
 | 
			
		||||
    constructor() {
 | 
			
		||||
        this.pluginRequireMap = new Map()
 | 
			
		||||
    }
 | 
			
		||||
    require(target: any, result: any) {
 | 
			
		||||
        this.pluginRequireMap.set(target.toString(), result)
 | 
			
		||||
        return result
 | 
			
		||||
    require(loadMetadata: plugin.PluginLoadMetadata) {
 | 
			
		||||
        let metadata = loadMetadata.instance.description
 | 
			
		||||
        if (metadata && metadata.type == this.type) {
 | 
			
		||||
            loadMetadata.metadata = metadata
 | 
			
		||||
            loadMetadata.loaded = true
 | 
			
		||||
            this.pluginRequireMap.set(metadata.source.toString(), loadMetadata.instance)
 | 
			
		||||
        }
 | 
			
		||||
        return loadMetadata
 | 
			
		||||
    }
 | 
			
		||||
    build(metadata: plugin.PluginMetadata) {
 | 
			
		||||
        return this.pluginRequireMap.get(metadata.source.toString())
 | 
			
		||||
 
 | 
			
		||||
@@ -18,8 +18,13 @@ export class IocLoader implements plugin.PluginLoader {
 | 
			
		||||
        this.pluginMetadataMap = getPluginSources()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    require(target: any, result: any) {
 | 
			
		||||
        return this.pluginMetadataMap.get(target.toString())
 | 
			
		||||
    require(loadMetadata: plugin.PluginLoadMetadata) {
 | 
			
		||||
        let metadata = this.pluginMetadataMap.get(loadMetadata.file.toString())
 | 
			
		||||
        if (metadata && metadata.type == this.type) {
 | 
			
		||||
            loadMetadata.metadata = metadata
 | 
			
		||||
            loadMetadata.loaded = true
 | 
			
		||||
        }
 | 
			
		||||
        return loadMetadata
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    build(metadata: plugin.PluginMetadata) {
 | 
			
		||||
 
 | 
			
		||||
@@ -64,8 +64,13 @@ export class PluginManagerImpl implements plugin.PluginManager {
 | 
			
		||||
        this.initialize()
 | 
			
		||||
        for (const [, scanner] of this.sacnnerMap) {
 | 
			
		||||
            try {
 | 
			
		||||
                scanner.scan(folder).forEach(file => {
 | 
			
		||||
                    this.loadPlugin(file, scanner)
 | 
			
		||||
                scanner.scan(folder).forEach(loadMetadata => {
 | 
			
		||||
                    try {
 | 
			
		||||
                        this.loadPlugin(scanner.load(loadMetadata))
 | 
			
		||||
                    } catch (error) {
 | 
			
		||||
                        console.error(`plugin scanner ${scanner.type} load ${loadMetadata.name} occurred error ${error}`)
 | 
			
		||||
                        console.ex(error)
 | 
			
		||||
                    }
 | 
			
		||||
                })
 | 
			
		||||
            } catch (error) {
 | 
			
		||||
                console.error(`plugin scanner ${scanner.type} occurred error ${error}`)
 | 
			
		||||
@@ -89,28 +94,28 @@ export class PluginManagerImpl implements plugin.PluginManager {
 | 
			
		||||
            ext()
 | 
			
		||||
            this.runCatch(plugin, stage)
 | 
			
		||||
            this.runCatch(plugin, `${this.serverType}${stage}`)
 | 
			
		||||
            plugin.description.loader[stage](plugin)
 | 
			
		||||
            plugin.description.loadMetadata.loader[stage](plugin)
 | 
			
		||||
        } catch (ex) {
 | 
			
		||||
            console.i18n("ms.plugin.manager.stage.exec.error", { plugin: plugin.description.name, executor: stage, error: ex })
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private loadPlugin(file: string, scanner: plugin.PluginScanner) {
 | 
			
		||||
    private loadPlugin(loadMetadata: plugin.PluginLoadMetadata) {
 | 
			
		||||
        try {
 | 
			
		||||
            let requireInstance = scanner.load(file)
 | 
			
		||||
            for (const [, loader] of this.loaderMap) {
 | 
			
		||||
                let metadata = loader.require(file, requireInstance)
 | 
			
		||||
                if (metadata && metadata.source && metadata.name) {
 | 
			
		||||
                    metadata.loader = loader
 | 
			
		||||
                if (loader.require(loadMetadata).loaded) {
 | 
			
		||||
                    loadMetadata.loader = loader
 | 
			
		||||
                    let metadata = loadMetadata.metadata
 | 
			
		||||
                    this.metadataMap.set(metadata.name, metadata)
 | 
			
		||||
                    metadata.loadMetadata = loadMetadata
 | 
			
		||||
                    return metadata
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            console.i18n("ms.plugin.manager.initialize.error", { name: file, ex: error })
 | 
			
		||||
            console.i18n("ms.plugin.manager.initialize.error", { name: loadMetadata.file, ex: error })
 | 
			
		||||
            console.ex(error)
 | 
			
		||||
        }
 | 
			
		||||
        console.console(`§efile §b${file} §ccan't load metadata. §eskip load!`)
 | 
			
		||||
        console.console(`§6scanner: §b${loadMetadata.scanner.type} §ccan\'t load §6file §b${loadMetadata.file}. §eskip!`)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -120,8 +125,8 @@ export class PluginManagerImpl implements plugin.PluginManager {
 | 
			
		||||
    loadFromFile(file: string, scanner = this.sacnnerMap.get('file')): plugin.Plugin {
 | 
			
		||||
        if (!file) { throw new Error('plugin file can\'t be null!') }
 | 
			
		||||
        if (!scanner) { throw new Error('plugin scanner can\'t be null!') }
 | 
			
		||||
        let metadata = this.loadPlugin(file, scanner)
 | 
			
		||||
        let plugin = metadata.loader.build(metadata)
 | 
			
		||||
        let metadata = this.loadPlugin(scanner.read(file))
 | 
			
		||||
        let plugin = metadata.loadMetadata.loader.build(metadata)
 | 
			
		||||
        this.load(plugin)
 | 
			
		||||
        this.enable(plugin)
 | 
			
		||||
        return plugin
 | 
			
		||||
@@ -157,7 +162,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.source, pl.description.scanner)
 | 
			
		||||
            this.loadFromFile(pl.description.source, pl.description.loadMetadata.scanner)
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -206,7 +211,9 @@ export class PluginManagerImpl implements plugin.PluginManager {
 | 
			
		||||
                console.error(`§4无法加载插件 §c${metadata.name} §4请检查 §c${metadata.type} §4加载器是否正常启用!`)
 | 
			
		||||
                continue
 | 
			
		||||
            }
 | 
			
		||||
            (pluginInstance = this.loaderMap.get(metadata.type).build(metadata)) && this.instanceMap.set(metadata.name, pluginInstance)
 | 
			
		||||
            pluginInstance = this.loaderMap.get(metadata.type).build(metadata)
 | 
			
		||||
            if (!pluginInstance) { console.error(`§4加载器 §c${metadata.type} §4加载插件 §c${metadata.name} §4失败!`); continue }
 | 
			
		||||
            this.instanceMap.set(metadata.name, pluginInstance)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,15 +6,20 @@ import { provideSingletonNamed } from "@ccms/container"
 | 
			
		||||
export class JSFileScanner implements plugin.PluginScanner {
 | 
			
		||||
    type: string = 'file'
 | 
			
		||||
 | 
			
		||||
    scan(target: any): string[] {
 | 
			
		||||
        return this.scanFolder(fs.concat(root, target))
 | 
			
		||||
    scan(target: any): plugin.PluginLoadMetadata[] {
 | 
			
		||||
        return this.scanFolder(fs.concat(root, target)).map((file) => this.read(file))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    load(file: string) {
 | 
			
		||||
        if (typeof file === "string") { return }
 | 
			
		||||
        this.updatePlugin(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
 | 
			
		||||
        return require(file.toString(), { cache: false })
 | 
			
		||||
        metadata.instance = require(metadata.file.toString(), { cache: false })
 | 
			
		||||
        return metadata
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private scanFolder(folder: any): string[] {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
/// <reference types="@ccms/types/dist/typings/bukkit" />
 | 
			
		||||
/// <reference types="@ccms/types/dist/typings/sponge" />
 | 
			
		||||
/// <reference types="@ccms/types/dist/typings/bungee" />
 | 
			
		||||
/// <reference types="@ccms/types/dist/typings/nukkit" />
 | 
			
		||||
/// <reference types="@javatypes/bungee-api" />
 | 
			
		||||
/// <reference types="@javatypes/spigot-api" />
 | 
			
		||||
/// <reference types="@javatypes/sponge-api" />
 | 
			
		||||
 | 
			
		||||
import { server } from '@ccms/api';
 | 
			
		||||
import { inject } from '@ccms/container';
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/// <reference types="@ccms/types/dist/typings/bukkit" />
 | 
			
		||||
/// <reference types="@ccms/types/dist/typings/sponge" />
 | 
			
		||||
/// <reference types="@ccms/types/dist/typings/bungee" />
 | 
			
		||||
/// <reference types="@javatypes/bungee-api" />
 | 
			
		||||
/// <reference types="@javatypes/spigot-api" />
 | 
			
		||||
/// <reference types="@javatypes/sponge-api" />
 | 
			
		||||
 | 
			
		||||
import { server, plugin as pluginApi, channel, constants } from '@ccms/api'
 | 
			
		||||
import { inject, optional } from '@ccms/container';
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,4 @@
 | 
			
		||||
/// <reference types="@ccms/nashorn" />
 | 
			
		||||
/// <reference types="@ccms/types/dist/typings/bukkit" />
 | 
			
		||||
/// <reference types="@ccms/types/dist/typings/sponge" />
 | 
			
		||||
/// <reference types="@ccms/types/dist/typings/bungee" />
 | 
			
		||||
 | 
			
		||||
import { plugin as pluginApi, server, task, constants } from '@ccms/api'
 | 
			
		||||
import { plugin, interfaces, cmd, tab, enable, config, disable } from '@ccms/plugin'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
/// <reference types="@ccms/types" />
 | 
			
		||||
/// <reference types="@javatypes/jdk" />
 | 
			
		||||
 | 
			
		||||
import { task, server } from "@ccms/api";
 | 
			
		||||
import { inject } from "@ccms/container";
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
/// <reference types="@ccms/types" />
 | 
			
		||||
 | 
			
		||||
import { task, server, constants } from "@ccms/api";
 | 
			
		||||
import { inject } from "@ccms/container";
 | 
			
		||||
import { plugin, interfaces, cmd } from "@ccms/plugin";
 | 
			
		||||
 
 | 
			
		||||
@@ -4,29 +4,37 @@ import { provideSingleton, inject } from '@ccms/container'
 | 
			
		||||
 | 
			
		||||
import * as fs from '@ccms/common/dist/fs'
 | 
			
		||||
 | 
			
		||||
interface MySQLPlugin {
 | 
			
		||||
    name: string
 | 
			
		||||
    source: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@provideSingleton(plugin.PluginScanner)
 | 
			
		||||
export class MySQLScanner implements plugin.PluginScanner {
 | 
			
		||||
    type: string = "mysql"
 | 
			
		||||
 | 
			
		||||
    private cacheDir = 'mysql-plugin-cache'
 | 
			
		||||
    private target: string
 | 
			
		||||
 | 
			
		||||
    @inject(database.DataBaseManager)
 | 
			
		||||
    private databaseManager: DataBaseManager
 | 
			
		||||
 | 
			
		||||
    scan(target: any): string[] {
 | 
			
		||||
    scan(target: any): plugin.PluginLoadMetadata[] {
 | 
			
		||||
        this.target = target
 | 
			
		||||
        let plugins = this.databaseManager.getMainDatabase().query<{ name: string }>(`SELECT name FROM ${this.target} WHERE LENGTH(source) != 0 AND deleted = 0`)
 | 
			
		||||
        return plugins.map(p => `mysql:${p.name}`)
 | 
			
		||||
        let plugins = this.databaseManager.getMainDatabase().query<MySQLPlugin>(`SELECT name FROM ${this.target} WHERE LENGTH(source) != 0 AND deleted = 0`)
 | 
			
		||||
        return plugins.map(p => this.read(p))
 | 
			
		||||
    }
 | 
			
		||||
    load(target: any) {
 | 
			
		||||
        if (typeof target !== "string" || !target.startsWith('mysql:')) { return }
 | 
			
		||||
        let name = target.split("mysql:")[1]
 | 
			
		||||
        if (!name) { console.warn(`[PluginScanner][mysql] plugin name can't be null!`); return }
 | 
			
		||||
        var plugin: any = this.databaseManager.getMainDatabase().query<{ source: string }>(`SELECT source FROM ${this.target} WHERE name = ? AND deleted = 0`, name)
 | 
			
		||||
        if (plugin.length == 0) { console.warn(`[PluginScanner][mysql] plugin ${target} not found at mysql database...`); return }
 | 
			
		||||
        let temp = fs.concat(root, 'mysql-plugin-cache', target, `${plugin[0]}.js`)
 | 
			
		||||
        base.save(temp, plugin[0].source)
 | 
			
		||||
    read(mysqlPlugin: MySQLPlugin): plugin.PluginLoadMetadata {
 | 
			
		||||
        return { name: mysqlPlugin.name, file: fs.concat(root, this.cacheDir, `${mysqlPlugin.name}.js`), type: this.type, mysqlPlugin, scanner: this }
 | 
			
		||||
    }
 | 
			
		||||
    load(metadata: plugin.PluginLoadMetadata) {
 | 
			
		||||
        if (metadata.type !== this.type) { return }
 | 
			
		||||
        var plugin: any = this.databaseManager.getMainDatabase().query<MySQLPlugin>(`SELECT source FROM ${this.target} WHERE name = ? AND deleted = 0`, metadata.name)
 | 
			
		||||
        if (plugin.length == 0) { throw new Error(`[MySQLScanner] plugin ${metadata.name} not found at mysql database...`) }; plugin = plugin[0]
 | 
			
		||||
        if (!plugin.source) { throw new Error(`[MySQLScanner] plugin ${metadata.name} source can\'t be undefiend or empty...`) }
 | 
			
		||||
        base.save(metadata.file, plugin.source)
 | 
			
		||||
        //@ts-ignore
 | 
			
		||||
        return require(temp, { cache: false })
 | 
			
		||||
        metadata.instance = require(metadata.file, { cache: false })
 | 
			
		||||
        return metadata
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user