Compare commits

...

12 Commits

Author SHA1 Message Date
7d6f6124b1 v0.9.1 2020-08-14 16:09:46 +08:00
e06fcc29e9 feat: backup plugins 2020-08-14 16:07:17 +08:00
79ece8db63 fix: bukkit chat init error & config save error 2020-08-14 16:05:44 +08:00
66c1d25888 v0.9.0 2020-07-28 09:51:56 +08:00
ca8d4778bb feat: optimize plugin event registry
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-07-28 09:41:10 +08:00
7dcc5ed45a feat: impl bukkit chat api
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-07-28 09:39:52 +08:00
91d2d836fc feat: add chat api
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-07-28 09:39:15 +08:00
e5e1bb70a1 revent: qrcode.ts
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-07-27 01:08:45 +08:00
e93a53aa9f backup: new qrcodeSigned-off-by: MiaoWoo <admin@yumc.pw>
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-07-27 01:07:35 +08:00
e3bceda06b fix: readSourceMap split error
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-07-26 21:33:58 +08:00
9367e8c2c9 feat: update plugins
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-07-25 15:24:02 +08:00
31c7ea2a29 feat: add keyvalue package
Signed-off-by: MiaoWoo <admin@yumc.pw>
2020-07-24 18:45:56 +08:00
55 changed files with 1485 additions and 236 deletions

View File

