feat: update ms system

This commit is contained in:
MiaoWoo 2020-09-17 17:44:48 +08:00
parent 7d6f6124b1
commit 8eaf789bce
23 changed files with 636 additions and 327 deletions

View File

@ -1,4 +1,4 @@
import { injectable } from "@ccms/container"; import { injectable } from "@ccms/container"
export namespace channel { export namespace channel {
/** /**
@ -17,32 +17,32 @@ export namespace channel {
* @param exec * @param exec
*/ */
listen(plugin: any, channel: string, exec: ChannelListener) { listen(plugin: any, channel: string, exec: ChannelListener) {
if (!plugin || !plugin.description || !plugin.description.name) throw new TypeError('Plugin can\'t be undefiend!'); if (!plugin || !plugin.description || !plugin.description.name) throw new TypeError('Plugin can\'t be undefiend!')
let name = plugin.description.name; let name = plugin.description.name
let listener = this.register(channel, exec) let listener = this.register(channel, exec)
if (!this.listenerMap[name]) this.listenerMap[name] = []; if (!this.listenerMap[name]) this.listenerMap[name] = []
let offExec = () => { let offExec = () => {
this.unregister(channel, listener); this.unregister(channel, listener)
console.debug(`[${name}] unregister channel ${channel}`); console.debug(`[${name}] unregister channel ${channel}`)
}; }
var off = { var off = {
channel, channel,
listener, listener,
off: offExec off: offExec
}; }
this.listenerMap[name].push(off); this.listenerMap[name].push(off)
console.debug(`[${name}] register channel ${channel} => ${exec.name || '[anonymous]'}`); console.debug(`[${name}] register channel ${channel} => ${exec.name || '[anonymous]'}`)
return off; return off
} }
/** /**
* *
* @param plugin * @param plugin
*/ */
disable(plugin: any) { disable(plugin: any) {
var channelCache = this.listenerMap[plugin.description.name]; var channelCache = this.listenerMap[plugin.description.name]
if (channelCache) { if (channelCache) {
channelCache.forEach(t => t.off()); channelCache.forEach(t => t.off())
delete this.listenerMap[plugin.description.name]; delete this.listenerMap[plugin.description.name]
} }
} }
/** /**

View File

@ -1,4 +1,7 @@
import { injectable } from '@ccms/container'
export namespace chat { export namespace chat {
@injectable()
export abstract class Chat { export abstract class Chat {
sendMessage(sender: any, message: string) { sendMessage(sender: any, message: string) {
throw new Error("Method not implemented.") throw new Error("Method not implemented.")
@ -9,7 +12,7 @@ export namespace chat {
clearActionBar(sender: any) { clearActionBar(sender: any) {
this.sendActionBar(sender, '') 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.") throw new Error("Method not implemented.")
} }
clearTitle(sender: any) { clearTitle(sender: any) {

View File

@ -130,23 +130,20 @@ export namespace event {
} }
priority = priority || EventPriority.NORMAL priority = priority || EventPriority.NORMAL
ignoreCancel = ignoreCancel || false ignoreCancel = ignoreCancel || false
// @ts-ignore
let executor = exec.name || exec.executor || '[anonymous]'
// noinspection JSUnusedGlobalSymbols // noinspection JSUnusedGlobalSymbols
var listener = this.register(eventCls, this.execute(name, exec, eventCls), priority, ignoreCancel) var listener = this.register(eventCls, this.execute(name, exec, eventCls), priority, ignoreCancel)
var listenerMap = this.listenerMap var listenerMap = this.listenerMap
// add to cache Be used for close plugin to close event // add to cache Be used for close plugin to close event
if (!listenerMap[name]) listenerMap[name] = [] if (!listenerMap[name]) listenerMap[name] = []
var offExec = () => { var off = () => {
this.unregister(eventCls, listener) this.unregister(eventCls, listener)
console.debug(i18n.translate("ms.api.event.unregister", { name, event: this.class2Name(eventCls), exec: exec.name || '[anonymous]' })) console.debug(i18n.translate("ms.api.event.unregister", { name, event: this.class2Name(eventCls), exec: executor }))
}
var off = {
event: eventCls,
listener: listener,
off: offExec
} }
listenerMap[name].push(off) listenerMap[name].push(off)
// noinspection JSUnresolvedVariable // 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 return off
} }
@ -157,7 +154,7 @@ export namespace event {
disable(plugin: any) { disable(plugin: any) {
var eventCache = this.listenerMap[plugin.description.name] var eventCache = this.listenerMap[plugin.description.name]
if (eventCache) { if (eventCache) {
eventCache.forEach(t => t.off()) eventCache.forEach(off => off())
delete this.listenerMap[plugin.description.name] delete this.listenerMap[plugin.description.name]
} }
} }

View File

@ -38,10 +38,6 @@ export namespace plugin {
* *
*/ */
type: string type: string
/**
*
*/
name?: string
/** /**
* *
*/ */
@ -116,22 +112,22 @@ export namespace plugin {
* Load * Load
* @param plugin * @param plugin
*/ */
load(plugin: Plugin): void load?(plugin: Plugin): void
/** /**
* Enable * Enable
* @param plugin * @param plugin
*/ */
enable(plugin: Plugin): void enable?(plugin: Plugin): void
/** /**
* Disable * Disable
* @param plugin * @param plugin
*/ */
disable(plugin: Plugin): void disable?(plugin: Plugin): void
/** /**
* Reload * Reload
* @param plugin * @param plugin
*/ */
reload(plugin: Plugin): void reload?(plugin: Plugin): void
} }
export interface Plugin { export interface Plugin {
description: PluginMetadata description: PluginMetadata

View File

@ -9,7 +9,7 @@ const Messenger = Bukkit.getMessenger()
export class BukkitChannel extends channel.Channel { export class BukkitChannel extends channel.Channel {
@inject(plugin.PluginInstance) @inject(plugin.PluginInstance)
private pluginInstance: any private pluginInstance: any
private cacheChannel = new Map<string, any[]>()
/** /**
* *
* @param player * @param player
@ -26,15 +26,29 @@ export class BukkitChannel extends channel.Channel {
* @param listener * @param listener
*/ */
register(channel: string, listener: channel.ChannelListener) { 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) => { onPluginMessageReceived: (/**String */ channel, /**Player */ player, /**byte[] */data) => {
try {
listener(data, { channel, player, data }) listener(data, { channel, player, data })
} catch (error) {
console.ex(error)
} }
})) }
})
Messenger.registerIncomingPluginChannel(this.pluginInstance, channel, pluginMessageListener)
Messenger.registerOutgoingPluginChannel(this.pluginInstance, channel) Messenger.registerOutgoingPluginChannel(this.pluginInstance, channel)
return pluginMessageListener
} }
unregister(channel: string, listener: any) { unregister(channel: string, listener: any) {
Messenger.unregisterIncomingPluginChannel(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) Messenger.unregisterOutgoingPluginChannel(this.pluginInstance, channel)
} }
}
} }

View File

@ -5,12 +5,12 @@ import bukkitChat from './enhance/chat'
@provideSingleton(chat.Chat) @provideSingleton(chat.Chat)
export class BukkitChat extends chat.Chat { export class BukkitChat extends chat.Chat {
sendMessage(sender: any, message: string) { sendMessage(sender: any, message: string) {
bukkitChat.send(sender, { text: message }, 0) bukkitChat.send(sender, JSON.stringify({ text: message }), 0)
} }
sendActionBar(sender: any, message: string) { 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) sender.sendTitle(title, subtitle, fadeIn, time, fadeOut)
} }
} }

View File

