领地系统8项修复: SSL断路器/计费/GUI-CLI切换/删除取消/管理细分/配置释放/txt迁移

This commit is contained in:
youpaishidifu
2026-06-23 19:52:55 +08:00
parent 542879177c
commit 05ebd07df6
4 changed files with 420 additions and 49 deletions
+104 -9
View File
@@ -127,11 +127,20 @@ public class AreaCLIManager {
.clickEvent(ClickEvent.runCommand(
"/protect tp " + land.name)));
line = line.append(Component.text(" "));
line = line.append(Component.text("§c[删除]")
.hoverEvent(HoverEvent.showText(
Component.text("§c点击删除此领地")))
.clickEvent(ClickEvent.runCommand(
"/protect 删除 " + land.name)));
if (areaProtect.hasPendingDelete(land.name)) {
// ★ 有待删除请求时显示取消按钮
line = line.append(Component.text("§e[取消删除]")
.hoverEvent(HoverEvent.showText(
Component.text("§e点击取消删除此领地")))
.clickEvent(ClickEvent.runCommand(
"/protect 取消删除 " + land.name)));
} else {
line = line.append(Component.text("§c[删除]")
.hoverEvent(HoverEvent.showText(
Component.text("§c点击删除此领地")))
.clickEvent(ClickEvent.runCommand(
"/protect 删除 " + land.name)));
}
}
p.sendMessage(line);
@@ -187,10 +196,11 @@ public class AreaCLIManager {
p.sendMessage(Component.text("§7规则数: §f" + land.ruleCount()));
p.sendMessage(Component.text("§7§l───────────────────────────────"));
// 显示权限列表(分页)
List<AreaProtection.AreaConfig> tempLandList = new ArrayList<>();
tempLandList.add(land);
showPermPage(p, land, permPage);
// ★ 细分管理选项
p.sendMessage(clickableAction("👤", "用户管理",
"/protect cli members " + land.name + " 1"));
p.sendMessage(clickableAction("🔑", "访客授权",
"/protect cli visitorperm " + land.name + " 1"));
p.sendMessage(Component.empty()
.append(Component.text("§a[◀ 返回列表]")
@@ -199,6 +209,91 @@ public class AreaCLIManager {
p.sendMessage(Component.text("§7§l───────────────────────────────"));
}
// ==================== 用户管理 ====================
public void showMemberList(Player p, String landName, int page) {
AreaProtection.AreaConfig land = areaProtect.getLand(landName);
if (land == null) {
p.sendMessage(Component.text("§c领地不存在: " + landName));
return;
}
Set<String> members = areaProtect.getLandMembers(landName);
List<String> memberList = new ArrayList<>(members);
int totalPages = Math.max(1, (int) Math.ceil((double) memberList.size() / PAGE_SIZE));
page = Math.max(1, Math.min(page, totalPages));
p.sendMessage(header("用户管理: " + landName));
if (memberList.isEmpty()) {
p.sendMessage(Component.text("§7暂无成员"));
} else {
int start = (page - 1) * PAGE_SIZE;
int end = Math.min(start + PAGE_SIZE, memberList.size());
for (int i = start; i < end; i++) {
String member = memberList.get(i);
Component line = Component.empty();
line = line.append(Component.text("§f" + member));
line = line.append(Component.text(" "));
line = line.append(Component.text("§c[移除]")
.hoverEvent(HoverEvent.showText(
Component.text("§c点击移除此成员")))
.clickEvent(ClickEvent.runCommand(
"/protect removemember " + land.name + " " + member)));
p.sendMessage(line);
}
}
// 分页
if (totalPages > 1) {
Component pagination = Component.empty();
if (page > 1) {
pagination = pagination.append(Component.text("§a[◀ 上一页]")
.clickEvent(ClickEvent.runCommand(
"/protect cli members " + land.name + " " + (page - 1))));
pagination = pagination.append(Component.text(" "));
}
pagination = pagination.append(Component.text("§7第" + page + "/" + totalPages + "页"));
if (page < totalPages) {
pagination = pagination.append(Component.text(" "));
pagination = pagination.append(Component.text("§a[下一页 ▶]")
.clickEvent(ClickEvent.runCommand(
"/protect cli members " + land.name + " " + (page + 1))));
}
p.sendMessage(pagination);
}
p.sendMessage(Component.empty()
.append(Component.text("§a[添加成员] ")
.clickEvent(ClickEvent.runCommand(
"/protect addvisitor " + land.name + " <玩家名>")))
.append(Component.text("§7§7点击后在聊天框输入玩家名")));
p.sendMessage(Component.empty()
.append(Component.text("§a[◀ 返回管理]")
.clickEvent(ClickEvent.runCommand(
"/protect cli manage " + land.name + " 1"))));
p.sendMessage(Component.text("§7§l───────────────────────────────"));
}
// ==================== 访客授权 ====================
public void showVisitorPerm(Player p, String landName, int page) {
AreaProtection.AreaConfig land = areaProtect.getLand(landName);
if (land == null) {
p.sendMessage(Component.text("§c领地不存在: " + landName));
return;
}
p.sendMessage(header("访客授权: " + landName));
showPermPage(p, land, page);
p.sendMessage(Component.empty()
.append(Component.text("§a[◀ 返回管理]")
.clickEvent(ClickEvent.runCommand(
"/protect cli manage " + land.name + " 1"))));
p.sendMessage(Component.text("§7§l───────────────────────────────"));
}
private void showPermPage(Player p, AreaProtection.AreaConfig land, int page) {
List<PermItem> perms = getPermList(land);
int totalPages = Math.max(1, (int) Math.ceil((double) perms.size() / PAGE_SIZE));
+21 -4
View File
@@ -80,6 +80,13 @@ public class AreaGUIManager implements Listener {
"",
"§e点击查看"));
// ★ 切换到CLI模式(位置42)
inv.setItem(42, createItem(Material.COMMAND_BLOCK, "§b§l切换到CLI模式",
"§7切换到命令行交互模式",
"§7权限管理更便捷,支持批量操作",
"",
"§e点击切换"));
// 返回按钮(位置49)
inv.setItem(49, createItem(Material.ARROW, "§c§l返回主菜单", ""));
@@ -447,6 +454,17 @@ public class AreaGUIManager implements Listener {
} else if (raw == 40) {
p.closeInventory();
p.sendMessage("§a文档链接: https://wiki.ypshidifu.cn");
} else if (raw == 42) {
// ★ 切换到CLI模式
p.closeInventory();
try {
plugin.getDb().setUiMode(p.getName(), 1);
} catch (Exception ignored) {}
if (plugin.areaCLIManager != null) {
plugin.areaCLIManager.showMainMenu(p);
} else {
p.sendMessage("§cCLI管理器未初始化");
}
} else if (raw == 49) {
plugin.getGui().openMain(p);
}
@@ -470,10 +488,9 @@ public class AreaGUIManager implements Listener {
if (event.isLeftClick() && !event.isShiftClick()) {
openLandManage(p, land.name);
} else if (event.isRightClick()) {
// 删除领地
areaProtect.deleteLand(land.name);
p.sendMessage("§a已删除领地: " + land.name);
openLandList(p, page);
// ★ 删除领地:走延迟确认流程
p.closeInventory();
p.performCommand("protect 删除 " + land.name);
} else if (event.isShiftClick() && event.isLeftClick()) {
// 传送
p.closeInventory();
+235 -32
View File
@@ -396,7 +396,6 @@ public class AreaProtection implements Listener {
migrateTxtToDb(); // ★ 迁移txt文件到数据库
loadAllAreas();
loadWhitelists();
loadAreaConfig();
recoverPendingEffects();
}
public void recoverPendingEffects() {
@@ -841,7 +840,12 @@ public class AreaProtection implements Listener {
// ★ 优先从数据库加载
loadAreasFromDb();
// ★ 兼容:如果没有从DB加载到数据,回退到txt(未迁移场景)
// ★ 自动迁移:如果DB有数据但txt文件仍存在,迁移txt中的白名单数据到DB后删除txt
if (!areas.isEmpty()) {
migrateTxtFilesToDb();
}
// ★ 兼容:如果没有从DB加载到数据,回退到txt(未迁移场景)并自动导入DB
if (areas.isEmpty()) {
File[] files = rootDir.listFiles(
(File d, String n) -> n.endsWith(".txt"));
@@ -851,14 +855,78 @@ public class AreaProtection implements Listener {
String name = f.getName().replace(".txt", "");
AreaConfig ac = parseArea(name, f);
areas.put(name, ac);
// ★ 自动写入数据库
saveAreaToDb(ac);
plugin.getLogger().info("[防护] 自动迁移txt→DB: " + name);
} catch (Exception e) {
plugin.getLogger().warning("[防护] 加载失败: " + f.getName());
}
}
// ★ 迁移完成后删除所有txt文件
if (!areas.isEmpty()) {
for (File f : files) {
if (f.delete()) {
plugin.getLogger().info("[防护] 已删除迁移后的txt: " + f.getName());
}
}
// 也删除白名单目录
File wlDir = new File(rootDir, "whitelists");
if (wlDir.exists() && wlDir.isDirectory()) {
File[] wlFiles = wlDir.listFiles();
if (wlFiles != null) {
for (File wf : wlFiles) {
wf.delete();
}
}
wlDir.delete();
}
plugin.getLogger().info("[防护] txt迁移完成,共导入 " + areas.size() + " 个领地");
}
}
}
}
/**
* 迁移txt文件到数据库(如果txt存在但DB已有数据,清理txt)
*/
private void migrateTxtFilesToDb() {
File[] files = rootDir.listFiles(
(File d, String n) -> n.endsWith(".txt"));
if (files == null || files.length == 0) return;
plugin.getLogger().info("[防护] 检测到 " + files.length + " 个残留txt文件,开始清理...");
for (File f : files) {
String name = f.getName().replace(".txt", "");
// 如果txt对应的领地不在DB中,先导入
if (!areas.containsKey(name)) {
try {
AreaConfig ac = parseArea(name, f);
areas.put(name, ac);
saveAreaToDb(ac);
plugin.getLogger().info("[防护] 迁移txt→DB: " + name);
} catch (Exception e) {
plugin.getLogger().warning("[防护] 迁移失败: " + f.getName());
}
}
// 删除txt
if (f.delete()) {
plugin.getLogger().info("[防护] 已删除: " + f.getName());
}
}
// 清理白名单目录
File wlDir = new File(rootDir, "whitelists");
if (wlDir.exists() && wlDir.isDirectory()) {
File[] wlFiles = wlDir.listFiles();
if (wlFiles != null) {
for (File wf : wlFiles) {
wf.delete();
}
}
wlDir.delete();
plugin.getLogger().info("[防护] 已清理白名单目录");
}
}
/**
* 从数据库加载所有领地配置
*/
@@ -3595,6 +3663,13 @@ public class AreaProtection implements Listener {
return Collections.unmodifiableMap(areas);
}
/**
* 检查领地是否有待删除请求
*/
public boolean hasPendingDelete(String areaName) {
return pendingDeletes.containsKey(areaName);
}
/**
* 获取指定玩家拥有的所有领地
*/
@@ -4423,7 +4498,10 @@ public class AreaProtection implements Listener {
"setowner", "addvisitor", "removevisitor", "listvisitors", "transfer",
"info", "setowner", "addvisitor", "removevisitor",
"listvisitors", "transfer", "shop",
"menu", "菜单", "sdf1debug", "testclear", "cli"
"menu", "菜单", "sdf1debug", "testclear", "cli",
"取消删除", "canceldelete",
"removemember", "addmember",
"config", "配置"
));
String first = original[0].toLowerCase();
@@ -4617,11 +4695,17 @@ public class AreaProtection implements Listener {
if (mode.equals("gui") || mode.equals("0")) {
plugin.getDb().setUiMode(p.getName(), 0);
p.sendMessage("§a已切换为 §6GUI §a模式");
p.sendMessage("§7下次输入 /protect 将打开GUI菜单");
// ★ 立即打开GUI
if (plugin.areaGUIManager != null) {
plugin.areaGUIManager.openMainMenu(p);
}
} else if (mode.equals("cli") || mode.equals("1")) {
plugin.getDb().setUiMode(p.getName(), 1);
p.sendMessage("§a已切换为 §6CLI §a模式");
p.sendMessage("§7下次输入 /protect 将显示文本帮助");
// ★ 立即显示CLI菜单
if (plugin.areaCLIManager != null) {
plugin.areaCLIManager.showMainMenu(p);
}
} else {
p.sendMessage("§c用法: /protect uimode <gui|cli>");
}
@@ -4662,12 +4746,36 @@ public class AreaProtection implements Listener {
}
plugin.areaCLIManager.showLandManage(p, args[2], permPage);
break;
case "members":
if (args.length < 3) {
p.sendMessage("§c用法: /protect cli members <领地名> [页码]");
break;
}
int memberPage = 1;
if (args.length >= 4) {
try { memberPage = Integer.parseInt(args[3]); } catch (Exception ignored) {}
}
plugin.areaCLIManager.showMemberList(p, args[2], memberPage);
break;
case "visitorperm":
if (args.length < 3) {
p.sendMessage("§c用法: /protect cli visitorperm <领地名> [页码]");
break;
}
int vPermPage = 1;
if (args.length >= 4) {
try { vPermPage = Integer.parseInt(args[3]); } catch (Exception ignored) {}
}
plugin.areaCLIManager.showVisitorPerm(p, args[2], vPermPage);
break;
case "toggle":
if (args.length < 4) {
p.sendMessage("§c用法: /protect cli toggle <领地名> <权限key>");
break;
}
plugin.areaCLIManager.togglePerm(p, args[2], args[3]);
// ★ 切换后刷新当前领地管理页面
plugin.areaCLIManager.showLandManage(p, args[2], 1);
break;
case "create":
// 跳转到创建命令
@@ -4704,9 +4812,38 @@ public class AreaProtection implements Listener {
p.sendMessage("§c区域已存在");
return true;
}
// ★ 检查每人领地数量上限
long playerLandCount = areas.values().stream()
.filter(a -> p.getName().equalsIgnoreCase(a.owner))
.count();
if (playerLandCount >= globalMaxLandsPerPlayer) {
p.sendMessage("§c§l[防护] §f已达领地上限!每人最多 §e" + globalMaxLandsPerPlayer + " §f个领地");
return true;
}
Location l1 = pos1.get(u);
Location l2 = pos2.get(u);
// ★ 领地计费:计算面积并扣除余额
int width = Math.abs(l2.getBlockX() - l1.getBlockX()) + 1;
int length = Math.abs(l2.getBlockZ() - l1.getBlockZ()) + 1;
int area = width * length;
int cost = area * globalCreatePricePerSqm;
if (cost > 0) {
BondManager bm = plugin.getBonds();
if (bm != null) {
int balance = bm.getBonds(p.getName());
if (balance < cost) {
p.sendMessage("§c§l[防护] §f余额不足!需要 §e" + cost + " §f债券,当前余额 §e" + balance);
return true;
}
if (!bm.deductBonds(p.getName(), cost, "land_create", p.getName(), p.getName(), "创建领地: " + areaName + " (" + area + "㎡×" + globalCreatePricePerSqm + ")")) {
p.sendMessage("§c§l[防护] §f扣费失败,请稍后重试");
return true;
}
p.sendMessage("§a§l[防护] §f创建领地扣除 §e" + cost + " §f债券(" + area + "㎡×" + globalCreatePricePerSqm + "/㎡)");
}
}
// ★ 创建AreaConfig并保存到数据库
AreaConfig ac = new AreaConfig();
ac.name = areaName;
@@ -4720,31 +4857,6 @@ public class AreaProtection implements Listener {
ac.yMax = 255;
saveAreaToDb(ac);
// 兼容:同时创建txt文件
File f = new File(rootDir, areaName + ".txt");
try {
PrintWriter pw = new PrintWriter(
new OutputStreamWriter(
new FileOutputStream(f),
StandardCharsets.UTF_8));
pw.println("# 区域: " + areaName);
pw.println("起点: " + l1.getWorld().getName()
+ "," + l1.getBlockX()
+ "," + l1.getBlockZ());
pw.println("终点: " + l2.getWorld().getName()
+ "," + l2.getBlockX()
+ "," + l2.getBlockZ());
pw.println("高度范围: 0-255");
pw.println();
pw.println("# 禁止放置方块");
pw.println("# 禁止破坏方块");
pw.println("# 禁止PVP");
pw.println("# 效果: 夜视 1 999");
pw.println("# 进入提示: 欢迎来到保护区");
pw.println("# 离开提示: 已离开保护区");
pw.close();
} catch (IOException ignored) {}
p.sendMessage("§a§l[防护] §f区域 "
+ areaName + " 已创建 (owner: " + p.getName() + ")");
loadAllAreas();
@@ -4938,6 +5050,53 @@ public class AreaProtection implements Listener {
return true;
}
// ===== config 全局配置 =====
if (sub.equals("config") || sub.equals("配置")) {
if (!isAreaAdmin(sender)) {
sender.sendMessage("§c需要管理员权限");
return true;
}
if (args.length < 3) {
// 查看配置
sender.sendMessage("§e§l==== 全局配置 ====");
sender.sendMessage("§a创建价格(每㎡): §f" + globalCreatePricePerSqm);
sender.sendMessage("§a每人最大领地数: §f" + globalMaxLandsPerPlayer);
sender.sendMessage("§7用法: /protect config <key> <value>");
sender.sendMessage("§7可用key: create_price, max_lands, default_height, peace_duration");
return true;
}
String key = args[1];
String value = args[2];
try {
switch (key) {
case "create_price":
globalCreatePricePerSqm = Integer.parseInt(value);
updateAreaConfig("create_price_per_sqm", value);
sender.sendMessage("§a创建价格已更新为: " + value + "/㎡");
break;
case "max_lands":
globalMaxLandsPerPlayer = Integer.parseInt(value);
updateAreaConfig("max_lands_per_player", value);
sender.sendMessage("§a每人最大领地数已更新为: " + value);
break;
case "default_height":
updateAreaConfig("default_height", value);
sender.sendMessage("§a默认高度已更新为: " + value);
break;
case "peace_duration":
updateAreaConfig("peace_mode_max_duration", value);
sender.sendMessage("§a和平模式最大时长已更新为: " + value + "秒");
break;
default:
sender.sendMessage("§c未知配置项: " + key);
break;
}
} catch (NumberFormatException e) {
sender.sendMessage("§c值必须是数字");
}
return true;
}
// ===== 重载 =====
if (sub.equals("重载") || sub.equals("reload")) {
reload();
@@ -5256,6 +5415,30 @@ public class AreaProtection implements Listener {
return true;
}
// ===== 取消删除 =====
if (sub.equals("取消删除") || sub.equals("canceldelete")) {
if (!(sender instanceof Player)) {
sender.sendMessage("§c仅玩家可用");
return true;
}
Player p = (Player) sender;
if (args.length < 2) {
sender.sendMessage("§e用法: /protect 取消删除 <领地名>");
return true;
}
String areaName = args[1];
String resolved = resolveAreaName(areaName);
if (resolved != null) areaName = resolved;
PendingDelete pd = pendingDeletes.remove(areaName);
if (pd == null) {
sender.sendMessage("§c没有待删除的领地: " + areaName);
return true;
}
pd.task.cancel();
sender.sendMessage("§a§l[防护] §f已取消删除领地: §e" + areaName);
return true;
}
// ===== add 加白名单 =====
if (sub.equals("add")) {
if (args.length == 2) {
@@ -5799,8 +5982,8 @@ public class AreaProtection implements Listener {
return true;
}
// ===== removevisitor 移除访客 =====
if (sub.equals("removevisitor")) {
// ===== removevisitor / removemember 移除访客/成员 =====
if (sub.equals("removevisitor") || sub.equals("removemember")) {
if (!(sender instanceof Player)) {
sender.sendMessage("§c仅玩家可用");
return true;
@@ -6302,6 +6485,25 @@ public class AreaProtection implements Listener {
return seconds + "秒";
}
/**
* 更新area_config表中的配置值
*/
private void updateAreaConfig(String key, String value) {
if (dbConnection == null) return;
try {
PreparedStatement ps = dbConnection.prepareStatement(
"INSERT INTO area_config (key, value) VALUES (?, ?) "
+ "ON CONFLICT(key) DO UPDATE SET value = ?");
ps.setString(1, key);
ps.setString(2, value);
ps.setString(3, value);
ps.executeUpdate();
ps.close();
} catch (Exception e) {
plugin.getLogger().warning("[防护] 更新配置失败: " + e.getMessage());
}
}
/**
* 检查玩家的访客权限是否有效(未过期)
*/
@@ -6353,6 +6555,7 @@ public class AreaProtection implements Listener {
sendClickableHelp(s, "/protect tempon", "临时边框(15秒)");
sendClickableHelp(s, "/protect list", "列出白名单(需在领地内)");
sendClickableHelp(s, "/protect listitem", "列出物品黑名单");
sendClickableHelp(s, "/protect config", "查看/修改全局配置");
s.sendMessage("§e§l---- 权限管理 ----");
sendClickableHelp(s, "/protect setowner", "设置当前领地所有者(管理员)");
+60 -4
View File
@@ -101,6 +101,20 @@ public class WebManager {
// 库存高频拉取去重标志
private final AtomicBoolean stockFastPollTaskPending = new AtomicBoolean(false);
// ★ Web请求专用线程池(HTTP操作不阻塞DB队列)
private final java.util.concurrent.ExecutorService webExecutor =
java.util.concurrent.Executors.newFixedThreadPool(3, r -> {
Thread t = new Thread(r, "sdf1-web-worker");
t.setDaemon(true);
return t;
});
// ★ SSL断路器:连续失败超过阈值后暂停轮询,避免DB队列积压
private static final int SSL_CIRCUIT_THRESHOLD = 5; // 连续失败5次触发断路
private static final long SSL_CIRCUIT_COOLDOWN_MS = 60000; // 断路冷却60秒
private volatile int sslConsecutiveFailures = 0;
private volatile long sslCircuitOpenUntil = 0;
public WebManager(Main plugin) {
this.plugin = plugin;
this.config = plugin.getConfig2();
@@ -217,6 +231,36 @@ public class WebManager {
}
}
// ==================== SSL断路器 ====================
/**
* 检查SSL断路器是否开启(跳过轮询请求)
*/
private boolean isCircuitOpen() {
if (sslCircuitOpenUntil == 0) return false;
if (System.currentTimeMillis() < sslCircuitOpenUntil) return true;
// 冷却期结束,允许重试
sslCircuitOpenUntil = 0;
sslConsecutiveFailures = 0;
plugin.getLogger().info("[Web通信] SSL断路器冷却结束,恢复轮询");
return false;
}
/**
* 提交Web任务到专用线程池(HTTP操作不阻塞DB队列)
* HTTP完成后再把DB写入提交到DB队列
*/
private void submitWebTask(String name, Runnable action) {
if (isCircuitOpen()) return;
webExecutor.submit(() -> {
try {
action.run();
} catch (Exception e) {
plugin.getLogger().warning("[Web线程] " + name + " 异常: " + e.getClass().getSimpleName() + " - " + e.getMessage());
}
});
}
// ==================== 工具方法 ====================
/**
@@ -273,6 +317,7 @@ public class WebManager {
.build();
HttpResponse<String> resp = cfHttpClient.send(req, HttpResponse.BodyHandlers.ofString());
if (resp.statusCode() >= 200 && resp.statusCode() < 300) {
sslConsecutiveFailures = 0; // 成功 → 重置断路器
return resp.body();
}
// ★ 500时记录响应体(PHP错误信息在body里)
@@ -282,6 +327,14 @@ public class WebManager {
plugin.getLogger().warning("[Web通信] GET HTTP " + resp.statusCode() + ": " + shortUrl + " | 响应: " + shortBody);
return null;
} catch (Exception e) {
// ★ SSL断路器:跟踪连续失败
if (e.getMessage() != null && e.getMessage().contains("handshake")) {
sslConsecutiveFailures++;
if (sslConsecutiveFailures >= SSL_CIRCUIT_THRESHOLD) {
sslCircuitOpenUntil = System.currentTimeMillis() + SSL_CIRCUIT_COOLDOWN_MS;
plugin.getLogger().warning("[Web通信] SSL断路器触发: 连续失败" + sslConsecutiveFailures + "次,暂停轮询60秒");
}
}
plugin.getLogger().warning("[Web通信] GET异常: " + e.getClass().getSimpleName() + ": " + e.getMessage());
return null;
}
@@ -533,6 +586,8 @@ public class WebManager {
public void shutdown() {
stop();
stopDbWorker();
// ★ 关闭Web线程池
webExecutor.shutdownNow();
// 关闭回调服务器
if (callbackServer != null) {
try {
@@ -2963,7 +3018,8 @@ public class WebManager {
// 检查是否有玩家正在登录
if (!hasPendingLogins()) return;
submitDbTask("登录轮询-pollWebLoginConfirmations", () -> {
// ★ HTTP在webExecutor执行,不阻塞DB队列
submitWebTask("登录轮询-pollWebLoginConfirmations", () -> {
try {
pollWebLoginConfirmations();
} catch (Exception e) {
@@ -2974,7 +3030,7 @@ public class WebManager {
}
}
});
submitDbTask("登录轮询-pollWebLoginRequests", () -> {
submitWebTask("登录轮询-pollWebLoginRequests", () -> {
try {
pollWebLoginRequests();
} catch (Exception e) {
@@ -4083,11 +4139,11 @@ public class WebManager {
* 使用随机间隔避免与其他轮询任务同时执行
*/
private void scheduleRegisterRequestPoll() {
// ★ 注册请求轮询:通过DB队列串行化,高优先级
// ★ 注册请求轮询:HTTP在webExecutor执行,DB写入才走DB队列
new BukkitRunnable() {
@Override
public void run() {
submitDbTask("注册轮询-pollWebRegisterRequests", () -> {
submitWebTask("注册轮询-pollWebRegisterRequests", () -> {
try {
pollWebRegisterRequests();
} catch (Exception e) {