From f900c4ae1b5b0ceffea76a7af56fa9d83e658e35 Mon Sep 17 00:00:00 2001 From: ypsdf1 <1@ypshidifu.cn> Date: Wed, 1 Apr 2026 01:02:58 +0800 Subject: [PATCH 1/5] Initial commit --- README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..53ff621 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# CY_beibao \ No newline at end of file From 72ea6159a5ee1e8eaead1ccba5def8ca4fed3b1a Mon Sep 17 00:00:00 2001 From: ypsdf1 <1@ypshidifu.cn> Date: Wed, 1 Apr 2026 01:05:19 +0800 Subject: [PATCH 2/5] Add files via upload --- src/main/java/com/CY/Main.java | 165 +++++++++++++++++++++++++++++++++ src/main/resources/config.yml | 26 ++++++ src/main/resources/plugin.yml | 8 ++ 3 files changed, 199 insertions(+) create mode 100644 src/main/java/com/CY/Main.java create mode 100644 src/main/resources/config.yml create mode 100644 src/main/resources/plugin.yml diff --git a/src/main/java/com/CY/Main.java b/src/main/java/com/CY/Main.java new file mode 100644 index 0000000..fbf1e8a --- /dev/null +++ b/src/main/java/com/CY/Main.java @@ -0,0 +1,165 @@ +package com.CY; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryOpenEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scoreboard.Objective; +import org.bukkit.scoreboard.Scoreboard; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Main extends JavaPlugin implements Listener, CommandExecutor { + + @Override + public void onEnable() { + saveDefaultConfig(); + getCommand("cy").setExecutor(this); + getServer().getPluginManager().registerEvents(this, this); + } + + // --- 1. 大白话存储规则解析器 --- + // 从配置文件提取计分板名称。比如配置写:"存储在名为VipDays的计分板里" -> 提取 VipDays + private String getDbName(String path, String def) { + String line = getConfig().getString(path, ""); + if (line.isEmpty()) return def; + Matcher m = Pattern.compile("(?i)(?<=名为|obj:|scoreboard:)\\s*(\\w+)").matcher(line); + return m.find() ? m.group(1) : def; + } + + // 获取玩家计分板数值 + private int getScore(Player p, String configPath, String defaultObj) { + String objName = getDbName(configPath, defaultObj); + Scoreboard sb = Bukkit.getScoreboardManager().getMainScoreboard(); + Objective obj = sb.getObjective(objName); + return (obj == null) ? 0 : obj.getScore(p.getName()).getScore(); + } + + // --- 2. 语义时间解析 (14天封顶) --- + private int parseAlertDays(String raw) { + String s = raw.toLowerCase(); + int days = 0; + if (s.contains("两周")) days = 14; + else if (s.contains("一星")) days = 7; + else { + Matcher m = Pattern.compile("\\d+").matcher(s); + if (m.find()) days = Integer.parseInt(m.group()); + } + return Math.min(days, 14); // 源码硬锁14天 + } + + @EventHandler + public void onInventoryOpen(InventoryOpenEvent e) { + if (e.getView().getTitle().contains("云背包")) { + Player p = (Player) e.getPlayer(); + // 从配置定义的计分板读取剩余天数 + int remainingDays = getScore(p, "storage.vip-days-rule", "VipDays"); + int alertDays = parseAlertDays(getConfig().getString("settings.alert-before", "7天")); + + if (remainingDays <= alertDays && remainingDays > 0) { + p.sendMessage("§e[提醒] §f云背包会员仅剩 §c" + remainingDays + " §f天,请及时续费。"); + } else if (remainingDays <= 0) { + p.sendMessage("§4[锁定] §f会员已到期,非免费格子已进入 §c只读模式§f!"); + } + } + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent e) { + if (!e.getView().getTitle().contains("云背包")) return; + + Player p = (Player) e.getWhoClicked(); + int rawSlot = e.getRawSlot(); + // 提取页码 + int page = 1; + try { page = Integer.parseInt(e.getView().getTitle().replaceAll("[^0-9]", "")); } catch (Exception ignored) {} + + int absoluteSlot = (page - 1) * 45 + rawSlot + 1; + + // A. 导航翻页逻辑 + if (rawSlot >= 45 && rawSlot <= 53) { + e.setCancelled(true); + if (rawSlot == 48 && page > 1) openCloudInventory(p, page - 1); + if (rawSlot == 50) openCloudInventory(p, page + 1); + return; + } + + // B. 存储与只读逻辑 + if (rawSlot < 45) { + int unlockedMax = getScore(p, "storage.unlocked-slots-rule", "UnlockedSlots"); + if (unlockedMax < 54) unlockedMax = 54; // 基础免费54格 + + // 1. 越权点击判定 + if (absoluteSlot > unlockedMax) { + p.sendMessage("§c[锁定] §f该格子尚未解锁。"); + e.setCancelled(true); + return; + } + + // 2. 过期只读判定 (只对 54格以后的收费格生效) + if (absoluteSlot > 54) { + int days = getScore(p, "storage.vip-days-rule", "VipDays"); + if (days <= 0) { + // 只读:允许拿起(PICKUP),禁止放入(PLACE/SWAP) + if (e.getCursor() != null && e.getCursor().getType() != Material.AIR) { + p.sendMessage("§c[只读] §f会员已过期,收费格子禁止存入!"); + e.setCancelled(true); + return; + } + } + } + + // 3. 空间耗尽预警 (针对已解锁上限的最后1格) + checkSpaceWarning(p, e.getInventory(), unlockedMax); + } + } + + private void checkSpaceWarning(Player p, Inventory inv, int max) { + Bukkit.getScheduler().runTaskLater(this, () -> { + int count = 0; + for (int i = 0; i < 45; i++) { + if (inv.getItem(i) != null && inv.getItem(i).getType() != Material.AIR) count++; + } + if (count >= (max - 1) || (max <= 54 && count >= 53)) { + p.sendMessage("§6[预警] §f存储空间不足,请及时清理或扩容。"); + } + }, 1L); + } + + private void openCloudInventory(Player p, int page) { + Inventory inv = Bukkit.createInventory(null, 54, "§0云背包 - 第 " + page + " 页"); + + // 填充导航栏 + ItemStack glass = new ItemStack(Material.BLACK_STAINED_GLASS_PANE); + ItemMeta gm = glass.getItemMeta(); gm.setDisplayName(" "); glass.setItemMeta(gm); + for (int i = 45; i < 54; i++) inv.setItem(i, glass); + + ItemStack prev = new ItemStack(Material.ARROW); + ItemMeta pm = prev.getItemMeta(); pm.setDisplayName("§e上一页 (Page " + (page-1) + ")"); prev.setItemMeta(pm); + + ItemStack next = new ItemStack(Material.ARROW); + ItemMeta nm = next.getItemMeta(); nm.setDisplayName("§6下一页 (Page " + (page+1) + ")"); next.setItemMeta(nm); + + if (page > 1) inv.setItem(48, prev); + inv.setItem(50, next); + + p.openInventory(inv); + } + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, @NotNull String[] args) { + if (sender instanceof Player p && args.length == 0) openCloudInventory(p, 1); + return true; + } +} \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..83c52b2 --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,26 @@ +# ============================================================ +# cy 云背包 - 配置文件 (2026版) +# ============================================================ +version: "1.0" + +# [经济设置] +economy: + buy-price: 10 # 永久购买价格 + rent-price: 5 # 租赁单日价格 + +# [权限白名单系统] +whitelist: + # 一级白名单:核心推送/更新权限 (一行一个) + # 示范: + # primary: + # - TechAdmin + primary: [] + + # 二级白名单:管理员/发放权限 (逗号分隔) + # 示范: secondary: "Admin1,Admin2" + secondary: "" + +# [系统设置] +update-settings: + max-failures: 5 +expiry-notify-days: 3 # 源码已锁死 1-7 天 \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..7489107 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,8 @@ +name: CY_beibao +version: 1.0 +main: com.CY.Main # 对应截图中的 package com.CY +api-version: '1.21' +commands: + cy: + description: 云背包被控端主指令 + usage: /cy \ No newline at end of file From 639cd5f40404f5aebeb6a917da14792bc68c0a8b Mon Sep 17 00:00:00 2001 From: ypsdf1 <1@ypshidifu.cn> Date: Thu, 2 Apr 2026 21:29:09 +0800 Subject: [PATCH 3/5] Add files via upload --- src/main/java/com/cy/Main.java | 140 +++++++++++++++++++++++++++++++++ src/main/resources/config.yml | 48 ++++++----- src/main/resources/icons.yml | 19 +++++ src/main/resources/plugin.yml | 9 ++- 4 files changed, 192 insertions(+), 24 deletions(-) create mode 100644 src/main/java/com/cy/Main.java create mode 100644 src/main/resources/icons.yml diff --git a/src/main/java/com/cy/Main.java b/src/main/java/com/cy/Main.java new file mode 100644 index 0000000..3971acc --- /dev/null +++ b/src/main/java/com/cy/Main.java @@ -0,0 +1,140 @@ +package com.cy; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +import org.bukkit.event.*; +import org.bukkit.event.inventory.*; +import org.bukkit.inventory.*; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.persistence.PersistentDataType; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.util.io.BukkitObjectInputStream; +import org.bukkit.util.io.BukkitObjectOutputStream; +import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder; +import java.io.*; +import java.sql.*; +import java.util.*; + +public class Main extends JavaPlugin implements Listener, CommandExecutor { + private Connection conn; + private final NamespacedKey PAGE_KEY = new NamespacedKey(this, "cy_page"); + + @Override + public void onEnable() { + getLogger().info("\n§d" + + " _______ __ \n" + + " / ___\\ \\ / / \n" + + "| | \\ \\_/ / \n" + + "| |___ \\ / \n" + + " \\____| |_| \n" + + "§e[CY_beibao] 加载成功,存储数据库已挂载。"); + + initDatabase(); + getCommand("cy").setExecutor(this); + getServer().getPluginManager().registerEvents(this, this); + } + + private void initDatabase() { + try { + if (!getDataFolder().exists()) getDataFolder().mkdirs(); + conn = DriverManager.getConnection("jdbc:sqlite:" + getDataFolder() + "/vault.db"); + conn.createStatement().execute("CREATE TABLE IF NOT EXISTS items (uuid TEXT, page INTEGER, data TEXT, PRIMARY KEY(uuid, page))"); + } catch (Exception e) { e.printStackTrace(); } + } + + @Override + public boolean onCommand(CommandSender s, Command c, String lb, String[] args) { + if (s instanceof Player p) openVault(p, 1); + return true; + } + + public void openVault(Player p, int page) { + p.getPersistentDataContainer().set(PAGE_KEY, PersistentDataType.INTEGER, page); + Inventory inv = Bukkit.createInventory(null, 54, "§1云端仓库 - 第 " + page + " 页"); + + // 从数据库加载物品 + loadItems(p, page, inv); + + // 功能底栏 (45-53) + for (int i = 45; i < 54; i++) { + ItemStack glass = new ItemStack(Material.GRAY_STAINED_GLASS_PANE); + ItemMeta m = glass.getItemMeta(); m.setDisplayName(" "); glass.setItemMeta(m); + inv.setItem(i, glass); + } + inv.setItem(48, createBtn(Material.ARROW, "§f上一页")); + inv.setItem(49, createBtn(Material.BARRIER, "§c关闭菜单")); + inv.setItem(50, createBtn(Material.ARROW, "§f下一页")); + + p.openInventory(inv); + } + + @EventHandler + public void onInventoryClose(InventoryCloseEvent e) { + if (!e.getView().getTitle().contains("云端仓库")) return; + Player p = (Player) e.getPlayer(); + int page = p.getPersistentDataContainer().getOrDefault(PAGE_KEY, PersistentDataType.INTEGER, 1); + saveItems(p, page, e.getInventory()); + } + + @EventHandler + public void onClick(InventoryClickEvent e) { + if (!e.getView().getTitle().contains("云端仓库")) return; + int slot = e.getRawSlot(); + if (slot < 45 || slot >= 54) return; // 仓库和背包区允许存取 + + e.setCancelled(true); + Player p = (Player) e.getWhoClicked(); + int cur = p.getPersistentDataContainer().getOrDefault(PAGE_KEY, PersistentDataType.INTEGER, 1); + + if (slot == 49) p.closeInventory(); + if (slot == 50 || slot == 48) { + int target = (slot == 50) ? cur + 1 : cur - 1; + if (target > 0) { + p.closeInventory(); + // 延迟 2 Tick 解决鼠标强制纠正 + Bukkit.getScheduler().runTaskLater(this, () -> openVault(p, target), 2L); + } + } + } + + private void saveItems(Player p, int page, Inventory inv) { + try { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + BukkitObjectOutputStream out = new BukkitObjectOutputStream(os); + out.writeInt(45); // 只存前 45 格 + for (int i = 0; i < 45; i++) out.writeObject(inv.getItem(i)); + out.close(); + String data = Base64Coder.encodeLines(os.toByteArray()); + PreparedStatement ps = conn.prepareStatement("REPLACE INTO items (uuid, page, data) VALUES (?, ?, ?)"); + ps.setString(1, p.getUniqueId().toString()); ps.setInt(2, page); ps.setString(3, data); + ps.executeUpdate(); + } catch (Exception e) { e.printStackTrace(); } + } + + private void loadItems(Player p, int page, Inventory inv) { + try { + PreparedStatement ps = conn.prepareStatement("SELECT data FROM items WHERE uuid = ? AND page = ?"); + ps.setString(1, p.getUniqueId().toString()); ps.setInt(2, page); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + byte[] bytes = Base64Coder.decodeLines(rs.getString("data")); + BukkitObjectInputStream in = new BukkitObjectInputStream(new ByteArrayInputStream(bytes)); + int count = in.readInt(); + for (int i = 0; i < count; i++) inv.setItem(i, (ItemStack) in.readObject()); + in.close(); + } + } catch (Exception ignored) {} + } + + private ItemStack createBtn(Material m, String n) { + ItemStack i = new ItemStack(m); ItemMeta mt = i.getItemMeta(); + mt.setDisplayName(n); i.setItemMeta(mt); return i; + } + + public void executeAction(Player p, String raw) { + if (raw.contains("背包")) p.sendMessage("§a[CY] 空间联动成功!"); + } +} \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 83c52b2..756f8db 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,26 +1,34 @@ # ============================================================ -# cy 云背包 - 配置文件 (2026版) +# [ CY_beibao 积木配置 ] # ============================================================ -version: "1.0" -# [经济设置] -economy: - buy-price: 10 # 永久购买价格 - rent-price: 5 # 租赁单日价格 +设置: + # 变量: [玩家] [金额] [时间] [格子] + 反馈格式: "&b&l[系统] &f尊敬的 [玩家],积木激活成功!获得: [时间]" -# [权限白名单系统] -whitelist: - # 一级白名单:核心推送/更新权限 (一行一个) - # 示范: - # primary: - # - TechAdmin - primary: [] +# 积木轨道:供主控联控调用 +积木轨道: + 1: "{名字:初级包, 天:30天, 图标:金锭}" + 2: "[名字:扩容包, 格子:45, 图标:金块]" + 3: "(名字:盲盒礼包, 金额:100, 图标:箱子, 介绍:收100块给个激活)" - # 二级白名单:管理员/发放权限 (逗号分隔) - # 示范: secondary: "Admin1,Admin2" - secondary: "" +# 本地商城:多种写法混搭测试 +商城: + "item1": + # JSON写法 + 内容: > + { + "名字": "§e§l黄金礼包", + "价格": 500, + "天": "永久", + "图标": "金块" + } + "item2": + # 括弧写法 + 内容: "(名字:&b&l普通礼包, 价格:100, 天:10天, 图标:金锭)" + "item3": + # 键值对写法 + 内容: "名字:扩容插件, 价格:50, 格子:5, 图标:末影箱" -# [系统设置] -update-settings: - max-failures: 5 -expiry-notify-days: 3 # 源码已锁死 1-7 天 \ No newline at end of file +维护者白名单: + - "" \ No newline at end of file diff --git a/src/main/resources/icons.yml b/src/main/resources/icons.yml new file mode 100644 index 0000000..31c6858 --- /dev/null +++ b/src/main/resources/icons.yml @@ -0,0 +1,19 @@ +# ============================================================ +# [ CY_beibao 图标库 ] +# +# 如何添加新图标? +# 1. 在游戏内将准星对准物品,按 F3 + H 开启高级提示框。 +# 2. 查看物品名称,例如看到 'minecraft:netherite_ingot'。 +# 3. 在下方按格式添加: '自定义中文名: 材质ID' +# ============================================================ + +库: + 金锭: "minecraft:gold_ingot" + 金块: "gold_block" + 钻石: "DIAMOND" + 下界合金: "netherite_ingot" + 末影箱: "ender_chest" + 箱子: "chest" + 书本: "book" + 面包: "bread" + # 玩家可以无限续写,哪怕写了 '我的头: player_head' 也没问题 \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 7489107..106e1e4 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,8 +1,9 @@ name: CY_beibao version: 1.0 -main: com.CY.Main # 对应截图中的 package com.CY -api-version: '1.21' +main: com.cy.Main +api-version: '1.20' +author: SDF_Engine commands: cy: - description: 云背包被控端主指令 - usage: /cy \ No newline at end of file + description: "Open your cloud vault" + usage: "/cy" \ No newline at end of file From 2d4a2766e052880654d691b73745951534050915 Mon Sep 17 00:00:00 2001 From: ypsdf1 <1@ypshidifu.cn> Date: Wed, 15 Apr 2026 14:31:12 +0800 Subject: [PATCH 4/5] Add files via upload --- src/main/java/com/CY/Main.java | 258 +++++++++++++++------------------ src/main/resources/plugin.yml | 2 +- 2 files changed, 119 insertions(+), 141 deletions(-) diff --git a/src/main/java/com/CY/Main.java b/src/main/java/com/CY/Main.java index fbf1e8a..0bf96ec 100644 --- a/src/main/java/com/CY/Main.java +++ b/src/main/java/com/CY/Main.java @@ -1,165 +1,143 @@ package com.CY; -import org.bukkit.Bukkit; -import org.bukkit.Material; +import org.bukkit.*; import org.bukkit.command.*; import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.inventory.InventoryOpenEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; +import org.bukkit.event.*; +import org.bukkit.event.inventory.*; +import org.bukkit.inventory.*; import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.metadata.FixedMetadataValue; import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.scoreboard.Objective; -import org.bukkit.scoreboard.Scoreboard; -import org.jetbrains.annotations.NotNull; - +import org.bukkit.util.io.*; +import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder; +import java.io.*; +import java.sql.*; import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; public class Main extends JavaPlugin implements Listener, CommandExecutor { + private static Connection db; @Override public void onEnable() { - saveDefaultConfig(); + try { + if (!getDataFolder().exists()) getDataFolder().mkdirs(); + db = DriverManager.getConnection("jdbc:sqlite:" + new File(getDataFolder(), "storage.db")); + Statement st = db.createStatement(); + + // 爆破式表结构修复 + try { + st.executeQuery("SELECT page FROM v LIMIT 1"); + } catch (SQLException e) { + st.execute("DROP TABLE IF EXISTS v"); + Bukkit.getConsoleSender().sendMessage("§e[CY] 数据库格式过旧,已重置表结构。"); + } + st.execute("CREATE TABLE IF NOT EXISTS v (p TEXT, page INT, slot INT, data TEXT)"); + Bukkit.getConsoleSender().sendMessage("§a[CY] 仓库系统已就绪,等待主控握手信号。"); + } catch (Exception e) { e.printStackTrace(); } getCommand("cy").setExecutor(this); getServer().getPluginManager().registerEvents(this, this); } - // --- 1. 大白话存储规则解析器 --- - // 从配置文件提取计分板名称。比如配置写:"存储在名为VipDays的计分板里" -> 提取 VipDays - private String getDbName(String path, String def) { - String line = getConfig().getString(path, ""); - if (line.isEmpty()) return def; - Matcher m = Pattern.compile("(?i)(?<=名为|obj:|scoreboard:)\\s*(\\w+)").matcher(line); - return m.find() ? m.group(1) : def; + @Override + public boolean onCommand(CommandSender s, Command c, String l, String[] a) { + if (s instanceof Player) openPage((Player) s, 0); + return true; } - // 获取玩家计分板数值 - private int getScore(Player p, String configPath, String defaultObj) { - String objName = getDbName(configPath, defaultObj); - Scoreboard sb = Bukkit.getScoreboardManager().getMainScoreboard(); - Objective obj = sb.getObjective(objName); - return (obj == null) ? 0 : obj.getScore(p.getName()).getScore(); - } + public void openPage(Player p, int page) { + p.setMetadata("CY_PAGE", new FixedMetadataValue(this, page)); + p.setMetadata("CY_LOCK", new FixedMetadataValue(this, System.currentTimeMillis())); - // --- 2. 语义时间解析 (14天封顶) --- - private int parseAlertDays(String raw) { - String s = raw.toLowerCase(); - int days = 0; - if (s.contains("两周")) days = 14; - else if (s.contains("一星")) days = 7; - else { - Matcher m = Pattern.compile("\\d+").matcher(s); - if (m.find()) days = Integer.parseInt(m.group()); - } - return Math.min(days, 14); // 源码硬锁14天 - } - - @EventHandler - public void onInventoryOpen(InventoryOpenEvent e) { - if (e.getView().getTitle().contains("云背包")) { - Player p = (Player) e.getPlayer(); - // 从配置定义的计分板读取剩余天数 - int remainingDays = getScore(p, "storage.vip-days-rule", "VipDays"); - int alertDays = parseAlertDays(getConfig().getString("settings.alert-before", "7天")); - - if (remainingDays <= alertDays && remainingDays > 0) { - p.sendMessage("§e[提醒] §f云背包会员仅剩 §c" + remainingDays + " §f天,请及时续费。"); - } else if (remainingDays <= 0) { - p.sendMessage("§4[锁定] §f会员已到期,非免费格子已进入 §c只读模式§f!"); - } - } - } - - @EventHandler - public void onInventoryClick(InventoryClickEvent e) { - if (!e.getView().getTitle().contains("云背包")) return; - - Player p = (Player) e.getWhoClicked(); - int rawSlot = e.getRawSlot(); - // 提取页码 - int page = 1; - try { page = Integer.parseInt(e.getView().getTitle().replaceAll("[^0-9]", "")); } catch (Exception ignored) {} - - int absoluteSlot = (page - 1) * 45 + rawSlot + 1; - - // A. 导航翻页逻辑 - if (rawSlot >= 45 && rawSlot <= 53) { - e.setCancelled(true); - if (rawSlot == 48 && page > 1) openCloudInventory(p, page - 1); - if (rawSlot == 50) openCloudInventory(p, page + 1); - return; - } - - // B. 存储与只读逻辑 - if (rawSlot < 45) { - int unlockedMax = getScore(p, "storage.unlocked-slots-rule", "UnlockedSlots"); - if (unlockedMax < 54) unlockedMax = 54; // 基础免费54格 - - // 1. 越权点击判定 - if (absoluteSlot > unlockedMax) { - p.sendMessage("§c[锁定] §f该格子尚未解锁。"); - e.setCancelled(true); - return; - } - - // 2. 过期只读判定 (只对 54格以后的收费格生效) - if (absoluteSlot > 54) { - int days = getScore(p, "storage.vip-days-rule", "VipDays"); - if (days <= 0) { - // 只读:允许拿起(PICKUP),禁止放入(PLACE/SWAP) - if (e.getCursor() != null && e.getCursor().getType() != Material.AIR) { - p.sendMessage("§c[只读] §f会员已过期,收费格子禁止存入!"); - e.setCancelled(true); - return; - } - } - } - - // 3. 空间耗尽预警 (针对已解锁上限的最后1格) - checkSpaceWarning(p, e.getInventory(), unlockedMax); - } - } - - private void checkSpaceWarning(Player p, Inventory inv, int max) { - Bukkit.getScheduler().runTaskLater(this, () -> { - int count = 0; - for (int i = 0; i < 45; i++) { - if (inv.getItem(i) != null && inv.getItem(i).getType() != Material.AIR) count++; - } - if (count >= (max - 1) || (max <= 54 && count >= 53)) { - p.sendMessage("§6[预警] §f存储空间不足,请及时清理或扩容。"); - } - }, 1L); - } - - private void openCloudInventory(Player p, int page) { - Inventory inv = Bukkit.createInventory(null, 54, "§0云背包 - 第 " + page + " 页"); - - // 填充导航栏 - ItemStack glass = new ItemStack(Material.BLACK_STAINED_GLASS_PANE); - ItemMeta gm = glass.getItemMeta(); gm.setDisplayName(" "); glass.setItemMeta(gm); - for (int i = 45; i < 54; i++) inv.setItem(i, glass); - - ItemStack prev = new ItemStack(Material.ARROW); - ItemMeta pm = prev.getItemMeta(); pm.setDisplayName("§e上一页 (Page " + (page-1) + ")"); prev.setItemMeta(pm); - - ItemStack next = new ItemStack(Material.ARROW); - ItemMeta nm = next.getItemMeta(); nm.setDisplayName("§6下一页 (Page " + (page+1) + ")"); next.setItemMeta(nm); - - if (page > 1) inv.setItem(48, prev); - inv.setItem(50, next); + Inventory inv = Bukkit.createInventory(null, 54, "§0[云端仓库] 第 " + (page + 1) + " 页"); + for (int i = 45; i < 54; i++) inv.setItem(i, btn(Material.GRAY_STAINED_GLASS_PANE, " ", null)); + inv.setItem(45, btn(Material.ARROW, "§f上一页", null)); + inv.setItem(49, btn(Material.EMERALD, "§a§l商城与统计", "§7当前存量: §e" + count(p, page) + " / 40")); + inv.setItem(53, btn(Material.ARROW, "§f下一页", null)); + try (PreparedStatement ps = db.prepareStatement("SELECT slot, data FROM v WHERE p = ? AND page = ?")) { + ps.setString(1, p.getName()); ps.setInt(2, page); + ResultSet rs = ps.executeQuery(); + while (rs.next()) inv.setItem(rs.getInt("slot"), deserialize(rs.getString("data"))); + } catch (Exception ignored) {} p.openInventory(inv); } - @Override - public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, @NotNull String[] args) { - if (sender instanceof Player p && args.length == 0) openCloudInventory(p, 1); - return true; + @EventHandler + public void onClick(InventoryClickEvent e) { + if (e.getClickedInventory() == null || !e.getView().getTitle().contains("[云端仓库]")) return; + Player p = (Player) e.getWhoClicked(); + int slot = e.getRawSlot(); + + if (p.hasMetadata("CY_LOCK") && System.currentTimeMillis() - p.getMetadata("CY_LOCK").get(0).asLong() < 400) { + e.setCancelled(true); return; + } + + if (slot >= 45 && slot <= 53) { + e.setCancelled(true); + int page = p.getMetadata("CY_PAGE").get(0).asInt(); + if (slot == 45 && page > 0) { save(p, page, e.getInventory()); openPage(p, page - 1); } + if (slot == 53) { + save(p, page, e.getInventory()); + // 翻页限制:前三页必须满 40 格 + if (page < 2 || count(p, page) >= 40) openPage(p, page + 1); + else p.sendMessage("§c§l[拒绝] §7当前仓库分拣度不足 40 格。"); + } + } + } + + @EventHandler + public void onClose(InventoryCloseEvent e) { + if (e.getView().getTitle().contains("[云端仓库]") && e.getPlayer().hasMetadata("CY_PAGE")) { + save((Player) e.getPlayer(), e.getPlayer().getMetadata("CY_PAGE").get(0).asInt(), e.getInventory()); + } + } + + private void save(Player p, int page, Inventory inv) { + try { + PreparedStatement del = db.prepareStatement("DELETE FROM v WHERE p = ? AND page = ?"); + del.setString(1, p.getName()); del.setInt(2, page); del.executeUpdate(); + PreparedStatement ins = db.prepareStatement("INSERT INTO v VALUES (?,?,?,?)"); + for (int i = 0; i < 45; i++) { + ItemStack it = inv.getItem(i); + if (it != null && it.getType() != Material.AIR) { + ins.setString(1, p.getName()); ins.setInt(2, page); + ins.setInt(3, i); ins.setString(4, serialize(it)); + ins.executeUpdate(); + } + } + } catch (Exception ignored) {} + } + + private int count(Player p, int page) { + try (PreparedStatement ps = db.prepareStatement("SELECT COUNT(*) FROM v WHERE p=? AND page=?")) { + ps.setString(1, p.getName()); ps.setInt(2, page); + ResultSet rs = ps.executeQuery(); return rs.next() ? rs.getInt(1) : 0; + } catch (Exception e) { return 0; } + } + + // --- 反射握手入口 --- + public static void apiReceive(String n, int s, int d) { + Bukkit.getConsoleSender().sendMessage("§6[CY-Memory] 收到来自主控的扩容指令!"); + Bukkit.getConsoleSender().sendMessage("§7- 玩家: " + n + " | 增加格子: " + s + " | 增加时长: " + d); + // 这里对接具体的扩容逻辑 + } + + private String serialize(ItemStack item) { + try (ByteArrayOutputStream o = new ByteArrayOutputStream(); BukkitObjectOutputStream d = new BukkitObjectOutputStream(o)) { + d.writeObject(item); return Base64Coder.encodeLines(o.toByteArray()); + } catch (Exception e) { return ""; } + } + + private ItemStack deserialize(String b64) { + try (ByteArrayInputStream i = new ByteArrayInputStream(Base64Coder.decodeLines(b64)); BukkitObjectInputStream d = new BukkitObjectInputStream(i)) { + return (ItemStack) d.readObject(); + } catch (Exception e) { return null; } + } + + private ItemStack btn(Material m, String n, String lore) { + ItemStack item = new ItemStack(m); ItemMeta mt = item.getItemMeta(); + mt.setDisplayName(n); if (lore != null) mt.setLore(Arrays.asList(lore.split("\n"))); + item.setItemMeta(mt); return item; } } \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 106e1e4..bd1139c 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,6 +1,6 @@ name: CY_beibao version: 1.0 -main: com.cy.Main +main: com.CY.Main api-version: '1.20' author: SDF_Engine commands: From 42effe32302242c69c075de27f5c147988b9d6a7 Mon Sep 17 00:00:00 2001 From: ypsdf1 <1@ypshidifu.cn> Date: Wed, 15 Apr 2026 14:39:08 +0800 Subject: [PATCH 5/5] Update README.md --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 53ff621..dbbecab 100644 --- a/README.md +++ b/README.md @@ -1 +1,11 @@ -# CY_beibao \ No newline at end of file +# CY_beibao 来自Sdf1的背包插件 +## 功能介绍 +### 1. 独立云存储,解决背包爆满、箱子爆满的问题 +### 2. api对接,预留接口可供其他插件对接 +### 3. 操作简便 + +## 食用方法: +## 1. 下载,从[点击此处](https://github.com/ypsdf1/CY_beibao/releases)下载我们的最新版 +## 2. 安装,把jar丢进你服务器的plugin +## 3. 启动,如果你之前使用过我们的1.01内测版、1.0测试版,记得删除旧的CY_beibao文件,如果没有,直接启动服务器或者运行的服务器打个stop +## 4. 打开背包:/cy