diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e6edbc..e68fc88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # 更新日志 -## 1.4.0 - 2026-08-02 +## 1.3.0 - 2026-08-02 ### 新增 diff --git a/README.md b/README.md index a90c5c4..e88a727 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,15 @@ - `/essc admin` 管理模式切换 - `/maintenance` `(/maint)` 维护模式管理 +### 控制台命令 + +- `/essc help`、`/essc version`、`/essc reload` +- `/maintenance [on|off|status|reload|add|remove|list]`,也可通过 `/essc maintenance ...` 执行 +- `/seen [玩家]`、`/heal [玩家]`、`/feed [玩家]`、`/tpsbar [玩家]` +- `/essc skin [status|refresh] [玩家]` + +依赖发送者位置、背包或玩家交互状态的命令仅允许在游戏内执行。控制台执行玩家状态命令时必须明确提供目标玩家。 + ### 其它功能 - Shift + 右键快捷打开潜影盒 diff --git a/build.gradle b/build.gradle index 234fc6a..de6f2ca 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ plugins { } group = 'cn.infstar' -version = providers.gradleProperty('pluginVersion').orElse('1.4.0').get() +version = providers.gradleProperty('pluginVersion').orElse('1.3.0').get() repositories { mavenCentral() diff --git a/scripts/smoke-test-paper.sh b/scripts/smoke-test-paper.sh index 46e5cee..5be57f6 100644 --- a/scripts/smoke-test-paper.sh +++ b/scripts/smoke-test-paper.sh @@ -31,7 +31,7 @@ printf 'online-mode=false\nserver-port=0\nenable-query=false\n' > "$workspace/se server_pid=$! for _ in $(seq 1 120); do - if grep -Fq 'EssentialsC v1.4.0 已启用' "$workspace/server.log"; then + if grep -Fq 'EssentialsC v1.3.0 已启用' "$workspace/server.log"; then if grep -Eq 'UnsupportedClassVersionError|Error occurred while enabling EssentialsC|Could not load.*EssentialsC' "$workspace/server.log"; then cat "$workspace/server.log" exit 1 diff --git a/src/main/java/cn/infstar/essentialsC/EssentialsC.java b/src/main/java/cn/infstar/essentialsC/EssentialsC.java index 5a12a76..7369314 100644 --- a/src/main/java/cn/infstar/essentialsC/EssentialsC.java +++ b/src/main/java/cn/infstar/essentialsC/EssentialsC.java @@ -448,7 +448,8 @@ public final class EssentialsC extends JavaPlugin { @Override public Collection suggest(CommandSourceStack commandSourceStack, String[] args) { if (executor instanceof TabCompleter completer) { - return completer.onTabComplete(commandSourceStack.getSender(), commandAdapter, name, args); + String[] completionArgs = args.length == 0 ? new String[]{""} : args; + return completer.onTabComplete(commandSourceStack.getSender(), commandAdapter, name, completionArgs); } return List.of(); } diff --git a/src/main/java/cn/infstar/essentialsC/LangManager.java b/src/main/java/cn/infstar/essentialsC/LangManager.java index 6e39a4d..238c98e 100644 --- a/src/main/java/cn/infstar/essentialsC/LangManager.java +++ b/src/main/java/cn/infstar/essentialsC/LangManager.java @@ -21,6 +21,9 @@ import java.util.regex.Pattern; public class LangManager { private static final Pattern HEX_COLOR_PATTERN = Pattern.compile("(?i)&#([0-9a-f]{6})"); + private static final Pattern MINI_MESSAGE_TAG_PATTERN = Pattern.compile( + "(?])" + ); private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage(); private static final LegacyComponentSerializer LEGACY_AMPERSAND = LegacyComponentSerializer.legacyAmpersand(); private static final LegacyComponentSerializer LEGACY_SECTION = LegacyComponentSerializer.legacySection(); @@ -161,18 +164,25 @@ public class LangManager { langFile.setDefaults(selectedDefaults); } - private String applyPlaceholders(String value, Map placeholders) { + static String applyPlaceholders(String value, Map placeholders) { String result = value; + boolean miniMessage = looksLikeMiniMessage(value); for (Map.Entry entry : placeholders.entrySet()) { - result = result.replace("{" + entry.getKey() + "}", escapeMiniMessageReplacement(entry.getValue())); + String replacement = miniMessage + ? escapeMiniMessageReplacement(entry.getValue()) + : entry.getValue(); + result = result.replace("{" + entry.getKey() + "}", replacement == null ? "" : replacement); } return result; } - private String escapeMiniMessageReplacement(String value) { + static String escapeMiniMessageReplacement(String value) { if (value == null) { return ""; } + if (value.indexOf('§') >= 0) { + return MINI_MESSAGE.serialize(LEGACY_SECTION.deserialize(value)); + } return value.replace("\\", "\\\\").replace("<", "\\<"); } @@ -213,7 +223,7 @@ public class LangManager { } if (looksLikeMiniMessage(text)) { try { - return MINI_MESSAGE.deserialize(text); + return MINI_MESSAGE.deserialize(normalizeMiniMessageTags(text)); } catch (RuntimeException ignored) { // 配置中 MiniMessage 语法错误时回退到旧颜色码解析,避免消息完全不可用。 } @@ -221,8 +231,20 @@ public class LangManager { return LEGACY_SECTION.deserialize(translateColorCodes(text)); } - private boolean looksLikeMiniMessage(String text) { + private static boolean looksLikeMiniMessage(String text) { int open = text.indexOf('<'); return open >= 0 && text.indexOf('>', open) > open; } + + static String normalizeMiniMessageTags(String text) { + Matcher matcher = MINI_MESSAGE_TAG_PATTERN.matcher(text); + StringBuilder result = new StringBuilder(); + while (matcher.find()) { + matcher.appendReplacement(result, Matcher.quoteReplacement( + matcher.group(1) + matcher.group(2).toLowerCase(java.util.Locale.ROOT) + )); + } + matcher.appendTail(result); + return result.toString(); + } } diff --git a/src/main/java/cn/infstar/essentialsC/commands/BaseCommand.java b/src/main/java/cn/infstar/essentialsC/commands/BaseCommand.java index d08e74d..fd57c3c 100644 --- a/src/main/java/cn/infstar/essentialsC/commands/BaseCommand.java +++ b/src/main/java/cn/infstar/essentialsC/commands/BaseCommand.java @@ -49,12 +49,17 @@ public abstract class BaseCommand implements CommandExecutor { @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + return dispatch(sender, args); + } + + final boolean dispatch(CommandSender sender, String[] args) { + if (!sender.hasPermission(permission)) { + sender.sendMessage(getLang().getPrefixedString("messages.no-permission", + Map.of("permission", permission))); + return true; + } + if (sender instanceof Player player) { - if (!player.hasPermission(permission)) { - player.sendMessage(getLang().getPrefixedString("messages.no-permission", - Map.of("permission", permission))); - return true; - } return execute(player, args); } diff --git a/src/main/java/cn/infstar/essentialsC/commands/FeedCommand.java b/src/main/java/cn/infstar/essentialsC/commands/FeedCommand.java index 1198f1c..0cd57bb 100644 --- a/src/main/java/cn/infstar/essentialsC/commands/FeedCommand.java +++ b/src/main/java/cn/infstar/essentialsC/commands/FeedCommand.java @@ -1,6 +1,7 @@ package cn.infstar.essentialsC.commands; import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import java.util.Map; @@ -36,6 +37,30 @@ public class FeedCommand extends BaseCommand { return true; } + @Override + protected boolean executeConsole(CommandSender sender, String[] args) { + if (args.length != 1) { + sender.sendMessage(getLang().getPrefixedString("messages.feed-usage-console")); + return true; + } + if (!sender.hasPermission("essentialsc.command.feed.others")) { + sender.sendMessage(getLang().getPrefixedString("messages.no-permission-others")); + return true; + } + + Player target = Bukkit.getPlayerExact(args[0]); + if (target == null) { + sender.sendMessage(getLang().getPrefixedString("messages.player-not-found", Map.of("player", args[0]))); + return true; + } + + feedPlayer(target); + sender.sendMessage(getLang().getPrefixedString("messages.feed-other", Map.of("player", target.getName()))); + target.sendMessage(getLang().getPrefixedString("messages.feed-by-other", + Map.of("admin", getLang().getString("messages.console-name")))); + return true; + } + private void feedPlayer(Player player) { player.setFoodLevel(20); player.setSaturation(20f); diff --git a/src/main/java/cn/infstar/essentialsC/commands/HealCommand.java b/src/main/java/cn/infstar/essentialsC/commands/HealCommand.java index f143451..e93f4b5 100644 --- a/src/main/java/cn/infstar/essentialsC/commands/HealCommand.java +++ b/src/main/java/cn/infstar/essentialsC/commands/HealCommand.java @@ -3,6 +3,7 @@ package cn.infstar.essentialsC.commands; import org.bukkit.Bukkit; import org.bukkit.attribute.Attribute; import org.bukkit.attribute.AttributeInstance; +import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import java.util.Map; @@ -38,6 +39,30 @@ public class HealCommand extends BaseCommand { return true; } + @Override + protected boolean executeConsole(CommandSender sender, String[] args) { + if (args.length != 1) { + sender.sendMessage(getLang().getPrefixedString("messages.heal-usage-console")); + return true; + } + if (!sender.hasPermission("essentialsc.command.heal.others")) { + sender.sendMessage(getLang().getPrefixedString("messages.no-permission-others")); + return true; + } + + Player target = Bukkit.getPlayerExact(args[0]); + if (target == null) { + sender.sendMessage(getLang().getPrefixedString("messages.player-not-found", Map.of("player", args[0]))); + return true; + } + + healPlayer(target); + sender.sendMessage(getLang().getPrefixedString("messages.heal-other", Map.of("player", target.getName()))); + target.sendMessage(getLang().getPrefixedString("messages.heal-by-other", + Map.of("admin", getLang().getString("messages.console-name")))); + return true; + } + private void healPlayer(Player player) { AttributeInstance maxHealth = player.getAttribute(Attribute.MAX_HEALTH); player.setHealth(maxHealth == null ? player.getHealth() : maxHealth.getValue()); diff --git a/src/main/java/cn/infstar/essentialsC/commands/HelpCommand.java b/src/main/java/cn/infstar/essentialsC/commands/HelpCommand.java index 4abd187..07ee2d2 100644 --- a/src/main/java/cn/infstar/essentialsC/commands/HelpCommand.java +++ b/src/main/java/cn/infstar/essentialsC/commands/HelpCommand.java @@ -38,7 +38,12 @@ public class HelpCommand extends BaseCommand implements TabCompleter { @Override protected boolean executeConsole(CommandSender sender, String[] args) { - if (args.length > 0 && args[0].equalsIgnoreCase("reload")) { + if (args.length == 0 || args[0].equalsIgnoreCase("help")) { + sendConsoleHelp(sender); + return true; + } + + if (args[0].equalsIgnoreCase("reload")) { if (!sender.hasPermission("essentialsc.command.reload")) { sendNoPermission(sender, "essentialsc.command.reload"); return true; @@ -51,10 +56,62 @@ public class HelpCommand extends BaseCommand implements TabCompleter { sender.sendMessage(getLang().getPrefixedString("messages.config-reloaded")); return true; } - sender.sendMessage(getLang().getPrefixedString("messages.player-only")); + + if (args[0].equalsIgnoreCase("version") || args[0].equalsIgnoreCase("v")) { + sender.sendMessage(getLang().getPrefixedString("messages.version", + Map.of("version", plugin.getPluginMeta().getVersion()))); + sender.sendMessage(getLang().getPrefixedString("messages.paper-version", + Map.of("version", Bukkit.getVersion()))); + return true; + } + + String actualCommand = getActualCommand(args[0]); + BaseCommand targetCommand = CommandRegistry.getCommand(actualCommand); + if (actualCommand != null && targetCommand != null) { + return targetCommand.dispatch(sender, Arrays.copyOfRange(args, 1, args.length)); + } + + sender.sendMessage(getLang().getPrefixedString("messages.unknown-subcommand", + Map.of("command", args[0]))); + sender.sendMessage(getLang().getPrefixedString("messages.help-usage")); return true; } + private void sendConsoleHelp(CommandSender sender) { + LangManager lang = getLang(); + sendPrefixed(sender, lang.getString("help.title")); + sendPrefixed(sender, lang.getString("help.version", + Map.of("version", plugin.getPluginMeta().getVersion()))); + sender.sendMessage(""); + + sendPrefixed(sender, lang.getString("help.section-other")); + if (sender.hasPermission("essentialsc.command.reload")) { + sendPrefixed(sender, lang.getString("help.commands.reload")); + } + sendPrefixed(sender, lang.getString("help.commands.version")); + if (CommandRegistry.isAvailable("seen") && sender.hasPermission("essentialsc.command.seen")) { + sendPrefixed(sender, lang.getString("help.commands.seen")); + } + if (CommandRegistry.isAvailable("heal") && sender.hasPermission("essentialsc.command.heal")) { + sendPrefixed(sender, lang.getString("help.commands.heal")); + } + if (CommandRegistry.isAvailable("feed") && sender.hasPermission("essentialsc.command.feed")) { + sendPrefixed(sender, lang.getString("help.commands.feed")); + } + if (CommandRegistry.isAvailable("skin") && sender.hasPermission("essentialsc.command.skin")) { + sendPrefixed(sender, lang.getString("help.commands.skin")); + } + if (CommandRegistry.isAvailable("tpsbar") && sender.hasPermission("essentialsc.command.tpsbar")) { + sendPrefixed(sender, lang.getString("help.commands.tpsbar")); + } + if (CommandRegistry.isAvailable("maintenance") + && sender.hasPermission("essentialsc.command.maintenance")) { + sendPrefixed(sender, lang.getString("help.commands.maintenance")); + } + sender.sendMessage(""); + sendPrefixed(sender, lang.getString("help.footer")); + } + private boolean handleCommand(CommandSender sender, Player player, String[] args) { if (args.length > 0) { String subCommand = args[0].toLowerCase(); @@ -246,7 +303,7 @@ public class HelpCommand extends BaseCommand implements TabCompleter { } private void sendPrefixed(CommandSender sender, String message) { - sender.sendMessage(message); + sender.sendMessage(getLang().getPrefix() + message); } private void sendPrefixedLines(CommandSender sender, String message) { diff --git a/src/main/java/cn/infstar/essentialsC/commands/SeenCommand.java b/src/main/java/cn/infstar/essentialsC/commands/SeenCommand.java index 3608b2a..cdc3c66 100644 --- a/src/main/java/cn/infstar/essentialsC/commands/SeenCommand.java +++ b/src/main/java/cn/infstar/essentialsC/commands/SeenCommand.java @@ -2,6 +2,7 @@ package cn.infstar.essentialsC.commands; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import java.text.SimpleDateFormat; @@ -16,45 +17,56 @@ public class SeenCommand extends BaseCommand { @Override protected boolean execute(Player player, String[] args) { + return executeCommand(player, player, args); + } + + @Override + protected boolean executeConsole(CommandSender sender, String[] args) { + return executeCommand(sender, null, args); + } + + private boolean executeCommand(CommandSender sender, Player viewer, String[] args) { if (args.length == 0) { - player.sendMessage(getLang().getPrefixedString("messages.seen-usage")); + sender.sendMessage(getLang().getPrefixedString(viewer == null + ? "messages.seen-usage-console" + : "messages.seen-usage")); return true; } Player onlineTarget = Bukkit.getPlayerExact(args[0]); if (onlineTarget != null && VanishCommand.isVanished(onlineTarget) - && !player.hasPermission(VanishCommand.SEE_PERMISSION)) { - player.sendMessage(getLang().getPrefixedString("messages.player-not-found", Map.of("player", args[0]))); + && viewer != null && !viewer.hasPermission(VanishCommand.SEE_PERMISSION)) { + sender.sendMessage(getLang().getPrefixedString("messages.player-not-found", Map.of("player", args[0]))); return true; } OfflinePlayer target = Bukkit.getOfflinePlayer(args[0]); if (!target.hasPlayedBefore() && !target.isOnline()) { - player.sendMessage(getLang().getPrefixedString("messages.player-not-found", Map.of("player", args[0]))); + sender.sendMessage(getLang().getPrefixedString("messages.player-not-found", Map.of("player", args[0]))); return true; } SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - player.sendMessage(getLang().getPrefixedComponent("messages.seen-header", + sender.sendMessage(getLang().getPrefixedComponent("messages.seen-header", Map.of("player", String.valueOf(target.getName())))); if (target.isOnline()) { - player.sendMessage(getLang().getComponent("messages.seen-status-online")); + sender.sendMessage(getLang().getComponent("messages.seen-status-online")); Player onlinePlayer = target.getPlayer(); if (onlinePlayer != null) { - player.sendMessage(getLang().getComponent("messages.seen-world", + sender.sendMessage(getLang().getComponent("messages.seen-world", Map.of("world", onlinePlayer.getWorld().getName()))); } } else { - player.sendMessage(getLang().getComponent("messages.seen-status-offline")); + sender.sendMessage(getLang().getComponent("messages.seen-status-offline")); long lastSeen = target.getLastSeen(); if (lastSeen > 0) { - player.sendMessage(getLang().getComponent("messages.seen-last-online", + sender.sendMessage(getLang().getComponent("messages.seen-last-online", Map.of("time", format.format(new Date(lastSeen))))); } } - player.sendMessage(getLang().getComponent("messages.seen-first-joined", + sender.sendMessage(getLang().getComponent("messages.seen-first-joined", Map.of("time", format.format(new Date(target.getFirstPlayed()))))); return true; } diff --git a/src/main/java/cn/infstar/essentialsC/commands/TpsBarCommand.java b/src/main/java/cn/infstar/essentialsC/commands/TpsBarCommand.java index 3647e09..9a83a19 100644 --- a/src/main/java/cn/infstar/essentialsC/commands/TpsBarCommand.java +++ b/src/main/java/cn/infstar/essentialsC/commands/TpsBarCommand.java @@ -63,17 +63,37 @@ public class TpsBarCommand extends BaseCommand implements TabCompleter { @Override protected boolean executeConsole(CommandSender sender, String[] args) { - sender.sendMessage(getLang().getPrefixedString("messages.player-only")); + TpsBarService tpsBarService = plugin.getTpsBarManager(); + if (tpsBarService == null) { + sender.sendMessage(getLang().getPrefixedString("messages.module-disabled")); + return true; + } + if (args.length != 1) { + sender.sendMessage(tpsBarService.getUsageMessage()); + return true; + } + if (!sender.hasPermission("essentialsc.command.tpsbar.others")) { + sender.sendMessage(getLang().getPrefixedString("messages.no-permission", + Map.of("permission", "essentialsc.command.tpsbar.others"))); + return true; + } + + Player target = Bukkit.getPlayerExact(args[0]); + if (target == null) { + sender.sendMessage(tpsBarService.getPlayerNotFoundMessage(args[0])); + return true; + } + + boolean enabled = tpsBarService.toggle(target); + sender.sendMessage(getLang().getPrefixedString(enabled + ? "tpsbar.messages.enabled-other" + : "tpsbar.messages.disabled-other", Map.of("player", target.getName()))); return true; } @Override public List onTabComplete(CommandSender sender, Command command, String label, String[] args) { - if (args.length != 1 || !(sender instanceof Player player)) { - return List.of(); - } - - if (!player.hasPermission("essentialsc.command.tpsbar.others")) { + if (args.length != 1 || !sender.hasPermission("essentialsc.command.tpsbar.others")) { return List.of(); } diff --git a/src/main/java/cn/infstar/essentialsC/listeners/ShulkerBoxListener.java b/src/main/java/cn/infstar/essentialsC/listeners/ShulkerBoxListener.java index d3baa49..40c80cb 100644 --- a/src/main/java/cn/infstar/essentialsC/listeners/ShulkerBoxListener.java +++ b/src/main/java/cn/infstar/essentialsC/listeners/ShulkerBoxListener.java @@ -109,7 +109,7 @@ public class ShulkerBoxListener implements Listener { } } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + @EventHandler(priority = EventPriority.HIGHEST) public void onPlayerInteract(PlayerInteractEvent event) { if (event.getAction() != Action.RIGHT_CLICK_AIR && event.getAction() != Action.RIGHT_CLICK_BLOCK) { return; @@ -120,6 +120,10 @@ public class ShulkerBoxListener implements Listener { return; } + if (event.useItemInHand() == org.bukkit.event.Event.Result.DENY) { + return; + } + if (openShulkerBoxes.containsKey(player.getUniqueId())) { return; } diff --git a/src/main/java/cn/infstar/essentialsC/maintenance/MaintenanceListener.java b/src/main/java/cn/infstar/essentialsC/maintenance/MaintenanceListener.java index a69edb8..a9af27d 100644 --- a/src/main/java/cn/infstar/essentialsC/maintenance/MaintenanceListener.java +++ b/src/main/java/cn/infstar/essentialsC/maintenance/MaintenanceListener.java @@ -45,7 +45,9 @@ public final class MaintenanceListener implements Listener { } MaintenanceManager.AccessSnapshot snapshot = maintenanceManager.getAccessSnapshot(); - if (!snapshot.enabled() || snapshot.isWhitelisted(event.getUniqueId(), event.getName())) { + if (!snapshot.enabled() + || snapshot.isWhitelisted(event.getUniqueId(), event.getName()) + || maintenanceManager.isOperator(event.getUniqueId())) { return; } diff --git a/src/main/java/cn/infstar/essentialsC/maintenance/MaintenanceManager.java b/src/main/java/cn/infstar/essentialsC/maintenance/MaintenanceManager.java index cca20e4..3f4d71c 100644 --- a/src/main/java/cn/infstar/essentialsC/maintenance/MaintenanceManager.java +++ b/src/main/java/cn/infstar/essentialsC/maintenance/MaintenanceManager.java @@ -12,6 +12,7 @@ import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitTask; import java.io.File; import java.io.IOException; @@ -30,13 +31,18 @@ public final class MaintenanceManager { private final EssentialsC plugin; private final File configFile; + private final BukkitTask operatorCacheTask; private FileConfiguration config; private BossBar bossBar; private volatile AccessSnapshot accessSnapshot = AccessSnapshot.disabled(); + private volatile Set operatorUuids = Set.of(); public MaintenanceManager(EssentialsC plugin) { this.plugin = plugin; this.configFile = new File(plugin.getDataFolder(), "maintenance.yml"); + refreshOperatorCache(); + this.operatorCacheTask = plugin.getServer().getScheduler() + .runTaskTimer(plugin, this::refreshOperatorCache, 20L, 20L); reload(); } @@ -109,7 +115,11 @@ public final class MaintenanceManager { } public boolean canJoin(Player player) { - return player != null && (player.hasPermission(getBypassPermission()) || isWhitelisted(player)); + return player != null && (player.isOp() || player.hasPermission(getBypassPermission()) || isWhitelisted(player)); + } + + public boolean isOperator(UUID uniqueId) { + return uniqueId != null && operatorUuids.contains(uniqueId); } public AccessSnapshot getAccessSnapshot() { @@ -231,10 +241,20 @@ public final class MaintenanceManager { } public void shutdown() { + operatorCacheTask.cancel(); accessSnapshot = AccessSnapshot.disabled(); + operatorUuids = Set.of(); clearBossBar(); } + private void refreshOperatorCache() { + Set operators = new LinkedHashSet<>(); + for (org.bukkit.OfflinePlayer operator : plugin.getServer().getOperators()) { + operators.add(operator.getUniqueId()); + } + operatorUuids = Set.copyOf(operators); + } + private void clearBossBar() { if (bossBar == null) { return; diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 816fefc..5617769 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -29,11 +29,11 @@ mob-drops: # 允许末影人掉落搬运的方块。 allow-drops: true -# TPA 传送请求,行为参考 HuskHomes。 +# TPA 传送请求 tpa: # 请求有效时间,单位为秒。 timeout-seconds: 60 - # 每名接收者最多保留的待处理请求数;参考 EssentialsX,超出时移除最早请求。 + # 每名接收者最多保留的待处理请求数;超出时移除最早请求。 max-pending-requests: 5 # 位置规则 diff --git a/src/main/resources/lang/en_US.yml b/src/main/resources/lang/en_US.yml index 79d6181..55e9813 100644 --- a/src/main/resources/lang/en_US.yml +++ b/src/main/resources/lang/en_US.yml @@ -1,11 +1,12 @@ # English language file -# HuskHomes-inspired style: MiniMessage, green success, red errors, gray details. +# MiniMessage format with green success, red errors, and gray details. -prefix: '' +prefix: '<#00FB9A>[EssentialsC]: ' messages: no-permission: '<#FF3300>Error: <#FF7E5E>You do not have permission to use this command. Required: {permission}' player-only: '<#FF3300>Error: <#FF7E5E>This command can only be run by players.' + console-name: 'Console' config-reloaded: '<#00FB9A>Configuration reloaded.' version: '<#00FB9A>EssentialsC v{version}' paper-version: 'Running on Paper {version}' @@ -49,9 +50,11 @@ messages: heal-self: '<#00FB9A>Your health and hunger have been restored.' heal-other: '<#00FB9A>You healed {player}.' heal-by-other: '<#00FB9A>You were healed by {admin}.' + heal-usage-console: '<#FF3300>Error: <#FF7E5E>Usage: /heal [player]' feed-self: '<#00FB9A>Your hunger has been restored.' feed-other: '<#00FB9A>You fed {player}.' feed-by-other: '<#00FB9A>You were fed by {admin}.' + feed-usage-console: '<#FF3300>Error: <#FF7E5E>Usage: /feed [player]' repair-hand-success: '<#00FB9A>Repaired the item in your hand.' repair-all-success: '<#00FB9A>Repaired {count} item(s).' repair-not-damaged: '<#FF3300>Error: <#FF7E5E>This item is not damaged.' @@ -76,6 +79,8 @@ help: footer: 'Each command requires the corresponding permission.' commands: + reload: '/essc reload - Reload plugin configuration' + version: '/essc version - View plugin and server versions' workbench: '/workbench - Open a crafting table' anvil: '/anvil - Open an anvil' enchantingtable: '/enchantingtable - Open an enchanting table' @@ -91,10 +96,10 @@ help: fly: '/fly - Toggle flight mode' nightvision: '/nightvision - Toggle night vision' glow: '/glow - Toggle glowing' - heal: '/heal - Restore health and hunger' + heal: '/heal [player] - Restore health and hunger' vanish: '/vanish - Toggle vanish mode' - seen: '/seen - View player information' - feed: '/feed - Restore hunger' + seen: '/seen [player] - View player information' + feed: '/feed [player] - Restore hunger' repair: '/repair - Repair held or all items' tpa: '/tpa [player] - Request to teleport to a player' tpahere: '/tpahere [player] - Request a player teleport to you' diff --git a/src/main/resources/lang/zh_CN.yml b/src/main/resources/lang/zh_CN.yml index b75823a..e4ca994 100644 --- a/src/main/resources/lang/zh_CN.yml +++ b/src/main/resources/lang/zh_CN.yml @@ -1,11 +1,12 @@ # 简体中文语言文件 -# 文本风格参考 HuskHomes:MiniMessage、绿色成功、红色错误、灰色说明。 +# 支持 MiniMessage 格式,默认使用绿色成功、红色错误和灰色说明。 -prefix: '' +prefix: '<#00FB9A>[EssentialsC]: ' messages: no-permission: '<#FF3300>错误: <#FF7E5E>你没有权限执行此命令. 需要权限: {permission}' player-only: '<#FF3300>错误: <#FF7E5E>此命令只能由玩家执行.' + console-name: '控制台' config-reloaded: '<#00FB9A>配置已重载.' version: '<#00FB9A>EssentialsC v{version}' paper-version: '当前运行于 Paper {version}' @@ -49,9 +50,11 @@ messages: heal-self: '<#00FB9A>你的生命值和饥饿值已恢复.' heal-other: '<#00FB9A>你已治疗玩家 {player}.' heal-by-other: '<#00FB9A>管理员 {admin} 治疗了你.' + heal-usage-console: '<#FF3300>错误: <#FF7E5E>用法: /heal [玩家]' feed-self: '<#00FB9A>你的饥饿值已恢复.' feed-other: '<#00FB9A>你已喂饱玩家 {player}.' feed-by-other: '<#00FB9A>管理员 {admin} 喂饱了你.' + feed-usage-console: '<#FF3300>错误: <#FF7E5E>用法: /feed [玩家]' repair-hand-success: '<#00FB9A>手中物品已修复.' repair-all-success: '<#00FB9A>已修复 {count} 个物品.' repair-not-damaged: '<#FF3300>错误: <#FF7E5E>该物品没有损坏.' @@ -76,6 +79,8 @@ help: footer: '每个命令都需要对应权限.' commands: + reload: '/essc reload - 重载插件配置' + version: '/essc version - 查看插件与服务端版本' workbench: '/workbench - 打开工作台' anvil: '/anvil - 打开铁砧' enchantingtable: '/enchantingtable - 打开附魔台' @@ -91,10 +96,10 @@ help: fly: '/fly - 切换飞行模式' nightvision: '/nightvision - 切换夜视' glow: '/glow - 切换发光' - heal: '/heal - 恢复生命值和饥饿值' + heal: '/heal [玩家] - 恢复生命值和饥饿值' vanish: '/vanish - 切换隐身模式' - seen: '/seen - 查看玩家信息' - feed: '/feed - 恢复饥饿值' + seen: '/seen [玩家] - 查看玩家信息' + feed: '/feed [玩家] - 恢复饥饿值' repair: '/repair - 修复手中或全部物品' tpa: '/tpa [玩家] - 请求传送到玩家身边' tpahere: '/tpahere [玩家] - 请求玩家传送到你身边' diff --git a/src/test/java/cn/infstar/essentialsC/ConfigurationResourcesTest.java b/src/test/java/cn/infstar/essentialsC/ConfigurationResourcesTest.java index b5f8ced..2bac035 100644 --- a/src/test/java/cn/infstar/essentialsC/ConfigurationResourcesTest.java +++ b/src/test/java/cn/infstar/essentialsC/ConfigurationResourcesTest.java @@ -4,6 +4,8 @@ import org.bukkit.configuration.file.YamlConfiguration; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.junit.jupiter.api.Test; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import java.io.InputStream; import java.io.InputStreamReader; @@ -40,6 +42,9 @@ class ConfigurationResourcesTest { if (resourcePath.equals("paper-plugin.yml")) { assertEquals("1.21.11", configuration.getString("api-version")); } + if (resourcePath.startsWith("lang/")) { + assertTrue(!configuration.getString("prefix", "").isBlank()); + } } } @@ -51,6 +56,25 @@ class ConfigurationResourcesTest { assertEquals(chinese.getKeys(true), english.getKeys(true)); } + @Test + void miniMessageTagNamesAreNormalized() { + assertEquals("文本", LangManager.normalizeMiniMessageTags("文本")); + assertEquals("\\", LangManager.normalizeMiniMessageTags("\\")); + } + + @Test + void legacyFormattingCanBeNestedInMiniMessage() { + String message = LangManager.applyPlaceholders( + "维护状态: {status}", + java.util.Map.of("status", "§a开启") + ); + String rendered = PlainTextComponentSerializer.plainText().serialize( + MiniMessage.miniMessage().deserialize(message) + ); + + assertEquals("维护状态: 开启", rendered); + } + private YamlConfiguration loadResource(String resourcePath) throws Exception { try (InputStream input = getClass().getClassLoader().getResourceAsStream(resourcePath)) { assertNotNull(input, resourcePath);