fix: 修复全员下线后SN同步定时器永久停摆与冷静期提示误导
1. WebManager: Timer B/C/E 全员下线时改为保活调度(不再 return 停死), Timer C 兼任看门狗在检测到玩家上线时恢复;doActiveSyncBatch 移除 重复 schedule 调用避免产生双链。 2. SnManager.setStatus 写入 cancel_time,新增 cooldownMinutesLeft 辅助方法。 3. AreaProtection/AreaGUIManager/LandRecordManager/PVPManager 申领入口按 真实原因分流提示(冷静期剩余分钟数 vs 旧SN未注销),副作用与 return 不变。
This commit is contained in:
@@ -1769,8 +1769,13 @@ public class AreaGUIManager implements Listener {
|
||||
if (plugin.getSnManager() != null
|
||||
&& plugin.getSnManager().isBlocked(
|
||||
p, SnManager.TYPE_WAND)) {
|
||||
p.sendMessage("§e你已有区域选择工具的有效登记,"
|
||||
+ "旧SN未注销前不能重新申领");
|
||||
long cdMin = plugin.getSnManager().cooldownMinutesLeft(p.getName());
|
||||
if (cdMin > 0) {
|
||||
p.sendMessage("§e注销冷静期未结束,还剩约 §f" + cdMin + " §e分钟,期间无法申领区域选择工具");
|
||||
} else {
|
||||
p.sendMessage("§e你已有区域选择工具的有效登记,"
|
||||
+ "旧SN未注销前不能重新申领");
|
||||
}
|
||||
areaProtect.wandCooldownMap.put(p.getUniqueId(), now);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -6632,8 +6632,13 @@ public class AreaProtection implements Listener {
|
||||
if (plugin.getSnManager() != null
|
||||
&& plugin.getSnManager().isBlocked(
|
||||
p, SnManager.TYPE_WAND)) {
|
||||
long cdMin = plugin.getSnManager().cooldownMinutesLeft(p.getName());
|
||||
if (cdMin > 0) {
|
||||
p.sendMessage("§c§l[防护] §f注销冷静期未结束,还剩约 §e" + cdMin + " §f分钟,期间无法申领区域选择工具");
|
||||
} else {
|
||||
p.sendMessage("§c§l[防护] §f你已有区域选择工具的有效登记,"
|
||||
+ "旧SN未注销前不能重新申领");
|
||||
}
|
||||
wandCooldownMap.put(p.getUniqueId(), now);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -114,8 +114,13 @@ public class LandRecordManager implements Listener {
|
||||
if (plugin.getSnManager() != null
|
||||
&& plugin.getSnManager().isBlocked(
|
||||
p, SnManager.TYPE_ECHO)) {
|
||||
p.sendMessage("§c§l[回声碎片] §f你已有回声碎片的有效登记,"
|
||||
+ "旧SN未注销前不能重新申领");
|
||||
long cdMin = plugin.getSnManager().cooldownMinutesLeft(p.getName());
|
||||
if (cdMin > 0) {
|
||||
p.sendMessage("§c§l[回声碎片] §f注销冷静期未结束,还剩约 §e" + cdMin + " §f分钟,期间无法申领新设备");
|
||||
} else {
|
||||
p.sendMessage("§c§l[回声碎片] §f你已有回声碎片的有效登记,"
|
||||
+ "旧SN未注销前不能重新申领");
|
||||
}
|
||||
return;
|
||||
}
|
||||
ItemStack shard = createEchoShard();
|
||||
|
||||
@@ -490,8 +490,13 @@ public class PVPManager implements Listener {
|
||||
if (plugin.getSnManager() != null
|
||||
&& plugin.getSnManager().isBlocked(
|
||||
p, SnManager.TYPE_PVP)) {
|
||||
p.sendMessage("§c[PVP] 你已有PVP圈地棒的有效登记,"
|
||||
+ "旧SN未注销前不能重新申领");
|
||||
long cdMin = plugin.getSnManager().cooldownMinutesLeft(p.getName());
|
||||
if (cdMin > 0) {
|
||||
p.sendMessage("§c[PVP] 注销冷静期未结束,还剩约 " + cdMin + " 分钟,期间无法申领新设备");
|
||||
} else {
|
||||
p.sendMessage("§c[PVP] 你已有PVP圈地棒的有效登记,"
|
||||
+ "旧SN未注销前不能重新申领");
|
||||
}
|
||||
return;
|
||||
}
|
||||
ItemStack tool = createTool();
|
||||
|
||||
@@ -437,6 +437,13 @@ public class SnManager implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
/** 注销冷静期剩余分钟数,0 = 不在冷静期 */
|
||||
public long cooldownMinutesLeft(String player) {
|
||||
long cd = getCooldownUntil(player);
|
||||
long now = System.currentTimeMillis();
|
||||
return cd > now ? (cd - now + 59999) / 60000 : 0L;
|
||||
}
|
||||
|
||||
/** 启动 1 小时冷静期 */
|
||||
public void startCooldown(String player) {
|
||||
try {
|
||||
@@ -697,7 +704,8 @@ public class SnManager implements Listener {
|
||||
+ " bind_reason = CASE WHEN ? = 'illegal'"
|
||||
+ " THEN ? ELSE bind_reason END,"
|
||||
+ " bind_time = CASE WHEN ? = 'illegal'"
|
||||
+ " THEN ? ELSE bind_time END"
|
||||
+ " THEN ? ELSE bind_time END,"
|
||||
+ " cancel_time = CASE WHEN ? = 'cancelled' THEN ? ELSE cancel_time END"
|
||||
+ " WHERE sn = ?");
|
||||
ps.setString(1, status);
|
||||
ps.setString(2, remark);
|
||||
@@ -705,7 +713,9 @@ public class SnManager implements Listener {
|
||||
ps.setString(4, remark);
|
||||
ps.setString(5, status);
|
||||
ps.setLong(6, time);
|
||||
ps.setString(7, sn);
|
||||
ps.setString(7, status);
|
||||
ps.setLong(8, time);
|
||||
ps.setString(9, sn);
|
||||
int n = ps.executeUpdate();
|
||||
ps.close();
|
||||
return n;
|
||||
|
||||
@@ -1605,13 +1605,7 @@ public class WebManager {
|
||||
// ★ Timer D已独立运行,不再需要重启
|
||||
if (timersBCPaused) {
|
||||
timersBCPaused = false;
|
||||
long randB = (long)(Math.random() * 10) * 2;
|
||||
long randC = (long)(Math.random() * 10) * 2;
|
||||
long randE = (long)(Math.random() * 10) * 2;
|
||||
scheduleTimerB(40L + randB); // 2秒后重启B
|
||||
scheduleTimerC(120L + randC); // 6秒后重启C
|
||||
scheduleTimerE(240L + randE); // 12秒后重启E
|
||||
plugin.getLogger().info("[合并C] ★ 玩家上线,Timer B/C/E已恢复(随机偏移B=" + (randB/20) + "s C=" + (randC/20) + "s E=" + (randE/20) + "s)");
|
||||
plugin.getLogger().info("[合并C] ★ 玩家上线,Timer B/C/E 已恢复");
|
||||
}
|
||||
|
||||
// 玩家在线:全量批处理
|
||||
@@ -1932,10 +1926,12 @@ public class WebManager {
|
||||
return;
|
||||
}
|
||||
|
||||
// ★ 全员下线暂停:不调度下一轮
|
||||
// ★ 全员下线暂停:跳过本轮工作但保持链存活
|
||||
// ★ 关键:恢复逻辑在 doActiveSyncBatch 里,只由 Timer C 调用,
|
||||
// ★ 如果在这里直接 return 停死,就再也没人能唤醒它(生产事故根因)
|
||||
if (timersBCPaused) {
|
||||
plugin.getLogger().info("[合并B] ★ 全员下线,Timer B已暂停");
|
||||
return; // 不调度下一轮,定时器自然停止
|
||||
scheduleTimerB(calcStaggeredDelay(TIMER_B, 0, 10));
|
||||
return;
|
||||
}
|
||||
|
||||
// ★ PHP锁库退避检查
|
||||
@@ -1994,10 +1990,16 @@ public class WebManager {
|
||||
return;
|
||||
}
|
||||
|
||||
// ★ 全员下线暂停:不调度下一轮
|
||||
// ★ 全员下线暂停:Timer C 兼任看门狗,不能停死
|
||||
if (timersBCPaused) {
|
||||
plugin.getLogger().info("[合并C] ★ 全员下线,Timer C已暂停");
|
||||
return; // 不调度下一轮,定时器自然停止
|
||||
if (!Bukkit.getOnlinePlayers().isEmpty()) {
|
||||
timersBCPaused = false;
|
||||
plugin.getLogger().info("[合并C] ★ 检测到玩家上线,Timer B/C/E 恢复运行");
|
||||
// 三条链本身一直存活(下方都改为保活调度),这里无需重新 schedule B/E,继续本轮正常流程
|
||||
} else {
|
||||
scheduleTimerC(calcStaggeredDelay(TIMER_C, 10, 20));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// ★ PHP锁库退避检查
|
||||
@@ -2095,37 +2097,10 @@ public class WebManager {
|
||||
return;
|
||||
}
|
||||
|
||||
// ★ 全员下线暂停:不调度下一轮
|
||||
// ★ 全员下线暂停:跳过本轮工作但保持链存活(原实现会停死,且每轮打印 ASCII 噪音)
|
||||
if (timersBCPaused) {
|
||||
plugin.getLogger().info("[续费轮询E] ★ 全员下线,Timer E已暂停");
|
||||
log.error("\n" +
|
||||
" __ __ _ \n" +
|
||||
" \\ \\ / / | | \n" +
|
||||
" \\ \\ /\\ / /__ ___ ___ _ __ ___ ___ | |_ ___ \n" +
|
||||
" \\ \\/ \\/ / _ \\/ __/ _ \\| '_ ` _ \\ / _ \\ | __/ _ \\ \n" +
|
||||
" \\ /\\ / __/ (_| (_) | | | | | | __/ | || (_) | \n" +
|
||||
" \\/ \\/ \\___|\\___\\___/|_| |_| |_|\\___| \\__\\___/ _ \n" +
|
||||
" | | (_) \n" +
|
||||
" ___ __ _ ___ _ _ _ _ __ _ _ __ | |_ __ _ _ __ __ ___ __ _ _ __ ___ ___ _ ____ _____ _ __ \n" +
|
||||
" / __/ _` |/ _ \\ | | | | | | |/ _` | '_ \\ | __/ _` | '_ \\ \\ \\/ / |/ _` | '_ \\ / __|/ _ \\ '__\\ \\ / / _ \\ '__|\n" +
|
||||
" | (_| (_| | (_) | | |_| | |_| | (_| | | | | | || (_| | | | | > <| | (_| | | | | \\__ \\ __/ | \\ V / __/ | \n" +
|
||||
" \\___\\__,_|\\___/ \\__, |\\__,_|\\__,_|_| |_| \\__\\__,_|_| |_| /_/\\_\\_|\\__,_|_| |_| |___/\\___|_| \\_/ \\___|_| \n" +
|
||||
" _ __/ | ___ _ _ _ _ __ \n" +
|
||||
" (_) _ |___/ |__ \\ | | (_) | (_)/ _| \n" +
|
||||
" _ _ __(_) _ __ ___ ___ ) | _ _ _ __ ___| |__ _ __| |_| |_ _ _ ___ _ __ \n" +
|
||||
" | | '_ \\ | '_ ` _ \\ / __| / / | | | | '_ \\/ __| '_ \\| |/ _` | | _| | | | / __| '_ \\ \n" +
|
||||
" | | |_) | | | | | | | (__ / /_ | |_| | |_) \\__ \\ | | | | (_| | | | | |_| || (__| | | | \n" +
|
||||
" |_| .__(_) |_| |_| |_|\\___|____(_)__, | .__/|___/_| |_|_|\\__,_|_|_| \\__,_(_)___|_| |_| \n" +
|
||||
" | | __/ | | \n" +
|
||||
" |_| _ ____ _|___/|_|______ ___ \n" +
|
||||
" | | _ |___ \\ / _ \\ / /____ / _ \\ \n" +
|
||||
" _ __ ___ ___| |_(_) __) | | | |/ /_ / / (_) | \n" +
|
||||
" | '_ \\ / _ \\/ __| __| |__ <| | | | '_ \\ / / \\__, | \n" +
|
||||
" | |_) | (_) \\__ \\ |_ _ ___) | |_| | (_) / / / / \n" +
|
||||
" | .__/ \\___/|___/\\__(_) |____/ \\___/ \\___/_/ /_/ \n" +
|
||||
" | | \n" +
|
||||
" |_| ");
|
||||
return; // 不调度下一轮,定时器自然停止
|
||||
scheduleTimerE(calcStaggeredDelay(TIMER_E, 20, 30));
|
||||
return;
|
||||
}
|
||||
|
||||
// ★ PHP锁库退避检查
|
||||
|
||||
Reference in New Issue
Block a user