@@ -1,5 +1,5 @@
{
"version": "0.8.4",
"version": "0.9.1",
"useWorkspaces": true,
"npmClient": "yarn",
"packages": [

View File

@@ -1,6 +1,6 @@
{
"name": "@ccms/amqp",
"version": "0.8.4",
"version": "0.9.0",
"description": "MiaoScript amqp package",
"keywords": [
"miaoscript",
@@ -19,12 +19,12 @@
"test": "echo \"Error: run tests from root\" && exit 1"
},
"dependencies": {
"@ccms/api": "^0.8.4",
"@ccms/common": "^0.8.4",
"@ccms/container": "^0.8.4"
"@ccms/api": "^0.9.0",
"@ccms/common": "^0.9.0",
"@ccms/container": "^0.9.0"
},
"devDependencies": {
"@ccms/nashorn": "^0.8.4",
"@ccms/nashorn": "^0.9.0",
"@javatypes/amqp-client": "^0.0.2",
"@javatypes/spring-amqp": "^0.0.2",
"@javatypes/spring-rabbit": "^0.0.2",

View File

@@ -1,6 +1,6 @@
{
"name": "@ccms/api",
"version": "0.8.4",
"version": "0.9.0",
"description": "MiaoScript api package",
"keywords": [
"miaoscript",
@@ -19,9 +19,9 @@
"test": "echo \"Error: run tests from root\" && exit 1"
},
"dependencies": {
"@ccms/common": "^0.8.4",
"@ccms/container": "^0.8.4",
"@ccms/ployfill": "^0.8.4",
"@ccms/common": "^0.9.0",
"@ccms/container": "^0.9.0",
"@ccms/ployfill": "^0.9.0",
"base64-js": "^1.3.1",
"source-map-builder": "^0.0.7"
},

19
packages/api/src/chat.ts Normal file
View File

@@ -0,0 +1,19 @@
export namespace chat {
export abstract class Chat {
sendMessage(sender: any, message: string) {
throw new Error("Method not implemented.")
}
sendActionBar(sender: any, message: string) {
throw new Error("Method not implemented.")
}
clearActionBar(sender: any) {
this.sendActionBar(sender, '')
}
sendTitle(sender: any, title: string, subtitle: string = '', fadeIn: number = 1, time: number = 5, fadeOut: number = 1) {
throw new Error("Method not implemented.")
}
clearTitle(sender: any) {
this.sendTitle(sender, '', '', 0, 0, 0)
}
}
}

View File

@@ -97,10 +97,11 @@ export class MiaoScriptConsole implements Console {
MiaoScriptConsole.sourceMaps[fileName] = null
let sourceLine = base.read(fileName).split('\n')
let lastLine = sourceLine[sourceLine.length - 1]
// lastLine is similar //# sourceMappingURL=data:application/json;base64,
if (lastLine.startsWith('//# sourceMappingURL=')) {
let sourceContent = null
let sourceMappingURL = lastLine.split('sourceMappingURL=', 2)[1]
if (sourceMappingURL.startsWith('data:application/jsonbase64,')) {
if (sourceMappingURL.startsWith('data:application/json;base64,')) {
sourceContent = String.fromCharCode(...Array.from(base64.toByteArray(sourceMappingURL.split(',', 2)[1])))
} else if (sourceMappingURL.startsWith('http')) {
// TODO
@@ -122,6 +123,9 @@ export class MiaoScriptConsole implements Console {
}
} catch (error) {
console.debug('search source map', fileName, 'line', lineNumber, 'error:', error)
if (global.debug) {
console.ex(error)
}
}
return {
fileName,

View File

@@ -4,7 +4,7 @@
import i18n from '@ccms/i18n'
import { injectable, unmanaged } from '@ccms/container'
const Thread = Java.type('java.lang.Thread');
const Thread = Java.type('java.lang.Thread')
export namespace event {
/**
@@ -30,7 +30,7 @@ export namespace event {
protected baseEventDir = '';
constructor(@unmanaged() baseEventDir: string) {
this.baseEventDir = baseEventDir;
this.baseEventDir = baseEventDir
}
/**
@@ -39,74 +39,74 @@ export namespace event {
* org.spongepowered.api.event.game.GameRegistryEvent.Register => gameregistryevent$register
*/
mapEventName() {
if (this.baseEventDir === "") { throw new Error(i18n.translate('ms.api.event.empty.event.dir')); }
let count = 0;
let jar = this.getJarFile(this.baseEventDir);
let entries = jar.entries();
if (this.baseEventDir === "") { throw new Error(i18n.translate('ms.api.event.empty.event.dir')) }
let count = 0
let jar = this.getJarFile(this.baseEventDir)
let entries = jar.entries()
while (entries.hasMoreElements()) {
let entry = entries.nextElement();
let name = entry.name;
let entry = entries.nextElement()
let name = entry.name
if (name.startsWith(this.baseEventDir) && name.endsWith(".class")) {
// replace name to qualifiedName
let qualifiedName = name.replaceAll('/', '.');
let qualifiedName = name.replaceAll('/', '.')
try {
let clazz = base.getClass(qualifiedName.substring(0, qualifiedName.length - 6));
let clazz = base.getClass(qualifiedName.substring(0, qualifiedName.length - 6))
if (this.isValidEvent(clazz)) {
let simpleName = this.class2Name(clazz).toLowerCase();
console.trace(i18n.translate("ms.api.event.mapping", { canonicalName: clazz.canonicalName, simpleName }));
this.mapEvent[simpleName] = clazz;
count++;
let simpleName = this.class2Name(clazz).toLowerCase()
console.trace(i18n.translate("ms.api.event.mapping", { canonicalName: clazz.canonicalName, simpleName }))
this.mapEvent[simpleName] = clazz
count++
}
} catch (ex) {
//ignore already loaded class
}
}
}
return count;
return count
}
getJarFile(resource: string, loader?: any) {
let dirs = (loader || Thread.currentThread().getContextClassLoader()).getResources(resource);
let dirs = (loader || Thread.currentThread().getContextClassLoader()).getResources(resource)
if (dirs.hasMoreElements()) {
let url = dirs.nextElement();
if (url.protocol === "jar") { return url.openConnection().jarFile; }
let url = dirs.nextElement()
if (url.protocol === "jar") { return url.openConnection().jarFile }
}
throw new Error(i18n.translate("ms.api.event.resource.not.found", { resource }))
}
class2Name(clazz: any) {
return clazz.simpleName;
return clazz.simpleName
}
name2Class(name: any, event: string) {
let eventCls = this.mapEvent[event.toLowerCase()] || this.mapEvent[event.toLowerCase() + 'event'];
let eventCls = this.mapEvent[event.toLowerCase()] || this.mapEvent[event.toLowerCase() + 'event']
if (!eventCls) {
try {
eventCls = base.getClass(eventCls);
this.mapEvent[event] = eventCls;
eventCls = base.getClass(eventCls)
this.mapEvent[event] = eventCls
} catch (ex) {
console.i18n("ms.api.event.not.found", { name, event })
return;
return
}
}
return eventCls;
return eventCls
}
execute(name, exec, eventCls) {
return (...args: any[]) => {
try {
let event = args[args.length - 1];
let event = args[args.length - 1]
if (eventCls.isAssignableFrom(event.getClass())) {
let time = Date.now()
exec(event);
let cost = Date.now() - time;
exec(event)
let cost = Date.now() - time
if (cost > 20) {
console.i18n("ms.api.event.execute.slow", { name, event: this.class2Name(eventCls), cost })
}
}
} catch (ex) {
console.i18n("ms.api.event.execute.error", { name, event: this.class2Name(eventCls), ex })
console.ex(ex);
console.ex(ex)
}
}
}
@@ -120,34 +120,34 @@ export namespace event {
* @param ignoreCancel
*/
listen(plugin: any, event: string, exec: (event: any) => void, priority: EventPriority = EventPriority.NORMAL, ignoreCancel = false) {
if (!plugin || !plugin.description || !plugin.description.name) throw new TypeError(i18n.translate("ms.api.event.listen.plugin.name.empty"));
var name = plugin.description.name;
var eventCls = this.name2Class(name, event);
if (!eventCls) { return; }
if (!plugin || !plugin.description || !plugin.description.name) throw new TypeError(i18n.translate("ms.api.event.listen.plugin.name.empty"))
var name = plugin.description.name
var eventCls = this.name2Class(name, event)
if (!eventCls) { return }
if (typeof priority === 'boolean') {
ignoreCancel = priority;
priority = EventPriority.NORMAL;
ignoreCancel = priority
priority = EventPriority.NORMAL
}
priority = priority || EventPriority.NORMAL;
ignoreCancel = ignoreCancel || false;
priority = priority || EventPriority.NORMAL
ignoreCancel = ignoreCancel || false
// noinspection JSUnusedGlobalSymbols
var listener = this.register(eventCls, this.execute(name, exec, eventCls), priority, ignoreCancel);
var listenerMap = this.listenerMap;
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] = [];
if (!listenerMap[name]) listenerMap[name] = []
var offExec = () => {
this.unregister(eventCls, listener);
console.debug(i18n.translate("ms.api.event.unregister", { name, event: this.class2Name(eventCls) }));
};
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
};
listenerMap[name].push(off);
}
listenerMap[name].push(off)
// noinspection JSUnresolvedVariable
console.debug(i18n.translate("ms.api.event.register", { name, event: this.class2Name(eventCls) }));
return off;
console.debug(i18n.translate("ms.api.event.register", { name, event: this.class2Name(eventCls), exec: exec.name || '[anonymous]' }))
return off
}
/**
@@ -155,10 +155,10 @@ export namespace event {
* @param plugin 插件
*/
disable(plugin: any) {
var eventCache = this.listenerMap[plugin.description.name];
var eventCache = this.listenerMap[plugin.description.name]
if (eventCache) {
eventCache.forEach(t => t.off());
delete this.listenerMap[plugin.description.name];
eventCache.forEach(t => t.off())
delete this.listenerMap[plugin.description.name]
}
}
@@ -166,7 +166,7 @@ export namespace event {
* 判断
* @param clazz 事件类
*/
abstract isValidEvent(clazz: any): boolean;
abstract isValidEvent(clazz: any): boolean
/**
* 注册事件
* @param eventCls 事件类
@@ -174,12 +174,12 @@ export namespace event {
* @param priority 优先级
* @param ignoreCancel 是否忽略已取消的事件
*/
abstract register(eventCls: any, exec: Function, priority: any, ignoreCancel: boolean): any;
abstract register(eventCls: any, exec: Function, priority: any, ignoreCancel: boolean): any
/**
* 取消监听事件
* @param event 事件
* @param listener 监听器
*/
abstract unregister(event: any, listener: any): void;
abstract unregister(event: any, listener: any): void
}
}

View File

@@ -1,4 +1,6 @@
import "@ccms/nashorn"
export * from './chat'
export * from './task'
export * from './event'
export * from './console'

View File

@@ -1,6 +1,6 @@
{
"name": "@ccms/bukkit",
"version": "0.8.4",
"version": "0.9.1",
"description": "MiaoScript bukkit package",
"keywords": [
"miaoscript",
@@ -25,8 +25,8 @@
"typescript": "^3.9.6"
},
"dependencies": {
"@ccms/api": "^0.8.4",
"@ccms/common": "^0.8.4",
"@ccms/container": "^0.8.4"
"@ccms/api": "^0.9.0",
"@ccms/common": "^0.9.0",
"@ccms/container": "^0.9.0"
}
}

View File

@@ -0,0 +1,16 @@
import { chat } from '@ccms/api'
import { provideSingleton } from '@ccms/container'
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)
}
sendActionBar(sender: any, message: string) {
bukkitChat.send(sender, { text: message }, 2)
}
sendTitle(sender: any, title: string, subtitle: string = '', fadeIn: number = 1, time: number = 5, fadeOut: number = 1) {
sender.sendTitle(title, subtitle, fadeIn, time, fadeOut)
}
}

View File

@@ -1,29 +1,27 @@
/*global Java, base, module, exports, require*/
var nmsChatSerializerClass;
var nmsChatSerializerMethod;
var packetTypeConstructor;
var nmsChatMessageTypeClass;
var chatMessageTypes;
let ChatSerializer: any
let nmsChatSerializerMethodName: string
let PacketPlayOutChat: any
let chatMessageTypes: any
var RemapUtils;
let RemapUtils: any
var playerConnectionFieldName;
var sendPacketMethod;
let playerConnectionFieldName: string
let sendPacketMethodName: string
var downgrade = false;
let downgrade = false
/**
* 获取NMS版本
*/
//@ts-ignore
var nmsVersion = org.bukkit.Bukkit.server.class.name.split('.')[3];
let nmsVersion = undefined
/**
* 获取NMS类
*/
function nmsCls(name) {
function nmsCls(name: string) {
return base.getClass(['net.minecraft.server', nmsVersion, name].join('.'))
}
function remapMethod(clazz: any, origin: string, test: string, params) {
function remapMethod(clazz: any, origin: string, test: string, params: any) {
try {
return clazz.getMethod(origin, params)
} catch (ex) {
@@ -48,67 +46,60 @@ function remapFieldName(clazz: any, origin: string, test: string) {
}
function init() {
//@ts-ignore
nmsVersion = org.bukkit.Bukkit.server.class.name.split('.')[3]
try {
RemapUtils = Java.type('catserver.server.remapper.RemapUtils');
RemapUtils = Java.type('catserver.server.remapper.RemapUtils')
} catch (ex) {
}
nmsChatSerializerClass = nmsCls(nmsVersion.split("_")[1] > 7 ? "IChatBaseComponent$ChatSerializer" : "ChatSerializer");
nmsChatSerializerMethod = remapMethod(nmsChatSerializerClass, 'a', 'func_150699_a', base.getClass('java.lang.String'))
var packetTypeClass = nmsCls("PacketPlayOutChat");
Java.from(packetTypeClass.constructors).forEach(function(c) {
let nmsChatSerializerClass = nmsCls(nmsVersion.split("_")[1] > 7 ? "IChatBaseComponent$ChatSerializer" : "ChatSerializer")
let nmsChatSerializerMethod = remapMethod(nmsChatSerializerClass, 'a', 'func_150699_a', base.getClass('java.lang.String'))
nmsChatSerializerMethodName = nmsChatSerializerMethod.getName()
ChatSerializer = Java.type(nmsChatSerializerClass.getName())
let packetTypeClass = nmsCls("PacketPlayOutChat")
PacketPlayOutChat = Java.type(packetTypeClass.getName())
let packetTypeConstructor: { parameterTypes: any[] }
Java.from(packetTypeClass.constructors).forEach(function (c) {
if (c.parameterTypes.length === 2) {
packetTypeConstructor = c
}
});
nmsChatMessageTypeClass = packetTypeConstructor.parameterTypes[1];
})
let nmsChatMessageTypeClass = packetTypeConstructor.parameterTypes[1]
if (nmsChatMessageTypeClass.isEnum()) {
chatMessageTypes = nmsChatMessageTypeClass.getEnumConstants();
} else {
switch (nmsChatMessageTypeClass.name) {
case "int":
//@ts-ignore
nmsChatMessageTypeClass = java.lang.Integer;
break;
case "byte":
//@ts-ignore
nmsChatMessageTypeClass = java.lang.Byte;
break;
}
chatMessageTypes = nmsChatMessageTypeClass.getEnumConstants()
}
var entityPlayerClass = nmsCls('EntityPlayer');
var packetClass = nmsCls('Packet');
var playerConnectionField = remapFieldName(entityPlayerClass, 'playerConnection', 'field_71135_a')
let playerConnectionField = remapFieldName(nmsCls('EntityPlayer'), 'playerConnection', 'field_71135_a')
playerConnectionFieldName = playerConnectionField.getName()
sendPacketMethod = remapMethod(playerConnectionField.getType(), 'sendPacket', 'func_179290_a', packetClass)
sendPacketMethodName = remapMethod(playerConnectionField.getType(), 'sendPacket', 'func_179290_a', nmsCls('Packet')).getName()
}
function json(sender, json) {
function json(sender: { name: string }, json: string) {
if (downgrade) {
return '/tellraw ' + sender.name + ' ' + json
} else {
send(sender, json, 0);
return false;
send(sender, json, 0)
return false
}
}
function send(sender, json, type) {
//@ts-ignore
sendPacket(sender, packetTypeConstructor.newInstance(nmsChatSerializerMethod.invoke(null, json), chatMessageTypes == null ? nmsChatMessageTypeClass.valueOf(java.lang.String.valueOf(type)) : chatMessageTypes[type]))
function send(sender: any, json: any, type: number) {
sendPacket(sender, new PacketPlayOutChat(ChatSerializer[nmsChatSerializerMethodName](json), chatMessageTypes == null ? type : chatMessageTypes[type]))
}
function sendPacket(player, p) {
sendPacketMethod.invoke(player.handle[playerConnectionFieldName], p)
function sendPacket(player: { handle: { [x: string]: { [x: string]: (arg0: any) => void } } }, p: any) {
player.handle[playerConnectionFieldName][sendPacketMethodName](p)
}
try {
init();
init()
} catch (ex) {
org.bukkit.Bukkit.getConsoleSender().sendMessage(`§6[§cMS§6][§bbukkit§6][§achat§6] §cNMS Inject Error §4${ex} §cDowngrade to Command Mode...`)
downgrade = true;
downgrade = true
}
let chat = {
json
json,
send
}
export default chat

View File

@@ -1,18 +1,18 @@
import { server, constants } from '@ccms/api'
import { provideSingleton } from '@ccms/container';
import { provideSingleton } from '@ccms/container'
import * as reflect from '@ccms/common/dist/reflect'
import chat from './enhance/chat'
let Bukkit = org.bukkit.Bukkit;
let Bukkit = org.bukkit.Bukkit
@provideSingleton(server.Server)
export class BukkitServer extends server.ReflectServer {
private pluginsFolder: string;
private pluginsFolder: string
constructor() {
super();
this.pluginsFolder = Bukkit.getUpdateFolderFile().getParentFile().getCanonicalPath();
super()
this.pluginsFolder = Bukkit.getUpdateFolderFile().getParentFile().getCanonicalPath()
}
getPlayer(name: string) {
@@ -40,25 +40,25 @@ export class BukkitServer extends server.ReflectServer {
return Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command)
}
getPluginsFolder(): string {
return this.pluginsFolder;
return this.pluginsFolder
}
getNativePluginManager() {
return Bukkit.getPluginManager() as any;
return Bukkit.getPluginManager() as any
}
getDedicatedServer() {
return reflect.on(Bukkit.getServer()).get('console').get()
}
getNettyPipeline() {
return this.pipeline;
return this.pipeline
}
getRootLogger() {
return this.rootLogger;
return this.rootLogger
}
sendJson(sender: string | any, json: object | string): void {
if (typeof sender === "string") {
sender = this.getPlayer(sender)
}
let result = chat.json(sender, json)
let result = chat.json(sender, typeof json == "string" ? json : JSON.stringify(json))
if (result !== false) {
this.dispatchConsoleCommand(result)
}

View File

@@ -1,6 +1,6 @@
{
"name": "@ccms/bungee",
"version": "0.8.4",
"version": "0.9.0",
"description": "MiaoScript bungee package",
"keywords": [
"miaoscript",
@@ -25,8 +25,8 @@
"typescript": "^3.9.6"
},
"dependencies": {
"@ccms/api": "^0.8.4",
"@ccms/common": "^0.8.4",
"@ccms/container": "^0.8.4"
"@ccms/api": "^0.9.0",
"@ccms/common": "^0.9.0",
"@ccms/container": "^0.9.0"
}
}

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "@ccms/client",
"version": "0.8.3",
"version": "0.9.0",
"description": "MiaoScript client package",
"keywords": [
"miaoscript",

View File

@@ -1,6 +1,6 @@
{
"name": "@ccms/common",
"version": "0.8.4",
"version": "0.9.0",
"description": "MiaoScript api package",
"keywords": [
"miaoscript",
@@ -19,7 +19,7 @@
"test": "echo \"Error: run tests from root\" && exit 1"
},
"devDependencies": {
"@ccms/nashorn": "^0.8.4",
"@ccms/nashorn": "^0.9.0",
"@javatypes/jdk": "^0.0.2",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",

View File

@@ -1,6 +1,6 @@
{
"name": "@ccms/compile",
"version": "0.8.3",
"version": "0.9.0",
"description": "MiaoScript compile package",
"keywords": [
"miaoscript",

View File

@@ -1,6 +1,6 @@
{
"name": "@ccms/container",
"version": "0.8.4",
"version": "0.9.0",
"description": "MiaoScript container package",
"keywords": [
"miaoscript",
@@ -19,7 +19,7 @@
"test": "echo \"Error: run tests from root\" && exit 1"
},
"devDependencies": {
"@ccms/nashorn": "^0.8.4",
"@ccms/nashorn": "^0.9.0",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"typescript": "^3.9.6"

View File

@@ -1,6 +1,6 @@
{
"name": "@ccms/core",
"version": "0.8.4",
"version": "0.9.0",
"description": "MiaoScript api package",
"keywords": [
"miaoscript",
@@ -24,8 +24,8 @@
"typescript": "^3.9.6"
},
"dependencies": {
"@ccms/api": "^0.8.4",
"@ccms/container": "^0.8.4"
"@ccms/api": "^0.9.0",
"@ccms/container": "^0.9.0"
},
"gitHead": "781524f83e52cad26d7c480513e3c525df867121"
}

View File

@@ -4,7 +4,6 @@ import { plugin, server, task, constants } from '@ccms/api'
import { DefaultContainer as container, inject, provideSingleton, ContainerInstance, buildProviderModule } from '@ccms/container'
console.i18n("ms.core.ioc.completed", { scope: global.scope, time: (Date.now() - containerStartTime) / 1000 })
import http from '@ccms/common/dist/http'
import { EventEmitter } from 'events'
@provideSingleton(MiaoScriptCore)
class MiaoScriptCore {

View File

@@ -1,6 +1,6 @@
{
"name": "@ccms/database",
"version": "0.8.4",
"version": "0.9.0",
"description": "MiaoScript database package",
"keywords": [
"miaoscript",
@@ -25,7 +25,7 @@
"typescript": "^3.9.6"
},
"dependencies": {
"@ccms/api": "^0.8.4",
"@ccms/container": "^0.8.4"
"@ccms/api": "^0.9.0",
"@ccms/container": "^0.9.0"
}
}

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.error: "§6插件 §b{name} §6处理 §d{event} §6事件时发生异常 §4{ex}"
ms.api.event.listen.plugin.name.empty: "插件名称为空 请检查传入参数!"
ms.api.event.register: "[{name}] 注册事件 {event}"
ms.api.event.unregister: "[{name}] 注销事件 {event}"
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}..."

View File

@@ -1,6 +1,6 @@
{
"name": "@ccms/i18n",
"version": "0.8.4",
"version": "0.9.0",
"description": "MiaoScript i18n package",
"keywords": [
"miaoscript",
@@ -19,7 +19,7 @@
"test": "echo \"Error: run tests from root\" && exit 1"
},
"devDependencies": {
"@ccms/nashorn": "^0.8.4",
"@ccms/nashorn": "^0.9.0",
"@types/js-yaml": "^3.12.5",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",

View File

@@ -0,0 +1 @@
../../.npmignore

View File

@@ -1,6 +1,6 @@
{
"name": "@ccms/keyvalue",
"version": "0.8.4",
"version": "0.9.0",
"description": "MiaoScript keyvalue package",
"keywords": [
"miaoscript",
@@ -19,17 +19,18 @@
"test": "echo \"Error: run tests from root\" && exit 1"
},
"dependencies": {
"@ccms/api": "^0.8.4",
"@ccms/common": "^0.8.4",
"@ccms/container": "^0.8.4"
"@ccms/api": "^0.9.0",
"@ccms/common": "^0.9.0",
"@ccms/container": "^0.9.0"
},
"devDependencies": {
"@ccms/nashorn": "^0.8.4",
"@ccms/nashorn": "^0.9.0",
"@javatypes/amqp-client": "^0.0.2",
"@javatypes/spring-amqp": "^0.0.2",
"@javatypes/spring-rabbit": "^0.0.2",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"typescript": "^3.9.6"
}
},
"gitHead": "2589633069d24f646ac09261b1b2304c21d4ea75"
}

View File

@@ -0,0 +1,17 @@
import { ConnectionFactoryAdapter } from "./connection"
import { RabbitTemplateAdapter } from "./template"
import { JSClass } from "@ccms/container"
export const RabbitAdmin = Java.type('org.springframework.amqp.rabbit.core.RabbitAdmin')
export class RabbitAdminAdapter {
@JSClass('org.springframework.amqp.rabbit.core.RabbitAdmin')
private RabbitAdmin: org.springframework.amqp.rabbit.core.RabbitAdmin
private _Handler: org.springframework.amqp.rabbit.core.RabbitAdmin
constructor(config: RabbitTemplateAdapter | org.springframework.amqp.rabbit.core.RabbitTemplate | ConnectionFactoryAdapter | org.springframework.amqp.rabbit.connection.ConnectionFactory) {
this._Handler = new RabbitAdmin((config instanceof RabbitTemplateAdapter || config instanceof ConnectionFactoryAdapter) ? config.getHandler() : config)
}
getHandler(): org.springframework.amqp.rabbit.core.RabbitAdmin {
return this._Handler
}
}

View File

@@ -0,0 +1,30 @@
export const ConfirmCallback = Java.type('org.springframework.amqp.rabbit.core.RabbitTemplate.ConfirmCallback')
export const ReturnCallback = Java.type('org.springframework.amqp.rabbit.core.RabbitTemplate.ReturnCallback')
export abstract class ConfirmCallbackAdapter {
private _Handler: org.springframework.amqp.rabbit.core.RabbitTemplate.ConfirmCallback
constructor() {
let ConfirmCallbackImpl = Java.extend(ReturnCallback, {
confirm: (correlationData: org.springframework.amqp.rabbit.connection.CorrelationData, ack: boolean, cause: string) => this.confirm(correlationData, ack, cause)
})
this._Handler = new ConfirmCallbackImpl()
}
abstract confirm(correlationData: org.springframework.amqp.rabbit.connection.CorrelationData, ack: boolean, cause: string)
getHandler() {
return this._Handler
}
}
export abstract class ReturnCallbackAdapter {
private _Handler: org.springframework.amqp.rabbit.core.RabbitTemplate.ReturnCallback
constructor() {
let ReturnCallbackImpl = Java.extend(ReturnCallback, {
returnedMessage: (message: org.springframework.amqp.core.Message, replyCode: number, replyText: string, exchange: string, routingKey: string) => this.returnedMessage(message, replyCode, replyText, exchange, routingKey)
})
this._Handler = new ReturnCallbackImpl()
}
abstract returnedMessage(message: org.springframework.amqp.core.Message, replyCode: number, replyText: string, exchange: string, routingKey: string)
getHandler() {
return this._Handler
}
}

View File

@@ -0,0 +1,31 @@
import threadPool from '@ccms/common/dist/thread-pool'
export const ConnectionFactory = Java.type('org.springframework.amqp.rabbit.connection.ConnectionFactory')
const CachingConnectionFactory = Java.type('org.springframework.amqp.rabbit.connection.CachingConnectionFactory')
interface ConnectionConfig {
url: string,
username?: string,
password?: string,
publisherConfirms?: boolean
cacheSize?: number
}
export class ConnectionFactoryAdapter {
private _Handler: org.springframework.amqp.rabbit.connection.CachingConnectionFactory
constructor(config: ConnectionConfig) {
if (!config.url) { throw new Error('Connection UrI Can\'t be undefiend!') }
config = { publisherConfirms: true, cacheSize: 50, ...config }
this._Handler = new CachingConnectionFactory(new java.net.URI(config.url))
config.username && this._Handler.setUsername(config.username)
config.password && this._Handler.setPassword(config.password)
this._Handler.setPublisherConfirms(config.publisherConfirms)
this._Handler.setExecutor(threadPool.create({
groupName: '@ccms/amqp-rabbit'
}))
this._Handler.setChannelCacheSize(config.cacheSize)
}
getHandler() {
return this._Handler
}
}

View File

@@ -0,0 +1,30 @@
import { ConnectionFactoryAdapter, ConnectionFactory } from "./connection"
import { ChannelAwareMessageListenerAdapter, ChannelAwareMessageListener } from "./listener"
export const SimpleMessageListenerContainer = org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer
export const AcknowledgeMode: org.springframework.amqp.core.AcknowledgeMode = Java.type('org.springframework.amqp.core.AcknowledgeMode')
interface ContainerConfig {
connectionFactory: ConnectionFactoryAdapter | typeof ConnectionFactory
queueNames: string[]
messageListener: ChannelAwareMessageListenerAdapter | typeof ChannelAwareMessageListener
maxConcurrentConsumers?: number
concurrentConsumers?: number
acknowledgeMode?: org.springframework.amqp.core.AcknowledgeMode
}
export class MessageListenerContainerAdapter {
private _Handler: org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer
constructor(config: ContainerConfig) {
config = { concurrentConsumers: 5, maxConcurrentConsumers: 10, acknowledgeMode: AcknowledgeMode.AUTO, ...config }
this._Handler = new SimpleMessageListenerContainer(config.connectionFactory instanceof ConnectionFactoryAdapter ? config.connectionFactory.getHandler() : config.connectionFactory)
//@ts-ignore
this._Handler.setQueueNames(config.queueNames)
this._Handler.setMaxConcurrentConsumers(config.maxConcurrentConsumers)
this._Handler.setConcurrentConsumers(config.concurrentConsumers)
this._Handler.setAcknowledgeMode(config.acknowledgeMode)
this._Handler.setMessageListener(config.messageListener instanceof ChannelAwareMessageListenerAdapter ? config.messageListener.getHandler() : config.messageListener)
}
getHandler() {
return this._Handler
}
}

View File

@@ -0,0 +1,37 @@
const JavaString = Java.type('java.lang.String')
const MessageProperties = org.springframework.amqp.core.MessageProperties
const Message = org.springframework.amqp.core.Message
export const MessageConverter = Java.type('org.springframework.amqp.support.converter.MessageConverter')
export class MessageConverterAdapter {
private _Handler: org.springframework.amqp.support.converter.MessageConverter
constructor() {
var MessageConverterImpl = Java.extend(MessageConverter, {
toMessage: (object: any, messageProperties: org.springframework.amqp.core.MessageProperties) => this.toMessage(object, messageProperties),
fromMessage: (message: org.springframework.amqp.core.Message) => this.fromMessage(message)
})
this._Handler = new MessageConverterImpl()
}
toMessage(object: any, messageProperties: org.springframework.amqp.core.MessageProperties) {
if (typeof object == "string") {
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN)
return new Message(new JavaString(object).getBytes(), messageProperties)
} else {
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_JSON)
return new Message(new JavaString(JSON.stringify(object)).getBytes(), messageProperties)
}
}
fromMessage(message: org.springframework.amqp.core.Message) {
if (message.getMessageProperties().getContentType() == MessageProperties.CONTENT_TYPE_TEXT_PLAIN) {
return new JavaString(message.getBody(), message.getMessageProperties().getContentEncoding())
}
if (message.getMessageProperties().getContentType() == MessageProperties.CONTENT_TYPE_JSON) {
return JSON.parse(new JavaString(message.getBody(), message.getMessageProperties().getContentEncoding()))
}
return message
}
getHandler() {
return this._Handler
}
}

View File

@@ -0,0 +1,7 @@
export * from './admin'
export * from './callback'
export * from './connection'
export * from './container'
export * from './converter'
export * from './listener'
export * from './template'

View File

@@ -0,0 +1,49 @@
const JavaString = Java.type('java.lang.String')
export const MessageProperties = org.springframework.amqp.core.MessageProperties
export const ChannelAwareMessageListener = Java.type('org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener')
export const AmqpRejectAndDontRequeueException = org.springframework.amqp.AmqpRejectAndDontRequeueException
export const Channel: com.rabbitmq.client.Channel = Java.type('com.rabbitmq.client.Channel')
export const Message = org.springframework.amqp.core.Message
export type MessageHandler<T> = (content: T, message: org.springframework.amqp.core.Message, channel: com.rabbitmq.client.Channel) => any
export abstract class ChannelAwareMessageListenerAdapter<T = any> {
private _Handler: org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener
constructor(manual: boolean = false) {
let ChannelAwareMessageListenerImpl = Java.extend(ChannelAwareMessageListener, {
onMessage: (message: org.springframework.amqp.core.Message, channel: com.rabbitmq.client.Channel) => {
let content = new JavaString(message.getBody(), message.getMessageProperties().getContentEncoding() || "UTF-8")
try {
if (message.getMessageProperties().getContentType() == MessageProperties.CONTENT_TYPE_JSON) {
content = JSON.parse(content)
}
} catch (error) {
if (manual) {
channel.basicReject(message.getMessageProperties().getDeliveryTag(), true)
} else {
throw new AmqpRejectAndDontRequeueException(`${error}`, error)
}
}
manual ? this.manualOnMessage(content, message, channel) : this.onMessage(content, message, channel)
}
})
this._Handler = new ChannelAwareMessageListenerImpl()
}
abstract onMessage(content: T, message: org.springframework.amqp.core.Message, channel: com.rabbitmq.client.Channel): any
onError(error: Error, message: org.springframework.amqp.core.Message, channel: com.rabbitmq.client.Channel): any { }
getHandler() {
return this._Handler
}
private manualOnMessage(content: T, message: org.springframework.amqp.core.Message, channel: com.rabbitmq.client.Channel) {
let deliveryTag = message.getMessageProperties().getDeliveryTag()
try {
if (this.onMessage(content, message, channel)) {
channel.basicAck(deliveryTag, false)
} else if (message.getMessageProperties().getRedelivered()) {
channel.basicReject(deliveryTag, true)
} else {
channel.basicNack(deliveryTag, false, true)
}
} catch (error) {
channel.basicReject(deliveryTag, this.onError(error, message, channel))
}
}
}

View File

@@ -0,0 +1,27 @@
import { MessageConverterAdapter } from "./converter"
import { ConnectionFactoryAdapter } from "./connection"
import { ConfirmCallbackAdapter, ReturnCallbackAdapter } from "./callback"
export const RabbitTemplate = Java.type('org.springframework.amqp.rabbit.core.RabbitTemplate')
interface TemplateConfig {
connectionFactory: ConnectionFactoryAdapter | org.springframework.amqp.rabbit.connection.ConnectionFactory
confirmCallback?: ConfirmCallbackAdapter
returnCallback?: ReturnCallbackAdapter
messageConverter?: MessageConverterAdapter
}
export class RabbitTemplateAdapter {
private _Handler: org.springframework.amqp.rabbit.core.RabbitTemplate
constructor(config: TemplateConfig) {
config = { messageConverter: new MessageConverterAdapter(), ...config }
console.debug(JSON.stringify(config))
this._Handler = new RabbitTemplate(config.connectionFactory instanceof ConnectionFactoryAdapter ? config.connectionFactory.getHandler() : config.connectionFactory)
config.returnCallback && this._Handler.setReturnCallback(config.returnCallback.getHandler())
config.confirmCallback && this._Handler.setConfirmCallback(config.confirmCallback.getHandler())
config.messageConverter && this._Handler.setMessageConverter(config.messageConverter.getHandler())
}
getHandler() {
return this._Handler
}
}

View File

@@ -0,0 +1,89 @@
import { JSClass } from "@ccms/container"
import { RabbitAdminAdapter, MessageListenerContainerAdapter, AcknowledgeMode, MessageHandler, ChannelAwareMessageListenerAdapter } from "./adapter"
export class AmqpAdmin {
@JSClass('org.springframework.amqp.core.TopicExchange')
private TopicExchange: typeof org.springframework.amqp.core.TopicExchange
@JSClass('org.springframework.amqp.core.Queue')
private Queue: typeof org.springframework.amqp.core.Queue
@JSClass('org.springframework.amqp.core.Binding')
private Binding: typeof org.springframework.amqp.core.Binding
@JSClass('org.springframework.amqp.rabbit.core.RabbitAdmin')
private RabbitAdmin: typeof org.springframework.amqp.rabbit.core.RabbitAdmin
@JSClass('org.springframework.amqp.core.Binding.DestinationType')
private DestinationType: org.springframework.amqp.core.Binding.DestinationType
private amqpAdmin: org.springframework.amqp.rabbit.core.RabbitAdmin
constructor(amqpAdmin: org.springframework.amqp.rabbit.core.RabbitAdmin | any) {
if (amqpAdmin instanceof this.RabbitAdmin) {
this.amqpAdmin = amqpAdmin
} else if (amqpAdmin instanceof RabbitAdminAdapter) {
this.amqpAdmin = amqpAdmin.getHandler()
} else {
this.amqpAdmin = new RabbitAdminAdapter(amqpAdmin).getHandler()
}
}
getHandler() {
return this.amqpAdmin
}
getQueueProperties(name: string) {
return this.amqpAdmin.getQueueProperties(name)
}
declareExchange(name: string) {
let exchange = new this.TopicExchange(name, true, false)
this.amqpAdmin.declareExchange(exchange)
return this
}
declareQueue(name: string) {
let queue = new this.Queue(name, true)
this.amqpAdmin.declareQueue(queue)
return this
}
declareBinding(queue: string, exchange: string, routerKey: string, argument: any = null) {
let binding = new this.Binding(queue, this.DestinationType.QUEUE, exchange, routerKey, argument)
this.amqpAdmin.declareBinding(binding)
return this
}
declareQueueAndBindExchange(queue: string, exchange: string, routerKey: string) {
return this.declareQueue(queue).declareExchange(exchange).declareBinding(queue, exchange, routerKey)
}
createContainer<T>(queue: string, listener: MessageHandler<T>, acknowledgeMode = AcknowledgeMode.AUTO) {
let connection = this.amqpAdmin.getRabbitTemplate().getConnectionFactory()
return new MessageListenerContainerAdapter({
connectionFactory: connection,
queueNames: [queue],
messageListener: new SimpleMessageHandler(listener),
acknowledgeMode: acknowledgeMode
}).getHandler()
}
send()
send() {
}
getTemplate() {
return this.amqpAdmin.getRabbitTemplate()
}
close() {
this.getTemplate().stop()
}
}
export class SimpleMessageHandler extends ChannelAwareMessageListenerAdapter {
constructor(private handler: MessageHandler<any>) {
super()
}
onMessage(content: any, message: org.springframework.amqp.core.Message, channel: com.rabbitmq.client.Channel) {
return this.handler(content, message, channel)
}
}

View File

@@ -0,0 +1,3 @@
export const METADATA_KEY = {
}

View File

@@ -0,0 +1,34 @@
/// <reference types="@ccms/nashorn" />
/// <reference types="@javatypes/jdk" />
/// <reference types="@javatypes/spring-amqp" />
/// <reference types="@javatypes/spring-rabbit" />
/// <reference types="@javatypes/amqp-client" />
import { amqp } from '@ccms/api'
import { getContainer, reduceMetadata } from '@ccms/container'
function init() {
const beanFactory = base.getInstance().getAutowireCapableBeanFactory()
getContainer().bind(amqp.rabbit.Template).toDynamicValue((ctx) => {
let metadata = reduceMetadata(ctx)
if (!metadata.named) {
return beanFactory.getBean('rabbitTemplate')
} else {
return beanFactory.getBean(`${metadata.named}-rabbitTemplate`)
}
})
getContainer().bind(amqp.rabbit.Admin).toDynamicValue((ctx) => {
let metadata = reduceMetadata(ctx)
if (!metadata.named) {
return beanFactory.getBean('rabbitAdmin')
} else {
return beanFactory.getBean(`${metadata.named}-rabbitAdmin`)
}
})
}
init()
export * from './admin'
export * from './adapter'
export * from './manager'
export * from './constants'

View File

@@ -0,0 +1,5 @@
export class AmqpListener {
constructor() {
}
}

View File

@@ -0,0 +1,18 @@
import { amqp } from "@ccms/api"
import { provideSingleton } from "@ccms/container"
import { ConnectionFactoryAdapter, RabbitTemplateAdapter, RabbitAdminAdapter } from "./adapter"
import { AmqpAdmin } from "./admin"
@provideSingleton(amqp.Manager)
export class AmqpManager {
createConnection(url: string, username: string, password: string) {
return new ConnectionFactoryAdapter({ url, username, password })
}
createTemplate(adapter: ConnectionFactoryAdapter) {
return new RabbitTemplateAdapter({ connectionFactory: adapter })
}
createAdmin(adapter: RabbitTemplateAdapter | ConnectionFactoryAdapter) {
return new AmqpAdmin(new RabbitAdminAdapter(adapter))
}
}

View File

@@ -0,0 +1,7 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"baseUrl": "src",
"outDir": "dist"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@ccms/nashorn",
"version": "0.8.4",
"version": "0.9.0",
"description": "MiaoScript api package",
"keywords": [
"miaoscript",

View File

@@ -1,6 +1,6 @@
{
"name": "@ccms/nodejs",
"version": "0.8.4",
"version": "0.9.0",
"description": "MiaoScript nodejs package",
"keywords": [
"miaoscript",
@@ -19,7 +19,7 @@
"test": "echo \"Error: run tests from root\" && exit 1"
},
"devDependencies": {
"@ccms/nashorn": "^0.8.4",
"@ccms/nashorn": "^0.9.0",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"typescript": "^3.9.6"

View File

@@ -1,6 +1,6 @@
{
"name": "@ccms/nukkit",
"version": "0.8.4",
"version": "0.9.0",
"description": "MiaoScript nukkit package",
"keywords": [
"miaoscript",
@@ -25,8 +25,8 @@
"typescript": "^3.9.6"
},
"dependencies": {
"@ccms/api": "^0.8.4",
"@ccms/common": "^0.8.4",
"@ccms/container": "^0.8.4"
"@ccms/api": "^0.9.0",
"@ccms/common": "^0.9.0",
"@ccms/container": "^0.9.0"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@ccms/ployfill",
"version": "0.8.4",
"version": "0.9.0",
"description": "MiaoScript ployfill package",
"author": "MiaoWoo <admin@yumc.pw>",
"homepage": "https://github.com/circlecloud/ms.git",
@@ -14,12 +14,12 @@
"test": "echo \"Error: run tests from root\" && exit 1"
},
"dependencies": {
"@ccms/i18n": "^0.8.4",
"@ccms/nodejs": "^0.8.4",
"@ccms/i18n": "^0.9.0",
"@ccms/nodejs": "^0.9.0",
"core-js": "^3.6.5"
},
"devDependencies": {
"@ccms/nashorn": "^0.8.4",
"@ccms/nashorn": "^0.9.0",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"typescript": "^3.9.6"

View File

@@ -1,6 +1,6 @@
{
"name": "@ccms/plugin",
"version": "0.8.4",
"version": "0.9.1",
"description": "MiaoScript api package",
"keywords": [
"miaoscript",
@@ -25,10 +25,10 @@
"typescript": "^3.9.6"
},
"dependencies": {
"@ccms/api": "^0.8.4",
"@ccms/common": "^0.8.4",
"@ccms/container": "^0.8.4",
"@ccms/i18n": "^0.8.4",
"@ccms/api": "^0.9.0",
"@ccms/common": "^0.9.0",
"@ccms/container": "^0.9.0",
"@ccms/i18n": "^0.9.0",
"js-yaml": "^3.14.0"
}
}

View File

@@ -43,7 +43,7 @@ function loadConfig(plugin: plugin.Plugin) {
base.save(configFile, configFactory.dump(plugin[config.variable]))
console.i18n("ms.plugin.manager.config.save.default", { plugin: plugin.description.name, name: config.name, format: config.format })
} else {
plugin[config.variable] = configFactory.load(base.read(configFile))
Object.defineProperty(plugin, config.variable, { value: configFactory.load(base.read(configFile)) })
plugin[config.variable].save = () => {
let result = configFactory.dump(plugin[config.variable])
base.save(configFile, result)

View File

@@ -20,7 +20,7 @@ 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))
this.EventManager.listen(pluginInstance, event.name, pluginInstance[event.executor].bind(pluginInstance), event.priority, event.ignoreCancel)
}
}

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "@ccms/plugins",
"version": "0.8.4",
"version": "0.9.1",
"description": "MiaoScript plugins package",
"keywords": [
"miaoscript",
@@ -28,8 +28,8 @@
"typescript": "^3.9.6"
},
"dependencies": {
"@ccms/api": "^0.8.4",
"@ccms/container": "^0.8.4",
"@ccms/plugin": "^0.8.4"
"@ccms/api": "^0.9.0",
"@ccms/container": "^0.9.0",
"@ccms/plugin": "^0.9.1"
}
}

View File

@@ -0,0 +1,104 @@
import { constants, task, server, channel } from "@ccms/api"
import { JSPlugin, interfaces, Cmd, Tab, Listener, Config } from "@ccms/plugin"
import { QRCode, QRErrorCorrectLevel } from '@ccms/common/dist/qrcode'
import { inject, JSClass, optional } from '@ccms/container'
import http from '@ccms/common/dist/http'
import chat from '@ccms/bukkit/dist/enhance/chat'
const MapView = Java.type('org.bukkit.map.MapView')
const Bukkit = Java.type('org.bukkit.Bukkit')
const MapRenderer = Java.type('org.bukkit.map.MapRenderer')
const ItemStack = Java.type('org.bukkit.inventory.ItemStack')
const Material = Java.type('org.bukkit.Material')
const BufferedImage = Java.type('java.awt.image.BufferedImage')
const Graphics2D = Java.type('java.awt.Graphics2D')
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 {
private _proxy
constructor(image: any) {
const ProxyAdapter = Java.extend(MapRenderer, {
render: (_mapView, mapCanvas) => {
mapCanvas.drawImage(0, 0, image)
}
})
this._proxy = new ProxyAdapter()
}
getHandler() {
return this._proxy
}
}
interface PlaceholderAPI {
registerPlaceholderHook: (key: string, onPlaceholderRequest: (player, s) => string) => void
setPlaceholders: (player: any, str: string) => string
}
@JSPlugin({ version: '1.0.0', author: 'MiaoWoo', servers: [constants.ServerType.Bukkit], source: __filename })
export class MiaoCoin extends interfaces.Plugin {
@inject(server.Server)
private server: server.Server
@inject(task.TaskManager)
private taskManager: task.TaskManager
@JSClass('me.clip.placeholderapi.PlaceholderAPI')
private PlaceholderAPI: PlaceholderAPI
@JSClass('me.clip.placeholderapi.PlaceholderHook')
private PlaceholderHook: any
@Config()
private config = {
MySQL: {
}
}
enable() {
if (!this.PlaceholderAPI) {
console.console("§cCan't found me.clip.placeholderapi.PlaceholderAPI variable will not be replaced!")
} else {
this.PlaceholderAPI.registerPlaceholderHook("mcn", new this.PlaceholderHook({
onPlaceholderRequest: (player: any, s: string) => {
switch (s.toLowerCase()) {
case "has":
return ''
case "add":
return ''
case "remove":
return ''
default:
}
return `未知的参数: ${s}`
}
}))
}
}
disable() {
}
@Cmd()
mrd(sender: any, command: string, args: string[]) {
let cmd = args[0] || 'help'
let cmdKey = 'cmd' + cmd
if (!this[cmdKey]) {
console.sender(sender, '§4未知的子命令: §c' + cmd)
console.sender(sender, `§6请执行 §b/${command} §ahelp §6查看帮助!`)
return
}
args.shift()
this.taskManager.create(() => this[cmdKey](sender, ...args)).async().submit()
}
@Tab()
tabmrd(sender: any, _command: any, args: string | any[]) {
if (args.length === 1) return ['help', 'bind', 'show', 'query', 'draw', "ratio"]
if (args.length === 2 && args[0] === "bind" && sender.isOp()) return ['server']
}
}

View File

@@ -0,0 +1,87 @@
import { constants, channel, server } from "@ccms/api"
import { JSPlugin, interfaces, Cmd, Tab } from "@ccms/plugin"
import { inject, optional } from '@ccms/container'
const StandardCharsets = Java.type('java.nio.charset.StandardCharsets')
const JavaString = Java.type('java.lang.String')
@JSPlugin({ author: 'MiaoWoo', servers: [constants.ServerType.Bukkit], source: __filename })
export class MiaoGUI extends interfaces.Plugin {
@inject(server.Server)
private server: server.Server
@inject(channel.Channel)
@optional()
private channel: channel.Channel
private CHANNEL_NAME = `${MiaoGUI.name}:NORMAL`.toLowerCase();
private channelOff: { off: () => void }
enable() {
this.channelOff = this.channel?.listen(this, this.CHANNEL_NAME, (data) => {
this.logger.info('MiaoGUI', new JavaString(data))
})
}
disable() {
this.channelOff?.off()
}
@Cmd()
mgui(sender: any, command: string, args: string[]) {
let cmd = args[0] || 'help'
let cmdKey = 'cmd' + cmd
if (!this[cmdKey]) {
console.sender(sender, '§4未知的子命令: §c' + cmd)
console.sender(sender, `§6请执行 §b/${command} §ahelp §6查看帮助!`)
return
}
args.shift()
this[cmdKey](sender, ...args)
}
cmdhhelp(sender: any) {
this.logger.sender(sender, '')
}
cmdrun(sender: any, ...args: string[]) {
this.send(sender, "run", { cmd: args.join(' ') })
}
cmdconfig(sender: org.bukkit.entity.Player) {
this.send(sender, "config")
}
cmdopen(sender: any, name: string) {
this.send(sender, "open", { name })
}
cmdopenfor(sender: any, target: string, name: string) {
let player = this.server.getPlayer(target)
if (!player) { return this.logger.sender(sender, `§c玩家 ${target} 不在线或不存在!`) }
this.send(player, "open", { name })
}
cmdadd(sender: any, type: string, ...param: string[]) {
if (!type) return this.logger.sender(sender, '§c组件类型不得为空!')
this.send(sender, "add", { type, param })
}
cmdclear(sender: org.bukkit.entity.Player) {
this.send(sender, "clear")
}
@Tab()
tabmgui(args: string[]) {
if (args.length == 1) return ['open', 'openfor', 'config', 'add', 'clear']
if (args.length == 2) {
switch (args[0]) {
case "add":
return ['text', 'image', 'broadcast']
}
}
}
private send(target: any, type: string, data: any = {}) {
this.channel?.send(target, this.CHANNEL_NAME, new JavaString(JSON.stringify({ type, data })).getBytes(StandardCharsets.UTF_8))
}
}

View File

@@ -0,0 +1,498 @@
import { constants, task, server, channel } from "@ccms/api"
import { JSPlugin, interfaces, Cmd, Tab, Listener, Config } from "@ccms/plugin"
import { QRCode, QRErrorCorrectLevel } from '@ccms/common/dist/qrcode'
import { inject, JSClass, optional } from '@ccms/container'
import http from '@ccms/common/dist/http'
import chat from '@ccms/bukkit/dist/enhance/chat'
const MapView = Java.type('org.bukkit.map.MapView')
const Bukkit = Java.type('org.bukkit.Bukkit')
const MapRenderer = Java.type('org.bukkit.map.MapRenderer')
const ItemStack = Java.type('org.bukkit.inventory.ItemStack')
const Material = Java.type('org.bukkit.Material')
const BufferedImage = Java.type('java.awt.image.BufferedImage')
const Graphics2D = Java.type('java.awt.Graphics2D')
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 {
private _proxy
constructor(image: any) {
const ProxyAdapter = Java.extend(MapRenderer, {
render: (_mapView, mapCanvas) => {
mapCanvas.drawImage(0, 0, image)
}
})
this._proxy = new ProxyAdapter()
}
getHandler() {
return this._proxy
}
}
interface PlaceholderAPI {
registerPlaceholderHook: (key: string, onPlaceholderRequest: (player, s) => string) => void
setPlaceholders: (player: any, str: string) => string
}
@JSPlugin({ prefix: 'MRD', version: '1.1.0', author: 'MiaoWoo', servers: [constants.ServerType.Bukkit], source: __filename })
export class MiaoReward extends interfaces.Plugin {
private cacheBindUuid = ''
private cacheTasks = new Map<number, task.Cancelable>()
private isBinding = false
private isBindingUser = 'unknow'
private drawCooldown = new Map<string, number>()
@inject(server.Server)
private server: server.Server
@inject(task.TaskManager)
private taskManager: task.TaskManager
@inject(channel.Channel)
@optional() private Channel: channel.Channel
@Config()
private config = {
prefix: '§6[§b广告系统§6]§r',
serverId: '',
serverToken: '',
drawCommand: 'p give %player_name% %amount%',
drawCooldown: 300
}
@JSClass('me.clip.placeholderapi.PlaceholderAPI')
private PlaceholderAPI: PlaceholderAPI
@JSClass('me.clip.placeholderapi.PlaceholderHook')
private PlaceholderHook: any
private isBungeeCord = undefined
private channelOff: { off: () => void }
load() {
this.config.prefix = this.config.prefix || '§6[§b广告系统§6]§r'
this.config.drawCommand = this.config.drawCommand || 'p give %player_name% %amount%'
this.config.drawCooldown = this.config.drawCooldown || 300
}
enable() {
if (!this.PlaceholderAPI) {
console.console("§cCan't found me.clip.placeholderapi.PlaceholderAPI variable will not be replaced!")
} else {
this.PlaceholderAPI.registerPlaceholderHook("mrd", new this.PlaceholderHook({
onPlaceholderRequest: (player: any, s: string) => {
switch (s.toLowerCase()) {
case "server":
case "bserver":
default:
}
return "未知的参数"
}
}))
}
this.channelOff = this.Channel?.listen(this, 'BungeeCord', (data) => { this.isBungeeCord = true })
let players = this.server.getOnlinePlayers()
if (players.length) this.bungeeCordDetect(players[0])
}
disable() {
this.cacheTasks.forEach(t => t.cancel())
this.server.getOnlinePlayers().forEach(p => this.checkAndClear(p))
this.channelOff?.off()
}
@Cmd()
mrd(sender: any, command: string, args: string[]) {
let cmd = args[0] || 'help'
let cmdKey = 'cmd' + cmd
if (!this[cmdKey]) {
console.sender(sender, '§4未知的子命令: §c' + cmd)
console.sender(sender, `§6请执行 §b/${command} §ahelp §6查看帮助!`)
return
}
args.shift()
this.taskManager.create(() => this[cmdKey](sender, ...args)).async().submit()
}
cmdbind(sender: org.bukkit.entity.Player, server: boolean) {
if (this.isBinding) {
let bindUser = Bukkit.getPlayerExact(this.isBindingUser)
if (bindUser && bindUser.isOnline() && this.isQrCodeItem(bindUser.getItemInHand())[0]) {
return this.logger.sender(sender, "§c当前 §a" + this.isBindingUser + " §c玩家正在绑定账号 请稍候重试...")
}
}
if (sender.getItemInHand()?.getType() !== Material.AIR) { return this.logger.sender(sender, "§c请空手执行此命令!") }
this.isBinding = true
this.isBindingUser = sender.getDisplayName() || sender.getName()
if (server) {
if (!sender.isOp()) { return this.logger.sender(sender, '§4您没有配置服务器的权限!') }
this.bindServer(sender)
} else {
this.bindUser(sender)
}
}
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)
if (!Number.isInteger(amount)) {
return this.logger.sender(sender, '§4金额必须是数字!')
}
if (amount % 100 !== 0) {
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}`
let draw = http.get(address)
if (draw.code !== 200) {
return this.sendError(sender, `§4兑换异常 §6服务器返回: §c${draw.msg}`)
}
let drawAmount = draw.data
if (!drawAmount) {
return this.sendError(sender, '§c服务器返回金额 ' + draw.data + ' 可能存在异常')
}
let command = `p give ${sender.getName()} ${draw.data}`
if (!this.server.dispatchConsoleCommand(command)) {
return this.sendError(sender, '§6执行命令 §3/' + command + ' §c可能存在异常')
}
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}§c/mrd help §b查看广告系统帮助 §6快来一起看广告赚点券吧!`)
}
private sendError(sender, error) {
return this.logger.sender(sender, [
`§c========== ${this.config.prefix}§4兑换异常 §c==========`,
error,
`§6异常账号: §b${sender.getName()}`,
`§6异常时间: §a${new Date().toLocaleDateString()} ${new Date().toLocaleTimeString()}`,
`§c如果喵币被扣除且未得到奖励 请截图发往QQ群!`,
`§c========== ${this.config.prefix}§4兑换异常 §c==========`,
])
}
cmdrank(sender: any, boardcast: boolean) {
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}`)
if (result.code !== 200) {
return this.logger.sender(sender, `§c今日未查询到数据!`)
}
let ranks = [
`§6====== ${this.config.prefix} §a喵币兑换排行 §6======`,
...result.data.map((e, i) => `§6${i + 1}. §a${e.username} §6兑换 §3${e.count} §6次 §c${e.amount} §6喵币`),
`§6====== ${this.config.prefix} §a喵币兑换排行 §6======`,
]
if (boardcast) {
ranks.forEach(l => this.sendBoardcast(sender, l))
} else {
this.logger.sender(sender, ranks)
}
}
cmdserver(sender: any) {
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}`)
if (result.code !== 200) {
return this.logger.sender(sender, `§4操作异常 §6服务器返回: §c${result.msg}`)
}
this.logger.sender(sender, [
`§6====== ${this.config.prefix} §a服务器信息 §6======`,
`§6服务器: §a${result.data.name}`,
`§6喵币余额: §b${result.data.score} §6喵币`,
`§6喵币比例: §b${result.data.ratio}`,
`§6今日收入: §b${result.data.today} §6喵币`,
`§6====== ${this.config.prefix} §a服务器信息 §6======`,
])
}
cmdratio(sender: any, ratio: number, confirm: string) {
if (!sender.isOp()) { return this.logger.sender(sender, '§4你没有此命令的权限!') }
let mbr = (1 / ratio).toFixed(4)
if (!confirm) {
return this.logger.sender(sender, [
'§4警告: 您正在设置服务器喵币/点券兑换比例 设置后将实时生效!',
`§6您设置的兑换比例为 §c${ratio} §6=> §a${mbr}喵币 §6兑换 §c1点券`,
`§6玩家至少需要 §a${mbr}喵币 §6才可以兑换点券!`,
`§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}`)
if (result.code !== 200) {
return this.logger.sender(sender, `§4操作异常 §6服务器返回: §c${result.msg}`)
}
this.logger.sender(sender, `§a操作成功 §6服务器返回: §a${result.msg}`)
this.sendBoardcast(sender, `${this.config.prefix} §6当前兑换比例已调整为 §c${ratio} §6=> §a${mbr}喵币 §6兑换 §c1点券!`)
}
@JSClass('java.io.ByteArrayOutputStream')
private ByteArrayOutputStream
@JSClass('java.io.DataOutputStream')
private DataOutputStream
private sendBoardcast(player, message) {
if (!this.isBungeeCord) { return org.bukkit.Bukkit.broadcastMessage(message) }
let byteArray = new this.ByteArrayOutputStream()
let out = new this.DataOutputStream(byteArray)
out.writeUTF("Message")
out.writeUTF("ALL")
out.writeUTF(message)
player.sendPluginMessage(base.getInstance(), "BungeeCord", byteArray.toByteArray())
}
private bindServer(sender: org.bukkit.entity.Player) {
let scanObj = http.get(`https://reward.yumc.pw/server/scan`)
if (scanObj.code !== 200) {
return this.logger.sender(sender, '§c获取服务器绑定码失败! Error: ' + scanObj.msg)
}
this.cacheBindUuid = scanObj.data.uuid
this.setItemAndTp(sender, scanObj.data.url, () => {
let check = http.get(`https://reward.yumc.pw/server/check/token/${this.cacheBindUuid}`)
if (check.code == 200) {
this.config.serverId = check.data.serverId
this.config.serverToken = check.data.serverToken
// @ts-ignore
this.config.save()
this.checkAndClear(sender)
this.logger.sender(sender, '§a已成功绑定服务器: §b' + check.data.serverName)
return true
}
})
}
private bindUser(sender: org.bukkit.entity.Player) {
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}`)
if (check.code !== 200) {
return this.logger.sender(sender, '§4获取绑定参数异常! §cError: ' + check.msg)
}
let queryUser = this.queryUser(sender)
if (queryUser.code == 200) {
return this.logger.sender(sender, ['§4当前用户已绑定! §c如需解绑 请联系腐竹!', '§b如需看广告请进QQ群 点击聊天框上的圈云盒子!'])
}
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 queryUser = this.queryUser(sender)
if (queryUser.code == 200) {
this.sendResult(sender, '绑定成功', queryUser.data)
this.checkAndClear(sender)
return true
}
})
}
private setItemAndTp(sender: org.bukkit.entity.Player, content: string, func: () => boolean) {
this.taskManager.create(() => {
let [id, item] = this.createQrCodeMapItem(content)
let times = 0
let task = this.taskManager.create(() => {
chat.send(sender, JSON.stringify({
text: '§c§l请使用手机QQ扫描二维码绑定!'
}), 2)
if (func() || !sender.isOnline()) {
task.cancel()
this.cacheTasks.delete(id)
this.isBinding = false
}
if (times++ > 120) {
this.logger.sender(sender, '§c绑定超时!')
this.checkAndClear(sender)
task.cancel()
this.cacheTasks.delete(id)
this.isBinding = false
}
}).async().later(20).timer(20).submit()
this.cacheTasks.set(id, task)
sender.setItemInHand(item)
let temp = sender.getLocation()
temp.setPitch(90)
sender.teleport(temp)
}).submit()
}
private queryUser(sender: org.bukkit.entity.Player) {
return http.get(`https://reward.yumc.pw/server/queryUser/id/${this.config.serverId}/token/${this.config.serverToken}/uuid/${sender.getUniqueId().toString()}/username/${sender.getName()}`)
}
cmdquery(sender: org.bukkit.entity.Player) {
let check = this.queryUser(sender)
if (check.code !== 200) {
return this.logger.sender(sender, '§4查询异常! §cError: ' + check.msg)
}
this.sendResult(sender, '查询结果', check.data)
}
private sendResult(sender: any, title: string, data: any) {
this.logger.sender(sender, [
`§6====== ${this.config.prefix} §a${title} §6======`,
`§6用 户 名: §a${sender.getName()}`,
`§6U U I D: §b${sender.getUniqueId().toString()}`,
`§6喵 币: §b${data.balance}`,
`§6签 到: §b${data.sign}`,
`§6视频广告: §b${data.video}`,
`§6盒子广告: §b${data.box}`,
'§6==========================='
])
}
private createQrCodeMapItem(contents: string): [number, any] {
let view = Bukkit.createMap(Bukkit.getWorlds()[0])
view.setScale(MapView.Scale.FARTHEST)
view.getRenderers().forEach(r => view.removeRenderer(r))
view.addRenderer(new QRCodeRender(this.createQrcode(contents)).getHandler())
let item: org.bukkit.inventory.ItemStack = new ItemStack(Material.MAP)
let meta = item.getItemMeta()
meta.setDisplayName('§c请使用手机QQ扫描二维码绑定!')
meta.setLore(["QRCODE"])
item.setDurability(view.getId())
item.setItemMeta(meta)
return [view.getId(), item]
}
private createQrcode(contents: string) {
let bufferedImage = new BufferedImage(128, 128, BufferedImage.TYPE_INT_RGB)
let graphics2D = bufferedImage.getGraphics()
graphics2D.setPaint(Color.WHITE)
graphics2D.fillRect(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight())
let qrcode = this.js2qr(contents)
let startPoint = Math.round((bufferedImage.getWidth() - qrcode.getWidth()) / 2)
graphics2D.drawImage(qrcode, startPoint, startPoint, null)
// let font = new Font("DejaVuSans", Font.PLAIN, 10)
// graphics2D.setFont(font)
// let fm = graphics2D.getFontMetrics(font)
// let text = "Use QQ Scan Bind!"
// let textWidth = fm.stringWidth(text)
// let widthX = (128 - textWidth) / 2
// graphics2D.setColor(Color.BLACK)
// graphics2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB)
// graphics2D['drawString(java.lang.String, int, int)'](text, Math.round(widthX), 122)
graphics2D.dispose()
return bufferedImage
}
private js2qr(contents: string) {
let qrcode = new QRCode(14, QRErrorCorrectLevel.H)
qrcode.addData(contents)
qrcode.make()
let length = qrcode.getModuleCount()
let image: java.awt.image.BufferedImage = new BufferedImage(length, length, BufferedImage.TYPE_INT_RGB)
for (let x = 0; x < length; x++) {
for (let y = 0; y < length; y++) {
image.setRGB(x, y, qrcode.isDark(x, y) ? 0xFF000000 : 0xFFFFFFFF)
}
}
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) {
if (this.isBungeeCord === undefined && player) {
let byteArray = new this.ByteArrayOutputStream()
let out = new this.DataOutputStream(byteArray)
out.writeUTF("GetServer")
player.sendPluginMessage(base.getInstance(), "BungeeCord", byteArray.toByteArray())
}
}
@Listener()
PlayerJoinEvent(event: org.bukkit.event.player.PlayerJoinEvent) {
this.bungeeCordDetect(event.getPlayer())
}
@Listener()
PlayerDropItemEvent(event: org.bukkit.event.player.PlayerDropItemEvent) {
let [cancelled, id] = this.isQrCodeItem(event.getItemDrop().getItemStack())
if (id != null && id != undefined && cancelled) {
event.getItemDrop().remove()
this.cancelTask(id)
}
}
@Listener()
PlayerItemHeldEvent(event: org.bukkit.event.player.PlayerItemHeldEvent) {
let inv = event.getPlayer().getInventory()
let [cancelled, id] = this.isQrCodeItem(inv.getItem(event.getPreviousSlot() as any))
if (id != null && id != undefined && cancelled) {
inv.setItem(event.getPreviousSlot(), null)
this.cancelTask(id)
}
}
@Listener()
InventoryClickEvent(event: org.bukkit.event.inventory.InventoryClickEvent) {
let item = event.getCurrentItem()
let [cancelled, id] = this.isQrCodeItem(item)
if (id != null && id != undefined && cancelled) {
event.getInventory().setItem(event.getSlot(), null)
this.cancelTask(id)
event.setCancelled(true)
}
}
@Listener()
PlayerQuitEvent(event: org.bukkit.event.player.PlayerQuitEvent) {
this.checkAndClear(event.getPlayer())
}
private cancelTask(id) {
this.cacheTasks.get(id)?.cancel()
this.cacheTasks.delete(id)
this.isBinding = false
}
private checkAndClear(player: org.bukkit.entity.Player) {
if (this.isQrCodeItem(player.getItemInHand())[0]) {
player.setItemInHand(null)
}
}
private isQrCodeItem(item: org.bukkit.inventory.ItemStack): [boolean, number?] {
if (!item) { return [false] }
if ((item?.getType() == Material.MAP || item?.getType() == Material.FILLED_MAP) && item.hasItemMeta()) {
let meta = item.getItemMeta()
return [!!(Java.from(meta.getLore()).indexOf('QRCODE') != -1), item.getDurability()]
}
return [false]
}
cmdhelp(sender: any) {
let help = [
`§6====== ${this.config.prefix} §a帮助菜单 §6======`,
`§6/mrd bind §a绑定圈云盒子`,
`§6/mrd query §a查询当前账户`,
`§6/mrd draw §e<兑换数量> §a兑换点券`
]
if (sender.isOp()) {
help = help.concat([
`§c由于您是管理员 以为您展示额外命令`,
`§6/mrd bind server §a绑定服务器`,
`§6/mrd ratio §e<兑换比例> §a设置喵币/点券兑换比例`,
`§6/mrd statistic §3近期收入统计`,
`§6/mrd rank <boardcast>(是否公告) §2今日兑换排行`,
`§6/mrd server §c当前服务器信息`,
`§6兑换比例设置说明: §b默认比例为 0.001 §6=> §a1000喵币 §6兑换 §c1点券`,
`§c注意 设置比例后 玩家兑换点券数量不能少于 1点券`,
`§c比如 设置了0.001 那就是 玩家至少 1000喵币 才能兑换!`
])
}
this.logger.sender(sender, help)
}
@Tab()
tabmrd(sender: any, _command: any, args: string | any[]) {
if (args.length === 1) return ['help', 'bind', 'show', 'query', 'draw', 'ratio', 'rank', 'server']
if (args.length === 2 && args[0] === "bind" && sender.isOp()) return ['server']
}
}

View File

@@ -1,8 +1,8 @@
import { plugin as pluginApi, task, server } from '@ccms/api'
import { plugin as pluginApi, task, server, plugin, channel, constants } from '@ccms/api'
import { Translate } from '@ccms/i18n'
import { inject, DefaultContainer as container } from '@ccms/container'
import { interfaces, plugin, cmd, tab } from '@ccms/plugin'
import { inject, DefaultContainer as container, optional, JSClass } from '@ccms/container'
import { interfaces, JSPlugin, Cmd, Tab, enable, Listener, disable } from '@ccms/plugin'
import * as fs from '@ccms/common/dist/fs'
import * as reflect from '@ccms/common/dist/reflect'
@@ -26,15 +26,16 @@ let langMap = {
'main.command.not.exists': '§4未知的子命令: §c{command}',
'main.command.help.tip': '§6请执行 §b/{command} §ahelp §6查看帮助!',
'list.install.header': '§6当前 §bMiaoScript §6已安装下列插件:',
'list.install.body': '§6插件名称: §b{name} §6版本: §a{version} §6作者: §3{author}',
'list.install.body': '§6插件名称: §b{name}\n§6版本: §a{version}\n§6作者: §3{author}\n§6来源: §c{from}',
'list.header': '§6当前 §bMiaoScriptPackageCenter §6中存在下列插件:',
'list.body': '§6插件名称: §b{name} §6版本: §a{version} §6作者: §3{author} §6更新时间: §9{updated_at}',
'list.body': '§6插件名称: §b{name}\n§6版本: §a{version}\n§6作者: §3{author}\n§6更新时间: §9{updated_at}',
'plugin.not.exists': '§6插件 §b{name} §c不存在!',
'plugin.unload.finish': '§6插件 §b{name} §a已卸载!',
'plugin.reload.finish': '§6插件 §b{name} §a重载完成!',
'plugin.name.empty': '§c请输入插件名称!',
'cloud.update.finish': '§6成功从 §aMiaoScriptPackageCenter §6获取到 §a{length} §6个插件!',
'cloud.not.exists': '§6当前 §aMiaoScriptPackageCenter §c不存在 §a{name} §c插件!',
'cloud.update.exists': '§6插件 §b{name} §a发现新版本 §3{new_version} §6当前版本 §3{old_version}!',
'download.start': '§6开始下载插件: §b{name}',
'download.url': '§6插件下载地址: §b{url}',
'download.finish': '§6插件 §b{name} §a下载完毕 开始加载 ...',
@@ -51,9 +52,9 @@ let langMap = {
let fallbackMap = langMap
@plugin({ name: 'MiaoScriptPackageManager', prefix: 'PM', version: '1.0.1', author: 'MiaoWoo', source: __filename })
@JSPlugin({ prefix: 'PM', version: '1.2.0', author: 'MiaoWoo', source: __filename })
export class MiaoScriptPackageManager extends interfaces.Plugin {
@inject(pluginApi.PluginManager)
@inject(plugin.PluginManager)
private pluginManager: pluginApi.PluginManager
@inject(task.TaskManager)
private taskManager: task.TaskManager
@@ -63,11 +64,22 @@ export class MiaoScriptPackageManager extends interfaces.Plugin {
private server: server.Server
@inject(pluginApi.PluginFolder)
private pluginFolder: string
@inject(channel.Channel)
@optional() private channel: channel.Channel
@JSClass('java.io.ByteArrayOutputStream')
private ByteArrayOutputStream: any
@JSClass('java.io.DataOutputStream')
private DataOutputStream: any
@JSClass('com.google.common.io.ByteStreams')
private ByteStreams: any
private packageCache: any[] = [];
private packageNameCache: string[] = [];
private serverName: string
private translate: Translate
private channelOff: { off: () => void }
load() {
this.translate = new Translate({
@@ -77,13 +89,100 @@ export class MiaoScriptPackageManager extends interfaces.Plugin {
this.updateRepo(this.server.getConsoleSender())
}
@cmd()
@enable({ servers: [constants.ServerType.Bukkit, constants.ServerType.Sponge] })
serverEnbale() {
this.channelOff = this.channel?.listen(this, 'BungeeCord', (data) => {
let input = this.ByteStreams.newDataInput(data)
let subChannel = input.readUTF()
switch (subChannel) {
case "GetServer":
this.serverName = input.readUTF()
break
case "MiaoScriptPackageManager":
this.readForward(input)
break
}
})
let players = this.server.getOnlinePlayers()
if (players.length) this.bungeeCordDetect(players[0])
}
@disable({ servers: [constants.ServerType.Bukkit, constants.ServerType.Sponge] })
serverDisable() {
this.channelOff?.off()
}
private bungeeCordDetect(player) {
if (player) {
let byteArray = new this.ByteArrayOutputStream()
let out = new this.DataOutputStream(byteArray)
out.writeUTF("GetServer")
player.sendPluginMessage(base.getInstance(), "BungeeCord", byteArray.toByteArray())
}
}
private bungeeCordForward(player, command) {
if (player) {
let byteArray = new this.ByteArrayOutputStream()
let out = new this.DataOutputStream(byteArray)
out.writeUTF("Forward")
out.writeUTF("ALL")
out.writeUTF("MiaoScriptPackageManager")
out.writeUTF(JSON.stringify(command))
player.sendPluginMessage(base.getInstance(), "BungeeCord", byteArray.toByteArray())
}
}
private readForward(input) {
let message = JSON.parse(input.readUTF())
this.taskManager.create(() => this.main(this.server.getConsoleSender(), message.command, message.args)).async().submit()
this.sendBungeeCordMessage(message.sender, `§6[§cMS§6][§bPM§6] [§3BPM§6][§a${this.serverName}§6] §6命令 §b/mpm ${message.args?.join?.(' ')} §a执行成功!`)
}
@Listener({ servers: [constants.ServerType.Bukkit] })
PlayerJoinEvent(event: org.bukkit.event.player.PlayerJoinEvent) {
this.bungeeCordDetect(event.getPlayer())
if (event.getPlayer().isOp()) {
this.updateRepo(event.getPlayer())
}
}
@Listener({ servers: [constants.ServerType.Sponge] })
ClientConnectionEvent$Join(event: org.spongepowered.api.event.network.ClientConnectionEvent.Join) {
if (event.getTargetEntity().hasPermission('ms.mpm.admin')) {
this.updateRepo(event.getTargetEntity())
}
}
private sendBungeeCordMessage(sender, message) {
let players = this.server.getOnlinePlayers()
if (players.length) {
let byteArray = new this.ByteArrayOutputStream()
let out = new this.DataOutputStream(byteArray)
out.writeUTF("Message")
out.writeUTF(sender)
out.writeUTF(message)
this.channel.send(players[0], "BungeeCord", byteArray.toByteArray())
}
}
disable() {
this.channelOff?.off()
}
@Cmd()
bmpm(sender: any, command: string, args: string[]) {
if (!sender.isOp()) { return this.logger.sender(sender, '§c你没有此命令的权限!') }
this.bungeeCordForward(sender, { sender: sender.getName(), command, args })
this.logger.sender(sender, `[§3BPM§6][§a${this.serverName}§6] §6命令 §b/mpm ${args.join?.(' ')} §a发布成功!`)
}
@Cmd()
mpm(sender: any, command: string, args: string[]) {
if (!sender.isOp()) { return this.logger.sender(sender, '§c你没有此命令的权限!') }
this.taskManager.create(() => this.main(sender, command, args)).async().submit()
}
i18n(sender: any, name: string, params?: any) {
this.logger.sender(sender, this.translate.translate(name, params))
this.logger.sender(sender, this.translate.translate(name, params).split('\n'))
}
main(sender: any, command: string, args: string[]) {
@@ -114,7 +213,7 @@ export class MiaoScriptPackageManager extends interfaces.Plugin {
if (type == "i" || type == "install") {
this.i18n(sender, 'list.install.header')
this.pluginManager.getPlugins().forEach((plugin) => {
this.i18n(sender, 'list.install.body', plugin.description)
this.i18n(sender, 'list.install.body', { ...plugin.description, from: plugin.description.loadMetadata.type })
})
} else {
this.i18n(sender, 'list.header')
@@ -255,7 +354,7 @@ return '§a返回结果: §r'+ eval(${JSON.stringify(code)});`)
}
}
@tab()
@Tab()
tabmpm(sender: any, command: any, args: string | any[]) {
if (args.length === 1) { return ['list', 'install', 'update', 'upgrade', 'reload', 'restart', 'run', 'help', 'create', 'deploy'] }
if (args.length > 1) {
@@ -284,6 +383,13 @@ return '§a返回结果: §r'+ eval(${JSON.stringify(code)});`)
for (const pl of result.data) { this.packageCache[pl.name] = pl }
this.packageNameCache = Object.keys(this.packageCache)
this.i18n(sender, 'cloud.update.finish', { length: this.packageNameCache.length })
this.pluginManager.getPlugins().forEach(p => {
let cloudPlugin = this.packageCache[p.description.name]
//§6插件名称: §b{name}\n§6版本: §a{version}\n§6作者: §3{author}\§6更新时间: §9{updated_at}
if (cloudPlugin && cloudPlugin.version != p.description.version) {
this.i18n(sender, 'cloud.update.exists', { name: p.description.name, new_version: cloudPlugin.version, old_version: p.description.version })
}
})
}).async().submit()
}
@@ -295,7 +401,7 @@ return '§a返回结果: §r'+ eval(${JSON.stringify(code)});`)
http.download(this.packageCache[name].url, pluginFile)
this.i18n(sender, 'download.finish', { name })
if (!update) {
this.pluginManager.loadFromFile(fs.file(pluginFile))
this.pluginManager.loadFromFile(fs.concat(root, this.pluginFolder, name + '.js'))
this.i18n(sender, 'install.finish', { name })
} else {
this.i18n(sender, 'update.finish', { name })

View File

@@ -157,7 +157,7 @@ class SearchRankingController {
private createKeyworkSearch(keyword: string, type: string) {
let cacheDate = Date.now()
this.redisTemplate.opsForValue().set(this.getCacheKey(keyword, type), cacheDate)
this.redisTemplate.opsForValue().set(this.getCacheKey(keyword, type), cacheDate, 15, java.util.concurrent.TimeUnit.DAYS)
this.sendSearchCmd(keyword, type, cacheDate)
return { status: 201, msg: `关键词: ${keyword} 排名类型: ${type} 查询任务以创建...`, createTime: cacheDate }
}
@@ -179,6 +179,7 @@ class SearchRankingController {
sortType: 'va_rmdarkgmv30rt',
button_click: 'top'
} : {},
pageTotal: 3,
cacheKey: this.getResultCacheKey(keywords, type, dateCache)
},
resRouteSuffix: ROUTER_KEY,

View File

@@ -1,74 +1,110 @@
import { plugin as pluginApi } from '@ccms/api'
import { plugin, interfaces, cmd, listener, tab } from '@ccms/plugin'
import { inject } from '@ccms/container';
import { inject } from '@ccms/container'
import * as reflect from '@ccms/common/dist/reflect';
import http from '@ccms/common/dist/http';
import * as reflect from '@ccms/common/dist/reflect'
import http from '@ccms/common/dist/http'
const ByteArrayInputStream = Java.type("java.io.ByteArrayInputStream")
const ByteArrayOutputStream = Java.type("java.io.ByteArrayOutputStream")
const StandardCharsets = Java.type("java.nio.charset.StandardCharsets")
const GZIPInputStream = Java.type("java.util.zip.GZIPInputStream")
const GZIPOutputStream = Java.type("java.util.zip.GZIPOutputStream")
const JavaString = Java.type('java.lang.String')
const ByteArray = Java.type("byte[]")
class MiaoMessage {
// public static final String CHANNEL = "MiaoChat:Default".toLowerCase();
public static CHANNEL: string = "MiaoChat:Default".toLowerCase()
// private static final int MAX_MESSAGE_LENGTH = 32000;
private static MAX_MESSAGE_LENGTH = 32000;
private static copy(input, output) {
let buffer = new ByteArray(1024)
let n: number
while ((n = input.read(buffer)) != -1) {
output.write(buffer, 0, n)
}
input.close()
output.close()
}
public static encode(input: any): any {
let baos = new ByteArrayOutputStream()
MiaoMessage.copy(new ByteArrayInputStream(new JavaString(JSON.stringify(input)).getBytes(StandardCharsets.UTF_8)), new GZIPOutputStream(baos))
if (baos.size() > MiaoMessage.MAX_MESSAGE_LENGTH) { return null }
return baos.toByteArray()
}
public static decode(input: any): any {
let baos = new ByteArrayOutputStream()
MiaoMessage.copy(new GZIPInputStream(new ByteArrayInputStream(input)), baos)
return JSON.parse(baos.toString(StandardCharsets.UTF_8.name()))
}
}
@plugin({ name: 'Test', version: '1.0.0', author: 'MiaoWoo', source: __filename })
export class Test extends interfaces.Plugin {
@inject(pluginApi.PluginManager)
private PluginManager: pluginApi.PluginManager;
private PluginManager: pluginApi.PluginManager
load() {
this.logger.log('Test Plugin load from MiaoScript Plugin System...');
this.logger.log('Test Plugin load from MiaoScript Plugin System...')
}
enable() {
this.logger.log('Test Plugin enable from MiaoScript Plugin System...');
this.logger.log('Test Plugin enable from MiaoScript Plugin System...')
}
disable() {
this.logger.log('Test Plugin disable from MiaoScript Plugin System...');
this.logger.log('Test Plugin disable from MiaoScript Plugin System...')
}
@cmd()
test(sender: any, command: string, args: string[]) {
switch (args[0]) {
case "run":
var script = args.slice(1).join(' ');
console.sender(sender, '§b运行脚本:§r', script);
console.sender(sender, '§a返回结果:§r', eval(script) || '§4没有返回结果!');
break;
var script = args.slice(1).join(' ')
console.sender(sender, '§b运行脚本:§r', script)
console.sender(sender, '§a返回结果:§r', eval(script) || '§4没有返回结果!')
break
case "reload":
this.PluginManager.reload(this);
break;
this.PluginManager.reload(this)
break
case "yaml":
let yaml = require("js-yaml");
this.logger.log(yaml.safeDump({ key: 'value', map: { k1: 1, k2: '2' } }));
break;
let yaml = require("js-yaml")
this.logger.log(yaml.safeDump({ key: 'value', map: { k1: 1, k2: '2' } }))
break
case "speed":
this.logger.sender(sender, sender.location);
break;
this.logger.sender(sender, sender.location)
break
case "top":
let loc = sender.location;
let topY = loc.world.getHighestBlockYAt(loc);
loc.y = topY;
sender.teleport(loc);
break;
let loc = sender.location
let topY = loc.world.getHighestBlockYAt(loc)
loc.y = topY
sender.teleport(loc)
break
case "up":
if (!sender.openInventory) { return; }
let player = sender;
let location = player.location;
player.velocity = player.velocity.setY(0.5);
setTimeout(() => location.block.type = Java.type('org.bukkit.Material').STONE, 8);
break;
if (!sender.openInventory) { return }
let player = sender
let location = player.location
player.velocity = player.velocity.setY(0.5)
setTimeout(() => location.block.type = Java.type('org.bukkit.Material').STONE, 8)
break
case "add":
break;
break
case "get":
let result = http.get('https://www.baidu.com');
this.logger.sender(sender, JSON.stringify(result));
let result = http.get('https://www.baidu.com')
this.logger.sender(sender, JSON.stringify(result))
// 好了 扯结束 继续咸鱼
break;
break
case "ws":
let Sponge = Java.type('org.spongepowered.api.Sponge');
let Sponge = Java.type('org.spongepowered.api.Sponge')
// let promise = reflect.on(Bukkit.server).get('console').get('field_147144_o').get('field_151274_e').get().get(0);
console.log(reflect.on(Sponge.server).get('field_147144_o').get())
// let channel = reflect.on(promise).get('channel').get().pipeline().first();
// console.log(channel);
break;
break
default:
this.logger.log(sender, command, args);
this.logger.log(sender, command, args)
sender.sendMessage(JSON.stringify({ command, ...args }))
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@ccms/sponge",
"version": "0.8.4",
"version": "0.9.0",
"description": "MiaoScript api package",
"keywords": [
"miaoscript",
@@ -25,8 +25,8 @@
"typescript": "^3.9.6"
},
"dependencies": {
"@ccms/api": "^0.8.4",
"@ccms/common": "^0.8.4",
"@ccms/container": "^0.8.4"
"@ccms/api": "^0.9.0",
"@ccms/common": "^0.9.0",
"@ccms/container": "^0.9.0"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@ccms/spring",
"version": "0.8.4",
"version": "0.9.0",
"description": "MiaoScript spring package",
"keywords": [
"miaoscript",
@@ -24,9 +24,9 @@
"typescript": "^3.9.6"
},
"dependencies": {
"@ccms/api": "^0.8.4",
"@ccms/common": "^0.8.4",
"@ccms/container": "^0.8.4",
"@ccms/database": "^0.8.4"
"@ccms/api": "^0.9.0",
"@ccms/common": "^0.9.0",
"@ccms/container": "^0.9.0",
"@ccms/database": "^0.9.0"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@ccms/web",
"version": "0.8.4",
"version": "0.9.0",
"description": "MiaoScript web package",
"keywords": [
"miaoscript",
@@ -29,7 +29,7 @@
"typescript": "^3.9.6"
},
"dependencies": {
"@ccms/api": "^0.8.4",
"@ccms/container": "^0.8.4"
"@ccms/api": "^0.9.0",
"@ccms/container": "^0.9.0"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@ccms/websocket",
"version": "0.8.4",
"version": "0.9.0",
"description": "MiaoScript websocket package",
"keywords": [
"miaoscript",
@@ -19,7 +19,7 @@
"test": "echo \"Error: run tests from root\" && exit 1"
},
"devDependencies": {
"@ccms/nashorn": "^0.8.4",
"@ccms/nashorn": "^0.9.0",
"@javatypes/tomcat-websocket-api": "^0.0.2",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",