第20轮修复:①移除WorldCreator.keepSpawnInMemory(false)(Paper26.x已移除此API,导致/pvp join NoSuchMethodError崩溃)②访客授权GUI/CLI补全传送权限(openVisitorPerm/getPermList/toggle三处均缺传送项,导致4页翻完无传送选项);AreaConfig新增allowVisitorTeleport字段+DB自动迁移ALTER TABLE+canTeleportToLand检查该字段

This commit is contained in:
2026-07-08 20:41:09 +08:00
parent 612ec7a66b
commit ee22930efd
4 changed files with 26 additions and 4 deletions
@@ -547,6 +547,7 @@ public class AreaCLIManager {
case "animal_feed": oldState = land.denyAnimalFeeding; land.denyAnimalFeeding = !land.denyAnimalFeeding; break;
case "mob_attack": oldState = land.denyMobAttack; land.denyMobAttack = !land.denyMobAttack; break;
case "glowing": oldState = land.denyGlowing; land.denyGlowing = !land.denyGlowing; break;
case "visitor_teleport": oldState = land.allowVisitorTeleport; land.allowVisitorTeleport = !land.allowVisitorTeleport; break;
case "peace_mode": oldState = land.peaceMode; land.peaceMode = !land.peaceMode; break;
case "public_building": oldState = land.isPublicBuilding; land.isPublicBuilding = !land.isPublicBuilding; break;
}
@@ -604,6 +605,7 @@ public class AreaCLIManager {
case "glowing": return "denyGlowing";
case "peace_mode": return "peaceMode";
case "public_building": return "isPublicBuilding";
case "visitor_teleport": return "allowVisitorTeleport";
case "tp": return "allowTeleport";
default: return shortKey;
}
@@ -659,6 +661,7 @@ public class AreaCLIManager {
perms.add(new PermItem("animal_feed", land.denyAnimalFeeding ? "禁止投喂" : "允许投喂动物", !land.denyAnimalFeeding));
perms.add(new PermItem("mob_attack", land.denyMobAttack ? "禁止攻击生物" : "允许攻击生物", !land.denyMobAttack));
perms.add(new PermItem("glowing", "玩家发光", !land.denyGlowing));
perms.add(new PermItem("visitor_teleport", "传送", land.allowVisitorTeleport));
perms.add(new PermItem("peace_mode", "和平模式", land.peaceMode));
perms.add(new PermItem("public_building", "公共建筑设施", land.isPublicBuilding));
return perms;
@@ -698,6 +701,7 @@ public class AreaCLIManager {
case "glowing": return "玩家发光";
case "peace_mode": return "和平模式";
case "public_building": return "公共建筑设施";
case "visitor_teleport": return "传送";
case "tp": return "传送";
default: return key;
}
@@ -1185,6 +1189,9 @@ public class AreaCLIManager {
case "denyAnimalFeeding": return land.denyAnimalFeeding;
case "denyGlowing": return land.denyGlowing;
case "peaceMode": return land.peaceMode;
case "isPublicBuilding": return land.isPublicBuilding;
case "allowVisitorTeleport": return land.allowVisitorTeleport;
case "allowTeleport": return false; // per-player 默认关闭
default: return false;
}
}
@@ -660,6 +660,7 @@ public class AreaGUIManager implements Listener {
perms.add(new PermEntry(land.denyAnimalFeeding ? "禁止投喂" : "允许投喂动物", !land.denyAnimalFeeding));
perms.add(new PermEntry(land.denyMobAttack ? "禁止攻击生物" : "允许攻击生物", !land.denyMobAttack));
perms.add(new PermEntry("玩家发光", !land.denyGlowing));
perms.add(new PermEntry("传送", land.allowVisitorTeleport));
perms.add(new PermEntry("和平模式", land.peaceMode));
perms.add(new PermEntry("公共建筑设施", land.isPublicBuilding));
return perms;
@@ -718,6 +719,7 @@ public class AreaGUIManager implements Listener {
case "禁止投喂": case "允许投喂动物": land.denyAnimalFeeding = !land.denyAnimalFeeding; break;
case "禁止攻击生物": case "允许攻击生物": land.denyMobAttack = !land.denyMobAttack; break;
case "玩家发光": land.denyGlowing = !land.denyGlowing; break;
case "传送": land.allowVisitorTeleport = !land.allowVisitorTeleport; break;
case "和平模式": land.peaceMode = !land.peaceMode; break;
case "公共建筑设施": land.isPublicBuilding = !land.isPublicBuilding; break;
}
+14 -2
View File
@@ -882,6 +882,12 @@ public class AreaProtection implements Listener {
// ★ 优先从数据库加载
loadAreasFromDb();
// ★ 自动迁移:确保 allow_visitor_teleport 列存在(2026-07-08 新增)
try {
dbConnection.createStatement().execute("ALTER TABLE area_lands ADD COLUMN allow_visitor_teleport INTEGER DEFAULT 0");
plugin.getLogger().info("[防护] 已添加 allow_visitor_teleport 列");
} catch (Exception ignored) { /* 列已存在则忽略 */ }
// ★ 自动迁移:如果DB有数据但txt文件仍存在,迁移txt中的白名单数据到DB后删除txt
if (!areas.isEmpty()) {
migrateTxtFilesToDb();
@@ -1009,6 +1015,7 @@ public class AreaProtection implements Listener {
try { ac.warpPitch = rs.getFloat("warp_pitch"); } catch (Exception ignored) {}
try { ac.warpWorld = rs.getString("warp_world"); } catch (Exception ignored) {}
try { ac.isPublicBuilding = rs.getInt("is_public_building") == 1; } catch (Exception ignored) {}
try { ac.allowVisitorTeleport = rs.getInt("allow_visitor_teleport") == 1; } catch (Exception ignored) {}
ac.peaceMode = rs.getInt("peace_mode") == 1;
ac.peaceModeDuration = rs.getInt("peace_mode_duration") * 1000; // 转毫秒
ac.peaceWhitelist = new HashSet<>(splitToList(rs.getString("peace_whitelist")));
@@ -2261,6 +2268,8 @@ public class AreaProtection implements Listener {
if (isAreaAdmin(p)) return true;
if (p.getName().equalsIgnoreCase(ac.owner)) return true;
if (isLandAdmin(ac.name, p.getName())) return true;
// ★ 访客传送权限:领地级别允许则放行(优先于per-player检查)
if (ac.allowVisitorTeleport) return true;
int landId = getLandIdFromDb(ac.name);
if (landId <= 0) return false; // 无法验证则拒绝
Map<String, Boolean> perms = getPlayerPermMap(landId, p.getName());
@@ -7833,8 +7842,8 @@ public class AreaProtection implements Listener {
+ "confiscate_msg, enable_announce, announce_template, txt_content, created_at, "
+ "deny_thrown_projectiles, deny_glowing, deny_redstone_interaction, deny_door_interaction, "
+ "deny_noteblock_jukebox, deny_lead, deny_crop_harvest, deny_wool_shear, deny_animal_feeding, "
+ "warp_x, warp_y, warp_z, warp_yaw, warp_pitch, warp_world, deny_container, deny_mob_attack, is_public_building) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
+ "warp_x, warp_y, warp_z, warp_yaw, warp_pitch, warp_world, deny_container, deny_mob_attack, is_public_building, allow_visitor_teleport) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
stmt.setString(1, ac.name);
stmt.setString(2, ac.owner != null ? ac.owner : "");
@@ -7902,6 +7911,7 @@ public class AreaProtection implements Listener {
stmt.setInt(62, ac.denyContainer ? 1 : 0);
stmt.setInt(63, ac.denyMobAttack ? 1 : 0);
stmt.setInt(64, ac.isPublicBuilding ? 1 : 0);
stmt.setInt(65, ac.allowVisitorTeleport ? 1 : 0);
stmt.executeUpdate();
stmt.close();
// ★ 领地设置变更:立即触发PHP同步(防抖10秒)
@@ -10010,6 +10020,8 @@ public class AreaProtection implements Listener {
public String warpWorld = "";
// ★ 公共建筑标记
public boolean isPublicBuilding = false;
// ★ 访客传送权限(默认关闭:未授权访客不能传送进入领地)
public boolean allowVisitorTeleport = false;
@@ -265,7 +265,7 @@ public class PVPArenaManager implements Listener {
* 出生点合法性:脚下方块必须是【实体方块】(固体)才使用;若随机到水/空气等非实体方块,
* 则删除该世界、换一个新种子重新生成(极少数情况连续多次都抽到水出生点,会重试但概率极低)。
*
* 卡服修复:Bukkit.createWorld 仅做世界初始化(keepSpawnInMemory=false 不强制加载出生点区块),
* 卡服修复:Bukkit.createWorld 仅做世界初始化(不强制加载出生点区块),
* 主线程毫秒级返回;出生点周边区块由 preGenerateSpawnChunksAsync 异步预生成,不阻塞主线程。
*/
private World createPVPWorld() {
@@ -280,7 +280,8 @@ public class PVPArenaManager implements Listener {
c.environment(World.Environment.NORMAL);
c.type(WorldType.NORMAL);
c.seed(seed);
c.keepSpawnInMemory(false); // ★ 不强制加载出生点区块,避免同步卡服
// 注意:Paper 26.x 已移除 WorldCreator.keepSpawnInMemory(boolean),不再调用
// 区块加载控制改为 createWorld 后通过 world.setKeepLoaded(false) 实现
plugin.getLogger().info("[PVP] 生成PVP竞技场世界 " + pvpWorldName + " 种子=" + seed);
World pvpWorld = Bukkit.createWorld(c);