From e06fcc29e9c9fdcdd27e4f4603e7f5aa8186bde0 Mon Sep 17 00:00:00 2001 From: MiaoWoo Date: Fri, 14 Aug 2020 16:07:17 +0800 Subject: [PATCH] feat: backup plugins --- packages/plugins/src/MiaoCoin.ts | 104 ++++ packages/plugins/src/MiaoGUI.ts | 87 +++ packages/plugins/src/MiaoReward.ts | 498 ++++++++++++++++++ .../plugins/src/MiaoScriptPackageManager.ts | 126 ++++- packages/plugins/src/SearchRanking.ts | 2 +- packages/plugins/src/Test.ts | 108 ++-- 6 files changed, 878 insertions(+), 47 deletions(-) create mode 100644 packages/plugins/src/MiaoCoin.ts create mode 100644 packages/plugins/src/MiaoGUI.ts create mode 100644 packages/plugins/src/MiaoReward.ts diff --git a/packages/plugins/src/MiaoCoin.ts b/packages/plugins/src/MiaoCoin.ts new file mode 100644 index 00000000..98b1891d --- /dev/null +++ b/packages/plugins/src/MiaoCoin.ts @@ -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'] + } +} diff --git a/packages/plugins/src/MiaoGUI.ts b/packages/plugins/src/MiaoGUI.ts new file mode 100644 index 00000000..bf7425ba --- /dev/null +++ b/packages/plugins/src/MiaoGUI.ts @@ -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)) + } +} diff --git a/packages/plugins/src/MiaoReward.ts b/packages/plugins/src/MiaoReward.ts new file mode 100644 index 00000000..da563d57 --- /dev/null +++ b/packages/plugins/src/MiaoReward.ts @@ -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() + private isBinding = false + private isBindingUser = 'unknow' + private drawCooldown = new Map() + + @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 (是否公告) §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'] + } +} diff --git a/packages/plugins/src/MiaoScriptPackageManager.ts b/packages/plugins/src/MiaoScriptPackageManager.ts index 9637d3ad..c94798b3 100644 --- a/packages/plugins/src/MiaoScriptPackageManager.ts +++ b/packages/plugins/src/MiaoScriptPackageManager.ts @@ -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} §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.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.2', 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[]) { @@ -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() } diff --git a/packages/plugins/src/SearchRanking.ts b/packages/plugins/src/SearchRanking.ts index f494a68e..2ecb502b 100644 --- a/packages/plugins/src/SearchRanking.ts +++ b/packages/plugins/src/SearchRanking.ts @@ -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 } } diff --git a/packages/plugins/src/Test.ts b/packages/plugins/src/Test.ts index 18a0a173..1efdc5c2 100644 --- a/packages/plugins/src/Test.ts +++ b/packages/plugins/src/Test.ts @@ -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 })) } }