修复: PVP显式随机种子(根除固定-5092种子) + 菜单控制台指令+玩家名变量 + 商店跳过.web.md(PHP专属商品)

- PVPArenaManager: createPVPWorld改用worldSeedRandom.nextLong()显式设定随机种子(用户实测连续多次createWorld返回相同种子-5092048402667366605);增加删除后验证日志(确认目录消失或告警)
- Main.java(两处菜单点击): 新增permType提取→控制台/console身份执行(Bukkit.getConsoleSender);玩家名变量替换{player}/%player%/<player>/[player]/(player)等所有括号形式
- ShopManager: loadCategories文件过滤器增加!.web.md条件,游戏内不加载.web.md文件(PHP端专属商品)
This commit is contained in:
2026-07-08 10:57:46 +08:00
parent 3c15e6b060
commit 7631e30e2a
3 changed files with 66 additions and 6 deletions
+44 -2
View File
@@ -3573,6 +3573,14 @@ public class Main extends JavaPlugin
p.sendMessage("§c指令为空");
return;
}
// ★ 提取权限类型(控制台/玩家/OP)
String permType = "玩家";
for (String pl : subLm.getLore()) {
if (pl.contains("§7权限: §f")) {
permType = pl.replace("§7权限: §f", "").trim();
break;
}
}
p.closeInventory();
if (cmd.startsWith("/")) {
cmd = cmd.substring(1);
@@ -3581,7 +3589,20 @@ public class Main extends JavaPlugin
if (cmd.endsWith(".txt")) {
gui.openSubMenu(p, cmd);
} else {
Bukkit.dispatchCommand(p, cmd);
// ★ 替换玩家名变量:{player} %player% <player> [player] (player) 等
String playerName = p.getName();
cmd = cmd.replaceAll("\\{player\\}", playerName)
.replaceAll("%player%", playerName)
.replaceAll("<player>", playerName)
.replaceAll("\\[player\\]", playerName)
.replaceAll("\\(player\\)", playerName);
// ★ 根据权限类型选择执行身份
if ("控制台".equals(permType) || "console".equalsIgnoreCase(permType)) {
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), cmd);
p.sendMessage("§a[菜单] 已以控制台身份执行: /" + cmd);
} else {
Bukkit.dispatchCommand(p, cmd);
}
}
return;
}
@@ -3840,6 +3861,14 @@ public class Main extends JavaPlugin
p.sendMessage("§c指令为空");
return;
}
// ★ 提取权限类型(控制台/玩家/OP)
String permType = "玩家";
for (String pl : meta.getLore()) {
if (pl.contains("§7权限: §f")) {
permType = pl.replace("§7权限: §f", "").trim();
break;
}
}
p.closeInventory();
if (cmd.startsWith("/")) {
cmd = cmd.substring(1);
@@ -3847,7 +3876,20 @@ public class Main extends JavaPlugin
if (cmd.endsWith(".txt")) {
gui.openSubMenu(p, cmd);
} else {
Bukkit.dispatchCommand(p, cmd);
// ★ 替换玩家名变量
String playerName = p.getName();
cmd = cmd.replaceAll("\\{player\\}", playerName)
.replaceAll("%player%", playerName)
.replaceAll("<player>", playerName)
.replaceAll("\\[player\\]", playerName)
.replaceAll("\\(player\\)", playerName);
// ★ 根据权限类型选择执行身份
if ("控制台".equals(permType) || "console".equalsIgnoreCase(permType)) {
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), cmd);
p.sendMessage("§a[菜单] 已以控制台身份执行: /" + cmd);
} else {
Bukkit.dispatchCommand(p, cmd);
}
}
return;
}
+18 -2
View File
@@ -241,14 +241,30 @@ public class PVPArenaManager implements Listener {
WorldCreator creator = new WorldCreator(PVP_WORLD_NAME);
creator.environment(World.Environment.NORMAL);
creator.type(WorldType.NORMAL);
// ★ 不设 seed:让 MC 自行随机决定地形种子(插件对地形零干预)。
// 创建后通过 getSeed() 读取 MC 选定的种子并记录日志,便于排查"出生点脚下是水"的失败种子。
// ★ 显式设置随机种子:避免 MC 内部 RNG 在特定条件下产生相同种子导致地形重复。
// 用户实测发现连续多次 createWorld 返回相同种子(-5092048402667366605),
// 显式指定 worldSeedRandom.nextLong() 保证每次地形不同。
long seed = worldSeedRandom.nextLong();
creator.seed(seed);
plugin.getLogger().info("[PVP] 显式设定种子=" + seed + " (确保每次地形不同)");
// ★ 彻底清理磁盘上可能残留的旧世界目录:旧版本曾干预出生点(setSpawnLocation)并强制造陆,
// 若上次删除因 Windows 文件锁失败,createWorld 会误加载到旧世界(出生点被锁定/小岛地形)。
// 这里重试删除确保目录消失,保证每次进入都是 MC 自然生成的全新主世界与自然出生点。
deleteExistingPVPWorldFolder();
// ★ 验证:确认旧目录确实已消失
File folder = new File(Bukkit.getWorldContainer(), PVP_WORLD_NAME);
if (folder.exists()) {
plugin.getLogger().severe("[PVP] ⚠ 警告:deleteExistingPVPWorldFolder 执行后 pvp_arena 目录仍然存在!createWorld 可能复用旧世界");
moveWorldToTrash(folder);
if (folder.exists()) {
plugin.getLogger().severe("[PVP] ❌ 无法删除 pvp_arena 目录,本次可能仍会使用旧地形/旧种子");
}
} else {
plugin.getLogger().info("[PVP] ✅ 确认旧 pvp_arena 目录已清除,将生成全新世界");
}
World pvpWorld = creator.createWorld();
long dt = System.currentTimeMillis() - t0;
if (pvpWorld == null) {
+4 -2
View File
@@ -985,12 +985,14 @@ public class ShopManager implements Listener {
File[] allMd = dir.listFiles(
(d, n) -> n.endsWith(".md")
&& !n.equals("coupons.md"));
&& !n.equals("coupons.md")
&& !n.contains(".web")); // ★ 跳过 .web.md 文件(PHP专属商品,游戏内不加载)
if (allMd == null || allMd.length == 0) {
createDefaultCategories(dir);
allMd = dir.listFiles(
(d, n) -> n.endsWith(".md")
&& !n.equals("coupons.md"));
&& !n.equals("coupons.md")
&& !n.contains(".web")); // ★ 跳过 .web.md(PHP专属商品)
}
if (allMd == null) {
plugin.getLogger().warning(