修复Web通信禁用仍轮询+PVP远程武器与击杀奖励+修复splitToList编译错误
1. WebManager: 5个合并定时器(A/B/C/D/E)新增enabled运行时守卫,Web通信关闭后立即停止请求web配置地址;reloadWebConfig支持运行时启停定时器 2. PVPTestManager: 测试场新增弓/弩二选一(默认弓)默认装备,完成一波击杀发放16牛排补血 3. PVPArenaManager: 正式场新增弓/弩二选一(默认弓)默认装备+GUI切换,每2次杀敌发放16牛排补血 4. AreaProtection: 修复splitToList中[\""未结束字符串字面量导致的编译失败
This commit is contained in:
@@ -1041,25 +1041,62 @@ public class AreaProtection implements Listener {
|
||||
}
|
||||
|
||||
/**
|
||||
* 逗号分隔字符串 → List
|
||||
* 逗号分隔字符串或JSON数组 → List
|
||||
* 兼容: "a,b,c" 或 ["a","b","c"]
|
||||
*/
|
||||
private List<String> splitToList(String s) {
|
||||
List<String> result = new ArrayList<>();
|
||||
if (s == null || s.isEmpty()) return result;
|
||||
String trimmed = s.trim();
|
||||
// ★ 支持JSON数组格式(PHP端存储格式),如 ["速度","急迫"]
|
||||
if (trimmed.startsWith("[\"")) {
|
||||
try {
|
||||
String inner = trimmed.substring(1, trimmed.length() - 1);
|
||||
for (String part : inner.split(",")) {
|
||||
String cleaned = part.trim().replaceAll("^\"|\"$", "").replaceAll("^'|'$", "");
|
||||
if (!cleaned.isEmpty()) result.add(cleaned);
|
||||
}
|
||||
if (!result.isEmpty()) return result;
|
||||
} catch (Exception ignored) {
|
||||
// 回退到逗号分隔
|
||||
}
|
||||
}
|
||||
for (String item : s.split(",")) {
|
||||
String trimmed = item.trim();
|
||||
if (!trimmed.isEmpty()) result.add(trimmed);
|
||||
String itemTrimmed = item.trim().replaceAll("^\"|\"$", "").replaceAll("^'|'$", "");
|
||||
if (!itemTrimmed.isEmpty()) result.add(itemTrimmed);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析效果存储字符串 → List<String[]>
|
||||
* 格式: 效果名:等级:秒数|效果名:等级:秒数
|
||||
* 格式: 管道格式 "效果名:等级:秒数|效果名:等级:秒数"
|
||||
* 或 JSON格式 [["效果名",等级,秒数],...]
|
||||
*/
|
||||
private List<String[]> parseEffectsString(String s) {
|
||||
List<String[]> result = new ArrayList<>();
|
||||
if (s == null || s.isEmpty()) return result;
|
||||
|
||||
// ★ 支持JSON格式(PHP端存储格式)
|
||||
String trimmed = s.trim();
|
||||
if (trimmed.startsWith("[[")) {
|
||||
try {
|
||||
// 去掉外层方括号
|
||||
String inner = trimmed.substring(1, trimmed.length() - 1);
|
||||
// 用正则匹配每个 ["name",level,dur] 数组
|
||||
java.util.regex.Matcher m = java.util.regex.Pattern
|
||||
.compile("\\[\"([^\"]*)\",(\\d+),(\\d+)\\]")
|
||||
.matcher(inner);
|
||||
while (m.find()) {
|
||||
result.add(new String[]{m.group(1), m.group(2), m.group(3)});
|
||||
}
|
||||
if (!result.isEmpty()) return result;
|
||||
} catch (Exception ignored) {
|
||||
// JSON解析失败,回退到管道格式
|
||||
}
|
||||
}
|
||||
|
||||
// ★ 管道格式(Java端GUI存储格式): 效果名:等级:秒数|效果名:等级:秒数
|
||||
for (String part : s.split("\\|")) {
|
||||
String[] pieces = part.split(":");
|
||||
if (pieces.length >= 3) {
|
||||
|
||||
@@ -134,6 +134,7 @@ public class PVPArenaManager implements Listener {
|
||||
private final Map<String, Boolean> selShield = new ConcurrentHashMap<>(); // 该玩家是否拿盾牌
|
||||
private final Map<String, Integer> selFood = new ConcurrentHashMap<>(); // 熟牛肉数量
|
||||
private final Map<String, Integer> selWeaponTier = new ConcurrentHashMap<>(); // 主武器材质档 0铁1金2钻3合金
|
||||
private final Map<String, Integer> selRangedWeapon = new ConcurrentHashMap<>(); // 远程武器 0弓(默认) 1弩
|
||||
private final Map<String, Integer> selArmorTier = new ConcurrentHashMap<>(); // 护甲材质档 0铁1金2钻3合金
|
||||
|
||||
private static final int FOOD_MIN = 1;
|
||||
@@ -1021,6 +1022,7 @@ public class PVPArenaManager implements Listener {
|
||||
selShield.remove(playerName);
|
||||
selFood.remove(playerName);
|
||||
selWeaponTier.remove(playerName);
|
||||
selRangedWeapon.remove(playerName);
|
||||
selArmorTier.remove(playerName);
|
||||
equipInteracted.remove(playerName);
|
||||
if (p != null && p.isOnline()) {
|
||||
@@ -1140,6 +1142,19 @@ public class PVPArenaManager implements Listener {
|
||||
// 总榜击杀数
|
||||
pvpKills.put(kName, pvpKills.getOrDefault(kName, 0) + 1);
|
||||
|
||||
// ★ 击杀奖励:PVP世界每完成2次杀敌,发放16个牛排用于补血(背包满则只发能放下的)
|
||||
int newKills = pvpKills.get(kName);
|
||||
if (newKills % 2 == 0) {
|
||||
Player killerP = (Player) killer;
|
||||
Map<Integer, ItemStack> leftover = killerP.getInventory().addItem(new ItemStack(Material.COOKED_BEEF, 16));
|
||||
int given = 16;
|
||||
for (ItemStack rem : leftover.values()) given -= rem.getAmount();
|
||||
if (given > 0) {
|
||||
killerP.sendMessage("§a§l[PVP] §f完成 2 次杀敌!获得 §6" + given + " 个牛排 §7(补血)");
|
||||
killerP.playSound(killerP.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 0.6f, 1.4f);
|
||||
}
|
||||
}
|
||||
|
||||
// 击杀者连杀会话
|
||||
PVPManager.KillSession ks = arenaKillSessions.get(kName);
|
||||
if (ks == null
|
||||
@@ -1339,6 +1354,7 @@ public class PVPArenaManager implements Listener {
|
||||
selShield.remove(player.getName());
|
||||
selFood.remove(player.getName());
|
||||
selWeaponTier.remove(player.getName());
|
||||
selRangedWeapon.remove(player.getName());
|
||||
selArmorTier.remove(player.getName());
|
||||
equipInteracted.remove(player.getName());
|
||||
cancelKickTimeout(player.getName());
|
||||
@@ -1498,6 +1514,7 @@ public class PVPArenaManager implements Listener {
|
||||
if (!selEnchant.containsKey(name)) selEnchant.put(name, lockedEnchant != null ? lockedEnchant : false);
|
||||
if (!selShield.containsKey(name)) selShield.put(name, true);
|
||||
if (!selFood.containsKey(name)) selFood.put(name, FOOD_DEFAULT);
|
||||
if (!selRangedWeapon.containsKey(name)) selRangedWeapon.put(name, 0); // 0=弓(默认)
|
||||
|
||||
boolean weaponLocked = (lockedWeaponOwner != null && !lockedWeaponOwner.equals(name));
|
||||
boolean armorLocked = (lockedArmorOwner != null && !lockedArmorOwner.equals(name));
|
||||
@@ -1508,6 +1525,7 @@ public class PVPArenaManager implements Listener {
|
||||
boolean myEnchant = selEnchant.getOrDefault(name, false);
|
||||
boolean myShield = selShield.getOrDefault(name, true);
|
||||
int myFood = selFood.getOrDefault(name, FOOD_DEFAULT);
|
||||
int myRanged = selRangedWeapon.getOrDefault(name, 0); // 0弓 1弩
|
||||
|
||||
Inventory gui = Bukkit.createInventory(null, 54, EQUIPMENT_GUI_TITLE);
|
||||
|
||||
@@ -1534,6 +1552,7 @@ public class PVPArenaManager implements Listener {
|
||||
lore.add("");
|
||||
lore.add("§7你的附魔: " + (myEnchant ? "§a已开启" : "§c未开启"));
|
||||
lore.add("§7你的盾牌: " + (myShield ? "§a已装备" : "§c未装备"));
|
||||
lore.add("§7远程武器: " + (myRanged == 1 ? "§b弩" : "§b弓") + "§7(默认装备)");
|
||||
lore.add("§7熟牛肉: §a" + myFood + " §7个");
|
||||
if (weaponLocked || armorLocked || enchantLocked) {
|
||||
lore.add("");
|
||||
@@ -1598,6 +1617,16 @@ public class PVPArenaManager implements Listener {
|
||||
}
|
||||
gui.setItem(30, shBtn);
|
||||
|
||||
// 远程武器切换(29)— 弓/弩二选一,默认弓,属默认装备
|
||||
ItemStack rwBtn = new ItemStack(myRanged == 1 ? Material.CROSSBOW : Material.BOW);
|
||||
ItemMeta rwm = rwBtn.getItemMeta();
|
||||
if (rwm != null) {
|
||||
rwm.setDisplayName(myRanged == 1 ? "§b§l远程武器: 弩" : "§a§l远程武器: 弓");
|
||||
rwm.setLore(Arrays.asList("§7点击在 弓 / 弩 之间切换", "§e当前: " + (myRanged == 1 ? "弩 (默认装备)" : "弓 (默认装备)"), "§7会同时发放对应箭矢"));
|
||||
rwBtn.setItemMeta(rwm);
|
||||
}
|
||||
gui.setItem(29, rwBtn);
|
||||
|
||||
// 熟牛肉快捷数量(32-35 = 8/16/32/64,点哪个直接设为哪个)
|
||||
int[] foodOpts = {8, 16, 32, 64};
|
||||
for (int i = 0; i < 4; i++) {
|
||||
@@ -1614,6 +1643,7 @@ public class PVPArenaManager implements Listener {
|
||||
+ (myEnchant ? "(§d附魔§7)" : "(§7未附魔§7)"));
|
||||
lore.add(ARMOR_TIER_NAMES[myArmor] + "§7全套护甲");
|
||||
lore.add(myShield ? "§7含 §a盾牌" : "§7不含盾牌");
|
||||
lore.add("§7远程武器: " + (myRanged == 1 ? "§b弩" : "§b弓") + "(默认发放)");
|
||||
lore.add("§7熟牛肉 x" + myFood);
|
||||
confirmMeta.setLore(lore);
|
||||
confirm.setItemMeta(confirmMeta);
|
||||
@@ -1803,6 +1833,18 @@ public class PVPArenaManager implements Listener {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 远程武器切换(29)— 弓/弩二选一,默认弓
|
||||
if (slot == 29) {
|
||||
int cur = selRangedWeapon.getOrDefault(name, 0);
|
||||
selRangedWeapon.put(name, cur == 0 ? 1 : 0);
|
||||
equipInteracted.add(name);
|
||||
guiReopening.add(name);
|
||||
openEquipmentSelection(player);
|
||||
player.sendMessage("§a[PVP] 远程武器已切换为 " + (cur == 0 ? "§b弩" : "§b弓") + "§a(默认装备)");
|
||||
player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 0.7f, 1.2f);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 熟牛肉快捷数量(32-35 = 8/16/32/64),点哪个直接设为哪个
|
||||
if (slot >= 32 && slot <= 35) {
|
||||
int[] foodOpts = {8, 16, 32, 64};
|
||||
@@ -1850,6 +1892,7 @@ public class PVPArenaManager implements Listener {
|
||||
int aTier = selArmorTier.getOrDefault(name, 0);
|
||||
boolean shield = selShield.getOrDefault(name, true);
|
||||
int food = selFood.getOrDefault(name, FOOD_DEFAULT);
|
||||
int rw = selRangedWeapon.getOrDefault(name, 0); // 0弓 1弩
|
||||
|
||||
// 主武器:同材质剑 + 斧(两者都发,附魔作用于两者)
|
||||
player.getInventory().addItem(makeWeapon(SWORD_TIERS[wTier], "§l" + WEAPON_TIER_NAMES[wTier] + "§l剑", enchant));
|
||||
@@ -1884,6 +1927,29 @@ public class PVPArenaManager implements Listener {
|
||||
player.getInventory().setItemInOffHand(sh);
|
||||
}
|
||||
|
||||
// 远程武器(默认装备):弓/弩二选一,默认弓,统一发放对应弹药(箭)
|
||||
if (rw == 1) {
|
||||
ItemStack crossbow = new ItemStack(Material.CROSSBOW);
|
||||
ItemMeta cm = crossbow.getItemMeta();
|
||||
if (cm != null) {
|
||||
cm.setDisplayName("§b§lPVP弩");
|
||||
cm.setLore(Arrays.asList("§7远程武器(默认装备)", PVP_ITEM_MARKER));
|
||||
crossbow.setItemMeta(cm);
|
||||
}
|
||||
player.getInventory().addItem(crossbow);
|
||||
player.getInventory().addItem(new ItemStack(Material.ARROW, 64));
|
||||
} else {
|
||||
ItemStack bow = new ItemStack(Material.BOW);
|
||||
ItemMeta bm = bow.getItemMeta();
|
||||
if (bm != null) {
|
||||
bm.setDisplayName("§b§lPVP弓");
|
||||
bm.setLore(Arrays.asList("§7远程武器(默认装备)", PVP_ITEM_MARKER));
|
||||
bow.setItemMeta(bm);
|
||||
}
|
||||
player.getInventory().addItem(bow);
|
||||
player.getInventory().addItem(new ItemStack(Material.ARROW, 64));
|
||||
}
|
||||
|
||||
// 补血普通食物:熟牛肉(数量可调)
|
||||
if (food > 0) {
|
||||
ItemStack beef = new ItemStack(Material.COOKED_BEEF, food);
|
||||
@@ -1899,7 +1965,7 @@ public class PVPArenaManager implements Listener {
|
||||
plugin.getLogger().info("[PVP] 玩家 " + name + " 发放装备: "
|
||||
+ WEAPON_TIER_NAMES[wTier] + "剑+斧 / " + ARMOR_TIER_NAMES[aTier] + "护甲"
|
||||
+ (enchant ? "(附魔)" : "(未附魔)") + (shield ? " +盾牌" : "")
|
||||
+ " 熟牛肉x" + food);
|
||||
+ " 远程:" + (rw == 1 ? "弩" : "弓") + " 熟牛肉x" + food);
|
||||
}
|
||||
|
||||
// ==================== 命令入口 ====================
|
||||
@@ -2050,6 +2116,7 @@ public class PVPArenaManager implements Listener {
|
||||
selShield.clear();
|
||||
selFood.clear();
|
||||
selWeaponTier.clear();
|
||||
selRangedWeapon.clear();
|
||||
selArmorTier.clear();
|
||||
equipInteracted.clear();
|
||||
}
|
||||
|
||||
@@ -63,6 +63,8 @@ public class PVPTestManager implements Listener {
|
||||
/** 自由装备档位(测试世界跳过公平锁定,全员自由选武器/护甲材质) */
|
||||
private final Map<UUID, Integer> testWeaponTier = new HashMap<>(); // 0铁 1金 2钻 3下界合金
|
||||
private final Map<UUID, Integer> testArmorTier = new HashMap<>(); // 0无 1铁 2金 3钻 4下界合金
|
||||
private final Map<UUID, Integer> testRangedWeapon = new HashMap<>(); // 0弓(默认) 1弩
|
||||
private final Map<UUID, Integer> testWaveKills = new HashMap<>(); // 本波击杀计数(用于击杀奖励)
|
||||
|
||||
private static final Material[] SWORD_TIERS = {
|
||||
Material.IRON_SWORD, Material.GOLDEN_SWORD, Material.DIAMOND_SWORD, Material.NETHERITE_SWORD };
|
||||
@@ -263,12 +265,13 @@ public class PVPTestManager implements Listener {
|
||||
|
||||
TestDifficulty diff = chosenDiff.getOrDefault(p.getUniqueId(), TestDifficulty.EASY);
|
||||
TestEquip equip = chosenEquip.getOrDefault(p.getUniqueId(), TestEquip.MELEE);
|
||||
// 测试世界跳过公平锁定,全员自由选装备(附魔/盾牌/熟牛肉数量),默认附魔关、盾牌开、熟牛肉16
|
||||
// 测试世界跳过公平锁定,全员自由选装备(附魔/盾牌/熟牛肉数量),默认附魔关、盾牌开、熟牛肉16、远程武器弓
|
||||
testEnchant.putIfAbsent(p.getUniqueId(), false);
|
||||
testShield.putIfAbsent(p.getUniqueId(), true);
|
||||
testFood.putIfAbsent(p.getUniqueId(), 16);
|
||||
testWeaponTier.putIfAbsent(p.getUniqueId(), 0);
|
||||
testArmorTier.putIfAbsent(p.getUniqueId(), 0);
|
||||
testRangedWeapon.putIfAbsent(p.getUniqueId(), 0); // 0=弓(默认)
|
||||
boolean enchant = testEnchant.get(p.getUniqueId());
|
||||
boolean shield = testShield.get(p.getUniqueId());
|
||||
int food = testFood.get(p.getUniqueId());
|
||||
@@ -353,6 +356,21 @@ public class PVPTestManager implements Listener {
|
||||
}
|
||||
gui.setItem(38, shBtn);
|
||||
|
||||
// ★ 远程武器选择(39):弓/弩二选一,默认弓
|
||||
int rangedWeapon = testRangedWeapon.getOrDefault(p.getUniqueId(), 0);
|
||||
Material rangedIcon = rangedWeapon == 0 ? Material.BOW : Material.CROSSBOW;
|
||||
ItemStack rwBtn = new ItemStack(rangedIcon);
|
||||
ItemMeta rwM = rwBtn.getItemMeta();
|
||||
if (rwM != null) {
|
||||
String name = rangedWeapon == 0 ? "§a§l远程武器: 弓" : "§b§l远程武器: 弩";
|
||||
rwM.setDisplayName(name);
|
||||
rwM.setLore(Arrays.asList("§7切换弓/弩(默认弓)",
|
||||
"§e当前: " + (rangedWeapon == 0 ? "弓 (BOW)" : "弩 (CROSSBOW)"),
|
||||
"§7点击切换"));
|
||||
rwBtn.setItemMeta(rwM);
|
||||
}
|
||||
gui.setItem(39, rwBtn);
|
||||
|
||||
// ★ 熟牛肉快捷数量(45-48 = 8/16/32/64,点哪个直接设为哪个)
|
||||
int[] foodOpts = {8, 16, 32, 64};
|
||||
for (int i = 0; i < 4; i++) {
|
||||
@@ -529,6 +547,13 @@ public class PVPTestManager implements Listener {
|
||||
p.playSound(p.getLocation(), Sound.UI_BUTTON_CLICK, 0.7f, 1.2f);
|
||||
openGUI(p); return;
|
||||
}
|
||||
// ★ 远程武器切换(39):弓/弩二选一
|
||||
if (slot == 39) {
|
||||
int current = testRangedWeapon.getOrDefault(id, 0);
|
||||
testRangedWeapon.put(id, current == 0 ? 1 : 0);
|
||||
p.playSound(p.getLocation(), Sound.UI_BUTTON_CLICK, 0.7f, 1.2f);
|
||||
openGUI(p); return;
|
||||
}
|
||||
// ★ 熟牛肉直选(45-48 = 8/16/32/64)
|
||||
if (slot >= 45 && slot <= 48) {
|
||||
int[] foodOpts = {8, 16, 32, 64};
|
||||
@@ -610,10 +635,13 @@ public class PVPTestManager implements Listener {
|
||||
spawnWaveFor(p);
|
||||
|
||||
int beef = testFood.getOrDefault(p.getUniqueId(), 16);
|
||||
int rw = testRangedWeapon.getOrDefault(p.getUniqueId(), 0);
|
||||
String rangedName = rw == 1 ? "§b弩" : "§b弓";
|
||||
p.sendMessage("§a§l[PVP测试] 已进入测试场! 难度: " + diff.display + " 场景: " + equip.display
|
||||
+ " 武器:" + WEAPON_TIER_NAMES[wt] + " 护甲:" + ARMOR_TIER_NAMES[at]
|
||||
+ " 附魔:" + (enchant ? "§d开" : "§7关") + " 盾牌:" + (shield ? "§a有" : "§7无") + " 熟牛肉:" + beef);
|
||||
p.sendMessage("§7主武器: " + WEAPON_TIER_NAMES[wt] + "§b剑 + " + WEAPON_TIER_NAMES[wt] + "§b斧"
|
||||
+ " 附魔:" + (enchant ? "§d开" : "§7关") + " 盾牌:" + (shield ? "§a有" : "§7无")
|
||||
+ " 远程:" + rangedName + " 熟牛肉:" + beef);
|
||||
p.sendMessage("§7主武器: " + WEAPON_TIER_NAMES[wt] + "§b剑 + " + WEAPON_TIER_NAMES[wt] + "§b斧 + " + rangedName
|
||||
+ (at > 0 ? " + " + ARMOR_TIER_NAMES[at] + "§b护甲" : "")
|
||||
+ (enchant ? "(附魔)" : "(未附魔)") + (shield ? " +盾牌" : ""));
|
||||
p.sendMessage("§7练习生物血量统一为40。敌对生物互不攻击(友伤免疫)。");
|
||||
@@ -663,6 +691,30 @@ public class PVPTestManager implements Listener {
|
||||
p.getInventory().setItemInOffHand(sh);
|
||||
}
|
||||
|
||||
// ★ 远程武器(默认装备):弓/弩二选一,默认弓。统一发放对应弹药(箭)。
|
||||
int rw = testRangedWeapon.getOrDefault(p.getUniqueId(), 0);
|
||||
if (rw == 1) {
|
||||
ItemStack crossbow = new ItemStack(Material.CROSSBOW);
|
||||
ItemMeta cm = crossbow.getItemMeta();
|
||||
if (cm != null) {
|
||||
cm.setDisplayName("§a§l练习弩");
|
||||
cm.setLore(Arrays.asList("§7远程武器(默认装备)", "§e弓/弩二选一,默认弓"));
|
||||
crossbow.setItemMeta(cm);
|
||||
}
|
||||
p.getInventory().addItem(crossbow);
|
||||
p.getInventory().addItem(new ItemStack(Material.ARROW, 64));
|
||||
} else {
|
||||
ItemStack bow = new ItemStack(Material.BOW);
|
||||
ItemMeta bm = bow.getItemMeta();
|
||||
if (bm != null) {
|
||||
bm.setDisplayName("§a§l练习弓");
|
||||
bm.setLore(Arrays.asList("§7远程武器(默认装备)", "§e弓/弩二选一,默认弓"));
|
||||
bow.setItemMeta(bm);
|
||||
}
|
||||
p.getInventory().addItem(bow);
|
||||
p.getInventory().addItem(new ItemStack(Material.ARROW, 64));
|
||||
}
|
||||
|
||||
// 回血类食物:熟牛肉(维持饱食度),数量与主场一致可调(1~64),无金苹果(与主场一致)
|
||||
int beef = testFood.getOrDefault(p.getUniqueId(), 16);
|
||||
p.getInventory().addItem(new ItemStack(Material.COOKED_BEEF, beef));
|
||||
@@ -716,6 +768,16 @@ public class PVPTestManager implements Listener {
|
||||
pendingRespawn.add(p.getUniqueId());
|
||||
sendClearedMessage(p);
|
||||
|
||||
// ★ 击杀奖励:完成一波击杀发放16个牛排用于补血(背包满则只发能放下的)
|
||||
int reward = 16;
|
||||
Map<Integer, ItemStack> leftover = p.getInventory().addItem(new ItemStack(Material.COOKED_BEEF, reward));
|
||||
int given = reward;
|
||||
for (ItemStack rem : leftover.values()) given -= rem.getAmount();
|
||||
if (given > 0) {
|
||||
p.sendMessage("§a§l[PVP测试] §f完成一波击杀!获得 §6" + given + " 个牛排 §7(补血)");
|
||||
p.playSound(p.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 0.6f, 1.4f);
|
||||
}
|
||||
|
||||
// 间隔10秒后刷新下一波(若届时仍在场且无存活生物)
|
||||
Bukkit.getScheduler().runTaskLater(plugin, () -> {
|
||||
pendingRespawn.remove(p.getUniqueId());
|
||||
|
||||
@@ -87,6 +87,7 @@ public class WebManager {
|
||||
// 默认配置
|
||||
private String webBaseUrl = "https://caoyuan.ypshidifu.cn/plugin";
|
||||
private boolean enabled = false;
|
||||
private boolean pollingStarted = false; // 合并定时器是否已启动(用于运行时重载启停)
|
||||
private int tokenExpireSeconds = 600; // 10分钟
|
||||
private int syncIntervalMinutes = 5;
|
||||
private String secretKey = "sdf1_web_comm_2026_ypshidifu";
|
||||
@@ -789,8 +790,19 @@ public class WebManager {
|
||||
* 重载后端设置(仅Web通信相关配置)
|
||||
*/
|
||||
public void reloadWebConfig() {
|
||||
boolean wasEnabled = enabled;
|
||||
loadConfig();
|
||||
plugin.getLogger().info("[Web通信] Web后端配置已重载: 地址=" + webBaseUrl + " 密钥=" + secretKey);
|
||||
plugin.getLogger().info("[Web通信] Web后端配置已重载: 地址=" + webBaseUrl + " 启用=" + enabled + " 密钥=" + secretKey);
|
||||
|
||||
// ★★★ 运行时启停:根据enabled状态动态启动/停止轮询定时器
|
||||
// 1) 已禁用:定时器内部已有 !enabled 守卫,下一轮自动跳过HTTP请求(无需手动取消)
|
||||
// 2) 从禁用→启用且定时器未启动:立即启动合并定时器
|
||||
if (enabled && !pollingStarted) {
|
||||
plugin.getLogger().info("[Web通信] ★ 重载后检测到已启用,启动合并定时器(A/B/C/D/E)");
|
||||
startMergedPolling();
|
||||
} else if (!enabled && wasEnabled) {
|
||||
plugin.getLogger().info("[Web通信] ★ 重载后检测到已禁用,定时器将在下一轮自动停止请求Web后端");
|
||||
}
|
||||
plugin.getLogger().warning("\n" +
|
||||
" _ \n" +
|
||||
" | | \n" +
|
||||
@@ -1546,6 +1558,7 @@ public class WebManager {
|
||||
* 三者错峰:首次启动带±5秒随机偏移,后续通过calcStaggeredDelay自动错开≥8秒
|
||||
*/
|
||||
private void startMergedPolling() {
|
||||
pollingStarted = true; // ★ 标记定时器已启动(运行时重载启停判断用)
|
||||
// ★ 首次启动加±5秒随机偏移,避免精确对齐导致并发
|
||||
long randA = (long)(Math.random() * 10) * 2; // 0~10秒(偶数tick)
|
||||
long randB = (long)(Math.random() * 10) * 2;
|
||||
@@ -1575,6 +1588,13 @@ public class WebManager {
|
||||
long now = System.currentTimeMillis();
|
||||
synchronized (scheduleLock) { lastRunTimestamps[TIMER_A] = now; }
|
||||
|
||||
// ★★★ Web通信未启用时跳过所有轮询(支持运行时通过重载配置关闭)
|
||||
// 修复bug:之前仅在start()判断enabled,重载配置为false后定时器仍持续请求web配置地址
|
||||
if (!enabled) {
|
||||
scheduleTimerA(calcStaggeredDelay(TIMER_A, 3, 5));
|
||||
return;
|
||||
}
|
||||
|
||||
// ★ PHP锁库退避检查:如果PHP正忙,跳过本轮
|
||||
if (phpBusyUntil > System.currentTimeMillis()) {
|
||||
scheduleTimerA(calcStaggeredDelay(TIMER_A, 3, 5));
|
||||
@@ -1680,6 +1700,12 @@ public class WebManager {
|
||||
long now = System.currentTimeMillis();
|
||||
synchronized (scheduleLock) { lastRunTimestamps[TIMER_B] = now; }
|
||||
|
||||
// ★★★ Web通信未启用时跳过所有轮询(支持运行时关闭)
|
||||
if (!enabled) {
|
||||
scheduleTimerB(calcStaggeredDelay(TIMER_B, 0, 10));
|
||||
return;
|
||||
}
|
||||
|
||||
// ★ 全员下线暂停:不调度下一轮
|
||||
if (timersBCPaused) {
|
||||
plugin.getLogger().info("[合并B] ★ 全员下线,Timer B已暂停");
|
||||
@@ -1736,6 +1762,12 @@ public class WebManager {
|
||||
long now = System.currentTimeMillis();
|
||||
synchronized (scheduleLock) { lastRunTimestamps[TIMER_C] = now; }
|
||||
|
||||
// ★★★ Web通信未启用时跳过所有轮询(支持运行时关闭)
|
||||
if (!enabled) {
|
||||
scheduleTimerC(calcStaggeredDelay(TIMER_C, 10, 20));
|
||||
return;
|
||||
}
|
||||
|
||||
// ★ 全员下线暂停:不调度下一轮
|
||||
if (timersBCPaused) {
|
||||
plugin.getLogger().info("[合并C] ★ 全员下线,Timer C已暂停");
|
||||
@@ -1781,6 +1813,12 @@ public class WebManager {
|
||||
long now = System.currentTimeMillis();
|
||||
synchronized (scheduleLock) { lastRunTimestamps[TIMER_D] = now; }
|
||||
|
||||
// ★★★ Web通信未启用时跳过所有轮询(支持运行时关闭)
|
||||
if (!enabled) {
|
||||
scheduleTimerD(calcStaggeredDelay(TIMER_D, 15, 25));
|
||||
return;
|
||||
}
|
||||
|
||||
// ★ Timer D独立:不检查timersBCPaused(v17重构设计)
|
||||
|
||||
// ★ PHP锁库退避检查
|
||||
@@ -1816,6 +1854,12 @@ public class WebManager {
|
||||
long now = System.currentTimeMillis();
|
||||
synchronized (scheduleLock) { lastRunTimestamps[TIMER_E] = now; }
|
||||
|
||||
// ★★★ Web通信未启用时跳过所有轮询(支持运行时关闭)
|
||||
if (!enabled) {
|
||||
scheduleTimerE(calcStaggeredDelay(TIMER_E, 20, 30));
|
||||
return;
|
||||
}
|
||||
|
||||
// ★ 全员下线暂停:不调度下一轮
|
||||
if (timersBCPaused) {
|
||||
plugin.getLogger().info("[续费轮询E] ★ 全员下线,Timer E已暂停");
|
||||
|
||||
Reference in New Issue
Block a user