2020-02-26 17:58:23 +00:00
import i18n from '@ms/i18n'
2020-01-31 18:46:55 +00:00
import { plugin , server , command , event } from '@ms/api'
2020-02-26 02:15:33 +00:00
import { inject , provideSingleton , postConstruct , Container , ContainerInstance } from '@ms/container'
2019-09-07 04:23:15 +00:00
import * as fs from '@ms/common/dist/fs'
2020-02-23 16:12:32 +00:00
import { getPluginMetadatas , getPluginCommandMetadata , getPluginListenerMetadata , getPlugin , getPluginTabCompleterMetadata , getPluginConfigMetadata } from './utils'
2019-11-05 09:03:49 +00:00
import { interfaces } from './interfaces'
2020-02-24 08:24:36 +00:00
import { getConfigLoader } from './config'
2019-09-07 04:23:15 +00:00
2020-02-26 02:15:33 +00:00
@provideSingleton ( plugin . PluginManager )
2019-09-07 04:23:15 +00:00
export class PluginManagerImpl implements plugin . PluginManager {
2020-01-17 03:11:20 +00:00
@inject ( ContainerInstance )
private container : Container
2019-09-07 04:23:15 +00:00
@inject ( plugin . PluginInstance )
2019-11-05 09:03:49 +00:00
private pluginInstance : any
2020-02-24 08:24:36 +00:00
@inject ( plugin . PluginFolder )
private pluginFolder : string ;
2019-09-07 04:23:15 +00:00
@inject ( server . ServerType )
2019-11-05 09:03:49 +00:00
private serverType : string
2019-09-19 10:59:32 +00:00
@inject ( command . Command )
2019-11-05 09:03:49 +00:00
private CommandManager : command.Command
2019-09-19 10:59:32 +00:00
@inject ( event . Event )
2019-11-05 09:03:49 +00:00
private EventManager : event.Event
2019-09-07 04:23:15 +00:00
2019-11-05 09:03:49 +00:00
private pluginMap : Map < string , interfaces.Plugin >
2019-09-07 04:23:15 +00:00
@postConstruct ( )
2020-01-31 18:46:55 +00:00
initialize() {
2019-09-07 04:23:15 +00:00
if ( this . pluginInstance !== null ) {
// 如果plugin不等于null 则代表是正式环境
2020-02-26 17:58:23 +00:00
console . i18n ( 'ms.plugin.initialize' , { plugin : this.pluginInstance } )
2019-11-05 09:03:49 +00:00
this . pluginMap = new Map ( )
2020-02-26 17:58:23 +00:00
console . i18n ( 'ms.plugin.event.map' , { count : this.EventManager.mapEventName ( ) . toFixed ( 0 ) , type : this . serverType } ) ;
2019-09-07 04:23:15 +00:00
}
}
scan ( folder : string ) : void {
2019-11-05 09:03:49 +00:00
var plugin = fs . file ( root , folder )
2019-09-07 04:23:15 +00:00
var files = [ ]
// load common plugin
2020-02-26 17:58:23 +00:00
. concat ( this . scanFolder ( plugin ) )
2019-09-07 04:23:15 +00:00
// load space plugin
2020-02-26 17:58:23 +00:00
. concat ( this . scanFolder ( fs . file ( plugin , this . serverType ) ) )
2019-11-05 09:03:49 +00:00
this . loadPlugins ( files )
2019-09-07 04:23:15 +00:00
}
2020-01-17 03:11:20 +00:00
build ( ) : void {
this . buildPlugins ( )
2019-09-07 04:23:15 +00:00
}
2020-01-31 18:46:55 +00:00
private logStage ( plugin : interfaces.Plugin , stage : string ) {
console . log ( ` [ ${ plugin . description . name } ] ${ stage } ${ plugin . description . name } version ${ plugin . description . version } by ${ plugin . description . author || 'Unknow' } ` )
}
2019-09-19 10:59:32 +00:00
load ( . . . args : any [ ] ) : void {
2020-01-31 18:46:55 +00:00
this . checkAndGet ( args [ 0 ] ) . forEach ( ( plugin : interfaces.Plugin ) = > {
2020-02-26 17:58:23 +00:00
this . logStage ( plugin , i18n . translate ( "ms.plugin.stage.load" ) )
2020-02-24 08:24:36 +00:00
this . loadConfig ( plugin )
2020-01-31 18:46:55 +00:00
this . runCatch ( plugin , 'load' )
this . runCatch ( plugin , ` ${ this . serverType } load ` )
2019-11-05 09:03:49 +00:00
} )
2019-09-07 04:23:15 +00:00
}
2019-09-19 10:59:32 +00:00
enable ( . . . args : any [ ] ) : void {
2020-01-31 18:46:55 +00:00
this . checkAndGet ( args [ 0 ] ) . forEach ( ( plugin : interfaces.Plugin ) = > {
2020-02-26 17:58:23 +00:00
this . logStage ( plugin , i18n . translate ( "ms.plugin.stage.enable" ) )
2019-09-22 10:00:18 +00:00
this . runCatch ( plugin , 'enable' )
2019-11-05 09:03:49 +00:00
this . runCatch ( plugin , ` ${ this . serverType } enable ` )
this . registryCommand ( plugin )
this . registryListener ( plugin )
} )
2019-09-19 10:59:32 +00:00
}
disable ( . . . args : any [ ] ) : void {
2020-01-31 18:46:55 +00:00
this . checkAndGet ( args [ 0 ] ) . forEach ( ( plugin : interfaces.Plugin ) = > {
2020-02-26 17:58:23 +00:00
this . logStage ( plugin , i18n . translate ( "ms.plugin.stage.disable" ) )
2020-01-31 18:46:55 +00:00
this . runCatch ( plugin , 'disable' )
this . runCatch ( plugin , ` ${ this . serverType } disable ` )
this . unregistryCommand ( plugin )
this . unregistryListener ( plugin )
2019-11-05 09:03:49 +00:00
} )
2019-09-19 10:59:32 +00:00
}
reload ( . . . args : any [ ] ) : void {
2019-09-21 07:05:37 +00:00
this . checkAndGet ( args [ 0 ] ) . forEach ( ( pl : interfaces.Plugin ) = > {
2019-11-05 09:03:49 +00:00
this . disable ( pl )
this . loadPlugin ( pl . description . source )
pl = this . buildPlugin ( getPlugin ( pl . description . name ) )
this . load ( pl )
this . enable ( pl )
2019-09-19 10:59:32 +00:00
} )
2019-09-07 04:23:15 +00:00
}
2019-09-22 10:00:18 +00:00
getPlugins() {
2019-11-05 09:03:49 +00:00
return this . pluginMap
2019-09-22 10:00:18 +00:00
}
2019-09-07 04:23:15 +00:00
private runCatch ( pl : any , func : string ) {
try {
2019-11-05 09:03:49 +00:00
if ( pl [ func ] ) pl [ func ] . call ( pl )
2019-09-07 04:23:15 +00:00
} catch ( ex ) {
2020-02-25 15:15:39 +00:00
console . console ( ` §6Plugin §b ${ pl . description . name } §6exec §d ${ func } §6function error §4 ${ ex } ` )
2019-11-05 09:03:49 +00:00
console . ex ( ex )
2019-09-07 04:23:15 +00:00
}
}
2019-09-19 10:59:32 +00:00
private checkAndGet ( name : string | interfaces . Plugin | undefined ) : Map < string , interfaces.Plugin > | interfaces . Plugin [ ] {
2019-11-05 09:03:49 +00:00
if ( name == undefined ) { return this . pluginMap }
if ( typeof name == 'string' && this . pluginMap . has ( name ) ) { return [ this . pluginMap . get ( name ) ] }
if ( name instanceof interfaces . Plugin ) { return [ name as interfaces . Plugin ] }
throw new Error ( ` Plugin ${ JSON . stringify ( name ) } not exist! ` )
2019-09-19 10:59:32 +00:00
}
2020-02-26 17:58:23 +00:00
private scanFolder ( folder : any ) : string [ ] {
2019-11-05 09:03:49 +00:00
var files = [ ]
2020-02-26 17:58:23 +00:00
console . i18n ( 'ms.plugin.manager.scan' , { folder } )
this . checkUpdateFolder ( folder )
2020-02-23 16:12:32 +00:00
// must check file is exist maybe is a illegal symbolic link file
2020-02-26 17:58:23 +00:00
fs . list ( folder ) . forEach ( ( file : any ) = > file . toFile ( ) . exists ( ) ? files . push ( file . toFile ( ) ) : void 0 )
2019-11-05 09:03:49 +00:00
return files
2019-09-07 04:23:15 +00:00
}
/ * *
* 更 新 插 件
* @param path
* /
2020-01-15 08:48:18 +00:00
private checkUpdateFolder ( path : any ) {
2019-11-05 09:03:49 +00:00
var update = fs . file ( path , "update" )
2019-09-07 04:23:15 +00:00
if ( ! update . exists ( ) ) {
2019-11-05 09:03:49 +00:00
update . mkdirs ( )
2019-09-07 04:23:15 +00:00
}
}
private loadPlugins ( files : any [ ] ) : void {
2019-11-05 09:03:49 +00:00
this . loadJsPlugins ( files )
2019-09-07 04:23:15 +00:00
}
/ * *
* JS类型插件预加载
* /
private loadJsPlugins ( files : any [ ] ) {
2019-09-19 10:59:32 +00:00
files . filter ( file = > file . name . endsWith ( ".js" ) ) . forEach ( file = > this . loadPlugin ( file ) )
2019-09-07 04:23:15 +00:00
}
private loadPlugin ( file : any ) {
2019-09-19 10:59:32 +00:00
try {
2019-11-05 09:03:49 +00:00
this . updatePlugin ( file )
this . createPlugin ( file )
2019-09-19 10:59:32 +00:00
} catch ( ex ) {
2020-02-25 15:15:39 +00:00
console . console ( ` §6Plugin §b ${ file . name } §6initialize error §4 ${ ex . message } ` )
2019-11-05 09:03:49 +00:00
console . ex ( ex )
2019-09-19 10:59:32 +00:00
}
2019-09-07 04:23:15 +00:00
}
private updatePlugin ( file : any ) {
2019-11-05 09:03:49 +00:00
var update = fs . file ( fs . file ( file . parentFile , 'update' ) , file . name )
2019-09-07 04:23:15 +00:00
if ( update . exists ( ) ) {
2019-11-05 09:03:49 +00:00
console . info ( ` Auto Update Plugin ${ file . name } ... ` )
fs . move ( update , file , true )
2019-09-07 04:23:15 +00:00
}
}
2020-01-17 09:45:13 +00:00
private checkServers ( servers : string [ ] ) {
if ( ! servers ) { return true }
return servers ? . indexOf ( this . serverType ) != - 1 && servers ? . indexOf ( ` ! ${ this . serverType } ` ) == - 1
}
2020-01-17 03:11:20 +00:00
private createPlugin ( file : string ) {
2019-09-07 04:23:15 +00:00
//@ts-ignore
2020-01-17 09:45:13 +00:00
require ( file , { cache : false } )
2019-09-07 04:23:15 +00:00
}
2020-01-17 03:11:20 +00:00
private buildPlugins() {
2019-11-05 09:03:49 +00:00
let pluginMetadatas = getPluginMetadatas ( )
2020-01-17 09:45:13 +00:00
for ( const [ _ , metadata ] of pluginMetadatas ) {
if ( ! this . checkServers ( metadata . servers ) ) { continue }
2019-11-05 09:03:49 +00:00
this . buildPlugin ( metadata )
2020-01-17 09:45:13 +00:00
}
2019-09-19 10:59:32 +00:00
}
private buildPlugin ( metadata : interfaces.PluginMetadata ) {
2019-11-05 09:03:49 +00:00
this . bindPlugin ( metadata )
2020-01-17 03:11:20 +00:00
let pluginInstance = this . container . getNamed < interfaces.Plugin > ( plugin . Plugin , metadata . name )
2019-11-05 09:03:49 +00:00
if ( ! ( pluginInstance instanceof interfaces . Plugin ) ) {
console . console ( ` §4found error plugin §b ${ metadata . source } §4it's not extends interfaces.Plugin, the plugin will be ignore! ` )
return
}
this . pluginMap . set ( metadata . name , pluginInstance )
return pluginInstance
}
private bindPlugin ( metadata : interfaces.PluginMetadata ) {
2019-09-19 10:59:32 +00:00
try {
2020-01-17 03:11:20 +00:00
let pluginInstance = this . container . getNamed < interfaces.Plugin > ( plugin . Plugin , metadata . name )
2019-09-19 10:59:32 +00:00
if ( pluginInstance . description . source + '' !== metadata . source + '' ) {
2019-11-05 09:03:49 +00:00
console . console ( ` §4found duplicate plugin §b ${ pluginInstance . description . source } §4and §b ${ metadata . source } §4. the first plugin will be ignore! ` )
2019-09-19 10:59:32 +00:00
}
2020-01-17 03:11:20 +00:00
this . container . rebind ( plugin . Plugin ) . to ( metadata . target ) . inSingletonScope ( ) . whenTargetNamed ( metadata . name )
2019-09-19 10:59:32 +00:00
} catch {
2020-01-17 03:11:20 +00:00
this . container . bind ( plugin . Plugin ) . to ( metadata . target ) . inSingletonScope ( ) . whenTargetNamed ( metadata . name )
2019-09-19 10:59:32 +00:00
}
}
2020-02-24 08:24:36 +00:00
private loadConfig ( plugin : interfaces.Plugin ) {
let configs = getPluginConfigMetadata ( plugin ) ;
for ( let [ _ , config ] of configs ) {
let configFile = fs . concat ( root , this . pluginFolder , plugin . description . name , config . name + '.' + config . format )
console . log ( configFile )
let configFactory = getConfigLoader ( config . format ) ;
if ( ! fs . exists ( configFile ) ) {
base . save ( configFile , configFactory . dump ( plugin [ config . variable ] ) )
console . log ( ` [ ${ plugin . description . name } ] config ${ config . name } . ${ config . format } don't exists auto create from default variable... ` )
} else {
plugin [ config . variable ] = configFactory . load ( base . read ( configFile ) ) ;
}
}
2020-02-23 16:12:32 +00:00
}
2019-09-19 10:59:32 +00:00
private registryCommand ( pluginInstance : interfaces.Plugin ) {
2019-11-05 09:03:49 +00:00
let cmds = getPluginCommandMetadata ( pluginInstance )
let tabs = getPluginTabCompleterMetadata ( pluginInstance )
2020-01-17 03:11:20 +00:00
for ( const [ _ , cmd ] of cmds ) {
2019-11-05 09:03:49 +00:00
let tab = tabs . get ( cmd . name )
2020-01-17 09:45:13 +00:00
if ( ! this . checkServers ( cmd . servers ) ) { continue }
2019-09-19 10:59:32 +00:00
this . CommandManager . on ( pluginInstance , cmd . name , {
cmd : pluginInstance [ cmd . executor ] . bind ( pluginInstance ) ,
tab : tab ? pluginInstance [ tab . executor ] . bind ( pluginInstance ) : undefined
2019-11-05 09:03:49 +00:00
} )
2020-01-17 03:11:20 +00:00
}
2019-09-19 10:59:32 +00:00
}
private registryListener ( pluginInstance : interfaces.Plugin ) {
let events = getPluginListenerMetadata ( pluginInstance )
for ( const event of events ) {
2019-09-21 07:05:37 +00:00
// ignore space listener
2020-01-17 09:45:13 +00:00
if ( ! this . checkServers ( event . servers ) ) { continue }
2019-09-19 10:59:32 +00:00
// here must bind this to pluginInstance
2019-11-05 09:03:49 +00:00
this . EventManager . listen ( pluginInstance , event . name , pluginInstance [ event . executor ] . bind ( pluginInstance ) )
2019-09-07 04:23:15 +00:00
}
}
2019-09-22 10:00:18 +00:00
private unregistryCommand ( pluginInstance : interfaces.Plugin ) {
2019-11-05 09:03:49 +00:00
let cmds = getPluginCommandMetadata ( pluginInstance )
2019-09-22 10:00:18 +00:00
cmds . forEach ( cmd = > {
2019-11-05 09:03:49 +00:00
this . CommandManager . off ( pluginInstance , cmd . name )
2019-09-22 10:00:18 +00:00
} )
}
private unregistryListener ( pluginInstance : interfaces.Plugin ) {
2019-11-05 09:03:49 +00:00
this . EventManager . disable ( pluginInstance )
2019-09-22 10:00:18 +00:00
}
2019-09-07 04:23:15 +00:00
}