@ -9,6 +9,7 @@ let RemapUtils: any
let playerConnectionFieldName: string let playerConnectionFieldName: string
let sendPacketMethodName: string let sendPacketMethodName: string
let above_1_16 = false
let downgrade = false let downgrade = false
/** /**
* NMS版本 * NMS版本
@ -59,12 +60,18 @@ function init() {
let packetTypeClass = nmsCls("PacketPlayOutChat") let packetTypeClass = nmsCls("PacketPlayOutChat")
PacketPlayOutChat = Java.type(packetTypeClass.getName()) PacketPlayOutChat = Java.type(packetTypeClass.getName())
let packetTypeConstructor: { parameterTypes: any[] } 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) { if (c.parameterTypes.length === 2) {
packetTypeConstructor = c 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()) { if (nmsChatMessageTypeClass.isEnum()) {
chatMessageTypes = nmsChatMessageTypeClass.getEnumConstants() chatMessageTypes = nmsChatMessageTypeClass.getEnumConstants()
} }
@ -83,7 +90,13 @@ function json(sender: { name: string }, json: string) {
} }
function send(sender: any, json: any, type: number) { 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) { function sendPacket(player: { handle: { [x: string]: { [x: string]: (arg0: any) => void } } }, p: any) {

View File

@ -3,13 +3,14 @@
import { server } from '@ccms/api' import { server } from '@ccms/api'
import { Container } from '@ccms/container' import { Container } from '@ccms/container'
import { BukkitConsole } from './console'; import { BukkitConsole } from './console'
import './event'; import './chat'
import './server'; import './task'
import './command'; import './event'
import './channel'; import './server'
import './task'; import './command'
import './channel'
export default function BukkitImpl(container: Container) { export default function BukkitImpl(container: Container) {
container.bind(server.Console).toConstantValue(BukkitConsole); container.bind(server.Console).toConstantValue(BukkitConsole)
} }

View File

@ -63,4 +63,7 @@ export class BukkitServer extends server.ReflectServer {
this.dispatchConsoleCommand(result) this.dispatchConsoleCommand(result)
} }
} }
tabComplete?(sender: any, input: string, index?: number): string[] {
throw new Error("Method not implemented.")
}
} }

View File

@ -1,7 +1,9 @@
import * as querystring from 'querystring'
const URL = Java.type('java.net.URL') const URL = Java.type('java.net.URL')
const Paths = Java.type('java.nio.file.Paths'); const Paths = Java.type('java.nio.file.Paths')
const Files = Java.type('java.nio.file.Files'); const Files = Java.type('java.nio.file.Files')
const StandardCopyOption = Java.type('java.nio.file.StandardCopyOption'); const StandardCopyOption = Java.type('java.nio.file.StandardCopyOption')
export type Method = export type Method =
| 'get' | 'GET' | 'get' | 'GET'
@ -13,36 +15,49 @@ export type Method =
| 'patch' | 'PATCH' | 'patch' | 'PATCH'
interface RequestConfig { interface RequestConfig {
url?: string; url?: string
method?: Method; method?: Method
headers?: { [key: string]: string }; headers?: { [key: string]: string }
params?: { [key: string]: string }; params?: { [key: string]: string }
data?: any; data?: any
} }
function request(config: RequestConfig) { function request(config: RequestConfig) {
// @ts-ignore // @ts-ignore
let xhr = new XMLHttpRequest(); let xhr = new XMLHttpRequest()
xhr.open(config.method, config.url, false); xhr.open(config.method, config.url, false)
for (const header in config.headers) { 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)); let body = config.data
if ((xhr.getResponseHeader("Content-Type") + '').indexOf('application/json') != -1) { 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" xhr.responseType = "json"
} }
return xhr.get(); return xhr.get()
} }
function download(url: string, target: string) { function download(url: string, target: string) {
console.debug(`Start Download file ${target} from ${url}....`) 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...`) console.debug(`File ${target} Download Complate...`)
} }
function _proxy(method: Method) { function _proxy(method: Method) {
return function (url: string, data?: any, config?: RequestConfig) { return function (url: string, data?: any, config: RequestConfig = {}) {
return request({ url, method, data, ...config }); if (!config.headers) { config.headers = {} }
config.headers['Content-Type'] = config.headers['Content-Type'] ?? 'application/json'
return request({ url, method, data, ...config })
} }
} }

View File

@ -36,10 +36,10 @@ export const provideSingletonNamed = (identifier: interfaces.ServiceIdentifier<a
* @param className Java全类名 * @param className Java全类名
*/ */
export const JavaClass = (className: string) => { export const JavaClass = (className: string) => {
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] = Java.type(className).class; return } catch (error) { }
try { target[propertyKey] = base.getClass(className); 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 * @param className Java
*/ */
export const JSClass = (className: string) => { 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] = Java.type(className); return } catch (error) { }
try { target[propertyKey] = base.getClass(className).static; 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!')
} }
} }

View File

@ -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.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.execute.error: "§6插件 §b{name} §6处理 §d{event} §6事件时发生异常 §4{ex}"
ms.api.event.listen.plugin.name.empty: "插件名称为空 请检查传入参数!" ms.api.event.listen.plugin.name.empty: "插件名称为空 请检查传入参数!"
ms.api.event.register: "[{name}] 注册事件 {event} => {exec}" ms.api.event.register: "[{name}] 注册事件 {event} => 执行器 {exec}"
ms.api.event.unregister: "[{name}] 注销事件 {event} => {exec}" ms.api.event.unregister: "[{name}] 注销事件 {event} => 执行器 {exec}"
ms.api.command.register.input.error: "CommandExec 必须为一个函数... 输入: {exec}" ms.api.command.register.input.error: "CommandExec 必须为一个函数... 输入: {exec}"
ms.api.command.register: "[{plugin}] 注册命令 {name}({cmd})..." ms.api.command.register: "[{plugin}] 注册命令 {name}({cmd})..."
ms.api.command.unregister: "[{plugin}] 注销命令 {name}..." ms.api.command.unregister: "[{plugin}] 注销命令 {name}..."

View File

