修复领地权限系统4大bug:DB列缺失+成员不同步+管理员继承+login.db校验

1. AreaProtection.updateVisitorPermFromWeb: INSERT语句去掉不存在的land_name/synced_at列,修复"table area_land_permissions has no column named land_name"
2. AreaProtection.removePlayerFromAreaWhitelist: 移除成员时同步删除area_land_permissions记录,确保admin身份一并清除,不再继承
3. AreaProtection.removeLandMember: GUI移除成员也同步删除area_land_permissions
4. AreaProtection.insertLandPermission: 新增公共方法,PHP添加成员时写入权限表
5. AreaProtection.handlePendingAddMemberInput: 添加成员前校验login.db(DatabaseManager.userExists),拒绝不存在的玩家
6. WebManager: 新增add_visitor/remove_visitor变更类型处理,PHP添加/移除成员后Java自动同步白名单+权限表
This commit is contained in:
2026-07-11 13:36:26 +08:00
parent 5ec61a1d2e
commit e08a6bc4c7
2 changed files with 94 additions and 6 deletions
+63 -6
View File
@@ -2569,6 +2569,28 @@ public class AreaProtection implements Listener {
}
}
/**
* 插入一条领地权限记录(角色为 member/visitor),已存在则忽略
* 用于 PHP 端添加成员时同步到 Java
*/
public void insertLandPermission(String landName, String playerName, String role) {
int landId = getLandIdFromDb(landName);
if (landId <= 0 || dbConnection == null) return;
try {
PreparedStatement stmt = dbConnection.prepareStatement(
"INSERT OR IGNORE INTO area_land_permissions (land_id, player_name, role, permissions, granted_at) " +
"VALUES (?, ?, ?, '', ?)");
stmt.setInt(1, landId);
stmt.setString(2, playerName);
stmt.setString(3, role != null && !role.isEmpty() ? role : "visitor");
stmt.setLong(4, System.currentTimeMillis() / 1000);
stmt.executeUpdate();
stmt.close();
} catch (SQLException e) {
// 忽略
}
}
/**
* ★ 核心方法:获取玩家的effective deny状态
* 优先级:per-player权限 > 领地默认权限
@@ -4222,6 +4244,8 @@ public class AreaProtection implements Listener {
}
saveWhitelists();
}
// ★ 同步删除 area_land_permissions 记录(确保 admin 身份一并清除)
removePlayerFromAreaWhitelist(landName, playerName);
}
/**
@@ -8510,14 +8534,14 @@ public class AreaProtection implements Listener {
// 更新数据库中的自定义权限(同时写回 effectiveRole,确保新记录角色正确)
PreparedStatement ps = dbConnection.prepareStatement(
"INSERT INTO area_land_permissions (land_id, land_name, player_name, role, permissions, granted_at, synced_at) " +
"VALUES (?, ?, ?, ?, ?, 0, 0) " +
"INSERT INTO area_land_permissions (land_id, player_name, role, permissions, granted_at) " +
"VALUES (?, ?, ?, ?, ?) " +
"ON CONFLICT(land_id, player_name) DO UPDATE SET permissions = excluded.permissions, role = excluded.role");
ps.setInt(1, landId);
ps.setString(2, lname);
ps.setString(3, playerName);
ps.setString(4, effectiveRole);
ps.setString(5, permsJson);
ps.setString(2, playerName);
ps.setString(3, effectiveRole);
ps.setString(4, permsJson);
ps.setLong(5, System.currentTimeMillis() / 1000);
ps.executeUpdate();
ps.close();
}
@@ -8762,6 +8786,14 @@ public class AreaProtection implements Listener {
return true;
}
// ★ 校验玩家是否在login.db中注册(防止添加不存在的玩家)
DatabaseManager dbMgr = plugin.getDb();
if (dbMgr != null && !dbMgr.userExists(playerName)) {
p.sendMessage("§c§l[添加成员] §f玩家 " + playerName + " 不存在(未在login.db中注册)");
p.sendMessage("§7请确认玩家名是否正确,或输入 cancel 取消");
return true;
}
// 添加成员
addLandMember(landName, playerName);
p.sendMessage("§a§l[添加成员] §f已成功添加 " + playerName + " 为领地成员");
@@ -9603,6 +9635,31 @@ public class AreaProtection implements Listener {
boolean ok = wl.remove(playerName);
if (!ok) ok = wl.remove(playerName.toLowerCase());
if (ok) saveWhitelists();
// ★ 移除成员时,同步删除 area_land_permissions 记录
// 确保 admin 身份一并清除,不再继承
if (dbConnection != null) {
try {
int landId = getLandIdFromDb(areaName);
if (landId > 0) {
PreparedStatement ps = dbConnection.prepareStatement(
"DELETE FROM area_land_permissions WHERE land_id = ? AND player_name = ?");
ps.setInt(1, landId);
ps.setString(2, playerName);
ps.executeUpdate();
ps.close();
// 同时删小写
PreparedStatement ps2 = dbConnection.prepareStatement(
"DELETE FROM area_land_permissions WHERE land_id = ? AND player_name = ?");
ps2.setInt(1, landId);
ps2.setString(2, playerName.toLowerCase());
ps2.executeUpdate();
ps2.close();
}
} catch (SQLException e) {
plugin.getLogger().warning("[防护] 移除成员时清理权限失败: " + e.getMessage());
}
}
return ok;
}
+31
View File
@@ -3886,6 +3886,37 @@ public class WebManager {
}
break;
}
case "add_visitor": {
// ★ PHP端添加成员 → 添加到Java白名单+权限表
String playerName2 = changeData.containsKey("player")
? String.valueOf(changeData.get("player")) : targetName;
String landName2 = String.valueOf(changeData.getOrDefault("land_name", ""));
String role2 = String.valueOf(changeData.getOrDefault("role", "visitor"));
if (!playerName2.isEmpty() && !landName2.isEmpty()) {
areaProtect.addPlayerToAreaWhitelist(landName2, playerName2);
// 写入area_land_permissions表:admin走setLandAdmin,其他角色走INSERT OR IGNORE
if ("admin".equalsIgnoreCase(role2)) {
areaProtect.setLandAdmin(landName2, playerName2, true);
} else {
areaProtect.insertLandPermission(landName2, playerName2, role2);
}
plugin.getLogger().info("[Web通信] PHP端添加成员: " + playerName2 + " → " + landName2 + " role=" + role2);
applied = true;
}
break;
}
case "remove_visitor": {
// ★ PHP端移除成员 → 从Java白名单+权限表删除
String playerName3 = changeData.containsKey("player")
? String.valueOf(changeData.get("player")) : targetName;
String landName3 = String.valueOf(changeData.getOrDefault("land_name", ""));
if (!playerName3.isEmpty() && !landName3.isEmpty()) {
areaProtect.removePlayerFromAreaWhitelist(landName3, playerName3);
plugin.getLogger().info("[Web通信] PHP端移除成员: " + playerName3 + " ← " + landName3);
applied = true;
}
break;
}
case "land_field_change": {
String field = String.valueOf(changeData.getOrDefault("field", ""));
String value = String.valueOf(changeData.getOrDefault("value", ""));