diff --git a/.idea/misc.xml b/.idea/misc.xml index 62e7bdb..f919b95 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,7 +8,7 @@ - + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 9d5c185..fe74110 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ jar - 21 + 25 UTF-8 @@ -55,10 +55,10 @@ org.apache.maven.plugins maven-compiler-plugin - 3.11.0 + 3.14.0 - 21 - 21 + 25 + 25 diff --git a/src/main/java/Sdf1_login/AreaProtection.java b/src/main/java/Sdf1_login/AreaProtection.java index 5b4992f..c23c657 100644 --- a/src/main/java/Sdf1_login/AreaProtection.java +++ b/src/main/java/Sdf1_login/AreaProtection.java @@ -1494,7 +1494,12 @@ public class AreaProtection implements Listener { // 没名字或不在白名单,传送虚空 Location voidLoc = ent.getLocation().clone(); voidLoc.setY(ent.getWorld().getMinHeight() - 50); - ent.teleport(voidLoc); + // 末影人受伤害会自动传送回来,所以直接移除实体 + if ("ENDERMAN".equals(ent.getType().name())) { + ent.remove(); + } else { + ent.teleport(voidLoc); + } banished++; /* plugin.getLogger().info("[和平] " + ent.getType().name() @@ -1518,7 +1523,12 @@ public class AreaProtection implements Listener { // 没有保护期且不在白名单,传送虚空 Location voidLoc = ent.getLocation().clone(); voidLoc.setY(ent.getWorld().getMinHeight() - 50); - ent.teleport(voidLoc); + // 末影人受伤害会自动传送回来,直接移除 + if ("ENDERMAN".equals(ent.getType().name())) { + ent.remove(); + } else { + ent.teleport(voidLoc); + } banished++; /* plugin.getLogger().info("[和平] " + ent.getType().name() @@ -1544,7 +1554,8 @@ public class AreaProtection implements Listener { "ILLUSIONER", "VEX", "PHANTOM", "SHULKER", "WARDEN", "BREEZE", "PIGLIN", "PIGLIN_BRUTE", "ZOGLIN", - "ZOMBIFIED_PIGLIN", "ENDER_DRAGON" + "ZOMBIFIED_PIGLIN", "ENDER_DRAGON", + "ENDERMAN", "WITHER" )); private boolean isHostile(String typeName) { diff --git a/src/main/java/Sdf1_login/GUIManager.java b/src/main/java/Sdf1_login/GUIManager.java index 7c23143..9b5de17 100644 --- a/src/main/java/Sdf1_login/GUIManager.java +++ b/src/main/java/Sdf1_login/GUIManager.java @@ -16,6 +16,7 @@ import org.bukkit.util.io.BukkitObjectInputStream; import org.bukkit.util.io.BukkitObjectOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.util.concurrent.ConcurrentHashMap; @@ -49,6 +50,11 @@ public class GUIManager implements Listener { private final Map userMgmtPages = new HashMap<>(); private static final int UMGMT_PAGE_SIZE = 45; + // ★ 宝箱物品拦截计数(事不过三) + private final ConcurrentHashMap + treasureMenuBlockCount = + new ConcurrentHashMap<>(); + public GUIManager(Main plugin) { @@ -1571,6 +1577,11 @@ public class GUIManager implements Listener { Inventory topInv, ItemStack item, int removeSlot) { + // ★ 拦截宝箱自定义物品(事不过三原则) + if (handleTreasureItemBlock(p, item)) { + return; + } + int amount = item.getAmount(); removeSnowballFromInventory(p); //没收雪球菜单 // 1. 从来源移除 @@ -1697,6 +1708,75 @@ public class GUIManager implements Listener { } + /** ★ 检测宝箱自定义物品(lore 含 §0§k 或 CUSTOM 标记) */ + boolean isTreasureCustomItem(ItemStack item) { + if (item == null) return false; + ItemMeta meta = item.getItemMeta(); + if (meta == null) return false; + List lore = meta.getLore(); + if (lore == null) return false; + for (String line : lore) { + if (line.contains("\u00a70\u00a7k") + || line.contains("CUSTOM")) { + return true; + } + } + return false; + } + + /** + * 宝箱自定义物品拦截(事不过三原则) + * @param player 玩家 + * @param item 要存入的物品 + * @return true表示已拦截(调用方应return),false表示未拦截(继续原逻辑) + */ + public boolean handleTreasureItemBlock(Player player, ItemStack item) { + if (!isTreasureCustomItem(item)) { + return false; // 不是宝箱物品,不拦截 + } + + String key = player.getUniqueId() + ":" + item.getType().name(); + int count = treasureMenuBlockCount.getOrDefault(key, 0) + 1; + treasureMenuBlockCount.put(key, count); + + if (count < 3) { + // 第1次或第2次:警告 + player.sendMessage("§c§l[宝箱物品] §e该物品是宝箱自定义物品,无法存入菜单!"); + player.sendMessage("§c§l[宝箱物品] §e第" + count + "/3次警告,第三次将没收物品!"); + return true; + } else { + // 第3次:没收物品 + // 1. 从玩家背包移除该物品 + boolean removed = false; + for (int i = 0; i < player.getInventory().getSize(); i++) { + ItemStack invItem = player.getInventory().getItem(i); + if (invItem != null && invItem.isSimilar(item)) { + player.getInventory().setItem(i, null); + removed = true; + break; + } + } + // 2. 如果背包没找到,可能在光标上(普通点击/拖拽场景) + if (!removed) { + org.bukkit.inventory.InventoryView view = player.getOpenInventory(); + ItemStack cursor = view.getCursor(); + if (cursor != null && cursor.isSimilar(item)) { + view.setCursor(null); + removed = true; + } + } + // 3. 清零计数器 + treasureMenuBlockCount.remove(key); + // 4. 发送没收提示 + player.sendMessage("§c§l[宝箱物品] §e你多次尝试存入宝箱物品,物品已被没收!"); + player.sendMessage("§c§l[宝箱物品] §c事不过三,这是第三次警告!"); + // 5. 记录日志 + plugin.getLogger().warning("[宝箱物品] 玩家 " + player.getName() + " 第三次尝试存入宝箱物品,物品已没收: " + item.getType().name()); + return true; + } + } + + // ===== 辅助方法 ===== // 只匹配带[菜单]的非雪球物品 diff --git a/src/main/java/Sdf1_login/Main.java b/src/main/java/Sdf1_login/Main.java index f4b11d8..63755a4 100644 --- a/src/main/java/Sdf1_login/Main.java +++ b/src/main/java/Sdf1_login/Main.java @@ -3976,6 +3976,11 @@ public class Main extends JavaPlugin if (toIconSlot) { ItemStack dragged = e.getOldCursor().clone(); + // ★ 拦截宝箱自定义物品(事不过三原则) + if (gui.handleTreasureItemBlock(p, dragged)) { + e.setCancelled(true); + return; + } String b64 = gui.serializeItem(dragged); if (b64 != null) { db.saveMenuIcon(p.getName(), b64, diff --git a/src/main/java/Sdf1_login/UpdateChecker.java b/src/main/java/Sdf1_login/UpdateChecker.java index a4e6e55..3d82d56 100644 --- a/src/main/java/Sdf1_login/UpdateChecker.java +++ b/src/main/java/Sdf1_login/UpdateChecker.java @@ -55,6 +55,10 @@ public class UpdateChecker { private static final String GITEE_API = "https://gitee.com/api/v5/repos/" + GITEE_OWNER + "/" + GITEE_REPO + "/releases/latest"; + // 第三路备选:宝塔静态HTML + private static final String FALLBACK_HTML = + "https://caoyuan.ypshidifu.cn/update_check.html"; + // 仓库页面链接 private static final String GITHUB_LINK = "https://github.com/" + GITHUB_OWNER + "/" + GITHUB_REPO + "/releases"; @@ -202,37 +206,47 @@ public class UpdateChecker { } else if (ge != null && ge.isSuccess()) { chosen = ge; } else { - // 双通道均失败 - failCount++; - StringBuilder sb = new StringBuilder(); - if (gh != null && gh.error != null) { - sb.append("§7GitHub: §c").append(gh.error); + // ★ 双通道均失败,尝试第三路备选(宝塔静态HTML) + plugin.getLogger().info("[Sdf1_login] GitHub/Gitee 均失败,尝试第三路备选 BaoTa..."); + CheckResult fbResult = tryFallbackChannel(); + if (fbResult != null && fbResult.isSuccess()) { + chosen = fbResult; + plugin.getLogger().info("[Sdf1_login] 第三路备选 BaoTa 检查成功: " + fbResult.version); + } else { + failCount++; + StringBuilder sb = new StringBuilder(); + if (gh != null && gh.error != null) { + sb.append("§7GitHub: §c").append(gh.error); + } + if (ge != null && ge.error != null) { + if (sb.length() > 0) sb.append("\n"); + sb.append("§7Gitee: §c").append(ge.error); + } + if (fbResult != null && fbResult.error != null) { + if (sb.length() > 0) sb.append("\n"); + sb.append("§7BaoTa: §c").append(fbResult.error); + } + if (failCount >= TRIP_THRESHOLD) { + circuitTripped = true; + plugin.getLogger().warning( + "[Sdf1_login] 更新检测已熔断,连续失败" + failCount + "次"); + scheduleRetry(); + } + if (sender != null) { + final String msg = sb.toString(); + final boolean tripped = circuitTripped; + final int fc = failCount; + Bukkit.getScheduler().runTask(plugin, () -> { + sender.sendMessage("§c§l[更新] 检测失败"); + sender.sendMessage(msg); + sender.sendMessage("§e失败次数: " + fc + "/" + TRIP_THRESHOLD); + if (tripped) { + sender.sendMessage("§c已熔断,2分钟后自动重试,或重启服务器重置"); + } + }); + } + return; } - if (ge != null && ge.error != null) { - if (sb.length() > 0) sb.append("\n"); - sb.append("§7Gitee: §c").append(ge.error); - } - if (failCount >= TRIP_THRESHOLD) { - circuitTripped = true; - plugin.getLogger().warning( - "[Sdf1_login] 更新检测已熔断,连续失败" + failCount + "次"); - // 熔断后安排2分钟后重试 - scheduleRetry(); - } - if (sender != null) { - final String msg = sb.toString(); - final boolean tripped = circuitTripped; - final int fc = failCount; - Bukkit.getScheduler().runTask(plugin, () -> { - sender.sendMessage("§c§l[更新] 检测失败"); - sender.sendMessage(msg); - sender.sendMessage("§e失败次数: " + fc + "/" + TRIP_THRESHOLD); - if (tripped) { - sender.sendMessage("§c已熔断,2分钟后自动重试,或重启服务器重置"); - } - }); - } - return; } // 至少一个通道成功 @@ -314,6 +328,81 @@ public class UpdateChecker { // ========== HTTP 请求 ========== + /** + * 第三路备选:从宝塔静态HTML解析版本信息 + * HTML中用 块 + String html = sb.toString(); + + // ★ 去除 Cloudflare 注入的 CDN-CGI 链接(...) + html = html.replaceAll("]*>[^<]*", ""); + + int start = html.indexOf(""); + if (start < 0 || end < 0 || end <= start) { + return new CheckResult(null, "BaoTa", FALLBACK_HTML, + "BaoTa: 无法解析更新数据块"); + } + String block = html.substring(start, end); + + // 逐行查找匹配当前插件名的记录 + String[] lines = block.split("\n"); + for (String l : lines) { + l = l.trim(); + // 跳过空行、注释标记行、HTML注入标签 + if (l.isEmpty() || l.startsWith("