feat: optimize framework & support depends check

Signed-off-by: MiaoWoo <admin@yumc.pw>
This commit is contained in:
MiaoWoo 2020-10-13 09:24:58 +08:00
parent 47c85f70eb
commit e02f673c82
16 changed files with 173 additions and 128 deletions

View File

@ -1,6 +1,6 @@
import i18n from '@ccms/i18n' import i18n from '@ccms/i18n'
import { injectable } from "@ccms/container" import { injectable } from "@ccms/container"
import { plugin } from './interfaces' import { plugin } from './plugin'
export namespace command { export namespace command {
@injectable() @injectable()

View File

@ -1,4 +1,14 @@
export namespace constants { export namespace constants {
export namespace ServiceIdentifier {
/**
* Runtime Server NettyPipeline
*/
export const NettyPipeline = Symbol("NettyPipeline")
/**
* Runtime Server RootLogger
*/
export const RootLogger = Symbol("RootLogger")
}
export namespace Reflect { export namespace Reflect {
export const Method = { export const Method = {
getServerConnection: [/*spigot 1.8.8*/'aq',/*spigot 1.12.2*/ 'an', /*spigot 1.14.4+*/'getServerConnection', /*catserver 1.12.2*/'func_147137_ag'] getServerConnection: [/*spigot 1.8.8*/'aq',/*spigot 1.12.2*/ 'an', /*spigot 1.14.4+*/'getServerConnection', /*catserver 1.12.2*/'func_147137_ag']

View File

@ -1,11 +1,15 @@
import "@ccms/nashorn" import "@ccms/nashorn"
export * from './web'
export * from './amqp'
export * from './chat' export * from './chat'
export * from './task' export * from './task'
export * from './event' export * from './event'
export * from './proxy' export * from './proxy'
export * from './plugin'
export * from './server'
export * from './console' export * from './console'
export * from './channel' export * from './channel'
export * from './command' export * from './command'
export * from './database'
export * from './constants' export * from './constants'
export * from './interfaces'

View File

@ -1,5 +0,0 @@
export * from './web'
export * from './amqp'
export * from './plugin'
export * from './server'
export * from './database'

View File

@ -1,6 +0,0 @@
export interface NativePluginManager {
load(name: string): boolean;
unload(name: string): boolean;
reload(name: string): boolean;
delete(name: string): boolean;
}

View File

@ -162,6 +162,14 @@ export namespace plugin {
* Unknow * Unknow
*/ */
author?: string | string[] author?: string | string[]
/**
*
*/
depends?: string[]
/**
*
*/
nativeDepends?: string[]
/** /**
* __filename * __filename
*/ */

View File

@ -1,10 +1,7 @@
import * as reflect from '@ccms/common/dist/reflect' import * as reflect from '@ccms/common/dist/reflect'
import { injectable, inject } from '@ccms/container' import { injectable, Autowired, ContainerInstance, Container, postConstruct } from '@ccms/container'
import { NativePluginManager } from './native_plugin' import { constants } from './constants'
import { constants } from '../../constants'
export { NativePluginManager } from './native_plugin'
export namespace server { export namespace server {
/** /**
@ -19,6 +16,24 @@ export namespace server {
* Runtime Server Instance * Runtime Server Instance
*/ */
export const ServerInstance = Symbol("ServerInstance") export const ServerInstance = Symbol("ServerInstance")
@injectable()
export abstract class NativePluginManager {
has(name: string): boolean {
return true
}
load(name: string): boolean {
throw new Error("Method not implemented.")
}
unload(name: string): boolean {
throw new Error("Method not implemented.")
}
reload(name: string): boolean {
throw new Error("Method not implemented.")
}
delete(name: string): boolean {
throw new Error("Method not implemented.")
}
}
/** /**
* MiaoScript Server * MiaoScript Server
*/ */
@ -48,9 +63,6 @@ export namespace server {
getPluginsFolder(): string { getPluginsFolder(): string {
throw new Error("Method not implemented.") throw new Error("Method not implemented.")
} }
getNativePluginManager(): NativePluginManager {
throw new Error("Method not implemented.")
}
getDedicatedServer?(): any { getDedicatedServer?(): any {
throw new Error("Method not implemented.") throw new Error("Method not implemented.")
} }
@ -63,8 +75,9 @@ export namespace server {
} }
@injectable() @injectable()
export class ServerChecker { export class ServerChecker {
@inject(ServerType) @Autowired(ServerType)
private serverType: string private serverType: string
check(servers: string[]) { check(servers: string[]) {
// Not set servers -> allow // Not set servers -> allow
if (!servers || !servers.length) return true if (!servers || !servers.length) return true
@ -80,14 +93,17 @@ export namespace server {
} }
@injectable() @injectable()
export abstract class ReflectServer extends server.Server { export abstract class ReflectServer extends server.Server {
@Autowired(ContainerInstance)
private container: Container
protected pipeline: any protected pipeline: any
protected rootLogger: any protected rootLogger: any
constructor() { constructor() {
super() super()
this.reflect()
} }
@postConstruct()
protected reflect() { protected reflect() {
try { try {
let consoleServer = this.getDedicatedServer() let consoleServer = this.getDedicatedServer()
@ -107,7 +123,11 @@ export namespace server {
if (connection.class.name.indexOf('ServerConnection') !== -1 if (connection.class.name.indexOf('ServerConnection') !== -1
|| connection.class.name.indexOf('NetworkSystem') !== -1) { break } || connection.class.name.indexOf('NetworkSystem') !== -1) { break }
connection = undefined connection = undefined
} catch (error) { } } catch (error) {
if (global.debug) {
console.ex(error)
}
}
} }
if (!connection) { console.error("Can't found ServerConnection!"); return } if (!connection) { console.error("Can't found ServerConnection!"); return }
for (const field of constants.Reflect.Field.listeningChannels) { for (const field of constants.Reflect.Field.listeningChannels) {
@ -115,16 +135,30 @@ export namespace server {
promise = reflect.on(connection).get(field).get().get(0) promise = reflect.on(connection).get(field).get().get(0)
if (promise.class.name.indexOf('Promise') !== -1) { break } if (promise.class.name.indexOf('Promise') !== -1) { break }
promise = undefined promise = undefined
} catch (error) { } } catch (error) {
if (global.debug) {
console.ex(error)
}
}
} }
if (!promise) { console.error("Can't found listeningChannels!"); return } if (!promise) { console.error("Can't found listeningChannels!"); return }
this.pipeline = reflect.on(promise).get('channel').get().pipeline() this.pipeline = reflect.on(promise).get('channel').get().pipeline()
this.container.bind(constants.ServiceIdentifier.NettyPipeline).toConstantValue(this.pipeline)
} }
protected reflectRootLogger(consoleServer: any) { protected reflectRootLogger(consoleServer: any) {
try { try {
this.rootLogger = reflect.on(consoleServer).get('LOGGER').get().parent this.rootLogger = reflect.on(consoleServer).get('LOGGER').get().parent
} catch (error) { } catch (error) {
try { this.rootLogger = reflect.on(consoleServer).get(0).get().parent } catch (error) { } if (global.debug) {
console.ex(error)
}
try {
this.rootLogger = reflect.on(consoleServer).get(0).get().parent
} catch (error) {
if (global.debug) {
console.ex(error)
}
}
} }
if (this.rootLogger && this.rootLogger.class.name.indexOf('Logger') === -1) { if (this.rootLogger && this.rootLogger.class.name.indexOf('Logger') === -1) {
console.error('Error Logger Class: ' + this.rootLogger.class.name) console.error('Error Logger Class: ' + this.rootLogger.class.name)
@ -135,6 +169,7 @@ export namespace server {
this.rootLogger = this.rootLogger.parent this.rootLogger = this.rootLogger.parent
} }
if (!this.rootLogger) { console.error("Can't found rootLogger!") } if (!this.rootLogger) { console.error("Can't found rootLogger!") }
this.container.bind(constants.ServiceIdentifier.RootLogger).toConstantValue(this.rootLogger)
} }
} }
} }

View File

@ -1,14 +1,15 @@
/// <reference types="@javatypes/jdk" /> /// <reference types="@javatypes/jdk" />
const JavaClass = Java.type('java.lang.Class')
const JavaObject = Java.type('java.lang.Object')
const NoSuchFieldException = Java.type('java.lang.NoSuchFieldException')
const fieldCache = new Map<string, java.lang.reflect.Field>()
const methodCache = new Map<string, java.lang.reflect.Method>()
/** /**
* *
* Created by MiaoWoo on 2017/2/9 0009. * Created by MiaoWoo on 2017/2/9 0009.
*/ */
const JavaClass = Java.type('java.lang.Class')
const JavaObject = Java.type('java.lang.Object')
const NoSuchFieldException = Java.type('java.lang.NoSuchFieldException')
const methodCache = []
class Reflect { class Reflect {
private obj: java.lang.Object private obj: java.lang.Object
private class: java.lang.Class<any> private class: java.lang.Class<any>
@ -34,15 +35,26 @@ class Reflect {
return Java.from(declaredMethods(this.class)) return Java.from(declaredMethods(this.class))
} }
field(name: string | java.lang.String): Reflect { field(nameOrIndex: string | java.lang.String | number, declared = false): java.lang.reflect.Field {
try { if (nameOrIndex == undefined || nameOrIndex == null) throw new Error(`reflect field name can't be ${nameOrIndex} from ${this.class.getName()}!`)
// Try getting a public field let key = this.class.getName() + ':' + nameOrIndex + ':' + declared
let field = this.class.getField(name) if (fieldCache.has(key)) {
return on(field.get(this.obj)) return fieldCache.get(key)
} catch (ex) {
// Try again, getting a non-public field
return on(accessible(declaredField(this.class, name)).get(this.obj))
} }
let field = null
if (typeof nameOrIndex == "number") {
field = this.fields(declared)[nameOrIndex]
} else {
try {
// Try getting a public field
field = this.class.getField(nameOrIndex)
} catch (ex) {
// Try again, getting a non-public field
field = declaredField(this.class, nameOrIndex)
}
}
if (!field) throw new Error(`can't reflect field ${typeof nameOrIndex == "number" ? 'index' : 'name'} ${nameOrIndex} from ${this.class.getName()}!`)
return accessible(field)
} }
fields(declared = false): java.lang.reflect.Field[] { fields(declared = false): java.lang.reflect.Field[] {
@ -50,7 +62,7 @@ class Reflect {
} }
values(declared = false) { values(declared = false) {
return this.fields(declared).reduce((cache, field) => { return cache[field.getName()] = this.field(field.getName()).get() }, {}) as any return this.fields(declared).reduce((cache, field) => { return cache[field.getName()] = this.get(field.getName()).get() }, {}) as any
} }
call(...args: any[]): Reflect { call(...args: any[]): Reflect {
@ -64,17 +76,12 @@ class Reflect {
get(index: number, declared?: boolean): Reflect get(index: number, declared?: boolean): Reflect
get(prop: string): Reflect get(prop: string): Reflect
get(param?: string | number, declared: boolean = true): Reflect | any { get(param?: string | number, declared: boolean = true): Reflect | any {
if (param == undefined || param == null) return this.obj if (arguments.length === 0) return this.obj
if (typeof param == "number") { return on(this.field(param, declared).get(this.obj))
return on(accessible(this.fields(declared)[param]).get(this.obj))
}
if (typeof param == "string") {
return this.field(param)
}
} }
set(name: any, value: any): Reflect { set(param: string | number, value: any, declared: boolean = true): Reflect {
accessible(declaredField(this.class, name)).set(this.obj, value) this.field(param, declared).set(this.obj, value)
return this return this
} }
@ -136,64 +143,43 @@ function declaredField(clazz: java.lang.Class<any>, name: string | java.lang.Str
return field return field
} }
function declaredMethod(clazz: java.lang.Class<any>, name: string, ...clazzs: java.lang.Class<any>[]): java.lang.reflect.Method { function declaredMethod(clazz: java.lang.Class<any>, nameOrIndex: string | number, ...clazzs: java.lang.Class<any>[]): java.lang.reflect.Method {
let key = clazz.getName() + '.' + name + ':' + (clazzs || []).join(':') let key = clazz.getName() + '.' + nameOrIndex + ':' + (clazzs || []).map(c => c.getName()).join(':')
if (!methodCache[key]) { if (methodCache.has(key)) { return methodCache.get(key) }
if (typeof nameOrIndex === "number") {
methodCache.set(key, declaredMethods(clazz)[nameOrIndex])
} else {
try { try {
// @ts-ignore methodCache.set(key, clazz.getMethod(nameOrIndex, clazzs as any))
methodCache[key] = clazz.getMethod(name, clazzs)
} catch (ex) { } catch (ex) {
try { try {
methodCache[key] = clazz.getDeclaredMethod(name, clazzs as any) methodCache.set(key, clazz.getDeclaredMethod(nameOrIndex, clazzs as any))
} catch (ex) { } catch (ex) {
for (const m of Java.from(declaredMethods(clazz))) { for (const m of Java.from(declaredMethods(clazz))) {
if (m.getName() == name) { if (m.getName() == nameOrIndex) {
methodCache[key] = m methodCache.set(key, m)
break break
} }
} }
} }
} }
} }
return methodCache[key] if (!methodCache.has(key)) throw new Error(`can't reflect method ${typeof nameOrIndex == "number" ? 'index' : 'name'} ${nameOrIndex} from ${clazz.getName()}!`)
return methodCache.get(key)
} }
function declaredMethods(clazz: java.lang.Class<any>) { function declaredMethods(clazz: java.lang.Class<any>) {
return clazz.getDeclaredMethods() return clazz.getDeclaredMethods()
} }
let classMethodsCache: any[] = []
function mapToObject(javaObj) { function mapToObject(javaObj) {
if (!javaObj || !javaObj.class) { throw new TypeError(`参数 ${javaObj} 不是一个Java对象!`) } if (!Java.isJavaObject(javaObj)) { throw new TypeError(`参数 ${javaObj} 不是一个Java对象!`) }
let target = {} let target = Proxy.newProxy(javaObj, {
getJavaObjectMethods(javaObj).forEach(t => mapMethod(target, javaObj, t)) apply: (target, name, args) => { return args ? javaObj[name](args) : javaObj[name]() }
})
return target return target
} }
function getJavaObjectMethods(javaObj) {
let className = javaObj.class.name
if (!classMethodsCache[className]) {
let names: any[] = []
let methods = javaObj.class.methods
for (let i in methods) {
names.push(methods[i].name)
}
classMethodsCache[className] = names
}
return classMethodsCache[className]
}
function mapMethod(target, source, name) {
target[name] = function __SimpleDynamicMethod__(...args) {
if (args.length > 0) {
return source[name](args)
} else {
return source[name]()
}
}
}
function on(obj) { function on(obj) {
// if (!obj || !obj.class) { throw new TypeError(`参数 ${obj} 不是一个Java对象!`) } // if (!obj || !obj.class) { throw new TypeError(`参数 ${obj} 不是一个Java对象!`) }
return new Reflect(obj) return new Reflect(obj)

View File

@ -99,6 +99,7 @@ function initialize() {
console.i18n("ms.core.initialize.detect", { scope: global.scope, type }) console.i18n("ms.core.initialize.detect", { scope: global.scope, type })
container.bind(server.ServerType).toConstantValue(type) container.bind(server.ServerType).toConstantValue(type)
container.bind(server.ServerChecker).toSelf().inSingletonScope() container.bind(server.ServerChecker).toSelf().inSingletonScope()
container.bind(server.NativePluginManager).toSelf().inSingletonScope()
console.i18n("ms.core.package.initialize", { scope: global.scope, type }) console.i18n("ms.core.package.initialize", { scope: global.scope, type })
require(`${global.scope}/${type}`).default(container) require(`${global.scope}/${type}`).default(container)
require(`${global.scope}/plugin`) require(`${global.scope}/plugin`)

View File

@ -81,8 +81,18 @@ declare global {
i18n(name: string, ...params: any[]): void i18n(name: string, ...params: any[]): void
} }
interface ProxyConstructor { interface ProxyConstructor {
newProxy<T extends object>(target: T, handler: ProxyHandler<T>): T newProxy<T extends object>(target: T, handler: ProxyHandle<T>): T
} }
} }
export interface ProxyHandle<T = any> extends ProxyHandler<any> {
setPrototypeOf?(target: T, v: any): any
// get: (target: any, name: string) => any
// set: (target: any, name: string, value: any) => boolean
// construct: (target: any, ...args: any[]) => any
// has: (target: any, name: string) => boolean
// ownKeys: (target: any) => string[]
values?: (target: T) => any[]
// call: (target: any, name: string, ...args: any[]) => any
// deleteProperty: (target: any, name: string) => boolean
}
export { } export { }

View File

@ -1,17 +1,9 @@
import { ProxyHandle } from '@ccms/nashorn'
// Nashorn JSAdapter See https://wiki.openjdk.java.net/display/Nashorn/Nashorn+extensions#Nashornextensions-JSAdapterconstructor // Nashorn JSAdapter See https://wiki.openjdk.java.net/display/Nashorn/Nashorn+extensions#Nashornextensions-JSAdapterconstructor
let createProxy = eval(` let createProxy = eval(`
function(handle){ return new JSAdapter(handle) } function(handle){ return new JSAdapter(handle) }
`) `)
export interface ProxyHandle extends ProxyHandler<any> {
// get: (target: any, name: string) => any
// set: (target: any, name: string, value: any) => boolean
// construct: (target: any, ...args: any[]) => any
// has: (target: any, name: string) => boolean
// ownKeys: (target: any) => string[]
values: (target: any) => any[];
// call: (target: any, name: string, ...args: any[]) => any
// deleteProperty: (target: any, name: string) => boolean
}
export class Proxy { export class Proxy {
static newProxy(target: any, handle: Partial<ProxyHandle>): any { static newProxy(target: any, handle: Partial<ProxyHandle>): any {
return new Proxy(target, handle) return new Proxy(target, handle)

View File

@ -1,6 +1,6 @@
import i18n from '@ccms/i18n' import i18n from '@ccms/i18n'
import { plugin, server, event } from '@ccms/api' import { plugin, server } from '@ccms/api'
import { inject, provideSingleton, Container, ContainerInstance, Autowired } from '@ccms/container' import { provideSingleton, Container, ContainerInstance, Autowired } from '@ccms/container'
import './config' import './config'
import { interfaces } from './interfaces' import { interfaces } from './interfaces'
@ -13,15 +13,17 @@ const Thread = Java.type('java.lang.Thread')
@provideSingleton(plugin.PluginManager) @provideSingleton(plugin.PluginManager)
export class PluginManagerImpl implements plugin.PluginManager { export class PluginManagerImpl implements plugin.PluginManager {
@inject(ContainerInstance) @Autowired(ContainerInstance)
private container: Container private container: Container
@inject(plugin.PluginInstance) @Autowired(plugin.PluginInstance)
private pluginInstance: any private pluginInstance: any
@inject(server.ServerType) @Autowired(server.ServerType)
private serverType: string private serverType: string
@Autowired() @Autowired()
private serverChecker: server.ServerChecker private serverChecker: server.ServerChecker
@Autowired()
private nativePluginManager: server.NativePluginManager
@Autowired() @Autowired()
private taskManager: PluginTaskManager private taskManager: PluginTaskManager
@ -224,15 +226,28 @@ export class PluginManagerImpl implements plugin.PluginManager {
try { try {
this.buildPlugin(metadata) this.buildPlugin(metadata)
} catch (error) { } catch (error) {
console.console(error) console.console(`§4无法加载插件 §b${metadata.name} §4构建插件失败!`)
console.ex(error)
} }
}) })
} }
private checkDepends(depends: string | string[]) {
if (!depends) return true
for (const depend of depends) { if (!this.metadataMap.has(depend)) return false }
return true
}
private checkNativeDepends(depends: string | string[]) {
if (!depends) return true
for (const depend of depends) { if (!this.nativePluginManager.has(depend)) return false }
return true
}
private buildPlugin(metadata: plugin.PluginMetadata) { private buildPlugin(metadata: plugin.PluginMetadata) {
if (!this.loaderMap.has(metadata.type)) { throw new Error(`§4无法加载插件 §c${metadata.name} §4请检查 §c${metadata.type} §4加载器是否正常启用!`) }
if (this.instanceMap.has(metadata.name)) { throw new Error(`Plugin ${metadata.name} is already load from ${metadata.source}...`) } if (this.instanceMap.has(metadata.name)) { throw new Error(`Plugin ${metadata.name} is already load from ${metadata.source}...`) }
if (!this.loaderMap.has(metadata.type)) { throw new Error(`§4无法加载插件 §b${metadata.name} §4请检查 §c${metadata.type} §4加载器是否正常启用!`) }
if (!this.serverChecker.check(metadata.servers)) { throw new Error(`§6插件 §b${metadata.name} §c服务器类型不兼容(${metadata.servers.join(',')}) §6忽略加载...`) } if (!this.serverChecker.check(metadata.servers)) { throw new Error(`§6插件 §b${metadata.name} §c服务器类型不兼容(${metadata.servers.join(',')}) §6忽略加载...`) }
if (!this.checkDepends(metadata.depends)) { throw new Error(`§4无法加载插件 §b${metadata.name} §4请检查依赖 §3${metadata.depends.join(',')} §4是否安装完整!`) }
if (!this.checkNativeDepends(metadata.nativeDepends)) { throw new Error(`§4无法加载插件 §b${metadata.name} §4请检查插件依赖 §3${metadata.nativeDepends.join(',')} §4是否安装完整!`) }
let pluginInstance = this.loaderMap.get(metadata.type).build(metadata) let pluginInstance = this.loaderMap.get(metadata.type).build(metadata)
if (!pluginInstance) { throw new Error(`§4加载器 §c${metadata.type} §4加载插件 §c${metadata.name} §4失败!`) } if (!pluginInstance) { throw new Error(`§4加载器 §c${metadata.type} §4加载插件 §c${metadata.name} §4失败!`) }
this.instanceMap.set(metadata.name, pluginInstance) this.instanceMap.set(metadata.name, pluginInstance)

View File

@ -1,31 +1,32 @@
import { server } from '@ccms/api' import { constants, server } from '@ccms/api'
import { provideSingleton, inject } from '@ccms/container' import { provideSingleton, postConstruct, Autowired, Container, ContainerInstance } from '@ccms/container'
import { NativePluginManager } from '@ccms/api'
import { CommandMap } from './internal/command' import { CommandMap } from './internal/command'
@provideSingleton(server.Server) @provideSingleton(server.Server)
export class SpringServer implements server.Server { export class SpringServer extends server.Server {
@inject(CommandMap) @Autowired(ContainerInstance)
private container: Container
@Autowired()
private commandMap: CommandMap private commandMap: CommandMap
private nettyPipeline = base.getInstance().getAutowireCapableBeanFactory()
private rootLogger = Packages.org.slf4j.LoggerFactory.getLogger("root") || global.logger
@postConstruct()
initialize() {
this.container.bind(constants.ServiceIdentifier.NettyPipeline).toConstantValue(this.nettyPipeline)
this.container.bind(constants.ServiceIdentifier.RootLogger).toConstantValue(this.rootLogger)
}
getVersion(): string { getVersion(): string {
return "SpringFramework" return "SpringFramework"
} }
getPlayer(name: string) {
throw new Error("Method not implemented.")
}
getOnlinePlayers(): any[] {
throw new Error("Method not implemented.")
}
getConsoleSender() { getConsoleSender() {
return { return {
name: 'CONSOLE', name: 'CONSOLE',
sendMessage: (message: string) => console.console(message) sendMessage: (message: string) => console.console(message)
} }
} }
getService(service: string) {
throw new Error("Method not implemented.")
}
dispatchCommand(sender: any, command: string): boolean { dispatchCommand(sender: any, command: string): boolean {
let cmd_args = command.split(" ") let cmd_args = command.split(" ")
return this.commandMap.dispatch(sender, cmd_args.shift(), cmd_args || []) return this.commandMap.dispatch(sender, cmd_args.shift(), cmd_args || [])
@ -33,16 +34,10 @@ export class SpringServer implements server.Server {
dispatchConsoleCommand(command: string): boolean { dispatchConsoleCommand(command: string): boolean {
return this.dispatchCommand(this.getConsoleSender(), command) return this.dispatchCommand(this.getConsoleSender(), command)
} }
getPluginsFolder(): string {
throw new Error("Method not implemented.")
}
getNativePluginManager(): NativePluginManager {
throw new Error("Method not implemented.")
}
getNettyPipeline() { getNettyPipeline() {
return base.getInstance().getAutowireCapableBeanFactory() return this.nettyPipeline
} }
getRootLogger() { getRootLogger() {
return Packages.org.slf4j.LoggerFactory.getLogger("root") || global.logger return this.rootLogger
} }
} }