feat: optimize plugin scan & load

Signed-off-by: MiaoWoo <admin@yumc.pw>
This commit is contained in:
MiaoWoo 2020-06-23 15:51:55 +08:00
parent 18df821049
commit 4750f31b6e
13 changed files with 127 additions and 63 deletions

View File

@ -30,6 +30,44 @@ export namespace plugin {
getPlugins(): Map<string, plugin.Plugin> getPlugins(): Map<string, plugin.Plugin>
} }
export const PluginScanner = Symbol("PluginScanner") 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 type: string
/** /**
* *
* @param target * @param target
*/ */
scan(target: any): string[] scan(target: any): PluginLoadMetadata[]
/**
*
* @param target
*/
read(target: any): PluginLoadMetadata
/** /**
* *
* @param target * @param target
*/ */
load(target: any): any load(target: PluginLoadMetadata): PluginLoadMetadata
} }
export const PluginLoader = Symbol("PluginLoader") export const PluginLoader = Symbol("PluginLoader")
/** /**
@ -63,12 +106,12 @@ export namespace plugin {
* @param target * @param target
* @param result * @param result
*/ */
require(target: any, result: any): PluginMetadata require(loadMetadata: PluginLoadMetadata): PluginLoadMetadata
/** /**
* *
* @param metadata * @param metadata
*/ */
build(metadata: any): Plugin build(metadata: PluginMetadata): Plugin
/** /**
* Load * Load
* @param plugin * @param plugin
@ -133,13 +176,9 @@ export namespace plugin {
*/ */
type?: string type?: string
/** /**
* *
*/ */
scanner?: PluginScanner loadMetadata?: PluginLoadMetadata
/**
*
*/
loader?: PluginLoader
/** /**
* *
*/ */

View File

@ -15,6 +15,7 @@
}, },
"dependencies": { "dependencies": {
"@ccms/i18n": "^0.8.0", "@ccms/i18n": "^0.8.0",
"@ccms/nodejs": "^0.8.0",
"@ccms/nashorn": "^0.8.0", "@ccms/nashorn": "^0.8.0",
"core-js": "^3.6.5" "core-js": "^3.6.5"
}, },

View File

@ -1,5 +1,5 @@
/// <reference types="@ccms/nashorn" /> /// <reference types="@ccms/nashorn" />
import '@ccms/nodejs'
import i18n from '@ccms/i18n' import i18n from '@ccms/i18n'
let ployfillStartTime = new Date().getTime() let ployfillStartTime = new Date().getTime()
i18n.initialize() i18n.initialize()

View File

