From 8f944e0b14fc03a643b6c58eab8e6727b65c537b Mon Sep 17 00:00:00 2001 From: MiaoWoo Date: Thu, 17 Sep 2020 17:44:48 +0800 Subject: [PATCH] feat: update ms system --- packages/api/src/channel.ts | 28 +-- packages/api/src/chat.ts | 5 +- packages/api/src/event.ts | 15 +- packages/api/src/interfaces/plugin.ts | 12 +- packages/bukkit/src/channel.ts | 26 ++- packages/bukkit/src/chat.ts | 6 +- packages/bukkit/src/enhance/chat.ts | 19 +- packages/bukkit/src/index.ts | 15 +- packages/bukkit/src/server.ts | 3 + packages/common/src/http.ts | 49 +++-- packages/container/src/index.ts | 8 +- packages/i18n/languages/zh_cn.yml | 4 +- packages/ployfill/src/xml-http-request.ts | 204 ++++++++++---------- packages/plugin/src/decorators.ts | 4 + packages/plugin/src/event.ts | 5 +- packages/plugin/src/interfaces.ts | 5 +- packages/plugin/src/loader/basic-loader.ts | 12 +- packages/plugin/src/loader/ioc-loader.ts | 28 +-- packages/plugin/src/manager.ts | 13 +- packages/plugin/src/scanner/file-scanner.ts | 6 +- 20 files changed, 255 insertions(+), 212 deletions(-) diff --git a/packages/api/src/channel.ts b/packages/api/src/channel.ts index cfdac144..88500fde 100644 --- a/packages/api/src/channel.ts +++ b/packages/api/src/channel.ts @@ -1,4 +1,4 @@ -import { injectable } from "@ccms/container"; +import { injectable } from "@ccms/container" export namespace channel { /** @@ -17,32 +17,32 @@ export namespace channel { * @param exec 执行器 */ listen(plugin: any, channel: string, exec: ChannelListener) { - if (!plugin || !plugin.description || !plugin.description.name) throw new TypeError('Plugin can\'t be undefiend!'); - let name = plugin.description.name; + if (!plugin || !plugin.description || !plugin.description.name) throw new TypeError('Plugin can\'t be undefiend!') + let name = plugin.description.name let listener = this.register(channel, exec) - if (!this.listenerMap[name]) this.listenerMap[name] = []; + if (!this.listenerMap[name]) this.listenerMap[name] = [] let offExec = () => { - this.unregister(channel, listener); - console.debug(`[${name}] unregister channel ${channel}`); - }; + this.unregister(channel, listener) + console.debug(`[${name}] unregister channel ${channel}`) + } var off = { channel, listener, off: offExec - }; - this.listenerMap[name].push(off); - console.debug(`[${name}] register channel ${channel} => ${exec.name || '[anonymous]'}`); - return off; + } + this.listenerMap[name].push(off) + console.debug(`[${name}] register channel ${channel} => ${exec.name || '[anonymous]'}`) + return off } /** * 关闭插件注册的通道 * @param plugin 插件 */ disable(plugin: any) { - var channelCache = this.listenerMap[plugin.description.name]; + var channelCache = this.listenerMap[plugin.description.name] if (channelCache) { - channelCache.forEach(t => t.off()); - delete this.listenerMap[plugin.description.name]; + channelCache.forEach(t => t.off()) + delete this.listenerMap[plugin.description.name] } } /** diff --git a/packages/api/src/chat.ts b/packages/api/src/chat.ts index b35a3c9b..e11019b8 100644 --- a/packages/api/src/chat.ts +++ b/packages/api/src/chat.ts @@ -1,4 +1,7 @@ +import { injectable } from '@ccms/container' + export namespace chat { + @injectable() export abstract class Chat { sendMessage(sender: any, message: string) { throw new Error("Method not implemented.") @@ -9,7 +12,7 @@ export namespace chat { clearActionBar(sender: any) { this.sendActionBar(sender, '') } - sendTitle(sender: any, title: string, subtitle: string = '', fadeIn: number = 1, time: number = 5, fadeOut: number = 1) { + sendTitle(sender: any, title: string, subtitle: string = '', fadeIn: number = 20, time: number = 100, fadeOut: number = 20) { throw new Error("Method not implemented.") } clearTitle(sender: any) { diff --git a/packages/api/src/event.ts b/packages/api/src/event.ts index bbf5506e..7bd8bd57 100644 --- a/packages/api/src/event.ts +++ b/packages/api/src/event.ts @@ -130,23 +130,20 @@ export namespace event { } priority = priority || EventPriority.NORMAL ignoreCancel = ignoreCancel || false + // @ts-ignore + let executor = exec.name || exec.executor || '[anonymous]' // noinspection JSUnusedGlobalSymbols var listener = this.register(eventCls, this.execute(name, exec, eventCls), priority, ignoreCancel) var listenerMap = this.listenerMap // add to cache Be used for close plugin to close event if (!listenerMap[name]) listenerMap[name] = [] - var offExec = () => { + var off = () => { this.unregister(eventCls, listener) - console.debug(i18n.translate("ms.api.event.unregister", { name, event: this.class2Name(eventCls), exec: exec.name || '[anonymous]' })) - } - var off = { - event: eventCls, - listener: listener, - off: offExec + console.debug(i18n.translate("ms.api.event.unregister", { name, event: this.class2Name(eventCls), exec: executor })) } listenerMap[name].push(off) // noinspection JSUnresolvedVariable - console.debug(i18n.translate("ms.api.event.register", { name, event: this.class2Name(eventCls), exec: exec.name || '[anonymous]' })) + console.debug(i18n.translate("ms.api.event.register", { name, event: this.class2Name(eventCls), exec: executor })) return off } @@ -157,7 +154,7 @@ export namespace event { disable(plugin: any) { var eventCache = this.listenerMap[plugin.description.name] if (eventCache) { - eventCache.forEach(t => t.off()) + eventCache.forEach(off => off()) delete this.listenerMap[plugin.description.name] } } diff --git a/packages/api/src/interfaces/plugin.ts b/packages/api/src/interfaces/plugin.ts index 2116ad2e..e6cc74f2 100644 --- a/packages/api/src/interfaces/plugin.ts +++ b/packages/api/src/interfaces/plugin.ts @@ -38,10 +38,6 @@ export namespace plugin { * 插件加载类型 */ type: string - /** - * 插件名称 - */ - name?: string /** * 插件文件 */ @@ -116,22 +112,22 @@ export namespace plugin { * Load 阶段 * @param plugin 插件 */ - load(plugin: Plugin): void + load?(plugin: Plugin): void /** * Enable 阶段 * @param plugin 插件 */ - enable(plugin: Plugin): void + enable?(plugin: Plugin): void /** * Disable 阶段 * @param plugin 插件 */ - disable(plugin: Plugin): void + disable?(plugin: Plugin): void /** * Reload 阶段 * @param plugin 插件 */ - reload(plugin: Plugin): void + reload?(plugin: Plugin): void } export interface Plugin { description: PluginMetadata diff --git a/packages/bukkit/src/channel.ts b/packages/bukkit/src/channel.ts index d0f7caf2..36d210b2 100644 --- a/packages/bukkit/src/channel.ts +++ b/packages/bukkit/src/channel.ts @@ -9,7 +9,7 @@ const Messenger = Bukkit.getMessenger() export class BukkitChannel extends channel.Channel { @inject(plugin.PluginInstance) private pluginInstance: any - + private cacheChannel = new Map() /** * 给玩家发送通道消息 * @param player 接受消息的玩家 @@ -26,15 +26,29 @@ export class BukkitChannel extends channel.Channel { * @param listener 监听器 */ register(channel: string, listener: channel.ChannelListener) { - Messenger.registerIncomingPluginChannel(this.pluginInstance, channel, new PluginMessageListener({ + if (!this.cacheChannel.has(channel)) this.cacheChannel.set(channel, []) + this.cacheChannel.get(channel).push(listener) + let pluginMessageListener = new PluginMessageListener({ onPluginMessageReceived: (/**String */ channel, /**Player */ player, /**byte[] */data) => { - listener(data, { channel, player, data }) + try { + listener(data, { channel, player, data }) + } catch (error) { + console.ex(error) + } } - })) + }) + Messenger.registerIncomingPluginChannel(this.pluginInstance, channel, pluginMessageListener) Messenger.registerOutgoingPluginChannel(this.pluginInstance, channel) + return pluginMessageListener } unregister(channel: string, listener: any) { - Messenger.unregisterIncomingPluginChannel(this.pluginInstance, channel) - Messenger.unregisterOutgoingPluginChannel(this.pluginInstance, channel) + if (!this.cacheChannel.has(channel)) return + let cacheListener = this.cacheChannel.get(channel) + cacheListener = cacheListener.filter(l => l != listener) + Messenger.unregisterIncomingPluginChannel(this.pluginInstance, channel, listener) + if (cacheListener.length == 0) { + this.cacheChannel.delete(channel) + Messenger.unregisterOutgoingPluginChannel(this.pluginInstance, channel) + } } } diff --git a/packages/bukkit/src/chat.ts b/packages/bukkit/src/chat.ts index 867128bf..8323ec2e 100644 --- a/packages/bukkit/src/chat.ts +++ b/packages/bukkit/src/chat.ts @@ -5,12 +5,12 @@ import bukkitChat from './enhance/chat' @provideSingleton(chat.Chat) export class BukkitChat extends chat.Chat { sendMessage(sender: any, message: string) { - bukkitChat.send(sender, { text: message }, 0) + bukkitChat.send(sender, JSON.stringify({ text: message }), 0) } sendActionBar(sender: any, message: string) { - bukkitChat.send(sender, { text: message }, 2) + bukkitChat.send(sender, JSON.stringify({ text: message }), 2) } - sendTitle(sender: any, title: string, subtitle: string = '', fadeIn: number = 1, time: number = 5, fadeOut: number = 1) { + sendTitle(sender: any, title: string, subtitle: string = '', fadeIn: number = 20, time: number = 100, fadeOut: number = 20) { sender.sendTitle(title, subtitle, fadeIn, time, fadeOut) } } diff --git a/packages/bukkit/src/enhance/chat.ts b/packages/bukkit/src/enhance/chat.ts index e9fce741..21b9de6e 100644 --- a/packages/bukkit/src/enhance/chat.ts +++ b/packages/bukkit/src/enhance/chat.ts @@ -9,6 +9,7 @@ let RemapUtils: any let playerConnectionFieldName: string let sendPacketMethodName: string +let above_1_16 = false let downgrade = false /** * 获取NMS版本 @@ -59,12 +60,18 @@ function init() { let packetTypeClass = nmsCls("PacketPlayOutChat") PacketPlayOutChat = Java.type(packetTypeClass.getName()) let packetTypeConstructor: { parameterTypes: any[] } - Java.from(packetTypeClass.constructors).forEach(function (c) { + let constructors = packetTypeClass.constructors + Java.from(constructors).forEach(function (c) { if (c.parameterTypes.length === 2) { packetTypeConstructor = c } + if (c.parameterTypes.length === 3) { + packetTypeConstructor = c + above_1_16 = true + } }) - let nmsChatMessageTypeClass = packetTypeConstructor.parameterTypes[1] + let parameterTypes = packetTypeConstructor.parameterTypes + let nmsChatMessageTypeClass = parameterTypes[1] if (nmsChatMessageTypeClass.isEnum()) { chatMessageTypes = nmsChatMessageTypeClass.getEnumConstants() } @@ -83,7 +90,13 @@ function json(sender: { name: string }, json: string) { } function send(sender: any, json: any, type: number) { - sendPacket(sender, new PacketPlayOutChat(ChatSerializer[nmsChatSerializerMethodName](json), chatMessageTypes == null ? type : chatMessageTypes[type])) + let packet + if (above_1_16) { + packet = new PacketPlayOutChat(ChatSerializer[nmsChatSerializerMethodName](json), chatMessageTypes == null ? type : chatMessageTypes[type], sender.getUniqueId()) + } else { + packet = new PacketPlayOutChat(ChatSerializer[nmsChatSerializerMethodName](json), chatMessageTypes == null ? type : chatMessageTypes[type]) + } + sendPacket(sender, packet) } function sendPacket(player: { handle: { [x: string]: { [x: string]: (arg0: any) => void } } }, p: any) { diff --git a/packages/bukkit/src/index.ts b/packages/bukkit/src/index.ts index 942f1584..d0e331e1 100644 --- a/packages/bukkit/src/index.ts +++ b/packages/bukkit/src/index.ts @@ -3,13 +3,14 @@ import { server } from '@ccms/api' import { Container } from '@ccms/container' -import { BukkitConsole } from './console'; -import './event'; -import './server'; -import './command'; -import './channel'; -import './task'; +import { BukkitConsole } from './console' +import './chat' +import './task' +import './event' +import './server' +import './command' +import './channel' export default function BukkitImpl(container: Container) { - container.bind(server.Console).toConstantValue(BukkitConsole); + container.bind(server.Console).toConstantValue(BukkitConsole) } diff --git a/packages/bukkit/src/server.ts b/packages/bukkit/src/server.ts index 5edfec01..fbc049fc 100644 --- a/packages/bukkit/src/server.ts +++ b/packages/bukkit/src/server.ts @@ -63,4 +63,7 @@ export class BukkitServer extends server.ReflectServer { this.dispatchConsoleCommand(result) } } + tabComplete?(sender: any, input: string, index?: number): string[] { + throw new Error("Method not implemented.") + } } diff --git a/packages/common/src/http.ts b/packages/common/src/http.ts index f9f2ad8e..8ea16491 100644 --- a/packages/common/src/http.ts +++ b/packages/common/src/http.ts @@ -1,7 +1,9 @@ +import * as querystring from 'querystring' + const URL = Java.type('java.net.URL') -const Paths = Java.type('java.nio.file.Paths'); -const Files = Java.type('java.nio.file.Files'); -const StandardCopyOption = Java.type('java.nio.file.StandardCopyOption'); +const Paths = Java.type('java.nio.file.Paths') +const Files = Java.type('java.nio.file.Files') +const StandardCopyOption = Java.type('java.nio.file.StandardCopyOption') export type Method = | 'get' | 'GET' @@ -13,36 +15,49 @@ export type Method = | 'patch' | 'PATCH' interface RequestConfig { - url?: string; - method?: Method; - headers?: { [key: string]: string }; - params?: { [key: string]: string }; - data?: any; + url?: string + method?: Method + headers?: { [key: string]: string } + params?: { [key: string]: string } + data?: any } function request(config: RequestConfig) { // @ts-ignore - let xhr = new XMLHttpRequest(); - xhr.open(config.method, config.url, false); + let xhr = new XMLHttpRequest() + xhr.open(config.method, config.url, false) for (const header in config.headers) { - xhr.setRequestHeader(header, config.headers[header]); + xhr.setRequestHeader(header, config.headers[header]) } - xhr.send(typeof config.data === "string" ? config.data : JSON.stringify(config.data)); - if ((xhr.getResponseHeader("Content-Type") + '').indexOf('application/json') != -1) { + let body = config.data + if (body && typeof body !== "string") { + switch (config.headers['Content-Type']) { + case "application/json": + body = JSON.stringify(body) + break + case "application/x-www-form-urlencoded": + body = querystring.encode(body) + break + } + } + xhr.send(body) + if (xhr.getResponseHeader("Content-Type").indexOf('application/json') != -1) { xhr.responseType = "json" } - return xhr.get(); + return xhr.get() } function download(url: string, target: string) { console.debug(`Start Download file ${target} from ${url}....`) - Files.copy(new URL(url).openStream(), Paths.get(target), StandardCopyOption.REPLACE_EXISTING); + Files.copy(new URL(url).openStream(), Paths.get(target), StandardCopyOption.REPLACE_EXISTING) console.debug(`File ${target} Download Complate...`) } function _proxy(method: Method) { - return function (url: string, data?: any, config?: RequestConfig) { - return request({ url, method, data, ...config }); + return function (url: string, data?: any, config: RequestConfig = {}) { + if (!config.headers) { config.headers = {} } + config.headers['Content-Type'] = config.headers['Content-Type'] ?? 'application/json' + return request({ url, method, data, ...config }) } } diff --git a/packages/container/src/index.ts b/packages/container/src/index.ts index 15bdae90..75e89453 100644 --- a/packages/container/src/index.ts +++ b/packages/container/src/index.ts @@ -36,10 +36,10 @@ export const provideSingletonNamed = (identifier: interfaces.ServiceIdentifier { - return function (target: any, propertyKey: string, index?: number) { + return function (target: object, propertyKey: string, index?: number) { try { target[propertyKey] = Java.type(className).class; return } catch (error) { } try { target[propertyKey] = base.getClass(className); return } catch (error) { } - console.warn('JavaClass Inject target', target, 'propertyKey', propertyKey, 'failed!') + console.warn('JavaClass', className, 'Inject target', target.constructor.name, 'propertyKey', propertyKey, 'failed!') } } @@ -48,10 +48,10 @@ export const JavaClass = (className: string) => { * @param className Java 全类名 */ export const JSClass = (className: string) => { - return function (target: any, propertyKey: string, index?: number) { + return function (target: object, propertyKey: string, index?: number) { try { target[propertyKey] = Java.type(className); return } catch (error) { } try { target[propertyKey] = base.getClass(className).static; return } catch (error) { } - console.warn('JSClass Inject target', target, 'propertyKey', propertyKey, 'failed!') + console.warn('JSClass', className, 'Inject target', target.constructor.name, 'propertyKey', propertyKey, 'failed!') } } diff --git a/packages/i18n/languages/zh_cn.yml b/packages/i18n/languages/zh_cn.yml index 34338a8a..8ff4a0c3 100644 --- a/packages/i18n/languages/zh_cn.yml +++ b/packages/i18n/languages/zh_cn.yml @@ -21,8 +21,8 @@ ms.api.event.not.found: "§6插件 §b{name} §6注册事件 §c{event} §6失 ms.api.event.execute.slow: "§c注意! §6插件 §b{name} §6处理 §d{event} §6事件 §c耗时 §4{cost}ms !" ms.api.event.execute.error: "§6插件 §b{name} §6处理 §d{event} §6事件时发生异常 §4{ex}" ms.api.event.listen.plugin.name.empty: "插件名称为空 请检查传入参数!" -ms.api.event.register: "[{name}] 注册事件 {event} => {exec}" -ms.api.event.unregister: "[{name}] 注销事件 {event} => {exec}" +ms.api.event.register: "[{name}] 注册事件 {event} => 执行器 {exec}" +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}..." diff --git a/packages/ployfill/src/xml-http-request.ts b/packages/ployfill/src/xml-http-request.ts index fb3d97c7..c296db28 100644 --- a/packages/ployfill/src/xml-http-request.ts +++ b/packages/ployfill/src/xml-http-request.ts @@ -1,39 +1,39 @@ import '@ccms/nashorn' -const URL = Java.type("java.net.URL"); -const Files = Java.type("java.nio.file.Files"); -const StandardCopyOption = Java.type("java.nio.file.StandardCopyOption"); -const JavaString = Java.type("java.lang.String"); -const SecureRandom = Java.type("java.security.SecureRandom"); -const SSLContext = Java.type("javax.net.ssl.SSLContext"); -const HttpsURLConnection = Java.type("javax.net.ssl.HttpsURLConnection"); -const HostnameVerifier = Java.type("javax.net.ssl.HostnameVerifier"); -const X509TrustManager = Java.type("javax.net.ssl.X509TrustManager"); +const URL = Java.type("java.net.URL") +const Files = Java.type("java.nio.file.Files") +const StandardCopyOption = Java.type("java.nio.file.StandardCopyOption") +const JavaString = Java.type("java.lang.String") +const SecureRandom = Java.type("java.security.SecureRandom") +const SSLContext = Java.type("javax.net.ssl.SSLContext") +const HttpsURLConnection = Java.type("javax.net.ssl.HttpsURLConnection") +const HostnameVerifier = Java.type("javax.net.ssl.HostnameVerifier") +const X509TrustManager = Java.type("javax.net.ssl.X509TrustManager") -const SocketTimeoutException = Java.type('java.net.SocketTimeoutException'); +const SocketTimeoutException = Java.type('java.net.SocketTimeoutException') -const Callable = Java.type('java.util.concurrent.Callable'); +const Callable = Java.type('java.util.concurrent.Callable') const Executors = Java.type('java.util.concurrent.Executors') const UTF_8 = "UTF-8" -const TrustAnyHostnameVerifier = new HostnameVerifier({ verify: () => true }); +const TrustAnyHostnameVerifier = new HostnameVerifier({ verify: () => true }) const SSLSocketFactory = function initSSLSocketFactory() { - let sslContext = SSLContext.getInstance("TLS"); + let sslContext = SSLContext.getInstance("TLS") sslContext.init(null, [new X509TrustManager({ getAcceptedIssuers: () => null, checkClientTrusted: () => { }, checkServerTrusted: () => { } - })], new SecureRandom()); - return sslContext.getSocketFactory(); -}(); + })], new SecureRandom()) + return sslContext.getSocketFactory() +}() interface Future { - cancel(): boolean; - isCancelled(): boolean; - isDone(): boolean; - get(): T; + cancel(): boolean + isCancelled(): boolean + isDone(): boolean + get(): T } enum ReadyState { @@ -50,14 +50,14 @@ type RequestMethod = | 'options' | 'OPTIONS' | 'post' | 'POST' | 'put' | 'PUT' - | 'patch' | 'PATCH'; + | 'patch' | 'PATCH' type ResponseType = | 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' - | 'stream'; + | 'stream' type EventType = | 'load' | 'error' @@ -65,191 +65,191 @@ type EventType = | 'progress' | 'timeout' | 'loadend' - | 'loadstart'; -type HttpHeader = { [key: string]: string }; + | 'loadstart' +type HttpHeader = { [key: string]: string } -const executor = Executors.newCachedThreadPool(); +const executor = Executors.newCachedThreadPool() export class XMLHttpRequest { - private _timeout: number; + private _timeout: number private _responseType: ResponseType = 'text'; - private _withCredentials: boolean; + private _withCredentials: boolean private _readyState: ReadyState = ReadyState.UNSENT; - private _method: RequestMethod; - private _url: string; - private _async: boolean; - private _mimeType: string; + private _method: RequestMethod + private _url: string + private _async: boolean + private _mimeType: string private _requestHeaders: HttpHeader = {}; private _status: number = 0; private _statusText: string = null; - private _response: any; - private _responseText: any; - private _responseURL: string; + private _response: any + private _responseText: any + private _responseURL: string private _responseHeaders: HttpHeader = {}; private _connection = null; get timeout() { - return this._timeout; + return this._timeout } set timeout(timeout: number) { this._timeout = timeout } get readyState() { - return this._readyState; + return this._readyState } set responseType(type: ResponseType) { - this._responseType = type; + this._responseType = type } get responseType() { - return this._responseType; + return this._responseType } get status() { - return this._status; + return this._status } get statusText() { - return this._statusText; + return this._statusText } get response() { - return this._response || this.get(); + return this._response || this.get() } get responseText() { - return this._responseText; + return this._responseText } get responseXML() { - return this._response; + return this._response } get responseURL() { - return this._responseURL; + return this._responseURL } - public onload: () => void; - public onerror: (ex: Error) => void; - public onabort: () => void; - public onprogress: () => void; - public ontimeout: (ex: Error) => void; - public onloadend: () => void; - public onloadstart: () => void; - public onreadystatechange: () => void; + public onload: () => void + public onerror: (ex: Error) => void + public onabort: () => void + public onprogress: () => void + public ontimeout: (ex: Error) => void + public onloadend: () => void + public onloadstart: () => void + public onreadystatechange: () => void setRequestHeader(key: string, val: string) { - this._requestHeaders[key] = val; + this._requestHeaders[key] = val } getResponseHeader(key: string): string { - return this._responseHeaders[key]; + return this._responseHeaders[key] } getAllResponseHeaders(): any { - return this._responseHeaders; + return this._responseHeaders } addEventListener(event: EventType, callback: Function) { - this[`on${event.toLowerCase()}`] = callback; + this[`on${event.toLowerCase()}`] = callback } overrideMimeType(mimeType: string) { - this._mimeType = mimeType; + this._mimeType = mimeType } open(method: RequestMethod, url: string, async: boolean = true, user?: string, password?: string) { if (this._readyState !== ReadyState.UNSENT) { throw new Error(`Error Status ${this._readyState}!`) } - this._method = method; - this._url = url; - this._async = async; + this._method = method + this._url = url + this._async = async this._connection = new URL(this._url).openConnection() if (this._connection instanceof HttpsURLConnection) { - this._connection.setHostnameVerifier(TrustAnyHostnameVerifier); - this._connection.setSSLSocketFactory(SSLSocketFactory); + this._connection.setHostnameVerifier(TrustAnyHostnameVerifier) + this._connection.setSSLSocketFactory(SSLSocketFactory) } - this._connection.setRequestMethod(this._method); - this._connection.setDoOutput(true); - this._connection.setDoInput(true); - this._connection.setConnectTimeout(this._timeout); - this._connection.setReadTimeout(this._timeout); + this._connection.setRequestMethod(this._method) + this._connection.setDoOutput(true) + this._connection.setDoInput(true) + this._connection.setConnectTimeout(this._timeout) + this._connection.setReadTimeout(this._timeout) - this.setReadyState(ReadyState.OPENED); + this.setReadyState(ReadyState.OPENED) } send(body?: string | object): Future { for (const header in this._requestHeaders) { - this._connection.setRequestProperty(header, this._requestHeaders[header]); + this._connection.setRequestProperty(header, this._requestHeaders[header]) } if (this._readyState !== ReadyState.OPENED) { throw new Error(`Error Status ${this._readyState}!`) } - let future = executor.submit(new Callable({ call: () => this._send(body) })); + let future = executor.submit(new Callable({ call: () => this._send(body) })) if (!this._async) { future.get() } - return future; + return future } get() { if (this._response === undefined) { switch (this._responseType) { case "json": - return this._response = JSON.parse(this._responseText); + return this._response = JSON.parse(this._responseText) case "text": - return this._response = this._responseText; + return this._response = this._responseText default: throw Error(`Unsupport ResponseType: ${this._responseType} !`) } } - return this._response; + return this._response } abort() { - this._connection.disconnect(); - this.onabort && this.onabort(); + this._connection.disconnect() + this.onabort && this.onabort() } private _send(body?: string | object) { try { - this._connection.connect(); - this.onloadstart && this.onloadstart(); + this._connection.connect() + this.onloadstart && this.onloadstart() if (body) { - let bodyType = Object.prototype.toString.call(body); + let bodyType = Object.prototype.toString.call(body) if (typeof body !== "string") { throw new Error(`body(${bodyType}) must be string!`) } - var out = this._connection.getOutputStream(); - out.write(new JavaString(body).getBytes(UTF_8)); - out.flush(); - out.close(); + var out = this._connection.getOutputStream() + out.write(new JavaString(body).getBytes(UTF_8)) + out.flush() + out.close() } - this.setReadyState(ReadyState.LOADING); - this._status = this._connection.getResponseCode(); - this._statusText = this._connection.getResponseMessage(); + this.setReadyState(ReadyState.LOADING) + this._status = this._connection.getResponseCode() + this._statusText = this._connection.getResponseMessage() if (this._status >= 0 && this._status < 300) { - this._responseText = this.readOutput(this._connection.getInputStream()); + this._responseText = this.readOutput(this._connection.getInputStream()) } else if (this._status >= 300 && this._status < 400) { - this._responseURL = this.getResponseHeader('Location'); + this._responseURL = this.getResponseHeader('Location') } else { - this._responseText = this.readOutput(this._connection.getErrorStream()); + this._responseText = this.readOutput(this._connection.getErrorStream()) } - this.setResponseHeaders(this._connection.getHeaderFields()); - this.onloadend && this.onloadend(); + this.setResponseHeaders(this._connection.getHeaderFields()) + this.onloadend && this.onloadend() } catch (ex) { if (ex instanceof SocketTimeoutException && this.ontimeout) { return this.ontimeout(ex) } else if (this.onerror) { - return this.onerror(ex); + return this.onerror(ex) } - throw ex; + throw ex } finally { - this._connection.disconnect(); - this.setReadyState(ReadyState.DONE); + this._connection.disconnect() + this.setReadyState(ReadyState.DONE) } } private setResponseHeaders(header: any) { header.forEach((key: string | number, value: string | any[]) => { - this._responseHeaders[key] = value[value.length - 1] - }); + this._responseHeaders[key + ''] = value[value.length - 1] + '' + }) } private setReadyState(state: ReadyState) { - this._readyState = state; - this.onreadystatechange && this.onreadystatechange(); + this._readyState = state + this.onreadystatechange && this.onreadystatechange() } private readOutput(input: any) { - var tempFile = Files.createTempFile('xhr', '.response'); - Files.copy(input, tempFile, StandardCopyOption['REPLACE_EXISTING']); tempFile.toFile().deleteOnExit(); - return new JavaString(Files.readAllBytes(tempFile), 'UTF-8'); + var tempFile = Files.createTempFile('xhr', '.response') + Files.copy(input, tempFile, StandardCopyOption['REPLACE_EXISTING']); tempFile.toFile().deleteOnExit() + return new JavaString(Files.readAllBytes(tempFile), 'UTF-8') } } diff --git a/packages/plugin/src/decorators.ts b/packages/plugin/src/decorators.ts index f9f42932..014964fd 100644 --- a/packages/plugin/src/decorators.ts +++ b/packages/plugin/src/decorators.ts @@ -30,6 +30,7 @@ export function plugin(metadata: pluginApi.PluginMetadata | any) { export function cmd(metadata: interfaces.CommandMetadata = {}) { return function (target: any, key: string, value: any) { metadata.name = metadata.name || key + metadata.target = target metadata.executor = key metadata.paramtypes = Reflect.getMetadata("design:paramtypes", target, key) const previousMetadata: Map = getPluginCommandMetadata(target) @@ -46,6 +47,7 @@ export function tab(metadata: interfaces.CommandMetadata = {}) { return function (target: any, key: string, value: any) { metadata.name = metadata.name || (key.startsWith('tab') ? key.split('tab', 2)[1] : key) if (!metadata.name) { return } + metadata.target = target metadata.executor = key metadata.paramtypes = Reflect.getMetadata("design:paramtypes", target, key) const previousMetadata: Map = getPluginTabCompleterMetadata(target) @@ -61,9 +63,11 @@ export function tab(metadata: interfaces.CommandMetadata = {}) { export function listener(metadata: interfaces.ListenerMetadata = {}) { return function (target: any, key: string, value: any) { metadata.name = metadata.name || key + metadata.target = target metadata.executor = key const previousMetadata: interfaces.ListenerMetadata[] = getPluginListenerMetadata(target) Reflect.defineMetadata(METADATA_KEY.listener, [metadata, ...previousMetadata], target.constructor) + Reflect.defineMetadata(METADATA_KEY.listener, metadata, target[key]) } } diff --git a/packages/plugin/src/event.ts b/packages/plugin/src/event.ts index cb884a67..de2a62b2 100644 --- a/packages/plugin/src/event.ts +++ b/packages/plugin/src/event.ts @@ -20,7 +20,10 @@ export class PluginEventManager { // ignore space listener if (!this.ServerChecker.check(event.servers)) { continue } // here must bind this to pluginInstance - this.EventManager.listen(pluginInstance, event.name, pluginInstance[event.executor].bind(pluginInstance), event.priority, event.ignoreCancel) + let exec = event.target[event.executor] + let execBinded = exec.bind(pluginInstance) + execBinded.executor = event.executor + exec.off = this.EventManager.listen(pluginInstance, event.name, execBinded, event.priority, event.ignoreCancel) } } diff --git a/packages/plugin/src/interfaces.ts b/packages/plugin/src/interfaces.ts index 192418a6..4283e33a 100644 --- a/packages/plugin/src/interfaces.ts +++ b/packages/plugin/src/interfaces.ts @@ -25,6 +25,10 @@ export namespace interfaces { public disable() { } } export interface ExecMetadata extends plugin.BaseMetadata { + /** + * 执行器所在对象 用于绑定this + */ + target?: any /** * 执行器 */ @@ -68,5 +72,4 @@ export namespace interfaces { */ readonly?: boolean } - export type PluginLike = Plugin | string } diff --git a/packages/plugin/src/loader/basic-loader.ts b/packages/plugin/src/loader/basic-loader.ts index 4bc3c25d..b56babbd 100644 --- a/packages/plugin/src/loader/basic-loader.ts +++ b/packages/plugin/src/loader/basic-loader.ts @@ -1,9 +1,11 @@ import { plugin } from "@ccms/api" -import { provideSingleton } from "@ccms/container" +import { provideSingletonNamed } from "@ccms/container" -@provideSingleton(plugin.PluginLoader) +const LOADER_TYPE_NAME = 'basic' + +@provideSingletonNamed(plugin.PluginLoader, LOADER_TYPE_NAME) export class BasicLoader implements plugin.PluginLoader { - type: string = 'basic' + type: string = LOADER_TYPE_NAME private pluginRequireMap: Map @@ -22,8 +24,4 @@ export class BasicLoader implements plugin.PluginLoader { build(metadata: plugin.PluginMetadata) { return this.pluginRequireMap.get(metadata.source.toString()) } - load(plugin: plugin.Plugin): void { } - enable(plugin: plugin.Plugin): void { } - disable(plugin: plugin.Plugin): void { } - reload(plugin: plugin.Plugin): void { } } diff --git a/packages/plugin/src/loader/ioc-loader.ts b/packages/plugin/src/loader/ioc-loader.ts index 1344dbd5..2d272245 100644 --- a/packages/plugin/src/loader/ioc-loader.ts +++ b/packages/plugin/src/loader/ioc-loader.ts @@ -1,16 +1,18 @@ import { plugin, server } from "@ccms/api" -import { inject, ContainerInstance, Container, provideSingleton } from "@ccms/container" +import { inject, ContainerInstance, Container, provideSingletonNamed } from "@ccms/container" import { interfaces } from "../interfaces" import { getPluginStageMetadata, getPluginSources } from "../utils" -@provideSingleton(plugin.PluginLoader) +const LOADER_TYPE_NAME = 'ioc' + +@provideSingletonNamed(plugin.PluginLoader, LOADER_TYPE_NAME) export class IocLoader implements plugin.PluginLoader { - type: string = 'ioc' + type: string = LOADER_TYPE_NAME @inject(ContainerInstance) private container: Container - @inject(server.ServerType) - private serverType: string + @inject(server.ServerChecker) + private serverChecker: server.ServerChecker private pluginMetadataMap: Map @@ -28,7 +30,6 @@ export class IocLoader implements plugin.PluginLoader { } build(metadata: plugin.PluginMetadata) { - if (!this.allowProcess(metadata.servers)) { return } let pluginInstance: plugin.Plugin try { this.bindPlugin(metadata) @@ -73,23 +74,10 @@ export class IocLoader implements plugin.PluginLoader { } } - private allowProcess(servers: string[]) { - // Not set servers -> allow - if (!servers || !servers.length) return true - // include !type -> deny - let denyServers = servers.filter(svr => svr.startsWith("!")) - if (denyServers.length !== 0) { - return !denyServers.includes(`!${this.serverType}`) - } else { - // only include -> allow - return servers.includes(this.serverType) - } - } - private stage(pluginInstance: plugin.Plugin, stageName: string) { let stages = getPluginStageMetadata(pluginInstance, stageName) for (const stage of stages) { - if (!this.allowProcess(stage.servers)) { continue } + if (!this.serverChecker.check(stage.servers)) { continue } console.i18n("ms.plugin.manager.stage.exec", { plugin: pluginInstance.description.name, name: stage.executor, stage: stageName, servers: stage.servers }) try { pluginInstance[stage.executor].apply(pluginInstance) diff --git a/packages/plugin/src/manager.ts b/packages/plugin/src/manager.ts index 487469da..6bec8559 100644 --- a/packages/plugin/src/manager.ts +++ b/packages/plugin/src/manager.ts @@ -19,6 +19,8 @@ export class PluginManagerImpl implements plugin.PluginManager { private serverType: string @inject(event.Event) private EventManager: event.Event + @inject(server.ServerChecker) + private serverChecker: server.ServerChecker @inject(PluginCommandManager) private commandManager: PluginCommandManager @@ -74,7 +76,7 @@ export class PluginManagerImpl implements plugin.PluginManager { try { this.loadPlugin(scanner.load(loadMetadata)) } catch (error) { - console.error(`plugin scanner ${scanner.type} load ${loadMetadata.name} occurred error ${error}`) + console.error(`plugin scanner ${scanner.type} load ${loadMetadata.file} occurred error ${error}`) console.ex(error) } }) @@ -102,7 +104,7 @@ export class PluginManagerImpl implements plugin.PluginManager { process.emit(`plugin.before.${stage}`, plugin) this.runCatch(plugin, stage) this.runCatch(plugin, `${this.serverType}${stage}`) - plugin.description.loadMetadata.loader[stage](plugin) + plugin.description.loadMetadata.loader[stage]?.(plugin) process.emit(`plugin.after.${stage}`, plugin) } catch (ex) { console.i18n("ms.plugin.manager.stage.exec.error", { plugin: plugin.description.name, executor: stage, error: ex }) @@ -112,7 +114,7 @@ export class PluginManagerImpl implements plugin.PluginManager { private loadPlugin(loadMetadata: plugin.PluginLoadMetadata) { if (!loadMetadata) { throw new Error('loadMetadata can\'t be undefiend when loadPlugin!') } - if (loadMetadata.loaded) { throw new Error(`Plugin ${loadMetadata.name} is already loaded by ${loadMetadata.loader?.type}!`) } + if (loadMetadata.loaded) { throw new Error(`Plugin file ${loadMetadata.file} is already loaded by ${loadMetadata.loader?.type}!`) } try { for (const [, loader] of this.loaderMap) { if (this.loaderRequirePlugin(loadMetadata, loader)?.loaded) return loadMetadata.metadata @@ -215,16 +217,17 @@ export class PluginManagerImpl implements plugin.PluginManager { try { this.buildPlugin(metadata) } catch (error) { - console.error(error) + console.console(error) } }) } 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.serverChecker.check(metadata.servers)) { throw new Error(`§6插件 §b${metadata.name} §c服务器类型不兼容(${metadata.servers.join(',')}) §6忽略加载...`) } let pluginInstance = this.loaderMap.get(metadata.type).build(metadata) if (!pluginInstance) { throw new Error(`§4加载器 §c${metadata.type} §4加载插件 §c${metadata.name} §4失败!`) } - if (this.instanceMap.has(metadata.name)) { throw new Error(`Plugin ${metadata.name} is already load from ${metadata.source}...`) } this.instanceMap.set(metadata.name, pluginInstance) return pluginInstance } diff --git a/packages/plugin/src/scanner/file-scanner.ts b/packages/plugin/src/scanner/file-scanner.ts index 9458c2c6..a712a0ce 100644 --- a/packages/plugin/src/scanner/file-scanner.ts +++ b/packages/plugin/src/scanner/file-scanner.ts @@ -2,9 +2,11 @@ import { plugin } from "@ccms/api" import * as fs from '@ccms/common/dist/fs' import { provideSingletonNamed } from "@ccms/container" -@provideSingletonNamed(plugin.PluginScanner, 'file') +const SCANNER_TYPE_NAME = 'file' + +@provideSingletonNamed(plugin.PluginScanner, SCANNER_TYPE_NAME) export class JSFileScanner implements plugin.PluginScanner { - type: string = 'file' + type: string = SCANNER_TYPE_NAME scan(target: any): plugin.PluginLoadMetadata[] { return this.scanFolder(fs.concat(root, target)).map((file) => this.read(file))