修复: 自动登录逻辑重构+轮询间隔增大降低DB锁

This commit is contained in:
2026-06-25 18:03:45 +08:00
parent 2867c7614a
commit b6863c45c7
2 changed files with 36 additions and 137 deletions
+18 -121
View File
@@ -1742,11 +1742,13 @@ public class Main extends JavaPlugin
// ===== 快速重连三层检查(严格按流程图) =====
// ★ 检查点1:检查玩家是否有Java手动登录记录( loggedIn 集合)
// 注意:loggedIn在onQuit时会被清除,因此快速重连时检查点1通常失败
// 这是正常行为 — 快速重连走检查点2(isWebLoginVerified)通过IP+内存验证
if (loggedIn.contains(name)) {
getLogger().info("[Web登录] 检查点1通过: 玩家 " + name + " 有Java手动登录记录,放行");
// 继续走正常登录流程(不中断)
} else {
getLogger().info("[Web登录] 检查点1失败: 玩家 " + name + " 无Java手动登录记录");
getLogger().info("[Web登录] 检查点1失败: 玩家 " + name + " 无Java手动登录记录(正常,已在onQuit清除)");
}
// 辅助操作:生成Web Token + 同步PHP注册数据
@@ -1757,137 +1759,32 @@ public class Main extends JavaPlugin
}
}
// ★ 检查点2:检查服务器内存中是否有该玩家来自PHP的验证请求
if (webManager != null && webManager.isWebLoginVerified(name)) {
getLogger().info("[Web登录] 检查点2通过: 玩家 " + name + " 内存中有PHP验证记录,自动登录");
// ★ 同IP判断:只作为开启自动登录验证的钥匙,不作为自动登录依据
boolean sameIP = canAutoLogin(p);
if (!sameIP) {
// 不同IP → 跳过检查点2,直接强制密码验证
getLogger().info("[Web登录] 同IP检查: 玩家 " + name + " IP不同,跳过快速重连验证");
} else {
getLogger().info("[Web登录] 同IP检查: 玩家 " + name + " IP相同,允许快速重连验证");
}
// ★ 检查点2:只有同IP才检查服务器内存中是否有该玩家来自PHP的验证请求
if (sameIP && webManager != null && webManager.isWebLoginVerified(name)) {
getLogger().info("[Web登录] 检查点2通过: 玩家 " + name + " 同IP且内存中有PHP验证记录,自动登录");
webManager.clearWebLoginVerified(name);
autoLogin(p, "web_password");
return; // 检查点2通过 → 直接放行,不继续后面流程
} else {
getLogger().info("[Web登录] 检查点2失败: 玩家 " + name + " 内存中无PHP验证记录");
if (sameIP) {
getLogger().info("[Web登录] 检查点2失败: 玩家 " + name + " 同IP但内存中无PHP验证记录");
}
}
// ★ 检查点3:上述2路验证都失败 → 强制重新验证密码(走正常登录流程)
getLogger().info("[Web登录] 检查点3: 上述2路验证都失败,强制重新验证密码");
// 强制重发资源包(清掉客户端拒绝记录)
Bukkit.getScheduler()
.runTaskLater(this, () -> {
// ★ 资源包:只发送一次,延迟3秒确保客户端就绪
if (radio != null) {
final Player jp = p;
Bukkit.getScheduler().runTaskLater(
this, () -> {
if (jp.isOnline()) {
radio.sendResourcePack(jp);
}
}, 60L);
}
}, 20L);
// 重置聊天输入状态(防止上次退出时残留)
chatInput.reset(p);
if (needsPasswordChange.contains(name))
needsPasswordChange.remove(name);
// 改完了直接打包,要打sdf1一起
lastActivity.put(p.getUniqueId(),
System.currentTimeMillis());
if (needsPasswordChange.contains(name))
needsPasswordChange.remove(name);
boolean isBedrock =
verification.isBedrockPlayer(p);
boolean isOnlineMode =
verification.isOnlineMode();
// ★ 登录阶段始终允许飞行,防止悬空被踢出
p.setAllowFlight(true);
if (!db.userExists(name)) {
if (config.maxAccountsPerIP > 0
&& ip != null
&& !ipGroup.canRegister(ip)) {
p.kickPlayer(
buildIPKickMessage(p, ip));
return;
}
}
if (!"manual".equals(config.approvalMode)
&& !db.userExists(name)) {
AccountRequestManager.Request req =
accountRequest.createRequest(
name, name, ip);
if (req != null) {
p.kickPlayer(
config.msg("need_approval")
.replace("{id}",
String.valueOf(
req.id)));
return;
}
}
// [ADDED] 资源包(延迟发送,不强制)
// ★ 资源包:只发送一次,延迟3秒确保客户端就绪
if (radio != null) {
final Player jp = p;
Bukkit.getScheduler().runTaskLater(
this, () -> {
if (jp.isOnline()) {
radio.sendResourcePack(jp);
}
}, 60L);
}
if (isOnlineMode) {
autoLogin(p, "premium");
return;
}
if (isBedrock) {
autoLogin(p, "bedrock");
return;
}
// ★ 关键安全修复:快速重连也必须检查Java是否有Web登录验证记录
// 否则PHP假密码登录成功后,玩家快速重连会被盲目放行
if (webManager != null && webManager.isWebLoginVerified(name)) {
getLogger().info("[Web登录] 快速重连检查: 玩家 " + name + " 有Java验证记录,放行");
webManager.clearWebLoginVerified(name);
autoLogin(p, "web_password");
return;
} else {
getLogger().info("[Web登录] 快速重连检查: 玩家 " + name + " 无Java验证记录,拒绝快速重连");
}
// ★ 关键安全修复:快速重连时必须检查Java是否有Web登录验证记录
// 如果玩家在Web端通过Java密码验证登录过,5分钟内重连直接放行
// 如果没有Java验证记录 → 拒绝快速重连(防止PHP假密码绕过)
if (webManager != null && webManager.isWebLoginVerified(name)) {
getLogger().info("[Web登录] 快速重连检查: 玩家 " + name + " 有Java验证记录,放行");
webManager.clearWebLoginVerified(name);
autoLogin(p, "web_password");
return;
} else {
getLogger().info("[Web登录] 快速重连检查: 玩家 " + name + " 无Java验证记录,拒绝快速重连");
}
if (canAutoLogin(p)) {
autoLogin(p, "ip_reconnect");
return;
}
afk.remove(p.getUniqueId());
afk.recordAction(p.getUniqueId());
hideInventoryDelayed(p, 0);
// [ADDED] 发送资源包
// ★ 资源包:只发送一次,延迟3秒确保客户端就绪
if (radio != null) {
final Player jp = p;
Bukkit.getScheduler().runTaskLater(
this, () -> {
if (jp.isOnline()) {
radio.sendResourcePack(jp);
}
}, 60L);
}
// [ADDED] 发送资源包(延迟5秒确保客户端就绪)
// ★ 资源包:只发送一次,延迟3秒确保客户端就绪
if (radio != null) {
final Player jp = p;
+18 -16
View File
@@ -1357,11 +1357,11 @@ public class WebManager {
checkSyncNotify();
}
}.runTaskTimerAsynchronously(plugin, 20L * randomIntervalSeconds(5), 20L * randomIntervalSeconds(5)); // activeSync 5±5秒
}.runTaskTimerAsynchronously(plugin, 20L * randomIntervalSeconds(10), 20L * randomIntervalSeconds(10)); // activeSync调度 10±3秒
}
/**
* 交易高频轮询:每5秒检查PHP端是否有待处理交易
* 交易高频轮询:每15秒(±5)检查PHP端是否有待处理交易
* 检测到pending交易时立即拉取,不等60-90秒的全量同步
*/
/**
@@ -1424,17 +1424,19 @@ public class WebManager {
}
}
}
}.runTaskTimerAsynchronously(plugin, 20L * randomIntervalSeconds(8), 20L * randomIntervalSeconds(8)); // 交易轮询 8±5秒
}.runTaskTimerAsynchronously(plugin, 20L * randomIntervalSeconds(15), 20L * randomIntervalSeconds(15)); // 交易轮询 15±5秒
}
/**
* 生成定时器随机间隔(基础值 ±5秒)
* 生成定时器随机间隔(比例偏移,基础值越大偏移越大)
* 偏移量 = ±max(2, base/3),确保小基数也有随机性,大基数不过度离散
* 每个请求都是独立随机数,避开并发导致的SQL锁死
* @param baseSeconds 基础秒数
* @return 随机秒数 [base-5, base+5],最小1秒
* @return 随机秒数 [base-offset, base+offset],最小1秒
*/
private long randomIntervalSeconds(long baseSeconds) {
long offset = (long) (Math.random() * 11) - 5; // -5 ~ +5
long maxOffset = Math.max(2, baseSeconds / 3);
long offset = (long) (Math.random() * (maxOffset * 2 + 1)) - maxOffset;
return Math.max(1, baseSeconds + offset);
}
@@ -1463,7 +1465,7 @@ public class WebManager {
private volatile boolean shopStockPulling = false;
/**
* 库存高频轮询:每5秒请求Web端库存变更检测接口
* 库存高频轮询:每15秒(±5)请求Web端库存变更检测接口
* 无改动跳过,有改动立即拉取完整库存并更新游戏
* 60~90秒的全量同步定时器作为兜底
*/
@@ -1555,8 +1557,8 @@ public class WebManager {
shopStockPollFailCount++;
}
}
}.runTaskTimerAsynchronously(plugin, 20L * randomIntervalSeconds(8), 20L * randomIntervalSeconds(8)); // 库存轮询 8±5秒
plugin.getLogger().info("[Web通信] 库存高频轮询已启动(每5秒)");
}.runTaskTimerAsynchronously(plugin, 20L * randomIntervalSeconds(15), 20L * randomIntervalSeconds(15)); // 库存轮询 15±5秒
plugin.getLogger().info("[Web通信] 库存高频轮询已启动(每15秒±5)");
}
/**
@@ -1625,14 +1627,14 @@ public class WebManager {
// 静默,避免刷屏
}
}
}.runTaskTimerAsynchronously(plugin, 20L * randomIntervalSeconds(2), 20L * randomIntervalSeconds(2)); // CDK轮询 2±10秒(最小1秒)
plugin.getLogger().info("[Web通信] CDK远程验证轮询已启动(每2秒)");
}.runTaskTimerAsynchronously(plugin, 20L * randomIntervalSeconds(8), 20L * randomIntervalSeconds(8)); // CDK轮询 8±3秒
plugin.getLogger().info("[Web通信] CDK远程验证轮询已启动(每8秒±3)");
}
// ==================== 领地数据即时同步 ====================
/**
* ★ 领地数据即时同步:每10秒从PHP拉取最新领地配置
* ★ 领地数据即时同步:每15秒(±5)从PHP拉取最新领地配置
* 配合60~90秒全量同步作为兜底
*/
private volatile boolean landSyncPulling = false;
@@ -1688,8 +1690,8 @@ public class WebManager {
landSyncFailCount++;
}
}
}.runTaskTimerAsynchronously(plugin, 20L * randomIntervalSeconds(10), 20L * randomIntervalSeconds(10)); // 领地同步 10±10秒
plugin.getLogger().info("[Web通信] 领地配置即时同步已启动(每10秒)");
}.runTaskTimerAsynchronously(plugin, 20L * randomIntervalSeconds(15), 20L * randomIntervalSeconds(15)); // 领地同步 15±5秒
plugin.getLogger().info("[Web通信] 领地配置即时同步已启动(每15秒±5)");
}
/**
@@ -4847,7 +4849,7 @@ public class WebManager {
// 启动注册请求轮询
scheduleRegisterRequestPoll();
plugin.getLogger().info("[Web通信] Web注册请求轮询已启动(30~90秒随机间隔,静默运行)");
plugin.getLogger().info("[Web通信] Web注册请求轮询已启动(12±4秒随机间隔,静默运行)");
}
/**
@@ -4872,7 +4874,7 @@ public class WebManager {
}
});
}
}.runTaskTimer(plugin, 20L * randomIntervalSeconds(3), 20L * randomIntervalSeconds(3)); // 注册轮询 3±10秒(最小1秒)
}.runTaskTimer(plugin, 20L * randomIntervalSeconds(12), 20L * randomIntervalSeconds(12)); // 注册轮询 12±4秒
}
/**