@ -10,9 +10,14 @@ export class BasicLoader implements plugin.PluginLoader {
constructor() { constructor() {
this.pluginRequireMap = new Map() this.pluginRequireMap = new Map()
} }
require(target: any, result: any) { require(loadMetadata: plugin.PluginLoadMetadata) {
this.pluginRequireMap.set(target.toString(), result) let metadata = loadMetadata.instance.description
return result 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) { build(metadata: plugin.PluginMetadata) {
return this.pluginRequireMap.get(metadata.source.toString()) return this.pluginRequireMap.get(metadata.source.toString())

View File

@ -18,8 +18,13 @@ export class IocLoader implements plugin.PluginLoader {
this.pluginMetadataMap = getPluginSources() this.pluginMetadataMap = getPluginSources()
} }
require(target: any, result: any) { require(loadMetadata: plugin.PluginLoadMetadata) {
return this.pluginMetadataMap.get(target.toString()) 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) { build(metadata: plugin.PluginMetadata) {

View File

@ -64,8 +64,13 @@ export class PluginManagerImpl implements plugin.PluginManager {
this.initialize() this.initialize()
for (const [, scanner] of this.sacnnerMap) { for (const [, scanner] of this.sacnnerMap) {
try { try {
scanner.scan(folder).forEach(file => { scanner.scan(folder).forEach(loadMetadata => {
this.loadPlugin(file, scanner) 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) { } catch (error) {
console.error(`plugin scanner ${scanner.type} occurred error ${error}`) console.error(`plugin scanner ${scanner.type} occurred error ${error}`)
@ -89,28 +94,28 @@ export class PluginManagerImpl implements plugin.PluginManager {
ext() ext()
this.runCatch(plugin, stage) this.runCatch(plugin, stage)
this.runCatch(plugin, `${this.serverType}${stage}`) this.runCatch(plugin, `${this.serverType}${stage}`)
plugin.description.loader[stage](plugin) plugin.description.loadMetadata.loader[stage](plugin)
} catch (ex) { } catch (ex) {
console.i18n("ms.plugin.manager.stage.exec.error", { plugin: plugin.description.name, executor: stage, error: 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 { try {
let requireInstance = scanner.load(file)
for (const [, loader] of this.loaderMap) { for (const [, loader] of this.loaderMap) {
let metadata = loader.require(file, requireInstance) if (loader.require(loadMetadata).loaded) {
if (metadata && metadata.source && metadata.name) { loadMetadata.loader = loader
metadata.loader = loader let metadata = loadMetadata.metadata
this.metadataMap.set(metadata.name, metadata) this.metadataMap.set(metadata.name, metadata)
metadata.loadMetadata = loadMetadata
return metadata return metadata
} }
} }
} catch (error) { } 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.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 { loadFromFile(file: string, scanner = this.sacnnerMap.get('file')): plugin.Plugin {
if (!file) { throw new Error('plugin file can\'t be null!') } if (!file) { throw new Error('plugin file can\'t be null!') }
if (!scanner) { throw new Error('plugin scanner can\'t be null!') } if (!scanner) { throw new Error('plugin scanner can\'t be null!') }
let metadata = this.loadPlugin(file, scanner) let metadata = this.loadPlugin(scanner.read(file))
let plugin = metadata.loader.build(metadata) let plugin = metadata.loadMetadata.loader.build(metadata)
this.load(plugin) this.load(plugin)
this.enable(plugin) this.enable(plugin)
return plugin return plugin
@ -157,7 +162,7 @@ export class PluginManagerImpl implements plugin.PluginManager {
reload(...args: any[]): void { reload(...args: any[]): void {
this.checkAndGet(args[0]).forEach((pl: plugin.Plugin) => { this.checkAndGet(args[0]).forEach((pl: plugin.Plugin) => {
this.disable(pl) this.disable(pl)
this.loadFromFile(pl.description.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加载器是否正常启用!`) console.error(`§4无法加载插件 §c${metadata.name} §4请检查 §c${metadata.type} §4加载器是否正常启用!`)
continue 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)
} }
} }

View File

@ -6,15 +6,20 @@ import { provideSingletonNamed } from "@ccms/container"
export class JSFileScanner implements plugin.PluginScanner { export class JSFileScanner implements plugin.PluginScanner {
type: string = 'file' type: string = 'file'
scan(target: any): string[] { scan(target: any): plugin.PluginLoadMetadata[] {
return this.scanFolder(fs.concat(root, target)) return this.scanFolder(fs.concat(root, target)).map((file) => this.read(file))
} }
load(file: string) { read(file: any): plugin.PluginLoadMetadata {
if (typeof file === "string") { return } return { file, type: this.type, scanner: this, loaded: false }
this.updatePlugin(file) }
load(metadata: plugin.PluginLoadMetadata): plugin.PluginLoadMetadata {
if (metadata.type !== this.type) { return }
this.updatePlugin(metadata.file)
//@ts-ignore //@ts-ignore
return require(file.toString(), { cache: false }) metadata.instance = require(metadata.file.toString(), { cache: false })
return metadata
} }
private scanFolder(folder: any): string[] { private scanFolder(folder: any): string[] {

View File

@ -1,7 +1,6 @@
/// <reference types="@ccms/types/dist/typings/bukkit" /> /// <reference types="@javatypes/bungee-api" />
/// <reference types="@ccms/types/dist/typings/sponge" /> /// <reference types="@javatypes/spigot-api" />
/// <reference types="@ccms/types/dist/typings/bungee" /> /// <reference types="@javatypes/sponge-api" />
/// <reference types="@ccms/types/dist/typings/nukkit" />
import { server } from '@ccms/api'; import { server } from '@ccms/api';
import { inject } from '@ccms/container'; import { inject } from '@ccms/container';

View File

@ -1,6 +1,6 @@
/// <reference types="@ccms/types/dist/typings/bukkit" /> /// <reference types="@javatypes/bungee-api" />
/// <reference types="@ccms/types/dist/typings/sponge" /> /// <reference types="@javatypes/spigot-api" />
/// <reference types="@ccms/types/dist/typings/bungee" /> /// <reference types="@javatypes/sponge-api" />
import { server, plugin as pluginApi, channel, constants } from '@ccms/api' import { server, plugin as pluginApi, channel, constants } from '@ccms/api'
import { inject, optional } from '@ccms/container'; import { inject, optional } from '@ccms/container';

View File

@ -1,7 +1,4 @@
/// <reference types="@ccms/nashorn" /> /// <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 as pluginApi, server, task, constants } from '@ccms/api'
import { plugin, interfaces, cmd, tab, enable, config, disable } from '@ccms/plugin' import { plugin, interfaces, cmd, tab, enable, config, disable } from '@ccms/plugin'

View File

@ -1,4 +1,4 @@
/// <reference types="@ccms/types" /> /// <reference types="@javatypes/jdk" />
import { task, server } from "@ccms/api"; import { task, server } from "@ccms/api";
import { inject } from "@ccms/container"; import { inject } from "@ccms/container";

View File

@ -1,5 +1,3 @@
/// <reference types="@ccms/types" />
import { task, server, constants } from "@ccms/api"; import { task, server, constants } from "@ccms/api";
import { inject } from "@ccms/container"; import { inject } from "@ccms/container";
import { plugin, interfaces, cmd } from "@ccms/plugin"; import { plugin, interfaces, cmd } from "@ccms/plugin";

View File

@ -4,29 +4,37 @@ import { provideSingleton, inject } from '@ccms/container'
import * as fs from '@ccms/common/dist/fs' import * as fs from '@ccms/common/dist/fs'
interface MySQLPlugin {
name: string
source: string
}
@provideSingleton(plugin.PluginScanner) @provideSingleton(plugin.PluginScanner)
export class MySQLScanner implements plugin.PluginScanner { export class MySQLScanner implements plugin.PluginScanner {
type: string = "mysql" type: string = "mysql"
private cacheDir = 'mysql-plugin-cache'
private target: string private target: string
@inject(database.DataBaseManager) @inject(database.DataBaseManager)
private databaseManager: DataBaseManager private databaseManager: DataBaseManager
scan(target: any): string[] { scan(target: any): plugin.PluginLoadMetadata[] {
this.target = target this.target = target
let plugins = this.databaseManager.getMainDatabase().query<{ name: string }>(`SELECT name FROM ${this.target} WHERE LENGTH(source) != 0 AND deleted = 0`) let plugins = this.databaseManager.getMainDatabase().query<MySQLPlugin>(`SELECT name FROM ${this.target} WHERE LENGTH(source) != 0 AND deleted = 0`)
return plugins.map(p => `mysql:${p.name}`) return plugins.map(p => this.read(p))
} }
load(target: any) { read(mysqlPlugin: MySQLPlugin): plugin.PluginLoadMetadata {
if (typeof target !== "string" || !target.startsWith('mysql:')) { return } return { name: mysqlPlugin.name, file: fs.concat(root, this.cacheDir, `${mysqlPlugin.name}.js`), type: this.type, mysqlPlugin, scanner: this }
let name = target.split("mysql:")[1] }
if (!name) { console.warn(`[PluginScanner][mysql] plugin name can't be null!`); return } load(metadata: plugin.PluginLoadMetadata) {
var plugin: any = this.databaseManager.getMainDatabase().query<{ source: string }>(`SELECT source FROM ${this.target} WHERE name = ? AND deleted = 0`, name) if (metadata.type !== this.type) { return }
if (plugin.length == 0) { console.warn(`[PluginScanner][mysql] plugin ${target} not found at mysql database...`); return } var plugin: any = this.databaseManager.getMainDatabase().query<MySQLPlugin>(`SELECT source FROM ${this.target} WHERE name = ? AND deleted = 0`, metadata.name)
let temp = fs.concat(root, 'mysql-plugin-cache', target, `${plugin[0]}.js`) if (plugin.length == 0) { throw new Error(`[MySQLScanner] plugin ${metadata.name} not found at mysql database...`) }; plugin = plugin[0]
base.save(temp, plugin[0].source) 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 //@ts-ignore
return require(temp, { cache: false }) metadata.instance = require(metadata.file, { cache: false })
return metadata
} }
} }