@ -1,39 +1,39 @@
import '@ccms/nashorn' import '@ccms/nashorn'
const URL = Java.type("java.net.URL"); const URL = Java.type("java.net.URL")
const Files = Java.type("java.nio.file.Files"); const Files = Java.type("java.nio.file.Files")
const StandardCopyOption = Java.type("java.nio.file.StandardCopyOption"); const StandardCopyOption = Java.type("java.nio.file.StandardCopyOption")
const JavaString = Java.type("java.lang.String"); const JavaString = Java.type("java.lang.String")
const SecureRandom = Java.type("java.security.SecureRandom"); const SecureRandom = Java.type("java.security.SecureRandom")
const SSLContext = Java.type("javax.net.ssl.SSLContext"); const SSLContext = Java.type("javax.net.ssl.SSLContext")
const HttpsURLConnection = Java.type("javax.net.ssl.HttpsURLConnection"); const HttpsURLConnection = Java.type("javax.net.ssl.HttpsURLConnection")
const HostnameVerifier = Java.type("javax.net.ssl.HostnameVerifier"); const HostnameVerifier = Java.type("javax.net.ssl.HostnameVerifier")
const X509TrustManager = Java.type("javax.net.ssl.X509TrustManager"); 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 Executors = Java.type('java.util.concurrent.Executors')
const UTF_8 = "UTF-8" const UTF_8 = "UTF-8"
const TrustAnyHostnameVerifier = new HostnameVerifier({ verify: () => true }); const TrustAnyHostnameVerifier = new HostnameVerifier({ verify: () => true })
const SSLSocketFactory = function initSSLSocketFactory() { const SSLSocketFactory = function initSSLSocketFactory() {
let sslContext = SSLContext.getInstance("TLS"); let sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, [new X509TrustManager({ sslContext.init(null, [new X509TrustManager({
getAcceptedIssuers: () => null, getAcceptedIssuers: () => null,
checkClientTrusted: () => { }, checkClientTrusted: () => { },
checkServerTrusted: () => { } checkServerTrusted: () => { }
})], new SecureRandom()); })], new SecureRandom())
return sslContext.getSocketFactory(); return sslContext.getSocketFactory()
}(); }()
interface Future<T> { interface Future<T> {
cancel(): boolean; cancel(): boolean
isCancelled(): boolean; isCancelled(): boolean
isDone(): boolean; isDone(): boolean
get(): T; get(): T
} }
enum ReadyState { enum ReadyState {
@ -50,14 +50,14 @@ type RequestMethod =
| 'options' | 'OPTIONS' | 'options' | 'OPTIONS'
| 'post' | 'POST' | 'post' | 'POST'
| 'put' | 'PUT' | 'put' | 'PUT'
| 'patch' | 'PATCH'; | 'patch' | 'PATCH'
type ResponseType = type ResponseType =
| 'arraybuffer' | 'arraybuffer'
| 'blob' | 'blob'
| 'document' | 'document'
| 'json' | 'json'
| 'text' | 'text'
| 'stream'; | 'stream'
type EventType = type EventType =
| 'load' | 'load'
| 'error' | 'error'
@ -65,191 +65,191 @@ type EventType =
| 'progress' | 'progress'
| 'timeout' | 'timeout'
| 'loadend' | 'loadend'
| 'loadstart'; | 'loadstart'
type HttpHeader = { [key: string]: string }; type HttpHeader = { [key: string]: string }
const executor = Executors.newCachedThreadPool(); const executor = Executors.newCachedThreadPool()
export class XMLHttpRequest { export class XMLHttpRequest {
private _timeout: number; private _timeout: number
private _responseType: ResponseType = 'text'; private _responseType: ResponseType = 'text';
private _withCredentials: boolean; private _withCredentials: boolean
private _readyState: ReadyState = ReadyState.UNSENT; private _readyState: ReadyState = ReadyState.UNSENT;
private _method: RequestMethod; private _method: RequestMethod
private _url: string; private _url: string
private _async: boolean; private _async: boolean
private _mimeType: string; private _mimeType: string
private _requestHeaders: HttpHeader = {}; private _requestHeaders: HttpHeader = {};
private _status: number = 0; private _status: number = 0;
private _statusText: string = null; private _statusText: string = null;
private _response: any; private _response: any
private _responseText: any; private _responseText: any
private _responseURL: string; private _responseURL: string
private _responseHeaders: HttpHeader = {}; private _responseHeaders: HttpHeader = {};
private _connection = null; private _connection = null;
get timeout() { get timeout() {
return this._timeout; return this._timeout
} }
set timeout(timeout: number) { set timeout(timeout: number) {
this._timeout = timeout this._timeout = timeout
} }
get readyState() { get readyState() {
return this._readyState; return this._readyState
} }
set responseType(type: ResponseType) { set responseType(type: ResponseType) {
this._responseType = type; this._responseType = type
} }
get responseType() { get responseType() {
return this._responseType; return this._responseType
} }
get status() { get status() {
return this._status; return this._status
} }
get statusText() { get statusText() {
return this._statusText; return this._statusText
} }
get response() { get response() {
return this._response || this.get(); return this._response || this.get()
} }
get responseText() { get responseText() {
return this._responseText; return this._responseText
} }
get responseXML() { get responseXML() {
return this._response; return this._response
} }
get responseURL() { get responseURL() {
return this._responseURL; return this._responseURL
} }
public onload: () => void; public onload: () => void
public onerror: (ex: Error) => void; public onerror: (ex: Error) => void
public onabort: () => void; public onabort: () => void
public onprogress: () => void; public onprogress: () => void
public ontimeout: (ex: Error) => void; public ontimeout: (ex: Error) => void
public onloadend: () => void; public onloadend: () => void
public onloadstart: () => void; public onloadstart: () => void
public onreadystatechange: () => void; public onreadystatechange: () => void
setRequestHeader(key: string, val: string) { setRequestHeader(key: string, val: string) {
this._requestHeaders[key] = val; this._requestHeaders[key] = val
} }
getResponseHeader(key: string): string { getResponseHeader(key: string): string {
return this._responseHeaders[key]; return this._responseHeaders[key]
} }
getAllResponseHeaders(): any { getAllResponseHeaders(): any {
return this._responseHeaders; return this._responseHeaders
} }
addEventListener(event: EventType, callback: Function) { addEventListener(event: EventType, callback: Function) {
this[`on${event.toLowerCase()}`] = callback; this[`on${event.toLowerCase()}`] = callback
} }
overrideMimeType(mimeType: string) { overrideMimeType(mimeType: string) {
this._mimeType = mimeType; this._mimeType = mimeType
} }
open(method: RequestMethod, url: string, async: boolean = true, user?: string, password?: string) { open(method: RequestMethod, url: string, async: boolean = true, user?: string, password?: string) {
if (this._readyState !== ReadyState.UNSENT) { throw new Error(`Error Status ${this._readyState}!`) } if (this._readyState !== ReadyState.UNSENT) { throw new Error(`Error Status ${this._readyState}!`) }
this._method = method; this._method = method
this._url = url; this._url = url
this._async = async; this._async = async
this._connection = new URL(this._url).openConnection() this._connection = new URL(this._url).openConnection()
if (this._connection instanceof HttpsURLConnection) { if (this._connection instanceof HttpsURLConnection) {
this._connection.setHostnameVerifier(TrustAnyHostnameVerifier); this._connection.setHostnameVerifier(TrustAnyHostnameVerifier)
this._connection.setSSLSocketFactory(SSLSocketFactory); this._connection.setSSLSocketFactory(SSLSocketFactory)
} }
this._connection.setRequestMethod(this._method); this._connection.setRequestMethod(this._method)
this._connection.setDoOutput(true); this._connection.setDoOutput(true)
this._connection.setDoInput(true); this._connection.setDoInput(true)
this._connection.setConnectTimeout(this._timeout); this._connection.setConnectTimeout(this._timeout)
this._connection.setReadTimeout(this._timeout); this._connection.setReadTimeout(this._timeout)
this.setReadyState(ReadyState.OPENED); this.setReadyState(ReadyState.OPENED)
} }
send(body?: string | object): Future<string> { send(body?: string | object): Future<string> {
for (const header in this._requestHeaders) { 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}!`) } 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() } if (!this._async) { future.get() }
return future; return future
} }
get() { get() {
if (this._response === undefined) { if (this._response === undefined) {
switch (this._responseType) { switch (this._responseType) {
case "json": case "json":
return this._response = JSON.parse(this._responseText); return this._response = JSON.parse(this._responseText)
case "text": case "text":
return this._response = this._responseText; return this._response = this._responseText
default: default:
throw Error(`Unsupport ResponseType: ${this._responseType} !`) throw Error(`Unsupport ResponseType: ${this._responseType} !`)
} }
} }
return this._response; return this._response
} }
abort() { abort() {
this._connection.disconnect(); this._connection.disconnect()
this.onabort && this.onabort(); this.onabort && this.onabort()
} }
private _send(body?: string | object) { private _send(body?: string | object) {
try { try {
this._connection.connect(); this._connection.connect()
this.onloadstart && this.onloadstart(); this.onloadstart && this.onloadstart()
if (body) { 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!`) } if (typeof body !== "string") { throw new Error(`body(${bodyType}) must be string!`) }
var out = this._connection.getOutputStream(); var out = this._connection.getOutputStream()
out.write(new JavaString(body).getBytes(UTF_8)); out.write(new JavaString(body).getBytes(UTF_8))
out.flush(); out.flush()
out.close(); out.close()
} }
this.setReadyState(ReadyState.LOADING); this.setReadyState(ReadyState.LOADING)
this._status = this._connection.getResponseCode(); this._status = this._connection.getResponseCode()
this._statusText = this._connection.getResponseMessage(); this._statusText = this._connection.getResponseMessage()
if (this._status >= 0 && this._status < 300) { 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) { } else if (this._status >= 300 && this._status < 400) {
this._responseURL = this.getResponseHeader('Location'); this._responseURL = this.getResponseHeader('Location')
} else { } else {
this._responseText = this.readOutput(this._connection.getErrorStream()); this._responseText = this.readOutput(this._connection.getErrorStream())
} }
this.setResponseHeaders(this._connection.getHeaderFields()); this.setResponseHeaders(this._connection.getHeaderFields())
this.onloadend && this.onloadend(); this.onloadend && this.onloadend()
} catch (ex) { } catch (ex) {
if (ex instanceof SocketTimeoutException && this.ontimeout) { if (ex instanceof SocketTimeoutException && this.ontimeout) {
return this.ontimeout(ex) return this.ontimeout(ex)
} else if (this.onerror) { } else if (this.onerror) {
return this.onerror(ex); return this.onerror(ex)
} }
throw ex; throw ex
} finally { } finally {
this._connection.disconnect(); this._connection.disconnect()
this.setReadyState(ReadyState.DONE); this.setReadyState(ReadyState.DONE)
} }
} }
private setResponseHeaders(header: any) { private setResponseHeaders(header: any) {
header.forEach((key: string | number, value: string | 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) { private setReadyState(state: ReadyState) {
this._readyState = state; this._readyState = state
this.onreadystatechange && this.onreadystatechange(); this.onreadystatechange && this.onreadystatechange()
} }
private readOutput(input: any) { private readOutput(input: any) {
var tempFile = Files.createTempFile('xhr', '.response'); var tempFile = Files.createTempFile('xhr', '.response')
Files.copy(input, tempFile, StandardCopyOption['REPLACE_EXISTING']); tempFile.toFile().deleteOnExit(); Files.copy(input, tempFile, StandardCopyOption['REPLACE_EXISTING']); tempFile.toFile().deleteOnExit()
return new JavaString(Files.readAllBytes(tempFile), 'UTF-8'); return new JavaString(Files.readAllBytes(tempFile), 'UTF-8')
} }
} }

View File

@ -30,6 +30,7 @@ export function plugin(metadata: pluginApi.PluginMetadata | any) {
export function cmd(metadata: interfaces.CommandMetadata = {}) { export function cmd(metadata: interfaces.CommandMetadata = {}) {
return function (target: any, key: string, value: any) { return function (target: any, key: string, value: any) {
metadata.name = metadata.name || key metadata.name = metadata.name || key
metadata.target = target
metadata.executor = key metadata.executor = key
metadata.paramtypes = Reflect.getMetadata("design:paramtypes", target, key) metadata.paramtypes = Reflect.getMetadata("design:paramtypes", target, key)
const previousMetadata: Map<string, interfaces.CommandMetadata> = getPluginCommandMetadata(target) const previousMetadata: Map<string, interfaces.CommandMetadata> = getPluginCommandMetadata(target)
@ -46,6 +47,7 @@ export function tab(metadata: interfaces.CommandMetadata = {}) {
return function (target: any, key: string, value: any) { return function (target: any, key: string, value: any) {
metadata.name = metadata.name || (key.startsWith('tab') ? key.split('tab', 2)[1] : key) metadata.name = metadata.name || (key.startsWith('tab') ? key.split('tab', 2)[1] : key)
if (!metadata.name) { return } if (!metadata.name) { return }
metadata.target = target
metadata.executor = key metadata.executor = key
metadata.paramtypes = Reflect.getMetadata("design:paramtypes", target, key) metadata.paramtypes = Reflect.getMetadata("design:paramtypes", target, key)
const previousMetadata: Map<string, interfaces.CommandMetadata> = getPluginTabCompleterMetadata(target) const previousMetadata: Map<string, interfaces.CommandMetadata> = getPluginTabCompleterMetadata(target)
@ -61,9 +63,11 @@ export function tab(metadata: interfaces.CommandMetadata = {}) {
export function listener(metadata: interfaces.ListenerMetadata = {}) { export function listener(metadata: interfaces.ListenerMetadata = {}) {
return function (target: any, key: string, value: any) { return function (target: any, key: string, value: any) {
metadata.name = metadata.name || key metadata.name = metadata.name || key
metadata.target = target
metadata.executor = key metadata.executor = key
const previousMetadata: interfaces.ListenerMetadata[] = getPluginListenerMetadata(target) const previousMetadata: interfaces.ListenerMetadata[] = getPluginListenerMetadata(target)
Reflect.defineMetadata(METADATA_KEY.listener, [metadata, ...previousMetadata], target.constructor) Reflect.defineMetadata(METADATA_KEY.listener, [metadata, ...previousMetadata], target.constructor)
Reflect.defineMetadata(METADATA_KEY.listener, metadata, target[key])
} }
} }

View File

@ -20,7 +20,10 @@ export class PluginEventManager {
// ignore space listener // ignore space listener
if (!this.ServerChecker.check(event.servers)) { continue } if (!this.ServerChecker.check(event.servers)) { continue }
// here must bind this to pluginInstance // 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)
} }
} }

View File

@ -25,6 +25,10 @@ export namespace interfaces {
public disable() { } public disable() { }
} }
export interface ExecMetadata extends plugin.BaseMetadata { export interface ExecMetadata extends plugin.BaseMetadata {
/**
* this
*/
target?: any
/** /**
* *
*/ */
@ -68,5 +72,4 @@ export namespace interfaces {
*/ */
readonly?: boolean readonly?: boolean
} }
export type PluginLike = Plugin | string
} }

View File

@ -1,9 +1,11 @@
import { plugin } from "@ccms/api" 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 { export class BasicLoader implements plugin.PluginLoader {
type: string = 'basic' type: string = LOADER_TYPE_NAME
private pluginRequireMap: Map<string, any> private pluginRequireMap: Map<string, any>
@ -22,8 +24,4 @@ export class BasicLoader implements plugin.PluginLoader {
build(metadata: plugin.PluginMetadata) { build(metadata: plugin.PluginMetadata) {
return this.pluginRequireMap.get(metadata.source.toString()) 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 { }
} }

View File

@ -1,16 +1,18 @@
import { plugin, server } from "@ccms/api" 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 { interfaces } from "../interfaces"
import { getPluginStageMetadata, getPluginSources } from "../utils" 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 { export class IocLoader implements plugin.PluginLoader {
type: string = 'ioc' type: string = LOADER_TYPE_NAME
@inject(ContainerInstance) @inject(ContainerInstance)
private container: Container private container: Container
@inject(server.ServerType) @inject(server.ServerChecker)
private serverType: string private serverChecker: server.ServerChecker
private pluginMetadataMap: Map<string, plugin.PluginMetadata> private pluginMetadataMap: Map<string, plugin.PluginMetadata>
@ -28,7 +30,6 @@ export class IocLoader implements plugin.PluginLoader {
} }
build(metadata: plugin.PluginMetadata) { build(metadata: plugin.PluginMetadata) {
if (!this.allowProcess(metadata.servers)) { return }
let pluginInstance: plugin.Plugin let pluginInstance: plugin.Plugin
try { try {
this.bindPlugin(metadata) 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) { private stage(pluginInstance: plugin.Plugin, stageName: string) {
let stages = getPluginStageMetadata(pluginInstance, stageName) let stages = getPluginStageMetadata(pluginInstance, stageName)
for (const stage of stages) { 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 }) console.i18n("ms.plugin.manager.stage.exec", { plugin: pluginInstance.description.name, name: stage.executor, stage: stageName, servers: stage.servers })
try { try {
pluginInstance[stage.executor].apply(pluginInstance) pluginInstance[stage.executor].apply(pluginInstance)

View File

@ -19,6 +19,8 @@ export class PluginManagerImpl implements plugin.PluginManager {
private serverType: string private serverType: string
@inject(event.Event) @inject(event.Event)
private EventManager: event.Event private EventManager: event.Event
@inject(server.ServerChecker)
private serverChecker: server.ServerChecker
@inject(PluginCommandManager) @inject(PluginCommandManager)
private commandManager: PluginCommandManager private commandManager: PluginCommandManager
@ -74,7 +76,7 @@ export class PluginManagerImpl implements plugin.PluginManager {
try { try {
this.loadPlugin(scanner.load(loadMetadata)) this.loadPlugin(scanner.load(loadMetadata))
} catch (error) { } 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) console.ex(error)
} }
}) })
@ -102,7 +104,7 @@ export class PluginManagerImpl implements plugin.PluginManager {
process.emit(`plugin.before.${stage}`, plugin) process.emit(`plugin.before.${stage}`, plugin)
this.runCatch(plugin, stage) this.runCatch(plugin, stage)
this.runCatch(plugin, `${this.serverType}${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) process.emit(`plugin.after.${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 })
@ -112,7 +114,7 @@ export class PluginManagerImpl implements plugin.PluginManager {
private loadPlugin(loadMetadata: plugin.PluginLoadMetadata) { private loadPlugin(loadMetadata: plugin.PluginLoadMetadata) {
if (!loadMetadata) { throw new Error('loadMetadata can\'t be undefiend when loadPlugin!') } 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 { try {
for (const [, loader] of this.loaderMap) { for (const [, loader] of this.loaderMap) {
if (this.loaderRequirePlugin(loadMetadata, loader)?.loaded) return loadMetadata.metadata if (this.loaderRequirePlugin(loadMetadata, loader)?.loaded) return loadMetadata.metadata
@ -215,16 +217,17 @@ export class PluginManagerImpl implements plugin.PluginManager {
try { try {
this.buildPlugin(metadata) this.buildPlugin(metadata)
} catch (error) { } catch (error) {
console.error(error) console.console(error)
} }
}) })
} }
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.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) 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失败!`) }
if (this.instanceMap.has(metadata.name)) { throw new Error(`Plugin ${metadata.name} is already load from ${metadata.source}...`) }
this.instanceMap.set(metadata.name, pluginInstance) this.instanceMap.set(metadata.name, pluginInstance)
return pluginInstance return pluginInstance
} }

View File

@ -2,9 +2,11 @@ import { plugin } from "@ccms/api"
import * as fs from '@ccms/common/dist/fs' import * as fs from '@ccms/common/dist/fs'
import { provideSingletonNamed } from "@ccms/container" 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 { export class JSFileScanner implements plugin.PluginScanner {
type: string = 'file' type: string = SCANNER_TYPE_NAME
scan(target: any): plugin.PluginLoadMetadata[] { scan(target: any): plugin.PluginLoadMetadata[] {
return this.scanFolder(fs.concat(root, target)).map((file) => this.read(file)) return this.scanFolder(fs.concat(root, target)).map((file) => this.read(file))

View File

@ -0,0 +1,50 @@
# MiaoReward
## 插件简介
- 还在为服务器收入不足而倒闭烦恼嘛
- 还在为肝帝不氪金而烦恼嘛
- 快来接入 喵式奖励
- 通过QQ小程序广告 增加服务器收入
## 插件展示
- 插件帮助![image.png](https://i.loli.net/2020/09/12/uNbwO2sCVriZzJx.png)
- 扫码绑定![image.png](https://i.loli.net/2020/09/13/hOtyV8bP9kGeuCD.png)
- 查看个人信息![image.png](https://i.loli.net/2020/09/12/bTLGjQC7eqxAUSi.png)
- 兑换排行![image.png](https://i.loli.net/2020/09/12/zNbgKQChtj7IikD.png)
- 玩家兑换公告![image.png](https://i.loli.net/2020/09/12/JWFoUtwXBSP6fcA.png)
- 配置兑换比例![image.png](https://i.loli.net/2020/09/12/HpGVWErwZ7YTBxK.png)
- 服务器信息查询![image.png](https://i.loli.net/2020/09/12/CbvSDfg4BF39PLI.png)
## 插件命令
```
>mrd
[17:56:54 INFO]: [MS][MRD] ====== [广告系统] 帮助菜单 ======
[17:56:54 INFO]: [MS][MRD] /mrd bind 绑定圈云盒子
[17:56:54 INFO]: [MS][MRD] /mrd query 查询当前账户
[17:56:54 INFO]: [MS][MRD] /mrd draw <兑换数量> 兑换点券
[17:56:54 INFO]: [MS][MRD] 由于您是管理员 以为您展示额外命令
[17:56:54 INFO]: [MS][MRD] /mrd bind server 绑定服务器
[17:56:54 INFO]: [MS][MRD] /mrd ratio <兑换比例> 设置喵币/点券兑换比例
[17:56:54 INFO]: [MS][MRD] /mrd statistic 近期收入统计
[17:56:54 INFO]: [MS][MRD] /mrd rank <boardcast>(是否公告) 今日兑换排行
[17:56:54 INFO]: [MS][MRD] /mrd server 当前服务器信息
[17:56:54 INFO]: [MS][MRD] 兑换比例设置说明: 默认比例为 0.001 => 1000喵币 兑换 1点券
[17:56:54 INFO]: [MS][MRD] 注意 设置比例后 玩家兑换点券数量不能少于 1点券
[17:56:54 INFO]: [MS][MRD] 比如 设置了0.001 那就是 玩家至少 1000喵币 才能兑换!
```
## 插件安装/配置
### 基础准备工作
- 申请内测服务器账号
- QQ搜索小程序 `圈云盒子` 并登录
### 服务器准备工作
- 先安装 `MiaoScript` [站内地址]()
- 执行 `mpm install MiaoReward`
### 绑定服务器
- 执行 `/mrd bind server`
- 使用QQ扫码
- 选择需要绑定的服务器
- 完成绑定
- 执行 `/mrd server` 查看服务器信息
- 如需绑定多台 可以直接复制绑定之后的 `MiaoScript/plugins/MiaoReward/config.yml` 文件到多台服务器上 然后 `/mrd reload` 重载插件

View File

@ -1,34 +1,48 @@
import { constants, task, server, channel } from "@ccms/api" import { constants, task, server, channel, chat } from "@ccms/api"
import { JSPlugin, interfaces, Cmd, Tab, Listener, Config } from "@ccms/plugin" import { JSPlugin, interfaces, Cmd, Tab, Listener, Config } from "@ccms/plugin"
import { QRCode, QRErrorCorrectLevel } from '@ccms/common/dist/qrcode' import { QRCode, QRErrorCorrectLevel } from '@ccms/common/dist/qrcode'
import { inject, JSClass, optional } from '@ccms/container' import { inject, JSClass, optional } from '@ccms/container'
import http from '@ccms/common/dist/http' import http from '@ccms/common/dist/http'
import chat from '@ccms/bukkit/dist/enhance/chat'
const MapView = Java.type('org.bukkit.map.MapView') let MapView
const Bukkit = Java.type('org.bukkit.Bukkit') let Bukkit
const MapRenderer = Java.type('org.bukkit.map.MapRenderer') let MapRenderer
const ItemStack = Java.type('org.bukkit.inventory.ItemStack') let ItemStack
const Material = Java.type('org.bukkit.Material') let Material
try {
MapView = Java.type('org.bukkit.map.MapView')
Bukkit = Java.type('org.bukkit.Bukkit')
MapRenderer = Java.type('org.bukkit.map.MapRenderer')
ItemStack = Java.type('org.bukkit.inventory.ItemStack')
Material = Java.type('org.bukkit.Material')
} catch (error) {
}
const BufferedImage = Java.type('java.awt.image.BufferedImage') const BufferedImage = Java.type('java.awt.image.BufferedImage')
const Graphics2D = Java.type('java.awt.Graphics2D')
const Color = Java.type('java.awt.Color') const Color = Java.type('java.awt.Color')
const Image = Java.type('java.awt.Image')
const Font = Java.type('java.awt.Font')
const RenderingHints = Java.type('java.awt.RenderingHints')
class QRCodeRender { class QRCodeRender {
private _proxy private _proxy
constructor(image: any) { private image
const ProxyAdapter = Java.extend(MapRenderer, { private rendered = false
render: (_mapView, mapCanvas) => { constructor() {
mapCanvas.drawImage(0, 0, image) const MapRendererAdapter = Java.extend(MapRenderer, {
render: (_mapView, mapCanvas, _player) => {
if (this.rendered) return
if (this.image) {
mapCanvas.drawImage(0, 0, this.image)
}
this.rendered = true
} }
}) })
this._proxy = new ProxyAdapter() this._proxy = new MapRendererAdapter()
}
setImage(image) {
this.image = image
this.rendered = false
} }
getHandler() { getHandler() {
return this._proxy return this._proxy
@ -40,14 +54,21 @@ interface PlaceholderAPI {
setPlaceholders: (player: any, str: string) => string setPlaceholders: (player: any, str: string) => string
} }
@JSPlugin({ prefix: 'MRD', version: '1.1.0', author: 'MiaoWoo', servers: [constants.ServerType.Bukkit], source: __filename }) @JSPlugin({ prefix: 'MRD', version: '1.2.0', author: 'MiaoWoo', servers: [constants.ServerType.Bukkit], source: __filename })
export class MiaoReward extends interfaces.Plugin { export class MiaoReward extends interfaces.Plugin {
private serverInfo
private cacheBindUuid = '' private cacheBindUuid = ''
private cacheTasks = new Map<number, task.Cancelable>() private zeroMapView = undefined
private zeroMapRender: QRCodeRender = undefined
private isBinding = false private isBinding = false
private isBindingUser = 'unknow' private bindingUser = 'unknow'
private bindingTask = undefined
private bindingLeftTime = 45
private bindingNotify = new java.util.HashSet<org.bukkit.entity.Player>()
private drawCooldown = new Map<string, number>() private drawCooldown = new Map<string, number>()
@inject(chat.Chat)
private chat: chat.Chat
@inject(server.Server) @inject(server.Server)
private server: server.Server private server: server.Server
@inject(task.TaskManager) @inject(task.TaskManager)
@ -76,9 +97,32 @@ export class MiaoReward extends interfaces.Plugin {
this.config.prefix = this.config.prefix || '§6[§b广告系统§6]§r' this.config.prefix = this.config.prefix || '§6[§b广告系统§6]§r'
this.config.drawCommand = this.config.drawCommand || 'p give %player_name% %amount%' this.config.drawCommand = this.config.drawCommand || 'p give %player_name% %amount%'
this.config.drawCooldown = this.config.drawCooldown || 300 this.config.drawCooldown = this.config.drawCooldown || 300
this.taskManager.create(() => {
if (this.config.serverId) {
let result = this.httpPost(`https://reward.yumc.pw/server/server`, {
id: this.config.serverId,
token: this.config.serverToken
})
if (result.code == 200) {
this.serverInfo = result.data
}
}
}).async().submit()
} }
enable() { enable() {
this.initPlaceholderAPI()
this.channelOff = this.Channel?.listen(this, 'BungeeCord', () => {
this.isBungeeCord = true
this.PlayerJoinEvent['off']()
this.channelOff.off()
})
let players = this.server.getOnlinePlayers()
if (players.length) this.bungeeCordDetect(players[0])
this.initZeroMap()
}
private initPlaceholderAPI() {
if (!this.PlaceholderAPI) { if (!this.PlaceholderAPI) {
console.console("§cCan't found me.clip.placeholderapi.PlaceholderAPI variable will not be replaced!") console.console("§cCan't found me.clip.placeholderapi.PlaceholderAPI variable will not be replaced!")
} else { } else {
@ -93,13 +137,17 @@ export class MiaoReward extends interfaces.Plugin {
} }
})) }))
} }
this.channelOff = this.Channel?.listen(this, 'BungeeCord', (data) => { this.isBungeeCord = true }) }
let players = this.server.getOnlinePlayers()
if (players.length) this.bungeeCordDetect(players[0]) private initZeroMap() {
this.zeroMapRender = new QRCodeRender()
this.zeroMapView = Bukkit.getMap(0) || Bukkit.createMap(Bukkit.getWorlds()[0])
this.zeroMapView.setScale(MapView.Scale.FARTHEST)
this.zeroMapView.getRenderers().forEach(r => this.zeroMapView.removeRenderer(r))
this.zeroMapView.addRenderer(this.zeroMapRender.getHandler())
} }
disable() { disable() {
this.cacheTasks.forEach(t => t.cancel())
this.server.getOnlinePlayers().forEach(p => this.checkAndClear(p)) this.server.getOnlinePlayers().forEach(p => this.checkAndClear(p))
this.channelOff?.off() this.channelOff?.off()
} }
@ -117,31 +165,83 @@ export class MiaoReward extends interfaces.Plugin {
this.taskManager.create(() => this[cmdKey](sender, ...args)).async().submit() this.taskManager.create(() => this[cmdKey](sender, ...args)).async().submit()
} }
cmdbind(sender: org.bukkit.entity.Player, server: boolean) { private scanAuth(sender: org.bukkit.entity.Player, scanType: string, scanObj: { title: string, content: string }, success: (token: string, user: any) => void, cancel?: () => void) {
this.chat.sendTitle(sender, this.config.prefix, '§a正在获取授权二维码...')
let scan = this.httpPost('https://reward.yumc.pw/auth/scan', { ...scanObj, type: scanType })
if (scan.code == 200) {
let sync = { scaned: false, timeout: false }
this.taskManager.create(() => {
let result = this.httpPost('https://reward.yumc.pw/auth/scanCheck', {
token: scan.data.token,
type: scanType,
status: 'noscan'
})
sync.scaned = true
if (result.code == 200 && result.data.status == "scaned") {
this.chat.sendTitle(sender, "§3已扫码", "§a请在手机上确认")
let result = this.httpPost('https://reward.yumc.pw/auth/scanCheck', {
token: scan.data.token,
type: scanType,
status: 'scaned'
})
if (result.code == 200) {
if (result.data.status == "confirm") {
this.chat.sendTitle(sender, '§3扫码完成')
success(scan.data.token, result.data.user)
} else if (result.data.status == "cancel") {
this.chat.sendTitle(sender, '§c已取消授权')
cancel?.()
} else {
this.chat.sendTitle(sender, "§c未知的结果", result.data.status)
}
} else {
this.chat.sendTitle(sender, "§4扫码异常", result.msg)
}
}
sync.scaned = true
}).async().submit()
this.setItemAndTp(sender, scan.data.url, sync)
this.chat.sendTitle(sender, '')
} else {
this.logger.sender(sender, '§4授权二维码获取失败!')
}
}
private bindCheck(sender: org.bukkit.entity.Player, cooldown: number) {
if (this.isBinding) { if (this.isBinding) {
let bindUser = Bukkit.getPlayerExact(this.isBindingUser) let bindUser = Bukkit.getPlayerExact(this.bindingUser)
if (bindUser && bindUser.isOnline() && this.isQrCodeItem(bindUser.getItemInHand())[0]) { if (bindUser && bindUser.isOnline() && this.isQrCodeItem(bindUser.getItemInHand())[0]) {
return this.logger.sender(sender, "§c当前 §a" + this.isBindingUser + " §c玩家正在绑定账号 请稍候重试...") this.bindingNotify.add(sender)
this.logger.sender(sender, "§c当前 §a" + this.bindingUser + " §c玩家正在扫码 §6请稍候 §e" + this.bindingLeftTime + "秒 §6后重试...")
return true
} }
} }
if (sender.getItemInHand()?.getType() !== Material.AIR) { return this.logger.sender(sender, "§c请空手执行此命令!") } if (this.drawCooldown.has(sender.getName())) {
let leftTime = cooldown - (Date.now() - this.drawCooldown.get(sender.getName())) / 1000
if (leftTime > 0) {
return this.logger.sender(sender, `§c扫码功能冷却中 剩余 ${leftTime} 秒!`)
}
}
this.drawCooldown.set(sender.getName(), Date.now())
this.isBinding = true this.isBinding = true
this.isBindingUser = sender.getDisplayName() || sender.getName() this.bindingUser = sender.getName()
this.bindingNotify.clear()
return false
}
cmdbind(sender: org.bukkit.entity.Player, server: boolean) {
if (this.bindCheck(sender, 60)) return
if (sender.getItemInHand()?.getType() !== Material.AIR) { return this.logger.sender(sender, "§c请空手执行此命令!") }
if (server) { if (server) {
if (!sender.isOp()) { return this.logger.sender(sender, '§4您没有配置服务器的权限!') } if (!sender.isOp()) { return this.logger.sender(sender, '§4您没有配置服务器的权限!') }
this.bindServer(sender) this.bindServer(sender)
} else { } else {
if (!this.serverInfo) { return this.logger.sender(sender, '§4当前服务器尚未配置绑定ID 请联系腐竹进行配置!') }
this.bindUser(sender) this.bindUser(sender)
} }
} }
cmddraw(sender: org.bukkit.entity.Player, amount: number) { cmddraw(sender: org.bukkit.entity.Player, amount: number) {
if (this.drawCooldown.has(sender.getName())) {
let leftTime = this.config.drawCooldown - (Date.now() - this.drawCooldown.get(sender.getName())) / 1000
if (leftTime > 0) {
return this.logger.sender(sender, `§c点券兑换冷却中 剩余 ${leftTime} 秒!`)
}
}
amount = Number(amount) amount = Number(amount)
if (!Number.isInteger(amount)) { if (!Number.isInteger(amount)) {
return this.logger.sender(sender, '§4金额必须是数字!') return this.logger.sender(sender, '§4金额必须是数字!')
@ -149,8 +249,26 @@ export class MiaoReward extends interfaces.Plugin {
if (amount % 100 !== 0) { if (amount % 100 !== 0) {
return this.logger.sender(sender, '§4金额必须是100倍数!') return this.logger.sender(sender, '§4金额必须是100倍数!')
} }
let address = `https://reward.yumc.pw/server/draw/id/${this.config.serverId}/token/${this.config.serverToken}/uuid/${sender.getUniqueId().toString()}/username/${sender.getName()}/amount/${amount}` if (this.bindCheck(sender, this.config.drawCooldown)) { return }
let draw = http.get(address) this.scanAuth(sender,
'draw', {
title: '兑换授权',
content: "是否授权 " + this.serverInfo.name + " 服务器\n兑换 " + amount + " 喵币 到 " + sender.getName()
}, (token: string) => {
this.drawCoin(sender, amount, token)
})
}
private drawCoin(sender: org.bukkit.entity.Player, amount: number, token: string) {
if (!token) return
let draw = this.httpPost('https://reward.yumc.pw/server/draw', {
id: this.config.serverId,
token: this.config.serverToken,
uuid: sender.getUniqueId().toString(),
username: sender.getName(),
amount,
userToken: token
})
if (draw.code !== 200) { if (draw.code !== 200) {
return this.sendError(sender, `§4兑换异常 §6服务器返回: §c${draw.msg}`) return this.sendError(sender, `§4兑换异常 §6服务器返回: §c${draw.msg}`)
} }
@ -158,14 +276,15 @@ export class MiaoReward extends interfaces.Plugin {
if (!drawAmount) { if (!drawAmount) {
return this.sendError(sender, '§c服务器返回金额 ' + draw.data + ' 可能存在异常') return this.sendError(sender, '§c服务器返回金额 ' + draw.data + ' 可能存在异常')
} }
let command = `p give ${sender.getName()} ${draw.data}` this.taskManager.create(() => {
let command = this.config.drawCommand.replace('%player_name%', sender.getName()).replace('%amount%', draw.data)
if (!this.server.dispatchConsoleCommand(command)) { if (!this.server.dispatchConsoleCommand(command)) {
return this.sendError(sender, '§6执行命令 §3/' + command + ' §c可能存在异常') return this.sendError(sender, '§6执行命令 §3/' + command + ' §c可能存在异常')
} }
this.logger.sender(sender, draw.msg.split('\n')) this.logger.sender(sender, draw.msg.split('\n'))
this.drawCooldown.set(sender.getName(), Date.now())
this.sendBoardcast(sender, `${this.config.prefix}§6玩家 §b${sender.getName()} §6成功将 §a${amount}喵币 §6兑换成 §c${draw.data}点券!`) this.sendBoardcast(sender, `${this.config.prefix}§6玩家 §b${sender.getName()} §6成功将 §a${amount}喵币 §6兑换成 §c${draw.data}点券!`)
this.sendBoardcast(sender, `${this.config.prefix}§c/mrd help §b查看广告系统帮助 §6快来一起看广告赚点券吧!`) this.sendBoardcast(sender, `${this.config.prefix}§c/mrd help §b查看广告系统帮助 §6快来一起看广告赚点券吧!`)
}).submit()
} }
private sendError(sender, error) { private sendError(sender, error) {
@ -181,7 +300,10 @@ export class MiaoReward extends interfaces.Plugin {
cmdrank(sender: any, boardcast: boolean) { cmdrank(sender: any, boardcast: boolean) {
if (!sender.isOp()) { return this.logger.sender(sender, '§4你没有此命令的权限!') } if (!sender.isOp()) { return this.logger.sender(sender, '§4你没有此命令的权限!') }
let result = http.get(`https://reward.yumc.pw/server/rank/id/${this.config.serverId}/token/${this.config.serverToken}`) let result = this.httpPost(`https://reward.yumc.pw/server/rank`, {
id: this.config.serverId,
token: this.config.serverToken
})
if (result.code !== 200) { if (result.code !== 200) {
return this.logger.sender(sender, `§c今日未查询到数据!`) return this.logger.sender(sender, `§c今日未查询到数据!`)
} }
@ -199,7 +321,10 @@ export class MiaoReward extends interfaces.Plugin {
cmdserver(sender: any) { cmdserver(sender: any) {
if (!sender.isOp()) { return this.logger.sender(sender, '§4你没有此命令的权限!') } if (!sender.isOp()) { return this.logger.sender(sender, '§4你没有此命令的权限!') }
let result = http.get(`https://reward.yumc.pw/server/server/id/${this.config.serverId}/token/${this.config.serverToken}`) let result = this.httpPost(`https://reward.yumc.pw/server/server`, {
id: this.config.serverId,
token: this.config.serverToken
})
if (result.code !== 200) { if (result.code !== 200) {
return this.logger.sender(sender, `§4操作异常 §6服务器返回: §c${result.msg}`) return this.logger.sender(sender, `§4操作异常 §6服务器返回: §c${result.msg}`)
} }
@ -225,7 +350,11 @@ export class MiaoReward extends interfaces.Plugin {
]) ])
} }
if (confirm != 'confirm') return this.logger.sender(sender, `§6请执行 §b/mrd ratio §c${ratio} §econfirm §c确认修改!`) if (confirm != 'confirm') return this.logger.sender(sender, `§6请执行 §b/mrd ratio §c${ratio} §econfirm §c确认修改!`)
let result = http.get(`https://reward.yumc.pw/server/ratio/id/${this.config.serverId}/token/${this.config.serverToken}/ratio/${ratio}`) let result = this.httpPost(`https://reward.yumc.pw/server/ratio`, {
id: this.config.serverId,
token: this.config.serverToken,
ratio
})
if (result.code !== 200) { if (result.code !== 200) {
return this.logger.sender(sender, `§4操作异常 §6服务器返回: §c${result.msg}`) return this.logger.sender(sender, `§4操作异常 §6服务器返回: §c${result.msg}`)
} }
@ -249,77 +378,88 @@ export class MiaoReward extends interfaces.Plugin {
} }
private bindServer(sender: org.bukkit.entity.Player) { private bindServer(sender: org.bukkit.entity.Player) {
this.logger.sender(sender, '§a正在请求二维码 请稍候...')
let scanObj = http.get(`https://reward.yumc.pw/server/scan`) let scanObj = http.get(`https://reward.yumc.pw/server/scan`)
if (scanObj.code !== 200) { if (scanObj.code !== 200) {
return this.logger.sender(sender, '§c获取服务器绑定码失败! Error: ' + scanObj.msg) return this.logger.sender(sender, '§c获取服务器绑定码失败! Error: ' + scanObj.msg)
} }
this.cacheBindUuid = scanObj.data.uuid this.cacheBindUuid = scanObj.data.uuid
this.setItemAndTp(sender, scanObj.data.url, () => { let sync = { scaned: false }
let check = http.get(`https://reward.yumc.pw/server/check/token/${this.cacheBindUuid}`) this.taskManager.create(() => {
let check = this.httpPost(`https://reward.yumc.pw/server/check`, {
token: this.cacheBindUuid,
sync: true
})
if (check.code == 200) { if (check.code == 200) {
this.config.serverId = check.data.serverId this.config.serverId = check.data.serverId
this.config.serverToken = check.data.serverToken this.config.serverToken = check.data.serverToken
// @ts-ignore // @ts-ignore
this.config.save() this.config.save()
this.checkAndClear(sender)
this.logger.sender(sender, '§a已成功绑定服务器: §b' + check.data.serverName) this.logger.sender(sender, '§a已成功绑定服务器: §b' + check.data.serverName)
return true
} }
}) sync.scaned = true
}).async().submit()
this.setItemAndTp(sender, scanObj.data.url, sync)
} }
private bindUser(sender: org.bukkit.entity.Player) { private bindUser(sender: org.bukkit.entity.Player) {
if (!this.config.serverId || !this.config.serverToken) { return this.logger.sender(sender, '§4当前服务器尚未配置绑定ID 请联系腐竹进行配置!') } if (!this.config.serverId || !this.config.serverToken) { return this.logger.sender(sender, '§4当前服务器尚未配置绑定ID 请联系腐竹进行配置!') }
let check = http.get(`https://reward.yumc.pw/server/query?id=${this.config.serverId}&token=${this.config.serverToken}`) this.logger.sender(sender, '§a正在请求二维码 请稍候...')
let check = this.httpPost(`https://reward.yumc.pw/server/query`, {
id: this.config.serverId,
token: this.config.serverToken
})
if (check.code !== 200) { if (check.code !== 200) {
return this.logger.sender(sender, '§4获取绑定参数异常! §cError: ' + check.msg) return this.logger.sender(sender, '§4获取绑定参数异常! §cError: ' + check.msg)
} }
let queryUser = this.queryUser(sender) let queryUser = this.queryUser(sender)
if (queryUser.code == 200) { if (queryUser.code == 200) {
return this.logger.sender(sender, ['§4当前用户已绑定! §c如需解绑 请联系腐竹!', '§b如需看广告请进QQ群 点击聊天框上的圈云盒子!']) return this.logger.sender(sender, ['§4当前用户已绑定! §c如需解绑 请联系腐竹!', '§b如需看广告请进QQ群 §a点击聊天框上的圈云盒子!'])
} }
let bindUrl = 'https://m.q.qq.com/a/p/1110360279?s=' + encodeURIComponent(`pages/my/index?bindType=user&serverId=${this.config.serverId}&uuid=${sender.getUniqueId().toString()}&username=${sender.getName()}`) let bindUrl = 'https://m.q.qq.com/a/p/1110360279?s=' + encodeURIComponent(`pages/my/index?bindType=user&serverId=${this.config.serverId}&uuid=${sender.getUniqueId().toString()}&username=${sender.getName()}`)
this.setItemAndTp(sender, bindUrl, () => { let sync = { scaned: false, timeout: false }
let queryUser = this.queryUser(sender) this.taskManager.create(() => {
let queryUser = this.queryUser(sender, true)
if (queryUser.code == 200) { if (queryUser.code == 200) {
this.sendResult(sender, '绑定成功', queryUser.data) this.sendResult(sender, '绑定成功', queryUser.data)
this.checkAndClear(sender) sync.scaned = true
return true
} }
}) }).async().submit()
this.setItemAndTp(sender, bindUrl, sync)
} }
private setItemAndTp(sender: org.bukkit.entity.Player, content: string, func: () => boolean) { private setItemAndTp(sender: org.bukkit.entity.Player, content: string, sync: { scaned: boolean }) {
this.taskManager.create(() => { this.taskManager.create(() => {
let [id, item] = this.createQrCodeMapItem(content) this.bindingLeftTime = 30
let times = 0 this.bindingTask = this.taskManager.create(() => {
let task = this.taskManager.create(() => { try {
chat.send(sender, JSON.stringify({ if (sync.scaned || !sender.isOnline() || !this.isHoldQrCodeItem(sender) || --this.bindingLeftTime < 0) {
text: '§c§l请使用手机QQ扫描二维码绑定!' if (this.bindingLeftTime < 0) {
}), 2) this.logger.sender(sender, '§c二维码已过期 请重新获取!')
if (func() || !sender.isOnline()) {
task.cancel()
this.cacheTasks.delete(id)
this.isBinding = false
} }
if (times++ > 120) { this.cancelTask(sender)
this.logger.sender(sender, '§c绑定超时!') return
this.checkAndClear(sender) }
task.cancel() this.chat.sendActionBar(sender, `§c§l手机QQ扫描二维码 剩余 ${this.bindingLeftTime} 秒...`)
this.cacheTasks.delete(id) } catch (error) {
this.isBinding = false console.ex(error)
} }
}).async().later(20).timer(20).submit() }).async().later(20).timer(20).submit()
this.cacheTasks.set(id, task) sender.setItemInHand(this.createQrCodeMapItem(content))
sender.setItemInHand(item)
let temp = sender.getLocation() let temp = sender.getLocation()
temp.setPitch(90) temp.setPitch(90)
sender.teleport(temp) sender.teleport(temp)
}).submit() }).submit()
} }
private queryUser(sender: org.bukkit.entity.Player) { private queryUser(sender: org.bukkit.entity.Player, sync = false) {
return http.get(`https://reward.yumc.pw/server/queryUser/id/${this.config.serverId}/token/${this.config.serverToken}/uuid/${sender.getUniqueId().toString()}/username/${sender.getName()}`) return this.httpPost(`https://reward.yumc.pw/server/queryUser`, {
id: this.config.serverId,
token: this.config.serverToken,
uuid: sender.getUniqueId().toString(),
username: sender.getName(),
sync
})
} }
cmdquery(sender: org.bukkit.entity.Player) { cmdquery(sender: org.bukkit.entity.Player) {
@ -343,26 +483,81 @@ export class MiaoReward extends interfaces.Plugin {
]) ])
} }
private createQrCodeMapItem(contents: string): [number, any] { private httpPost(url, data) {
let view = Bukkit.createMap(Bukkit.getWorlds()[0]) let startTime = Date.now()
view.setScale(MapView.Scale.FARTHEST) let result = http.post(url, data)
view.getRenderers().forEach(r => view.removeRenderer(r)) console.debug(`
view.addRenderer(new QRCodeRender(this.createQrcode(contents)).getHandler()) ====== HTTP POST ======
let item: org.bukkit.inventory.ItemStack = new ItemStack(Material.MAP) REQUEST URL : ${url}
let meta = item.getItemMeta() REQUEST DATA: ${JSON.stringify(data)}
meta.setDisplayName('§c请使用手机QQ扫描二维码绑定!') RESPONSE : ${JSON.stringify(result)}
meta.setLore(["QRCODE"]) CAST TIME : ${Date.now() - startTime}`)
item.setDurability(view.getId()) return result
item.setItemMeta(meta)
return [view.getId(), item]
} }
private createQrcode(contents: string) { // var server = container.get(api.server.Server)
// var console = server.getDedicatedServer()
// var mainWorld = reflect.on(console).get('field_71305_c').get()[0]
// 设置新的Data
// mapdata = new MapData(s);
// mapdata.field_76197_d = (byte)3;
// mapdata.func_176054_a((double)worldIn.func_72912_H().func_76079_c(), (double)worldIn.func_72912_H().func_76074_e(), (int)mapdata.field_76197_d);
// mapdata.field_76200_c = ((WorldServer)worldIn).dimension;
// mapdata.func_76185_a();
// worldMain.func_72823_a(s, (WorldSavedData)mapdata);
// s = "map_" + stack.getMetadata();
// mapdata = new MapData(s);
// mapdata.scale = 3;
// mapdata.calculateMapCenter((double)worldIn.getWorldInfo().getSpawnX(), (double)worldIn.getWorldInfo().getSpawnZ(), mapdata.scale);
// // mapdata.dimension = worldIn.provider.getDimension();
// mapdata.dimension = ((WorldServer) worldIn).dimension; // CraftBukkit - fixes Bukkit multiworld maps
// mapdata.markDirty();
// worldMain.setData(s, mapdata);
// var mapStorage = mainWorld.field_72988_C
// var loadedDataMap = reflect.on(mapStorage).get('field_75749_b').get()
// var loadedDataList = reflect.on(mapStorage).get('field_75750_c').get()
// var idCounts = reflect.on(mapStorage).get('field_75748_d').get()
// @Override
// public CraftMapView createMap(World world) {
// Validate.notNull((Object)world, (String)"World cannot be null", (Object[])new Object[0]);
// net.minecraft.item.ItemStack stack = new net.minecraft.item.ItemStack((Item)Items.field_151148_bJ, 1, -1);
// MapData worldmap = Items.field_151098_aY.func_77873_a(stack, (net.minecraft.world.World)((CraftWorld)world).getHandle());
// return worldmap.mapView;
// }
//net.minecraft.item.ItemMap
// @Override
// public CraftMapView createMap(World world) {
// Validate.notNull(world, "World cannot be null");
// net.minecraft.item.ItemStack stack = new net.minecraft.item.ItemStack(Items.MAP, 1, -1, true); // CatServer
// MapData worldmap = Items.FILLED_MAP.getMapData(stack, ((CraftWorld) world).getHandle());
// return worldmap.mapView;
// }
private createQrCodeMapItem(content: string) {
let item: org.bukkit.inventory.ItemStack
item = new ItemStack(Material.FILLED_MAP || Material.MAP)
let meta = <org.bukkit.inventory.meta.MapMeta>item.getItemMeta()
if (meta.setMapId) {
meta.setMapId(this.zeroMapView.getId())
} else {
item.setDurability(this.zeroMapView.getId())
}
meta.setDisplayName('§c请使用手机QQ扫描二维码!')
meta.setLore(["QRCODE"])
item.setItemMeta(meta)
this.zeroMapRender.setImage(this.createQrcode(content))
return item
}
private createQrcode(content: string) {
let bufferedImage = new BufferedImage(128, 128, BufferedImage.TYPE_INT_RGB) let bufferedImage = new BufferedImage(128, 128, BufferedImage.TYPE_INT_RGB)
let graphics2D = bufferedImage.getGraphics() let graphics2D = bufferedImage.getGraphics()
graphics2D.setPaint(Color.WHITE) graphics2D.setPaint(Color.WHITE)
graphics2D.fillRect(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight()) graphics2D.fillRect(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight())
let qrcode = this.js2qr(contents) let qrcode = this.js2qr(content)
let startPoint = Math.round((bufferedImage.getWidth() - qrcode.getWidth()) / 2) let startPoint = Math.round((bufferedImage.getWidth() - qrcode.getWidth()) / 2)
graphics2D.drawImage(qrcode, startPoint, startPoint, null) graphics2D.drawImage(qrcode, startPoint, startPoint, null)
// let font = new Font("DejaVuSans", Font.PLAIN, 10) // let font = new Font("DejaVuSans", Font.PLAIN, 10)
@ -392,11 +587,6 @@ export class MiaoReward extends interfaces.Plugin {
return image return image
} }
// let byteArray = new this.ByteArrayOutputStream()
// let out = new this.DataOutputStream(byteArray)
// out.writeUTF("GetServer")
// player.sendPluginMessage(base.getInstance(), "BungeeCord", byteArray.toByteArray())
private bungeeCordDetect(player) { private bungeeCordDetect(player) {
if (this.isBungeeCord === undefined && player) { if (this.isBungeeCord === undefined && player) {
let byteArray = new this.ByteArrayOutputStream() let byteArray = new this.ByteArrayOutputStream()
@ -416,7 +606,7 @@ export class MiaoReward extends interfaces.Plugin {
let [cancelled, id] = this.isQrCodeItem(event.getItemDrop().getItemStack()) let [cancelled, id] = this.isQrCodeItem(event.getItemDrop().getItemStack())
if (id != null && id != undefined && cancelled) { if (id != null && id != undefined && cancelled) {
event.getItemDrop().remove() event.getItemDrop().remove()
this.cancelTask(id) this.cancelTask(event.getPlayer())
} }
} }
@ -426,7 +616,7 @@ export class MiaoReward extends interfaces.Plugin {
let [cancelled, id] = this.isQrCodeItem(inv.getItem(event.getPreviousSlot() as any)) let [cancelled, id] = this.isQrCodeItem(inv.getItem(event.getPreviousSlot() as any))
if (id != null && id != undefined && cancelled) { if (id != null && id != undefined && cancelled) {
inv.setItem(event.getPreviousSlot(), null) inv.setItem(event.getPreviousSlot(), null)
this.cancelTask(id) this.cancelTask(event.getPlayer())
} }
} }
@ -436,7 +626,7 @@ export class MiaoReward extends interfaces.Plugin {
let [cancelled, id] = this.isQrCodeItem(item) let [cancelled, id] = this.isQrCodeItem(item)
if (id != null && id != undefined && cancelled) { if (id != null && id != undefined && cancelled) {
event.getInventory().setItem(event.getSlot(), null) event.getInventory().setItem(event.getSlot(), null)
this.cancelTask(id) this.cancelTask(event.getWhoClicked())
event.setCancelled(true) event.setCancelled(true)
} }
} }
@ -446,14 +636,28 @@ export class MiaoReward extends interfaces.Plugin {
this.checkAndClear(event.getPlayer()) this.checkAndClear(event.getPlayer())
} }
private cancelTask(id) { private cancelTask(player) {
this.cacheTasks.get(id)?.cancel() console.ex(new Error())
this.cacheTasks.delete(id)
this.isBinding = false this.isBinding = false
this.bindingTask.cancel()
this.bindingUser = 'unknow'
this.checkAndClear(player)
this.chat.sendActionBar(player, "")
this.zeroMapRender.setImage(undefined)
//@ts-ignore
this.bindingNotify.forEach(p => {
if (p.isOnline()) {
this.logger.sender(p, `§6用户 §a${player.getName()} §6扫码已完成 §a您可以继续操作!`)
}
})
}
private isHoldQrCodeItem(player: org.bukkit.entity.Player) {
return this.isQrCodeItem(player.getItemInHand())[0]
} }
private checkAndClear(player: org.bukkit.entity.Player) { private checkAndClear(player: org.bukkit.entity.Player) {
if (this.isQrCodeItem(player.getItemInHand())[0]) { if (this.isHoldQrCodeItem(player)) {
player.setItemInHand(null) player.setItemInHand(null)
} }
} }
@ -492,7 +696,7 @@ export class MiaoReward extends interfaces.Plugin {
@Tab() @Tab()
tabmrd(sender: any, _command: any, args: string | any[]) { tabmrd(sender: any, _command: any, args: string | any[]) {
if (args.length === 1) return ['help', 'bind', 'show', 'query', 'draw', 'ratio', 'rank', 'server'] if (args.length === 1) return ['help', 'bind', 'show', 'statistic', 'query', 'draw', 'ratio', 'rank', 'server']
if (args.length === 2 && args[0] === "bind" && sender.isOp()) return ['server'] if (args.length === 2 && args[0] === "bind" && sender.isOp()) return ['server']
} }
} }

View File

@ -25,6 +25,7 @@ let help = [
let langMap = { let langMap = {
'main.command.not.exists': '§4未知的子命令: §c{command}', 'main.command.not.exists': '§4未知的子命令: §c{command}',
'main.command.help.tip': '§6请执行 §b/{command} §ahelp §6查看帮助!', 'main.command.help.tip': '§6请执行 §b/{command} §ahelp §6查看帮助!',
'main.command.no.permission': '§c你没有此命令的权限!',
'list.install.header': '§6当前 §bMiaoScript §6已安装下列插件:', 'list.install.header': '§6当前 §bMiaoScript §6已安装下列插件:',
'list.install.body': '§6插件名称: §b{name}\n§6版本: §a{version}\n§6作者: §3{author}\n§6来源: §c{from}', 'list.install.body': '§6插件名称: §b{name}\n§6版本: §a{version}\n§6作者: §3{author}\n§6来源: §c{from}',
'list.header': '§6当前 §bMiaoScriptPackageCenter §6中存在下列插件:', 'list.header': '§6当前 §bMiaoScriptPackageCenter §6中存在下列插件:',
@ -117,7 +118,7 @@ export class MiaoScriptPackageManager extends interfaces.Plugin {
let byteArray = new this.ByteArrayOutputStream() let byteArray = new this.ByteArrayOutputStream()
let out = new this.DataOutputStream(byteArray) let out = new this.DataOutputStream(byteArray)
out.writeUTF("GetServer") out.writeUTF("GetServer")
player.sendPluginMessage(base.getInstance(), "BungeeCord", byteArray.toByteArray()) this.channel.send(player, "BungeeCord", byteArray.toByteArray())
} }
} }
private bungeeCordForward(player, command) { private bungeeCordForward(player, command) {
@ -128,7 +129,7 @@ export class MiaoScriptPackageManager extends interfaces.Plugin {
out.writeUTF("ALL") out.writeUTF("ALL")
out.writeUTF("MiaoScriptPackageManager") out.writeUTF("MiaoScriptPackageManager")
out.writeUTF(JSON.stringify(command)) out.writeUTF(JSON.stringify(command))
player.sendPluginMessage(base.getInstance(), "BungeeCord", byteArray.toByteArray()) this.channel.send(player, "BungeeCord", byteArray.toByteArray())
} }
} }
private readForward(input) { private readForward(input) {
@ -147,6 +148,7 @@ export class MiaoScriptPackageManager extends interfaces.Plugin {
@Listener({ servers: [constants.ServerType.Sponge] }) @Listener({ servers: [constants.ServerType.Sponge] })
ClientConnectionEvent$Join(event: org.spongepowered.api.event.network.ClientConnectionEvent.Join) { ClientConnectionEvent$Join(event: org.spongepowered.api.event.network.ClientConnectionEvent.Join) {
this.bungeeCordDetect(event.getTargetEntity())
if (event.getTargetEntity().hasPermission('ms.mpm.admin')) { if (event.getTargetEntity().hasPermission('ms.mpm.admin')) {
this.updateRepo(event.getTargetEntity()) this.updateRepo(event.getTargetEntity())
} }
@ -168,16 +170,22 @@ export class MiaoScriptPackageManager extends interfaces.Plugin {
this.channelOff?.off() this.channelOff?.off()
} }
@Cmd() @Cmd({ servers: [`!${constants.ServerType.Bungee}`] })
bmpm(sender: any, command: string, args: string[]) { bmpm(sender: any, command: string, args: string[]) {
if (!sender.isOp()) { return this.logger.sender(sender, '§c你没有此命令的权限!') } if (!sender.isOp()) { return this.i18n(sender, 'main.command.no.permission') }
this.bungeeCordForward(sender, { sender: sender.getName(), command, args }) this.bungeeCordForward(sender, { sender: sender.getName(), command, args })
this.logger.sender(sender, `[§3BPM§6][§a${this.serverName}§6] §6命令 §b/mpm ${args.join?.(' ')} §a发布成功!`) this.logger.sender(sender, `§6[§3BPM§6][§a${this.serverName}§6] §6命令 §b/mpm ${args.join?.(' ')} §a发布成功!`)
} }
@Cmd() @Cmd({ servers: [constants.ServerType.Bungee] })
mpmanager(sender: any, command: string, args: string[]) {
if (!sender.isOp()) { return this.i18n(sender, 'main.command.no.permission') }
this.taskManager.create(() => this.main(sender, command, args)).async().submit()
}
@Cmd({ servers: [`!${constants.ServerType.Bungee}`] })
mpm(sender: any, command: string, args: string[]) { mpm(sender: any, command: string, args: string[]) {
if (!sender.isOp()) { return this.logger.sender(sender, '§c你没有此命令的权限!') } if (!sender.isOp()) { return this.i18n(sender, 'main.command.no.permission') }
this.taskManager.create(() => this.main(sender, command, args)).async().submit() this.taskManager.create(() => this.main(sender, command, args)).async().submit()
} }
@ -225,6 +233,10 @@ export class MiaoScriptPackageManager extends interfaces.Plugin {
cmdinstall(sender: any, name: string) { cmdinstall(sender: any, name: string) {
if (!name) { return this.i18n(sender, 'plugin.name.empty') } if (!name) { return this.i18n(sender, 'plugin.name.empty') }
if (this.pluginManager.getPlugins().has(name)) {
return
}
this.download(sender, name) this.download(sender, name)
} }