#801 菜单控制台指令+玩家名变量(Main.java 两处处理器) - 原代码只匹配英文 {player} 等模式,实际菜单用中文 {玩家名} - 新增5种中文占位符支持:{玩家名} %玩家名% <玩家名> [玩家名] (玩家名) - 控制台权限检测本身正常(日志确认走了console路径) #802 PVP世界种子固定问题(PVPArenaManager.java createPVPWorld) - creator.createWorld() 改为 Bukkit.createWorld(creator)(完整Bukkit管线更可靠处理seed) - 新增种子验证:创建后比对期望种子 vs MC实际种子,日志明确报告是否匹配 - 种子不匹配时输出警告(Paper 26.x 已知行为,需观察 Bukkit.createWorld 是否解决) #803 .web.md PHP专属商品同步(ShopManager.java buildCatalogJson) - loadCategories() 继续跳过 .web.md(游戏内不加载,行为不变) - buildCatalogJson() 新增第二遍扫描:单独读取 .web.md 文件并纳入同步JSON - PHP 收银台现在能展示 .web.md 中的专属商品
This commit is contained in:
@@ -3589,13 +3589,19 @@ public class Main extends JavaPlugin
|
||||
if (cmd.endsWith(".txt")) {
|
||||
gui.openSubMenu(p, cmd);
|
||||
} else {
|
||||
// ★ 替换玩家名变量:{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);
|
||||
.replaceAll("\\(player\\)", playerName)
|
||||
// ★ 中文占位符(用户实际在菜单中使用的格式)
|
||||
.replaceAll("\\{玩家名\\}", playerName)
|
||||
.replaceAll("%玩家名%", playerName)
|
||||
.replaceAll("<玩家名>", playerName)
|
||||
.replaceAll("\\[玩家名\\]", playerName)
|
||||
.replaceAll("\\(玩家名\\)", playerName);
|
||||
// ★ 根据权限类型选择执行身份
|
||||
if ("控制台".equals(permType) || "console".equalsIgnoreCase(permType)) {
|
||||
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), cmd);
|
||||
@@ -3876,13 +3882,19 @@ public class Main extends JavaPlugin
|
||||
if (cmd.endsWith(".txt")) {
|
||||
gui.openSubMenu(p, cmd);
|
||||
} else {
|
||||
// ★ 替换玩家名变量
|
||||
// ★ 替换玩家名变量(支持中英文括号形式)
|
||||
String playerName = p.getName();
|
||||
cmd = cmd.replaceAll("\\{player\\}", playerName)
|
||||
.replaceAll("%player%", playerName)
|
||||
.replaceAll("<player>", playerName)
|
||||
.replaceAll("\\[player\\]", playerName)
|
||||
.replaceAll("\\(player\\)", playerName);
|
||||
.replaceAll("\\(player\\)", playerName)
|
||||
// ★ 中文占位符
|
||||
.replaceAll("\\{玩家名\\}", playerName)
|
||||
.replaceAll("%玩家名%", playerName)
|
||||
.replaceAll("<玩家名>", playerName)
|
||||
.replaceAll("\\[玩家名\\}", playerName)
|
||||
.replaceAll("\\(玩家名\\)", playerName);
|
||||
// ★ 根据权限类型选择执行身份
|
||||
if ("控制台".equals(permType) || "console".equalsIgnoreCase(permType)) {
|
||||
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), cmd);
|
||||
|
||||
@@ -265,18 +265,30 @@ public class PVPArenaManager implements Listener {
|
||||
plugin.getLogger().info("[PVP] ✅ 确认旧 pvp_arena 目录已清除,将生成全新世界");
|
||||
}
|
||||
|
||||
World pvpWorld = creator.createWorld();
|
||||
// ★ 使用 Bukkit.createWorld() 而非 creator.createWorld():
|
||||
// Bukkit 管线会完整处理 WorldCreator 的所有配置(包括 seed),在某些 Paper 版本中更可靠。
|
||||
// creator.createWorld() 是捷径方法,可能跳过某些初始化步骤导致 seed 被内部值覆盖。
|
||||
World pvpWorld = Bukkit.createWorld(creator);
|
||||
long dt = System.currentTimeMillis() - t0;
|
||||
if (pvpWorld == null) {
|
||||
plugin.getLogger().severe("[PVP] 无法创建PVP竞技场世界! (createWorld返回null, 耗时" + dt + "ms)");
|
||||
plugin.getLogger().severe("[PVP] 无法创建PVP竞技场世界! (Bukkit.createWorld返回null, 耗时" + dt + "ms)");
|
||||
return null;
|
||||
}
|
||||
plugin.getLogger().info("[PVP] createWorld 完成, 耗时=" + dt + "ms, 世界环境="
|
||||
|
||||
// ★ 关键验证:检查 MC 是否真的使用了我们指定的种子(Paper 26.x 已知会忽略 creator.seed)
|
||||
long actualSeed = pvpWorld.getSeed();
|
||||
boolean seedMatches = (actualSeed == seed);
|
||||
plugin.getLogger().info("[PVP] Bukkit.createWorld 完成, 耗时=" + dt + "ms, 世界环境="
|
||||
+ pvpWorld.getEnvironment()
|
||||
+ ", 生成器=" + (pvpWorld.getGenerator() == null ? "默认(无插件生成器)" : pvpWorld.getGenerator().getClass().getSimpleName())
|
||||
+ ", MC种子=" + pvpWorld.getSeed()
|
||||
+ ", 自然出生点=(" + pvpWorld.getSpawnLocation().getBlockX() + ","
|
||||
+ pvpWorld.getSpawnLocation().getBlockY() + "," + pvpWorld.getSpawnLocation().getBlockZ() + ")");
|
||||
+ ", 期望种子=" + seed
|
||||
+ ", MC实际种子=" + actualSeed
|
||||
+ (seedMatches ? " ✅ 种子匹配" : " ❌ 种子不匹配!MC忽略了我们的seed设定"));
|
||||
|
||||
if (!seedMatches) {
|
||||
plugin.getLogger().warning("[PVP] ⚠ Paper/MC 忽略了 WorldCreator.seed() 设定!"
|
||||
+ " 这可能是 Paper 26.x 的已知行为。地形可能仍为固定小岛。");
|
||||
}
|
||||
|
||||
reapplyWorldRules(pvpWorld);
|
||||
|
||||
|
||||
@@ -2264,6 +2264,8 @@ public class ShopManager implements Listener {
|
||||
*/
|
||||
public String buildCatalogJson() {
|
||||
JsonArray cats = new JsonArray();
|
||||
|
||||
// 第一遍:内存中的普通分类(不含 .web.md)
|
||||
for (ShopCategory cat : categories) {
|
||||
JsonObject catObj = new JsonObject();
|
||||
catObj.addProperty("name", cat.getName());
|
||||
@@ -2281,6 +2283,35 @@ public class ShopManager implements Listener {
|
||||
catObj.add("items", items);
|
||||
cats.add(catObj);
|
||||
}
|
||||
|
||||
// ★ 第二遍:扫描 .web.md 文件(PHP专属商品,游戏内不加载但需同步给 PHP)
|
||||
File shopDir = new File(plugin.getDataFolder(), "shop");
|
||||
if (shopDir.exists()) {
|
||||
File[] webMdFiles = shopDir.listFiles((d, n) -> n.endsWith(".web.md"));
|
||||
if (webMdFiles != null) {
|
||||
for (File webMd : webMdFiles) {
|
||||
ShopCategory webCat = parseFile(webMd);
|
||||
if (webCat != null && !webCat.getItems().isEmpty()) {
|
||||
JsonObject catObj = new JsonObject();
|
||||
catObj.addProperty("name", webCat.getName());
|
||||
catObj.addProperty("file", webMd.getName()); // 含 .web.md 后缀,PHP 可据此区分来源
|
||||
JsonArray items = new JsonArray();
|
||||
for (ShopItem it : webCat.getItems()) {
|
||||
JsonObject itObj = new JsonObject();
|
||||
itObj.addProperty("id", it.getId());
|
||||
itObj.addProperty("display_name", it.getDisplayName());
|
||||
itObj.addProperty("material", it.getMaterial() == null ? "STONE" : it.getMaterial().name());
|
||||
itObj.addProperty("buy_price", it.getBuyPrice());
|
||||
itObj.addProperty("sell_price", it.getSellPrice());
|
||||
items.add(itObj);
|
||||
}
|
||||
catObj.add("items", items);
|
||||
cats.add(catObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JsonObject root = new JsonObject();
|
||||
root.add("categories", cats);
|
||||
return root.toString();
|
||||
|
||||
Reference in New Issue
Block a user