feat: 成员独立权限系统 + PHP→Java同步 + SSL兼容性
This commit is contained in:
@@ -320,8 +320,10 @@ public class AreaCLIManager {
|
||||
// ★ 细分管理选项
|
||||
p.sendMessage(clickableAction("👤", "用户管理",
|
||||
"/protect cli members " + land.name + " 1"));
|
||||
p.sendMessage(clickableAction("🔑", "访客授权",
|
||||
p.sendMessage(clickableAction("🔑", "访客授权(全局)",
|
||||
"/protect cli visitorperm " + land.name + " 1"));
|
||||
p.sendMessage(clickableAction("🎯", "成员权限(独立)",
|
||||
"/protect cli memberperm " + land.name + " 1"));
|
||||
// ★ 新增:设置传送点
|
||||
p.sendMessage(clickableAction("📍", "设置传送点(当前位置)",
|
||||
"/protect settp " + land.name));
|
||||
@@ -546,11 +548,17 @@ public class AreaCLIManager {
|
||||
String key;
|
||||
String name;
|
||||
boolean enabled;
|
||||
boolean overridden; // 是否有per-player覆盖
|
||||
|
||||
PermItem(String key, String name, boolean enabled) {
|
||||
this(key, name, enabled, false);
|
||||
}
|
||||
|
||||
PermItem(String key, String name, boolean enabled, boolean overridden) {
|
||||
this.key = key;
|
||||
this.name = name;
|
||||
this.enabled = enabled;
|
||||
this.overridden = overridden;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -622,4 +630,337 @@ public class AreaCLIManager {
|
||||
default: return key;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== Per-Player 独立权限管理 ====================
|
||||
|
||||
/**
|
||||
* 显示成员列表(点击成员进入per-player权限编辑)
|
||||
*/
|
||||
public void showMemberPermList(Player p, String landName, int page) {
|
||||
savePageInfo(p, "memberperm", landName, page);
|
||||
|
||||
AreaProtection.AreaConfig land = areaProtect.getLand(landName);
|
||||
if (land == null) {
|
||||
p.sendMessage(Component.text("§c领地不存在: " + landName));
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取成员列表
|
||||
Set<String> members = areaProtect.getAreaMembers(landName);
|
||||
if (members == null || members.isEmpty()) {
|
||||
p.sendMessage(header("成员权限: " + landName));
|
||||
p.sendMessage(Component.text("§7暂无成员,请先使用 §e/protect addvisitor §7添加"));
|
||||
p.sendMessage(Component.empty()
|
||||
.append(Component.text("§a[◀ 返回管理]")
|
||||
.clickEvent(ClickEvent.runCommand(
|
||||
"/protect cli manage " + land.name + " 1"))));
|
||||
return;
|
||||
}
|
||||
|
||||
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 + " §7第" + page + "/" + totalPages + "页"));
|
||||
|
||||
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);
|
||||
boolean isOwner = member.equalsIgnoreCase(land.owner);
|
||||
|
||||
String roleTag = isOwner ? " §c[所有者]" : "";
|
||||
Component line = Component.empty();
|
||||
line = line.append(Component.text("§e● " + member + roleTag));
|
||||
line = line.append(Component.text(" "));
|
||||
line = line.append(Component.text("§a[编辑权限]")
|
||||
.hoverEvent(HoverEvent.showText(
|
||||
Component.text("§e设置该成员的独立权限")))
|
||||
.clickEvent(ClickEvent.runCommand(
|
||||
"/protect cli playerperm " + land.name + " " + member + " 1")));
|
||||
p.sendMessage(line);
|
||||
}
|
||||
|
||||
// 分页
|
||||
if (totalPages > 1) {
|
||||
Component pagination = Component.empty();
|
||||
if (page > 1) {
|
||||
pagination = pagination.append(Component.text("§a[◀ 上一页]")
|
||||
.clickEvent(ClickEvent.runCommand(
|
||||
"/protect cli memberperm " + 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 memberperm " + land.name + " " + (page + 1))));
|
||||
}
|
||||
p.sendMessage(pagination);
|
||||
}
|
||||
|
||||
p.sendMessage(Component.empty()
|
||||
.append(Component.text("§a[◀ 返回管理]")
|
||||
.clickEvent(ClickEvent.runCommand(
|
||||
"/protect cli manage " + land.name + " " + page))));
|
||||
p.sendMessage(Component.text("§7§l───────────────────────────────"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示某个成员的独立权限编辑页
|
||||
*/
|
||||
public void showPlayerPerm(Player p, String landName, String targetPlayer, int page) {
|
||||
savePageInfo(p, "playerperm", landName + ":" + targetPlayer, page);
|
||||
|
||||
AreaProtection.AreaConfig land = areaProtect.getLand(landName);
|
||||
if (land == null) {
|
||||
p.sendMessage(Component.text("§c领地不存在: " + landName));
|
||||
return;
|
||||
}
|
||||
|
||||
int landId = areaProtect.getLandIdFromDb(landName);
|
||||
if (landId <= 0) {
|
||||
p.sendMessage(Component.text("§c领地数据库ID获取失败"));
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取该成员的per-player权限
|
||||
Map<String, Boolean> playerPerms = areaProtect.getPlayerPermMap(landId, targetPlayer);
|
||||
List<PermItem> perms = getPermListWithOverrides(land, playerPerms);
|
||||
|
||||
int totalPages = Math.max(1, (int) Math.ceil((double) perms.size() / PAGE_SIZE));
|
||||
page = Math.max(1, Math.min(page, totalPages));
|
||||
|
||||
p.sendMessage(header(targetPlayer + " 的独立权限"));
|
||||
|
||||
// 显示说明
|
||||
p.sendMessage(Component.text("§7§l说明:"));
|
||||
p.sendMessage(Component.text(" §a✔ 已启用 §7= 允许该操作"));
|
||||
p.sendMessage(Component.text(" §c✘ 已禁用 §7= 禁止该操作"));
|
||||
p.sendMessage(Component.text(" §e★ 自定义 §7= 与领地默认不同"));
|
||||
p.sendMessage(Component.text(""));
|
||||
|
||||
int start = (page - 1) * PAGE_SIZE;
|
||||
int end = Math.min(start + PAGE_SIZE, perms.size());
|
||||
|
||||
for (int i = start; i < end; i++) {
|
||||
PermItem perm = perms.get(i);
|
||||
String icon;
|
||||
if (perm.overridden) {
|
||||
icon = perm.enabled ? "§a★" : "§c★"; // 星号表示per-player覆盖
|
||||
} else {
|
||||
icon = perm.enabled ? "§a✔" : "§c✘";
|
||||
}
|
||||
String state = perm.enabled ? "§a允许" : "§c禁止";
|
||||
String overrideTag = perm.overridden ? " §e[自定义]" : " §7[领地默认]";
|
||||
|
||||
Component line = Component.empty();
|
||||
line = line.append(Component.text(icon + " §f" + perm.name + " §7" + state + overrideTag));
|
||||
line = line.append(Component.text(" "));
|
||||
line = line.append(Component.text("§e[切换]")
|
||||
.hoverEvent(HoverEvent.showText(
|
||||
Component.text("§e切换该成员的此权限")))
|
||||
.clickEvent(ClickEvent.runCommand(
|
||||
"/protect cli toggleplayerperm " + land.name + " " + targetPlayer + " " + perm.key + " " + page)));
|
||||
p.sendMessage(line);
|
||||
}
|
||||
|
||||
// 分页
|
||||
if (totalPages > 1) {
|
||||
Component pagination = Component.empty();
|
||||
if (page > 1) {
|
||||
pagination = pagination.append(Component.text("§a[◀ 上一页]")
|
||||
.clickEvent(ClickEvent.runCommand(
|
||||
"/protect cli playerperm " + land.name + " " + targetPlayer + " " + (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 playerperm " + land.name + " " + targetPlayer + " " + (page + 1))));
|
||||
}
|
||||
p.sendMessage(pagination);
|
||||
}
|
||||
|
||||
// 清除按钮
|
||||
p.sendMessage(Component.empty()
|
||||
.append(Component.text("§c[清除所有自定义权限]")
|
||||
.hoverEvent(HoverEvent.showText(
|
||||
Component.text("§c将该成员的权限恢复为领地默认")))
|
||||
.clickEvent(ClickEvent.runCommand(
|
||||
"/protect cli clearplayerperm " + land.name + " " + targetPlayer))));
|
||||
|
||||
p.sendMessage(Component.empty()
|
||||
.append(Component.text("§a[◀ 返回成员列表]")
|
||||
.clickEvent(ClickEvent.runCommand(
|
||||
"/protect cli memberperm " + land.name + " 1"))));
|
||||
p.sendMessage(Component.text("§7§l───────────────────────────────"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换某个成员的独立权限
|
||||
*/
|
||||
public void togglePlayerPerm(Player p, String landName, String targetPlayer, String permKey, int page) {
|
||||
AreaProtection.AreaConfig land = areaProtect.getLand(landName);
|
||||
if (land == null) {
|
||||
p.sendMessage(Component.text("§c领地不存在: " + landName));
|
||||
return;
|
||||
}
|
||||
|
||||
// 权限检查
|
||||
boolean isOwner = p.getName().equalsIgnoreCase(land.owner);
|
||||
boolean isAdmin = areaProtect.isAreaAdmin(p);
|
||||
if (!isOwner && !isAdmin) {
|
||||
p.sendMessage(Component.text("§c需要领地所有者或管理员权限"));
|
||||
return;
|
||||
}
|
||||
|
||||
int landId = areaProtect.getLandIdFromDb(landName);
|
||||
if (landId <= 0) {
|
||||
p.sendMessage(Component.text("§c领地数据库ID获取失败"));
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取当前值,判断是否已有覆盖
|
||||
Map<String, Boolean> currentPerms = areaProtect.getPlayerPermMap(landId, targetPlayer);
|
||||
boolean currentVal = currentPerms.getOrDefault(permKey, getLandDefaultVal(land, permKey));
|
||||
|
||||
// 切换
|
||||
boolean newVal = !currentVal;
|
||||
areaProtect.setPlayerPerm(landId, targetPlayer, permKey, newVal);
|
||||
|
||||
String permName = getPermNameByKey(permKey);
|
||||
p.sendMessage("§a已设置 §e" + targetPlayer + " §a的 §e" + permName + " §7→ " + (newVal ? "§a允许" : "§c禁止"));
|
||||
|
||||
// 刷新页面
|
||||
showPlayerPerm(p, landName, targetPlayer, page);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除某成员所有per-player权限覆盖
|
||||
*/
|
||||
public void clearPlayerPerm(Player p, String landName, String targetPlayer) {
|
||||
AreaProtection.AreaConfig land = areaProtect.getLand(landName);
|
||||
if (land == null) {
|
||||
p.sendMessage(Component.text("§c领地不存在: " + landName));
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isOwner = p.getName().equalsIgnoreCase(land.owner);
|
||||
boolean isAdmin = areaProtect.isAreaAdmin(p);
|
||||
if (!isOwner && !isAdmin) {
|
||||
p.sendMessage(Component.text("§c需要领地所有者或管理员权限"));
|
||||
return;
|
||||
}
|
||||
|
||||
int landId = areaProtect.getLandIdFromDb(landName);
|
||||
if (landId <= 0) {
|
||||
p.sendMessage(Component.text("§c领地数据库ID获取失败"));
|
||||
return;
|
||||
}
|
||||
|
||||
areaProtect.setPlayerPermJson(landId, targetPlayer, "");
|
||||
p.sendMessage("§a已清除 §e" + targetPlayer + " §a的所有自定义权限,恢复为领地默认");
|
||||
|
||||
// 返回成员列表
|
||||
showMemberPermList(p, landName, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取带per-player覆盖的权限列表
|
||||
*/
|
||||
private List<PermItem> getPermListWithOverrides(AreaProtection.AreaConfig land, Map<String, Boolean> playerPerms) {
|
||||
List<PermItem> perms = new ArrayList<>();
|
||||
String[][] permDefs = {
|
||||
{"move", "移动", "denyMove"},
|
||||
{"block_place", "放置方块", "denyBlockPlace"},
|
||||
{"block_break", "破坏方块", "denyBlockBreak"},
|
||||
{"pvp", "玩家对战", "denyPVP"},
|
||||
{"mount", "骑乘坐具", "denyMount"},
|
||||
{"ender_pearl", "投掷末影珍珠", "denyEnderPearl"},
|
||||
{"thrown_projectiles", "投掷物(三叉戟/雪球/风蛋)", "denyThrownProjectiles"},
|
||||
{"raid", "袭击侦测", "denyRaid"},
|
||||
{"bow", "弓箭射击", "denyBow"},
|
||||
{"potion", "药水效果", "denyPotion"},
|
||||
{"fire", "点燃", "denyFire"},
|
||||
{"fire_spread", "火焰蔓延", "denyFireSpread"},
|
||||
{"pickup", "拾取物品", "denyPickup"},
|
||||
{"drop", "丢弃物品", "denyDrop"},
|
||||
{"explosion", "爆炸", "denyExplosion"},
|
||||
{"fall_damage", "摔落伤害", "denyFallDamage"},
|
||||
{"hunger", "饥饿", "denyHunger"},
|
||||
{"all_damage", "所有伤害", "denyAllDamage"},
|
||||
{"all_effects", "所有效果", "denyAllEffects"},
|
||||
{"item_frame", "展示框交互", "denyItemFrame"},
|
||||
{"redstone", "红石电路(中继器/比较器)", "denyRedstoneInteraction"},
|
||||
{"door", "门禁(门/按钮/压力板)", "denyDoorInteraction"},
|
||||
{"audio", "音频(音符盒/唱片机)", "denyNoteblockJukebox"},
|
||||
{"lead", "拴绳使用", "denyLead"},
|
||||
{"crop_harvest", "农作物收获", "denyCropHarvest"},
|
||||
{"wool_shear", "剪切羊毛/生物", "denyWoolShear"},
|
||||
{"animal_feed", "投喂动物", "denyAnimalFeeding"},
|
||||
{"glowing", "玩家发光", "denyGlowing"},
|
||||
{"peace_mode", "和平模式", "peaceMode"}
|
||||
};
|
||||
|
||||
for (String[] def : permDefs) {
|
||||
String key = def[0];
|
||||
String name = def[1];
|
||||
String field = def[2];
|
||||
boolean landDefault = getLandDefaultVal(land, field);
|
||||
|
||||
// 如果有per-player覆盖,使用覆盖值;否则使用领地默认
|
||||
if (playerPerms.containsKey(field)) {
|
||||
boolean playerVal = playerPerms.get(field);
|
||||
// 注意:per-player存储的是deny值
|
||||
// perm.enabled = true 表示"允许操作"(即deny=false)
|
||||
perms.add(new PermItem(key, name, !playerVal, true));
|
||||
} else {
|
||||
perms.add(new PermItem(key, name, !landDefault, false));
|
||||
}
|
||||
}
|
||||
return perms;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取领地默认deny值
|
||||
*/
|
||||
private boolean getLandDefaultVal(AreaProtection.AreaConfig land, String field) {
|
||||
switch (field) {
|
||||
case "denyMove": return land.denyMove;
|
||||
case "denyBlockPlace": return land.denyBlockPlace;
|
||||
case "denyBlockBreak": return land.denyBlockBreak;
|
||||
case "denyPVP": return land.denyPVP;
|
||||
case "denyMount": return land.denyMount;
|
||||
case "denyEnderPearl": return land.denyEnderPearl;
|
||||
case "denyThrownProjectiles": return land.denyThrownProjectiles;
|
||||
case "denyRaid": return land.denyRaid;
|
||||
case "denyBow": return land.denyBow;
|
||||
case "denyPotion": return land.denyPotion;
|
||||
case "denyFire": return land.denyFire;
|
||||
case "denyFireSpread": return land.denyFireSpread;
|
||||
case "denyPickup": return land.denyPickup;
|
||||
case "denyDrop": return land.denyDrop;
|
||||
case "denyExplosion": return land.denyExplosion;
|
||||
case "denyFallDamage": return land.denyFallDamage;
|
||||
case "denyHunger": return land.denyHunger;
|
||||
case "denyAllDamage": return land.denyAllDamage;
|
||||
case "denyAllEffects": return land.denyAllEffects;
|
||||
case "denyItemFrame": return land.denyItemFrame;
|
||||
case "denyRedstoneInteraction": return land.denyRedstoneInteraction;
|
||||
case "denyDoorInteraction": return land.denyDoorInteraction;
|
||||
case "denyNoteblockJukebox": return land.denyNoteblockJukebox;
|
||||
case "denyLead": return land.denyLead;
|
||||
case "denyCropHarvest": return land.denyCropHarvest;
|
||||
case "denyWoolShear": return land.denyWoolShear;
|
||||
case "denyAnimalFeeding": return land.denyAnimalFeeding;
|
||||
case "denyGlowing": return land.denyGlowing;
|
||||
case "peaceMode": return land.peaceMode;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ public class AreaGUIManager implements Listener {
|
||||
private static final String T_VISITOR_PERM = "§6§l访客权限";
|
||||
private static final String T_ADD_MEMBER = "§6§l添加成员";
|
||||
private static final String T_LAND_SETTINGS = "§6§l领地设置";
|
||||
private static final String T_MEMBER_PERM_LIST = "§6§l成员权限列表";
|
||||
private static final String T_PLAYER_PERM = "§6§l成员权限";
|
||||
|
||||
// 每页显示数量
|
||||
private static final int PAGE_SIZE = 45;
|
||||
@@ -44,6 +46,9 @@ public class AreaGUIManager implements Listener {
|
||||
// 玩家当前管理的领地
|
||||
private final Map<UUID, String> managingLand = new HashMap<>();
|
||||
|
||||
// ★ 玩家当前编辑的per-player目标玩家
|
||||
private final Map<UUID, String> playerPermTarget = new HashMap<>();
|
||||
|
||||
public AreaGUIManager(Main plugin, AreaProtection areaProtect) {
|
||||
this.plugin = plugin;
|
||||
this.areaProtect = areaProtect;
|
||||
@@ -309,6 +314,173 @@ public class AreaGUIManager implements Listener {
|
||||
p.openInventory(inv);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开成员权限列表(per-player权限编辑入口)
|
||||
*/
|
||||
public void openMemberPermList(Player p, String landName) {
|
||||
managingLand.put(p.getUniqueId(), landName);
|
||||
Inventory inv = Bukkit.createInventory(null, 54, T_MEMBER_PERM_LIST + " - " + landName);
|
||||
|
||||
AreaProtection.AreaConfig land = areaProtect.getLand(landName);
|
||||
if (land == null) {
|
||||
p.sendMessage("§c领地不存在");
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取成员列表
|
||||
Set<String> members = areaProtect.getLandMembers(landName);
|
||||
List<String> memberList = new ArrayList<>(members);
|
||||
|
||||
int slot = 0;
|
||||
for (String member : memberList) {
|
||||
if (slot >= 45) break;
|
||||
|
||||
boolean isOwner = member.equalsIgnoreCase(land.owner);
|
||||
String roleTag = isOwner ? " §c[所有者]" : "";
|
||||
|
||||
// 检查是否有自定义权限
|
||||
int landId = areaProtect.getLandIdFromDb(landName);
|
||||
String permJson = "";
|
||||
if (landId > 0) {
|
||||
permJson = areaProtect.getPlayerPermJson(landId, member);
|
||||
}
|
||||
boolean hasCustom = permJson != null && !permJson.isEmpty();
|
||||
String customTag = hasCustom ? " §e[自定义]" : "";
|
||||
|
||||
ItemStack playerItem = createItem(Material.PLAYER_HEAD,
|
||||
"§a§l" + member + roleTag + customTag,
|
||||
"§7左键编辑该成员的独立权限",
|
||||
"",
|
||||
"§e点击编辑");
|
||||
SkullMeta skull = (SkullMeta) playerItem.getItemMeta();
|
||||
if (skull != null) {
|
||||
try {
|
||||
Player target = Bukkit.getPlayer(member);
|
||||
if (target != null) skull.setOwningPlayer(target);
|
||||
} catch (Exception ignored) {}
|
||||
playerItem.setItemMeta(skull);
|
||||
}
|
||||
|
||||
inv.setItem(slot, playerItem);
|
||||
slot++;
|
||||
}
|
||||
|
||||
// 返回按钮(位置48)
|
||||
inv.setItem(48, createItem(Material.ARROW, "§c§l返回管理领地", ""));
|
||||
|
||||
p.openInventory(inv);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开某个成员的独立权限编辑页面
|
||||
*/
|
||||
public void openPlayerPerm(Player p, String landName, String targetPlayer) {
|
||||
managingLand.put(p.getUniqueId(), landName);
|
||||
// 用 playerPermTarget 保存目标玩家
|
||||
playerPermTarget.put(p.getUniqueId(), targetPlayer);
|
||||
Inventory inv = Bukkit.createInventory(null, 54, T_PLAYER_PERM + " - " + targetPlayer);
|
||||
|
||||
AreaProtection.AreaConfig land = areaProtect.getLand(landName);
|
||||
if (land == null) {
|
||||
p.sendMessage("§c领地不存在");
|
||||
return;
|
||||
}
|
||||
|
||||
int landId = areaProtect.getLandIdFromDb(landName);
|
||||
if (landId <= 0) {
|
||||
p.sendMessage("§c领地数据库ID获取失败");
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取per-player权限
|
||||
Map<String, Boolean> playerPerms = areaProtect.getPlayerPermMap(landId, targetPlayer);
|
||||
|
||||
// 权限列表
|
||||
String[][] permDefs = {
|
||||
{"移动", "denyMove"}, {"放置方块", "denyBlockPlace"}, {"破坏方块", "denyBlockBreak"},
|
||||
{"玩家对战", "denyPVP"}, {"骑乘坐具", "denyMount"}, {"投掷末影珍珠", "denyEnderPearl"},
|
||||
{"投掷物", "denyThrownProjectiles"}, {"袭击侦测", "denyRaid"}, {"弓箭射击", "denyBow"},
|
||||
{"药水效果", "denyPotion"}, {"点燃", "denyFire"}, {"火焰蔓延", "denyFireSpread"},
|
||||
{"拾取物品", "denyPickup"}, {"丢弃物品", "denyDrop"}, {"爆炸", "denyExplosion"},
|
||||
{"摔落伤害", "denyFallDamage"}, {"饥饿", "denyHunger"}, {"所有伤害", "denyAllDamage"},
|
||||
{"所有效果", "denyAllEffects"}, {"展示框交互", "denyItemFrame"}, {"红石电路", "denyRedstoneInteraction"},
|
||||
{"门禁", "denyDoorInteraction"}, {"音频", "denyNoteblockJukebox"}, {"拴绳使用", "denyLead"},
|
||||
{"农作物收获", "denyCropHarvest"}, {"剪切羊毛", "denyWoolShear"}, {"投喂动物", "denyAnimalFeeding"},
|
||||
{"玩家发光", "denyGlowing"}, {"和平模式", "peaceMode"}
|
||||
};
|
||||
|
||||
int slot = 0;
|
||||
for (String[] def : permDefs) {
|
||||
if (slot >= 45) break;
|
||||
String name = def[0];
|
||||
String field = def[1];
|
||||
|
||||
boolean landDefault = getLandDefaultVal(land, field);
|
||||
boolean isOverridden = playerPerms.containsKey(field);
|
||||
boolean effective = isOverridden ? playerPerms.get(field) : landDefault;
|
||||
|
||||
// enabled = true 表示"允许操作"(即deny=false)
|
||||
boolean allowed = !effective;
|
||||
Material mat = allowed ? Material.LIME_DYE : Material.GRAY_DYE;
|
||||
String status = allowed ? "§a✔ 允许" : "§c✘ 禁止";
|
||||
String overrideTag = isOverridden ? " §e★自定义" : " §7[默认]";
|
||||
|
||||
inv.setItem(slot, createItem(mat,
|
||||
"§e§l" + name + overrideTag,
|
||||
status,
|
||||
"",
|
||||
"§e点击切换"));
|
||||
slot++;
|
||||
}
|
||||
|
||||
// 清除所有自定义权限按钮(位置48)
|
||||
inv.setItem(48, createItem(Material.BARRIER, "§c§l清除所有自定义",
|
||||
"§7恢复为领地默认权限"));
|
||||
|
||||
// 返回按钮(位置49)
|
||||
inv.setItem(49, createItem(Material.ARROW, "§a§l返回成员列表", ""));
|
||||
|
||||
p.openInventory(inv);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取领地默认deny值(按字段名)
|
||||
*/
|
||||
private boolean getLandDefaultVal(AreaProtection.AreaConfig land, String field) {
|
||||
switch (field) {
|
||||
case "denyMove": return land.denyMove;
|
||||
case "denyBlockPlace": return land.denyBlockPlace;
|
||||
case "denyBlockBreak": return land.denyBlockBreak;
|
||||
case "denyPVP": return land.denyPVP;
|
||||
case "denyMount": return land.denyMount;
|
||||
case "denyEnderPearl": return land.denyEnderPearl;
|
||||
case "denyThrownProjectiles": return land.denyThrownProjectiles;
|
||||
case "denyRaid": return land.denyRaid;
|
||||
case "denyBow": return land.denyBow;
|
||||
case "denyPotion": return land.denyPotion;
|
||||
case "denyFire": return land.denyFire;
|
||||
case "denyFireSpread": return land.denyFireSpread;
|
||||
case "denyPickup": return land.denyPickup;
|
||||
case "denyDrop": return land.denyDrop;
|
||||
case "denyExplosion": return land.denyExplosion;
|
||||
case "denyFallDamage": return land.denyFallDamage;
|
||||
case "denyHunger": return land.denyHunger;
|
||||
case "denyAllDamage": return land.denyAllDamage;
|
||||
case "denyAllEffects": return land.denyAllEffects;
|
||||
case "denyItemFrame": return land.denyItemFrame;
|
||||
case "denyRedstoneInteraction": return land.denyRedstoneInteraction;
|
||||
case "denyDoorInteraction": return land.denyDoorInteraction;
|
||||
case "denyNoteblockJukebox": return land.denyNoteblockJukebox;
|
||||
case "denyLead": return land.denyLead;
|
||||
case "denyCropHarvest": return land.denyCropHarvest;
|
||||
case "denyWoolShear": return land.denyWoolShear;
|
||||
case "denyAnimalFeeding": return land.denyAnimalFeeding;
|
||||
case "denyGlowing": return land.denyGlowing;
|
||||
case "peaceMode": return land.peaceMode;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开添加成员菜单
|
||||
*/
|
||||
@@ -548,6 +720,9 @@ public class AreaGUIManager implements Listener {
|
||||
openMemberList(p, landName, 0);
|
||||
} else if (raw == 13) {
|
||||
openVisitorPerm(p, landName, 0);
|
||||
} else if (raw == 14) {
|
||||
// ★ 成员权限(独立)
|
||||
openMemberPermList(p, landName);
|
||||
} else if (raw == 15) {
|
||||
openLandSettings(p, landName);
|
||||
} else if (raw == 31) {
|
||||
@@ -634,6 +809,83 @@ public class AreaGUIManager implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
// ★ 成员权限列表
|
||||
if (title.startsWith(T_MEMBER_PERM_LIST)) {
|
||||
event.setCancelled(true);
|
||||
String landName = managingLand.get(p.getUniqueId());
|
||||
if (landName == null) return;
|
||||
|
||||
if (raw >= 0 && raw < 45) {
|
||||
ItemStack item = event.getView().getTopInventory().getItem(raw);
|
||||
if (item != null && item.getType() == Material.PLAYER_HEAD) {
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta != null && meta.getDisplayName() != null) {
|
||||
String playerName = meta.getDisplayName()
|
||||
.replaceAll("§[0-9a-fk-orA-FK-OR]", "")
|
||||
.replaceAll("\\[所有者\\]", "")
|
||||
.replaceAll("\\[自定义\\]", "")
|
||||
.trim();
|
||||
openPlayerPerm(p, landName, playerName);
|
||||
}
|
||||
}
|
||||
} else if (raw == 48) {
|
||||
openLandManage(p, landName);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// ★ 成员权限编辑
|
||||
if (title.startsWith(T_PLAYER_PERM)) {
|
||||
event.setCancelled(true);
|
||||
String landName = managingLand.get(p.getUniqueId());
|
||||
String targetPlayer = playerPermTarget.get(p.getUniqueId());
|
||||
if (landName == null || targetPlayer == null) return;
|
||||
|
||||
AreaProtection.AreaConfig land = areaProtect.getLand(landName);
|
||||
if (land == null) return;
|
||||
|
||||
int landId = areaProtect.getLandIdFromDb(landName);
|
||||
if (landId <= 0) return;
|
||||
|
||||
if (raw >= 0 && raw < 45) {
|
||||
// 切换权限
|
||||
String[][] permDefs = {
|
||||
{"移动", "denyMove"}, {"放置方块", "denyBlockPlace"}, {"破坏方块", "denyBlockBreak"},
|
||||
{"玩家对战", "denyPVP"}, {"骑乘坐具", "denyMount"}, {"投掷末影珍珠", "denyEnderPearl"},
|
||||
{"投掷物", "denyThrownProjectiles"}, {"袭击侦测", "denyRaid"}, {"弓箭射击", "denyBow"},
|
||||
{"药水效果", "denyPotion"}, {"点燃", "denyFire"}, {"火焰蔓延", "denyFireSpread"},
|
||||
{"拾取物品", "denyPickup"}, {"丢弃物品", "denyDrop"}, {"爆炸", "denyExplosion"},
|
||||
{"摔落伤害", "denyFallDamage"}, {"饥饿", "denyHunger"}, {"所有伤害", "denyAllDamage"},
|
||||
{"所有效果", "denyAllEffects"}, {"展示框交互", "denyItemFrame"}, {"红石电路", "denyRedstoneInteraction"},
|
||||
{"门禁", "denyDoorInteraction"}, {"音频", "denyNoteblockJukebox"}, {"拴绳使用", "denyLead"},
|
||||
{"农作物收获", "denyCropHarvest"}, {"剪切羊毛", "denyWoolShear"}, {"投喂动物", "denyAnimalFeeding"},
|
||||
{"玩家发光", "denyGlowing"}, {"和平模式", "peaceMode"}
|
||||
};
|
||||
|
||||
if (raw < permDefs.length) {
|
||||
String field = permDefs[raw][1];
|
||||
// 获取当前effective值
|
||||
Map<String, Boolean> currentPerms = areaProtect.getPlayerPermMap(landId, targetPlayer);
|
||||
boolean currentVal = currentPerms.containsKey(field)
|
||||
? currentPerms.get(field)
|
||||
: getLandDefaultVal(land, field);
|
||||
boolean newVal = !currentVal;
|
||||
areaProtect.setPlayerPerm(landId, targetPlayer, field, newVal);
|
||||
p.sendMessage("§a已设置 §e" + targetPlayer + " §a的权限");
|
||||
openPlayerPerm(p, landName, targetPlayer);
|
||||
}
|
||||
} else if (raw == 48) {
|
||||
// 清除所有自定义权限
|
||||
areaProtect.setPlayerPermJson(landId, targetPlayer, "");
|
||||
p.sendMessage("§a已清除 §e" + targetPlayer + " §a的所有自定义权限");
|
||||
openMemberPermList(p, landName);
|
||||
} else if (raw == 49) {
|
||||
// 返回成员列表
|
||||
openMemberPermList(p, landName);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 添加成员
|
||||
if (title.startsWith(T_ADD_MEMBER)) {
|
||||
event.setCancelled(true);
|
||||
|
||||
@@ -46,9 +46,6 @@ import org.bukkit.event.entity.EntitySpawnEvent;
|
||||
import org.bukkit.entity.Item;
|
||||
import org.bukkit.event.inventory.InventoryOpenEvent;
|
||||
|
||||
|
||||
|
||||
|
||||
public class AreaProtection implements Listener {
|
||||
|
||||
// 三级权限枚举
|
||||
@@ -2232,6 +2229,204 @@ public class AreaProtection implements Listener {
|
||||
return requiredLevel == PermissionLevel.VISITOR;
|
||||
}
|
||||
|
||||
// ===== Per-Player 独立权限系统 =====
|
||||
|
||||
/**
|
||||
* 获取玩家在指定领地的独立权限JSON
|
||||
* 格式: {"denyMove":true,"denyPVP":false,...}
|
||||
* 空字符串或null表示没有自定义权限(使用领地默认)
|
||||
*/
|
||||
public String getPlayerPermJson(int landId, String playerName) {
|
||||
if (dbConnection == null) return "";
|
||||
try {
|
||||
PreparedStatement stmt = dbConnection.prepareStatement(
|
||||
"SELECT permissions FROM area_land_permissions "
|
||||
+ "WHERE land_id = ? AND player_name = ?");
|
||||
stmt.setInt(1, landId);
|
||||
stmt.setString(2, playerName);
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
String result = "";
|
||||
if (rs.next()) {
|
||||
result = rs.getString("permissions");
|
||||
if (result == null) result = "";
|
||||
}
|
||||
rs.close();
|
||||
stmt.close();
|
||||
return result;
|
||||
} catch (SQLException e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置玩家在指定领地的独立权限JSON
|
||||
*/
|
||||
public void setPlayerPermJson(int landId, String playerName, String permJson) {
|
||||
if (dbConnection == null) return;
|
||||
try {
|
||||
PreparedStatement stmt = dbConnection.prepareStatement(
|
||||
"UPDATE area_land_permissions SET permissions = ? "
|
||||
+ "WHERE land_id = ? AND player_name = ?");
|
||||
stmt.setString(1, permJson != null ? permJson : "");
|
||||
stmt.setInt(2, landId);
|
||||
stmt.setString(3, playerName);
|
||||
stmt.executeUpdate();
|
||||
stmt.close();
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().warning("[防护] 设置玩家权限失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取玩家独立权限(解析JSON为Map)
|
||||
*/
|
||||
public Map<String, Boolean> getPlayerPermMap(int landId, String playerName) {
|
||||
Map<String, Boolean> map = new HashMap<>();
|
||||
String json = getPlayerPermJson(landId, playerName);
|
||||
if (json == null || json.isEmpty()) return map;
|
||||
// 简单解析 {"key":true,"key2":false}
|
||||
String clean = json.trim();
|
||||
if (clean.startsWith("{")) clean = clean.substring(1);
|
||||
if (clean.endsWith("}")) clean = clean.substring(0, clean.length() - 1);
|
||||
if (clean.isEmpty()) return map;
|
||||
for (String pair : clean.split(",")) {
|
||||
String[] kv = pair.split(":");
|
||||
if (kv.length == 2) {
|
||||
String key = kv[0].trim().replace("\"", "");
|
||||
boolean val = Boolean.parseBoolean(kv[1].trim().replace("\"", ""));
|
||||
map.put(key, val);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置玩家独立权限中的单个权限项
|
||||
* @param permName 权限名(如 "denyMove")
|
||||
* @param enabled true=启用限制(deny),false=取消限制(允许)
|
||||
*/
|
||||
public void setPlayerPerm(int landId, String playerName, String permName, boolean enabled) {
|
||||
Map<String, Boolean> map = getPlayerPermMap(landId, playerName);
|
||||
map.put(permName, enabled);
|
||||
String json = mapToJson(map);
|
||||
setPlayerPermJson(landId, playerName, json);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有有独立权限的成员列表
|
||||
*/
|
||||
public List<Map<String, Object>> getLandMembersWithPerms(int landId) {
|
||||
List<Map<String, Object>> list = new ArrayList<>();
|
||||
if (dbConnection == null) return list;
|
||||
try {
|
||||
PreparedStatement stmt = dbConnection.prepareStatement(
|
||||
"SELECT player_name, role, permissions FROM area_land_permissions "
|
||||
+ "WHERE land_id = ?");
|
||||
stmt.setInt(1, landId);
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
Map<String, Object> entry = new HashMap<>();
|
||||
entry.put("player", rs.getString("player_name"));
|
||||
entry.put("role", rs.getString("role"));
|
||||
entry.put("permissions", rs.getString("permissions"));
|
||||
list.add(entry);
|
||||
}
|
||||
rs.close();
|
||||
stmt.close();
|
||||
} catch (SQLException e) {
|
||||
// 忽略
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* ★ 核心方法:获取玩家的effective deny状态
|
||||
* 优先级:per-player权限 > 领地默认权限
|
||||
* @param permName 权限字段名(如 "denyMove")
|
||||
* @return true=限制生效,false=允许
|
||||
*/
|
||||
public boolean getEffectiveDeny(Player player, AreaConfig ac, String permName) {
|
||||
// ADMIN/OWNER不检查per-player deny
|
||||
PermissionLevel level = getPermissionLevel(player, ac);
|
||||
if (level == PermissionLevel.ADMIN || level == PermissionLevel.OWNER) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 尝试读取per-player权限
|
||||
int landId = getLandIdFromDb(ac.name);
|
||||
if (landId > 0) {
|
||||
Map<String, Boolean> playerPerms = getPlayerPermMap(landId, player.getName());
|
||||
if (playerPerms.containsKey(permName)) {
|
||||
return playerPerms.get(permName);
|
||||
}
|
||||
}
|
||||
|
||||
// 回退到领地默认权限
|
||||
return getLandDefaultDeny(ac, permName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取领地默认deny状态(按字段名映射)
|
||||
*/
|
||||
private boolean getLandDefaultDeny(AreaConfig ac, String permName) {
|
||||
switch (permName) {
|
||||
case "denyMove": return ac.denyMove;
|
||||
case "denyBlockPlace": return ac.denyBlockPlace;
|
||||
case "denyBlockBreak": return ac.denyBlockBreak;
|
||||
case "denyPVP": return ac.denyPVP;
|
||||
case "denyFallDamage": return ac.denyFallDamage;
|
||||
case "denyHunger": return ac.denyHunger;
|
||||
case "denyAllDamage": return ac.denyAllDamage;
|
||||
case "denyDrop": return ac.denyDrop;
|
||||
case "denyMount": return ac.denyMount;
|
||||
case "denyEnderPearl": return ac.denyEnderPearl;
|
||||
case "denyBow": return ac.denyBow;
|
||||
case "denyPotion": return ac.denyPotion;
|
||||
case "denyExplosion": return ac.denyExplosion;
|
||||
case "denyRaid": return ac.denyRaid;
|
||||
case "denyFireSpread": return ac.denyFireSpread;
|
||||
case "denyAllEffects": return ac.denyAllEffects;
|
||||
case "denyItemFrame": return ac.denyItemFrame;
|
||||
case "denyPickup": return ac.denyPickup;
|
||||
case "denyFire": return ac.denyFire;
|
||||
case "denyThrownProjectiles": return ac.denyThrownProjectiles;
|
||||
case "denyGlowing": return ac.denyGlowing;
|
||||
case "denyRedstoneInteraction": return ac.denyRedstoneInteraction;
|
||||
case "denyDoorInteraction": return ac.denyDoorInteraction;
|
||||
case "denyNoteblockJukebox": return ac.denyNoteblockJukebox;
|
||||
case "denyLead": return ac.denyLead;
|
||||
case "denyCropHarvest": return ac.denyCropHarvest;
|
||||
case "denyWoolShear": return ac.denyWoolShear;
|
||||
case "denyAnimalFeeding": return ac.denyAnimalFeeding;
|
||||
case "peaceMode": return ac.peaceMode;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map转简单JSON
|
||||
*/
|
||||
private String mapToJson(Map<String, Boolean> map) {
|
||||
if (map.isEmpty()) return "";
|
||||
StringBuilder sb = new StringBuilder("{");
|
||||
boolean first = true;
|
||||
for (Map.Entry<String, Boolean> e : map.entrySet()) {
|
||||
if (!first) sb.append(",");
|
||||
sb.append("\"").append(e.getKey()).append("\":").append(e.getValue());
|
||||
first = false;
|
||||
}
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定领地的成员列表
|
||||
*/
|
||||
public Set<String> getAreaMembers(String areaName) {
|
||||
Set<String> members = areaPlayerWhitelist.get(areaName);
|
||||
return members != null ? new HashSet<>(members) : new HashSet<>();
|
||||
}
|
||||
|
||||
// ===== 统一白名单检查(供所有事件处理器使用)=====
|
||||
|
||||
/**
|
||||
@@ -2529,8 +2724,8 @@ public class AreaProtection implements Listener {
|
||||
if (newArea != null) {
|
||||
AreaConfig ac = areas.get(newArea);
|
||||
if (ac != null) {
|
||||
// ★ denyMove检查:无OWNER/ADMIN权限的玩家不能移动
|
||||
if (ac.denyMove && !hasPermission(p, ac, PermissionLevel.OWNER)) {
|
||||
// ★ denyMove检查:支持per-player独立权限
|
||||
if (getEffectiveDeny(p, ac, "denyMove")) {
|
||||
Location safeLoc = findSafeExitLocation(p.getLocation(), ac);
|
||||
if (safeLoc != null) {
|
||||
p.teleport(safeLoc);
|
||||
@@ -2669,8 +2864,8 @@ public class AreaProtection implements Listener {
|
||||
|
||||
AreaConfig newAc = areas.get(newArea);
|
||||
if (newAc != null) {
|
||||
// ★ denyMove检查:无OWNER/ADMIN权限的玩家不能进入
|
||||
if (newAc.denyMove && !hasPermission(p, newAc, PermissionLevel.OWNER)) {
|
||||
// ★ denyMove检查:支持per-player独立权限
|
||||
if (getEffectiveDeny(p, newAc, "denyMove")) {
|
||||
Location safeLoc = findSafeExitLocation(event.getFrom(), newAc);
|
||||
if (safeLoc != null) {
|
||||
p.teleport(safeLoc);
|
||||
@@ -3877,14 +4072,11 @@ public class AreaProtection implements Listener {
|
||||
e.getBlock().getX(),
|
||||
e.getBlock().getY(),
|
||||
e.getBlock().getZ());
|
||||
if (ac != null && ac.denyBlockPlace) {
|
||||
// 访客禁止放置方块,所有者和管理员允许
|
||||
if (!hasPermission(p, ac, PermissionLevel.OWNER)) {
|
||||
if (ac != null && getEffectiveDeny(p, ac, "denyBlockPlace")) {
|
||||
e.setCancelled(true);
|
||||
p.sendMessage("§c§l[区域防护] §f禁止放置方块");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onBlockBreak(BlockBreakEvent e) {
|
||||
@@ -3894,14 +4086,11 @@ public class AreaProtection implements Listener {
|
||||
e.getBlock().getX(),
|
||||
e.getBlock().getY(),
|
||||
e.getBlock().getZ());
|
||||
if (ac != null && ac.denyBlockBreak) {
|
||||
// 访客禁止破坏方块,所有者和管理员允许
|
||||
if (!hasPermission(p, ac, PermissionLevel.OWNER)) {
|
||||
if (ac != null && getEffectiveDeny(p, ac, "denyBlockBreak")) {
|
||||
e.setCancelled(true);
|
||||
p.sendMessage("§c§l[区域防护] §f禁止破坏方块");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@EventHandler
|
||||
@@ -3914,9 +4103,9 @@ public class AreaProtection implements Listener {
|
||||
p.getLocation().getBlockY(),
|
||||
p.getLocation().getBlockZ());
|
||||
if (ac == null) return;
|
||||
// 保护性规则:对所有人无条件生效
|
||||
if (ac.denyAllDamage) { e.setCancelled(true); return; }
|
||||
if (ac.denyFallDamage
|
||||
// 保护性规则:支持per-player独立权限
|
||||
if (getEffectiveDeny(p, ac, "denyAllDamage")) { e.setCancelled(true); return; }
|
||||
if (getEffectiveDeny(p, ac, "denyFallDamage")
|
||||
&& e.getCause() == EntityDamageEvent.DamageCause.FALL)
|
||||
e.setCancelled(true);
|
||||
}
|
||||
@@ -3932,14 +4121,11 @@ public class AreaProtection implements Listener {
|
||||
p.getLocation().getBlockX(),
|
||||
p.getLocation().getBlockY(),
|
||||
p.getLocation().getBlockZ());
|
||||
if (ac != null && ac.denyPVP) {
|
||||
// 访客禁止PVP,所有者和管理员允许
|
||||
if (!hasPermission(p, ac, PermissionLevel.OWNER)) {
|
||||
if (ac != null && getEffectiveDeny(p, ac, "denyPVP")) {
|
||||
e.setCancelled(true);
|
||||
p.sendMessage("§c§l[区域防护] §f禁止PVP");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@EventHandler
|
||||
@@ -3951,7 +4137,7 @@ public class AreaProtection implements Listener {
|
||||
p.getLocation().getBlockX(),
|
||||
p.getLocation().getBlockY(),
|
||||
p.getLocation().getBlockZ());
|
||||
if (ac != null && ac.denyHunger)
|
||||
if (ac != null && getEffectiveDeny(p, ac, "denyHunger"))
|
||||
e.setCancelled(true);
|
||||
}
|
||||
|
||||
@@ -3965,13 +4151,11 @@ public class AreaProtection implements Listener {
|
||||
p.getLocation().getBlockX(),
|
||||
p.getLocation().getBlockY(),
|
||||
p.getLocation().getBlockZ());
|
||||
if (ac != null && ac.denyDrop) {
|
||||
if (!hasPermission(p, ac, PermissionLevel.OWNER)) {
|
||||
if (ac != null && getEffectiveDeny(p, ac, "denyDrop")) {
|
||||
e.setCancelled(true);
|
||||
p.sendMessage("§c§l[区域防护] §f禁止丢弃物品");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 替换整个 onInteract 方法
|
||||
@EventHandler
|
||||
@@ -4047,26 +4231,22 @@ public class AreaProtection implements Listener {
|
||||
p.getLocation().getBlockZ());
|
||||
if (ac == null) return;
|
||||
|
||||
// 末影珍珠禁止 (only for visitors)
|
||||
if (ac.denyEnderPearl
|
||||
// 末影珍珠禁止(支持per-player权限)
|
||||
if (getEffectiveDeny(p, ac, "denyEnderPearl")
|
||||
&& hand.getType() == Material.ENDER_PEARL) {
|
||||
if (!hasPermission(p, ac, PermissionLevel.OWNER)) {
|
||||
e.setCancelled(true);
|
||||
p.sendMessage("§c§l[区域防护] §f禁止使用末影珍珠");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 弓箭禁止 (only for visitors)
|
||||
if (ac.denyBow
|
||||
// 弓箭禁止(支持per-player权限)
|
||||
if (getEffectiveDeny(p, ac, "denyBow")
|
||||
&& (hand.getType() == Material.BOW
|
||||
|| hand.getType() == Material.CROSSBOW)) {
|
||||
if (!hasPermission(p, ac, PermissionLevel.OWNER)) {
|
||||
e.setCancelled(true);
|
||||
p.sendMessage("§c§l[区域防护] §f禁止使用弓箭");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 所有者+管理员跳过禁止使用物品检查
|
||||
if (hasPermission(p, ac, PermissionLevel.OWNER)) {
|
||||
@@ -4217,13 +4397,11 @@ public class AreaProtection implements Listener {
|
||||
shooter.getLocation().getBlockX(),
|
||||
shooter.getLocation().getBlockY(),
|
||||
shooter.getLocation().getBlockZ());
|
||||
if (ac != null && ac.denyPotion) {
|
||||
if (!hasPermission(shooter, ac, PermissionLevel.OWNER)) {
|
||||
if (ac != null && getEffectiveDeny(shooter, ac, "denyPotion")) {
|
||||
e.setCancelled(true);
|
||||
shooter.sendMessage("§c§l[区域防护] §f禁止使用药水");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== ★ 投掷物检测(三叉戟、雪球、风蛋、箭)=====
|
||||
@EventHandler
|
||||
@@ -4237,8 +4415,7 @@ public class AreaProtection implements Listener {
|
||||
shooter.getLocation().getBlockY(),
|
||||
shooter.getLocation().getBlockZ());
|
||||
if (ac == null) return;
|
||||
if (!ac.denyThrownProjectiles) return;
|
||||
if (hasPermission(shooter, ac, PermissionLevel.OWNER)) return;
|
||||
if (!getEffectiveDeny(shooter, ac, "denyThrownProjectiles")) return;
|
||||
// 检查投掷物类型:箭、三叉戟、雪球、风蛋、龙息弹等
|
||||
String typeName = proj.getType().name();
|
||||
if (typeName.contains("ARROW") || typeName.contains("TRIDENT")
|
||||
@@ -4262,8 +4439,7 @@ public class AreaProtection implements Listener {
|
||||
e.getClickedBlock().getY(),
|
||||
e.getClickedBlock().getZ());
|
||||
if (ac == null) return;
|
||||
if (!ac.denyDoorInteraction) return;
|
||||
if (hasPermission(p, ac, PermissionLevel.OWNER)) return;
|
||||
if (!getEffectiveDeny(p, ac, "denyDoorInteraction")) return;
|
||||
// 按钮
|
||||
if (mat.name().contains("BUTTON")) {
|
||||
e.setCancelled(true);
|
||||
@@ -4291,8 +4467,7 @@ public class AreaProtection implements Listener {
|
||||
e.getClickedBlock().getY(),
|
||||
e.getClickedBlock().getZ());
|
||||
if (ac == null) return;
|
||||
if (!ac.denyDoorInteraction) return;
|
||||
if (hasPermission(p, ac, PermissionLevel.OWNER)) return;
|
||||
if (!getEffectiveDeny(p, ac, "denyDoorInteraction")) return;
|
||||
String name = mat.name();
|
||||
if (name.contains("DOOR") || name.contains("GATE") || name.contains("FENCE_GATE")) {
|
||||
e.setCancelled(true);
|
||||
@@ -4312,8 +4487,7 @@ public class AreaProtection implements Listener {
|
||||
e.getClickedBlock().getY(),
|
||||
e.getClickedBlock().getZ());
|
||||
if (ac == null) return;
|
||||
if (!ac.denyRedstoneInteraction) return;
|
||||
if (hasPermission(p, ac, PermissionLevel.OWNER)) return;
|
||||
if (!getEffectiveDeny(p, ac, "denyRedstoneInteraction")) return;
|
||||
if (mat == Material.REPEATER || mat == Material.COMPARATOR
|
||||
|| mat.name().contains("REPEATER") || mat.name().contains("COMPARATOR")) {
|
||||
e.setCancelled(true);
|
||||
@@ -4333,8 +4507,7 @@ public class AreaProtection implements Listener {
|
||||
e.getClickedBlock().getY(),
|
||||
e.getClickedBlock().getZ());
|
||||
if (ac == null) return;
|
||||
if (!ac.denyNoteblockJukebox) return;
|
||||
if (hasPermission(p, ac, PermissionLevel.OWNER)) return;
|
||||
if (!getEffectiveDeny(p, ac, "denyNoteblockJukebox")) return;
|
||||
if (mat == Material.NOTE_BLOCK || mat.name().contains("JUKEBOX")
|
||||
|| mat.name().contains("CHERRY_JUKEBOX")) {
|
||||
e.setCancelled(true);
|
||||
@@ -4368,8 +4541,7 @@ public class AreaProtection implements Listener {
|
||||
loc.getBlockY(),
|
||||
loc.getBlockZ());
|
||||
if (ac == null) return;
|
||||
if (!ac.denyWoolShear) return;
|
||||
if (hasPermission(p, ac, PermissionLevel.OWNER)) return;
|
||||
if (!getEffectiveDeny(p, ac, "denyWoolShear")) return;
|
||||
String entityType = e.getEntity().getType().name();
|
||||
if (entityType.contains("SHEEP") || entityType.contains("MOoshroom")) {
|
||||
e.setCancelled(true);
|
||||
@@ -4397,8 +4569,7 @@ public class AreaProtection implements Listener {
|
||||
loc.getBlockY(),
|
||||
loc.getBlockZ());
|
||||
if (ac == null) return;
|
||||
if (!ac.denyAnimalFeeding) return;
|
||||
if (hasPermission(p, ac, PermissionLevel.OWNER)) return;
|
||||
if (!getEffectiveDeny(p, ac, "denyAnimalFeeding")) return;
|
||||
// 检查玩家手持的物品是否是食物
|
||||
ItemStack hand = p.getInventory().getItemInMainHand();
|
||||
if (hand != null && hand.getType().isEdible()) {
|
||||
@@ -4420,8 +4591,7 @@ public class AreaProtection implements Listener {
|
||||
loc.getBlockY(),
|
||||
loc.getBlockZ());
|
||||
if (ac == null) return;
|
||||
if (!ac.denyLead) return;
|
||||
if (hasPermission(p, ac, PermissionLevel.OWNER)) return;
|
||||
if (!getEffectiveDeny(p, ac, "denyLead")) return;
|
||||
e.setCancelled(true);
|
||||
p.sendMessage("§c§l[区域防护] §f禁止在此区域使用拴绳");
|
||||
}
|
||||
@@ -4832,6 +5002,50 @@ public class AreaProtection implements Listener {
|
||||
// ★ CLI全局配置页面
|
||||
plugin.areaCLIManager.showConfigPage(p);
|
||||
break;
|
||||
case "memberperm":
|
||||
// ★ 成员独立权限列表
|
||||
if (args.length < 3) {
|
||||
p.sendMessage("§c用法: /protect cli memberperm <领地名> [页码]");
|
||||
break;
|
||||
}
|
||||
int mpPage = 1;
|
||||
if (args.length >= 4) {
|
||||
try { mpPage = Integer.parseInt(args[3]); } catch (Exception ignored) {}
|
||||
}
|
||||
plugin.areaCLIManager.showMemberPermList(p, args[2], mpPage);
|
||||
break;
|
||||
case "playerperm":
|
||||
// ★ 某成员的独立权限编辑
|
||||
if (args.length < 4) {
|
||||
p.sendMessage("§c用法: /protect cli playerperm <领地名> <玩家> [页码]");
|
||||
break;
|
||||
}
|
||||
int ppPage = 1;
|
||||
if (args.length >= 5) {
|
||||
try { ppPage = Integer.parseInt(args[4]); } catch (Exception ignored) {}
|
||||
}
|
||||
plugin.areaCLIManager.showPlayerPerm(p, args[2], args[3], ppPage);
|
||||
break;
|
||||
case "toggleplayerperm":
|
||||
// ★ 切换某成员的独立权限
|
||||
if (args.length < 5) {
|
||||
p.sendMessage("§c用法: /protect cli toggleplayerperm <领地名> <玩家> <权限key> [页码]");
|
||||
break;
|
||||
}
|
||||
int tpPage = 1;
|
||||
if (args.length >= 6) {
|
||||
try { tpPage = Integer.parseInt(args[5]); } catch (Exception ignored) {}
|
||||
}
|
||||
plugin.areaCLIManager.togglePlayerPerm(p, args[2], args[3], args[4], tpPage);
|
||||
break;
|
||||
case "clearplayerperm":
|
||||
// ★ 清除某成员所有自定义权限
|
||||
if (args.length < 4) {
|
||||
p.sendMessage("§c用法: /protect cli clearplayerperm <领地名> <玩家>");
|
||||
break;
|
||||
}
|
||||
plugin.areaCLIManager.clearPlayerPerm(p, args[2], args[3]);
|
||||
break;
|
||||
case "create":
|
||||
// 跳转到创建命令
|
||||
// 直接转发
|
||||
@@ -6529,7 +6743,7 @@ public class AreaProtection implements Listener {
|
||||
/**
|
||||
* 从数据库获取领地ID
|
||||
*/
|
||||
private int getLandIdFromDb(String name) {
|
||||
public int getLandIdFromDb(String name) {
|
||||
if (dbConnection == null) return -1;
|
||||
try {
|
||||
PreparedStatement stmt = dbConnection.prepareStatement("SELECT id FROM area_lands WHERE name = ?");
|
||||
@@ -6544,6 +6758,54 @@ public class AreaProtection implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PHP端修改领地所有者时调用
|
||||
*/
|
||||
public void setLandOwnerFromWeb(String landName, String newOwner) {
|
||||
if (dbConnection == null) return;
|
||||
try {
|
||||
// 更新数据库
|
||||
PreparedStatement ps = dbConnection.prepareStatement("UPDATE area_lands SET owner = ? WHERE name = ?");
|
||||
ps.setString(1, newOwner);
|
||||
ps.setString(2, landName);
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
|
||||
// 更新内存中的AreaConfig
|
||||
AreaConfig ac = getLand(landName);
|
||||
if (ac != null) {
|
||||
ac.owner = newOwner;
|
||||
}
|
||||
|
||||
plugin.getLogger().info("[防护] PHP端更新领地所有者: " + landName + " → " + newOwner);
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().warning("[防护] 更新领地所有者失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PHP端清除成员权限时调用
|
||||
*/
|
||||
public void clearPlayerPermFromWeb(String landName, String playerName) {
|
||||
if (dbConnection == null) return;
|
||||
try {
|
||||
int landId = getLandIdFromDb(landName);
|
||||
if (landId < 0) return;
|
||||
|
||||
// 清除数据库中的自定义权限
|
||||
PreparedStatement ps = dbConnection.prepareStatement(
|
||||
"UPDATE area_land_permissions SET permissions = '' WHERE land_id = ? AND player_name = ?");
|
||||
ps.setInt(1, landId);
|
||||
ps.setString(2, playerName);
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
|
||||
plugin.getLogger().info("[防护] PHP端清除成员权限: " + playerName + " @ " + landName);
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().warning("[防护] 清除成员权限失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化时长(秒 → 可读字符串)
|
||||
*/
|
||||
@@ -7076,11 +7338,9 @@ public class AreaProtection implements Listener {
|
||||
if (!(e.getDamager() instanceof Player)) return;
|
||||
Player p = (Player) e.getDamager();
|
||||
|
||||
// ★ 统一白名单检查(globalPlayerWhitelist + areaPlayerWhitelist)
|
||||
// ★ 统一白名单检查(支持per-player权限)
|
||||
AreaConfig ac = findFrameArea(entity);
|
||||
if (ac == null || !ac.denyItemFrame) return;
|
||||
|
||||
if (hasPermission(p, ac, PermissionLevel.OWNER)) return;
|
||||
if (ac == null || !getEffectiveDeny(p, ac, "denyItemFrame")) return;
|
||||
|
||||
e.setCancelled(true);
|
||||
p.sendMessage("§c§l[区域防护] §f禁止破坏展示框");
|
||||
@@ -7096,13 +7356,11 @@ public class AreaProtection implements Listener {
|
||||
p2.getLocation().getBlockX(),
|
||||
p2.getLocation().getBlockY(),
|
||||
p2.getLocation().getBlockZ());
|
||||
if (ac2 != null && ac2.denyPVP) {
|
||||
if (!hasPermission(p2, ac2, PermissionLevel.OWNER)) {
|
||||
if (ac2 != null && getEffectiveDeny(p2, ac2, "denyPVP")) {
|
||||
e.setCancelled(true);
|
||||
p2.sendMessage("§c§l[区域防护] §f禁止PVP");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 展示框被点击(旋转/放入/取出)
|
||||
@EventHandler
|
||||
@@ -7117,12 +7375,9 @@ public class AreaProtection implements Listener {
|
||||
|
||||
Player p = e.getPlayer();
|
||||
|
||||
// ★ 使用统一的权限检查方法
|
||||
// ★ 使用统一的权限检查方法(支持per-player权限)
|
||||
AreaConfig ac = findFrameArea(clicked);
|
||||
if (ac == null || !ac.denyItemFrame) return;
|
||||
|
||||
// 所有者+管理员跳过
|
||||
if (hasPermission(p, ac, PermissionLevel.OWNER)) return;
|
||||
if (ac == null || !getEffectiveDeny(p, ac, "denyItemFrame")) return;
|
||||
|
||||
// 非白名单 → 拦截
|
||||
// 记录当前旋转角度
|
||||
@@ -7176,10 +7431,8 @@ public class AreaProtection implements Listener {
|
||||
AreaConfig ac = findFrameArea(entity);
|
||||
if (ac == null) return;
|
||||
|
||||
// ★ 使用统一的权限检查方法
|
||||
if (hasPermission(p, ac, PermissionLevel.OWNER)) return;
|
||||
|
||||
if (!ac.denyItemFrame) return;
|
||||
// ★ 使用统一的权限检查方法(支持per-player权限)
|
||||
if (!getEffectiveDeny(p, ac, "denyItemFrame")) return;
|
||||
|
||||
e.setCancelled(true);
|
||||
p.sendMessage("§c§l[区域防护] §f禁止破坏展示框");
|
||||
@@ -7195,13 +7448,11 @@ public class AreaProtection implements Listener {
|
||||
p2.getLocation().getBlockX(),
|
||||
p2.getLocation().getBlockY(),
|
||||
p2.getLocation().getBlockZ());
|
||||
if (ac2 != null && ac2.denyPVP) {
|
||||
if (!hasPermission(p2, ac2, PermissionLevel.OWNER)) {
|
||||
if (ac2 != null && getEffectiveDeny(p2, ac2, "denyPVP")) {
|
||||
e.setCancelled(true);
|
||||
p2.sendMessage("§c§l[区域防护] §f禁止PVP");
|
||||
}
|
||||
}
|
||||
}
|
||||
private AreaConfig findFrameArea(Entity frame) {
|
||||
Location loc = frame.getLocation();
|
||||
int bx = loc.getBlockX();
|
||||
@@ -7239,11 +7490,9 @@ public class AreaProtection implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
// ★ 使用统一的权限检查方法
|
||||
// ★ 使用统一的权限检查方法(支持per-player权限)
|
||||
AreaConfig ac = findFrameArea(hit);
|
||||
if (ac == null || !ac.denyItemFrame) return;
|
||||
|
||||
if (hasPermission(shooter, ac, PermissionLevel.OWNER)) return;
|
||||
if (ac == null || !getEffectiveDeny(shooter, ac, "denyItemFrame")) return;
|
||||
|
||||
e.setCancelled(true);
|
||||
shooter.sendMessage("§c§l[区域防护] §f禁止破坏展示框");
|
||||
@@ -7365,8 +7614,7 @@ public class AreaProtection implements Listener {
|
||||
p.getLocation().getBlockX(),
|
||||
p.getLocation().getBlockY(),
|
||||
p.getLocation().getBlockZ());
|
||||
if (ac != null && ac.denyGlowing) {
|
||||
if (hasPermission(p, ac, PermissionLevel.OWNER)) continue;
|
||||
if (ac != null && getEffectiveDeny(p, ac, "denyGlowing")) {
|
||||
if (p.hasPotionEffect(PotionEffectType.GLOWING)) {
|
||||
p.removePotionEffect(PotionEffectType.GLOWING);
|
||||
p.sendMessage("§c§l[区域防护] §f此区域禁止使用发光效果");
|
||||
|
||||
@@ -6147,7 +6147,8 @@ public class Main extends JavaPlugin
|
||||
// ★ cli: 子命令
|
||||
if (sub.equals("cli")) {
|
||||
return filterTab(
|
||||
Arrays.asList("menu", "lands", "manage", "members", "visitorperm", "toggle", "create", "config"),
|
||||
Arrays.asList("menu", "lands", "manage", "members", "visitorperm", "toggle", "create", "config",
|
||||
"memberperm", "playerperm", "toggleplayerperm", "clearplayerperm"),
|
||||
args[1]);
|
||||
}
|
||||
}
|
||||
@@ -6210,11 +6211,13 @@ public class Main extends JavaPlugin
|
||||
Arrays.asList("create_price", "max_lands", "default_height", "peace_duration"),
|
||||
args[2]);
|
||||
}
|
||||
// ★ cli manage/members/visitorperm/toggle: 第三层是领地名
|
||||
// ★ cli manage/members/visitorperm/toggle/memberperm/playerperm/toggleplayerperm/clearplayerperm: 第三层是领地名
|
||||
if (sub.equals("cli")) {
|
||||
String action = args[1].toLowerCase();
|
||||
if (action.equals("manage") || action.equals("members")
|
||||
|| action.equals("visitorperm") || action.equals("toggle")) {
|
||||
|| action.equals("visitorperm") || action.equals("toggle")
|
||||
|| action.equals("memberperm") || action.equals("playerperm")
|
||||
|| action.equals("toggleplayerperm") || action.equals("clearplayerperm")) {
|
||||
return filterTab(
|
||||
new ArrayList<>(areaProtection.getAreaNames()),
|
||||
args[2]);
|
||||
@@ -6222,7 +6225,8 @@ public class Main extends JavaPlugin
|
||||
}
|
||||
}
|
||||
|
||||
// ★ 第四层:cli toggle需要权限key,cli manage/members/visitorperm需要页码
|
||||
// ★ 第四层:cli toggle需要权限key,cli manage/members/visitorperm/memberperm需要页码
|
||||
// playerperm/toggleplayerperm需要玩家名,clearplayerperm需要玩家名
|
||||
if (args.length == 4) {
|
||||
if (sub.equals("cli")) {
|
||||
String action = args[1].toLowerCase();
|
||||
@@ -6238,10 +6242,42 @@ public class Main extends JavaPlugin
|
||||
args[3]);
|
||||
}
|
||||
if (action.equals("manage") || action.equals("members")
|
||||
|| action.equals("visitorperm")) {
|
||||
|| action.equals("visitorperm") || action.equals("memberperm")) {
|
||||
// 页码
|
||||
return Arrays.asList("1", "2", "3", "4", "5");
|
||||
}
|
||||
if (action.equals("playerperm") || action.equals("clearplayerperm")) {
|
||||
// 玩家名
|
||||
List<String> players = new ArrayList<>();
|
||||
for (Player pl : Bukkit.getOnlinePlayers())
|
||||
players.add(pl.getName());
|
||||
return filterTab(players, args[3]);
|
||||
}
|
||||
if (action.equals("toggleplayerperm")) {
|
||||
// 玩家名
|
||||
List<String> players = new ArrayList<>();
|
||||
for (Player pl : Bukkit.getOnlinePlayers())
|
||||
players.add(pl.getName());
|
||||
return filterTab(players, args[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ★ 第五层:cli toggleplayerperm需要权限key
|
||||
if (args.length == 5) {
|
||||
if (sub.equals("cli")) {
|
||||
String action = args[1].toLowerCase();
|
||||
if (action.equals("toggleplayerperm")) {
|
||||
// 权限key
|
||||
return filterTab(Arrays.asList(
|
||||
"denyMove", "denyBlockPlace", "denyBlockBreak", "denyPVP", "denyMount",
|
||||
"denyEnderPearl", "denyThrownProjectiles", "denyRaid", "denyBow", "denyPotion",
|
||||
"denyFire", "denyFireSpread", "denyPickup", "denyDrop", "denyExplosion",
|
||||
"denyFallDamage", "denyHunger", "denyAllDamage", "denyAllEffects",
|
||||
"denyItemFrame", "denyRedstoneInteraction", "denyDoorInteraction", "denyNoteblockJukebox",
|
||||
"denyLead", "denyCropHarvest", "denyWoolShear", "denyAnimalFeeding", "denyGlowing", "peaceMode"),
|
||||
args[4]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Web通信管理器 - 插件与PHP后端的桥梁
|
||||
@@ -315,18 +316,9 @@ public class WebManager {
|
||||
SSLContext sc = SSLContext.getInstance("TLSv1.2");
|
||||
sc.init(null, trustAllCerts, new SecureRandom());
|
||||
|
||||
// ★ 配置SSL参数:指定兼容CF的密码套件
|
||||
// ★ 不要硬编码密码套件!JVM可能不支持某些套件导致IllegalArgumentException
|
||||
// 只固定协议版本为TLSv1.2,密码套件让JVM自动选择
|
||||
javax.net.ssl.SSLParameters sslParams = sc.getDefaultSSLParameters();
|
||||
sslParams.setCipherSuites(new String[]{
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
|
||||
"TLS_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA384"
|
||||
});
|
||||
sslParams.setProtocols(new String[]{"TLSv1.2"});
|
||||
|
||||
// ★ java.net.http.HttpClient + HTTP/1.1
|
||||
@@ -339,7 +331,7 @@ public class WebManager {
|
||||
.version(HttpClient.Version.HTTP_1_1)
|
||||
.build();
|
||||
|
||||
plugin.getLogger().info("[Web通信] SSL已初始化(TLSv1.2, 信任所有证书, CF兼容密码套件, HTTP/1.1)");
|
||||
plugin.getLogger().info("[Web通信] SSL已初始化(TLSv1.2, 信任所有证书, 自动密码套件, HTTP/1.1)");
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().severe("[Web通信] SSL完全初始化失败: " + e.getMessage());
|
||||
}
|
||||
@@ -361,12 +353,7 @@ public class WebManager {
|
||||
sc.init(null, trustAllCerts, new SecureRandom());
|
||||
|
||||
javax.net.ssl.SSLParameters sslParams = sc.getDefaultSSLParameters();
|
||||
sslParams.setCipherSuites(new String[]{
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"
|
||||
});
|
||||
// ★ 不硬编码密码套件,让JVM自动选择支持的套件
|
||||
sslParams.setProtocols(new String[]{"TLSv1.2"});
|
||||
|
||||
cfHttpClient = HttpClient.newBuilder()
|
||||
@@ -654,6 +641,7 @@ public class WebManager {
|
||||
submitNormalDbTask("首次-syncAllPlayerIps", () -> syncAllPlayerIps());
|
||||
submitNormalDbTask("首次-syncServiceProviders", () -> syncServiceProviders());
|
||||
submitNormalDbTask("首次-syncLandData", () -> syncLandData());
|
||||
submitNormalDbTask("首次-pollAdminChanges", () -> pollAdminChanges());
|
||||
}
|
||||
}.runTaskLaterAsynchronously(plugin, 20L * 10);
|
||||
|
||||
@@ -1118,6 +1106,7 @@ public class WebManager {
|
||||
submitNormalDbTask("周期-syncAllPlayerIps", () -> syncAllPlayerIps());
|
||||
submitNormalDbTask("周期-syncServiceProviders", () -> syncServiceProviders());
|
||||
submitNormalDbTask("周期-syncLandData", () -> syncLandData());
|
||||
submitNormalDbTask("周期-pollAdminChanges", () -> pollAdminChanges());
|
||||
} else {
|
||||
// ★ 不在线:跳过非必要同步任务
|
||||
plugin.getLogger().info("[Web通信] 全员下线,跳过非登录类同步任务");
|
||||
@@ -2215,6 +2204,84 @@ public class WebManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 轮询PHP管理员变更(所有者变更、权限清除等)
|
||||
*/
|
||||
private long lastPollAdminChangesId = 0;
|
||||
|
||||
public void pollAdminChanges() {
|
||||
if (!enabled) return;
|
||||
|
||||
try {
|
||||
AreaProtection areaProtect = plugin.getAreaProtection();
|
||||
if (areaProtect == null) return;
|
||||
|
||||
// 查询PHP端管理员变更
|
||||
String url = webBaseUrl + "/api/land_api.php?action=poll_admin_changes&secret=" + secretKey + "&last_id=" + lastPollAdminChangesId + "&limit=50";
|
||||
String response = doGet(url);
|
||||
if (response == null) return;
|
||||
|
||||
Map<String, Object> result = parseJson(response);
|
||||
if (!Boolean.TRUE.equals(result.get("success"))) return;
|
||||
|
||||
List<Map<String, Object>> changes = (List<Map<String, Object>>) result.get("changes");
|
||||
if (changes == null || changes.isEmpty()) return;
|
||||
|
||||
List<Integer> ackedIds = new ArrayList<>();
|
||||
|
||||
for (Map<String, Object> change : changes) {
|
||||
int id = ((Number) change.getOrDefault("id", 0)).intValue();
|
||||
String changeType = String.valueOf(change.getOrDefault("change_type", ""));
|
||||
String targetName = String.valueOf(change.getOrDefault("target_name", ""));
|
||||
String changeDataStr = String.valueOf(change.getOrDefault("change_data", "{}"));
|
||||
|
||||
Map<String, Object> changeData = parseJson(changeDataStr);
|
||||
if (changeData == null) changeData = new HashMap<>();
|
||||
|
||||
try {
|
||||
switch (changeType) {
|
||||
case "owner_change": {
|
||||
// PHP端修改了领地所有者
|
||||
String newOwner = String.valueOf(changeData.getOrDefault("new_owner", ""));
|
||||
String landName = targetName;
|
||||
if (!newOwner.isEmpty() && !landName.isEmpty()) {
|
||||
// 更新本地领地所有者
|
||||
areaProtect.setLandOwnerFromWeb(landName, newOwner);
|
||||
plugin.getLogger().info("[Web通信] PHP端领地所有者变更: " + landName + " → " + newOwner);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "perm_clear": {
|
||||
// PHP端清除了成员权限
|
||||
String playerName = targetName;
|
||||
String landNameJson = String.valueOf(changeData.getOrDefault("land_name", ""));
|
||||
if (!playerName.isEmpty() && !landNameJson.isEmpty()) {
|
||||
// 清除本地成员权限
|
||||
areaProtect.clearPlayerPermFromWeb(landNameJson, playerName);
|
||||
plugin.getLogger().info("[Web通信] PHP端清除成员权限: " + playerName + " @ " + landNameJson);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
plugin.getLogger().fine("[Web通信] 未知变更类型: " + changeType);
|
||||
}
|
||||
ackedIds.add(id);
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().warning("[Web通信] 处理变更失败(id=" + id + "): " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 确认已处理的变更
|
||||
if (!ackedIds.isEmpty()) {
|
||||
String ackUrl = webBaseUrl + "/api/land_api.php?action=ack_admin_changes&secret=" + secretKey;
|
||||
String ackBody = "ids=" + String.join(",", ackedIds.stream().map(String::valueOf).collect(Collectors.toList()));
|
||||
httpPost(ackUrl, ackBody);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().warning("[Web通信] 轮询PHP管理员变更异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 5. 推送债券余额快照到Web端
|
||||
*/
|
||||
@@ -3072,6 +3139,7 @@ public class WebManager {
|
||||
submitDbTask("管理-syncUserRegistrations", () -> syncUserRegistrations());
|
||||
submitDbTask("管理-syncServiceProviders", () -> syncServiceProviders());
|
||||
submitDbTask("管理-syncLandData", () -> syncLandData());
|
||||
submitDbTask("管理-pollAdminChanges", () -> pollAdminChanges());
|
||||
}
|
||||
}.runTaskAsynchronously(plugin);
|
||||
sender.sendMessage("§a[Web] 同步任务已启动(通过DB队列串行化)...");
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import paramiko
|
||||
import sys
|
||||
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
try:
|
||||
ssh.connect('sfe4-connect.simpfun.cn', port=2046, username='sfe3192491.df7d2f7f', password='5350427807', timeout=30)
|
||||
sftp = ssh.open_sftp()
|
||||
sftp.put('D:/桌面/源码/mc_plugn/Sdf1_login/target/Sdf1_login.jar', '/plugins/Sdf1_login.jar')
|
||||
sftp.close()
|
||||
print('SFTP upload success')
|
||||
except Exception as e:
|
||||
print(f'Error: {e}', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
finally:
|
||||
ssh.close()
|
||||
Reference in New Issue
Block a user