修复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:
2026-07-11 01:04:32 +08:00
parent 9121bb069d
commit 4ae1589883
4 changed files with 219 additions and 9 deletions
+41 -4
View File
@@ -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) {
+68 -1
View File
@@ -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();
}
+65 -3
View File
@@ -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());
+45 -1
View File
@@ -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已暂停");