fix: 修复 1.3.0 实机测试问题
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
# 更新日志
|
||||
|
||||
## 1.4.0 - 2026-08-02
|
||||
## 1.3.0 - 2026-08-02
|
||||
|
||||
### 新增
|
||||
|
||||
|
||||
@@ -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 + 右键快捷打开潜影盒
|
||||
|
||||
+1
-1
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -448,7 +448,8 @@ public final class EssentialsC extends JavaPlugin {
|
||||
@Override
|
||||
public Collection<String> 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();
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
"(?<!\\\\)(</?)([A-Za-z][A-Za-z0-9_-]*)(?=[:>])"
|
||||
);
|
||||
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<String, String> placeholders) {
|
||||
static String applyPlaceholders(String value, Map<String, String> placeholders) {
|
||||
String result = value;
|
||||
boolean miniMessage = looksLikeMiniMessage(value);
|
||||
for (Map.Entry<String, String> 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,12 +49,17 @@ public abstract class BaseCommand implements CommandExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (sender instanceof Player player) {
|
||||
if (!player.hasPermission(permission)) {
|
||||
player.sendMessage(getLang().getPrefixedString("messages.no-permission",
|
||||
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) {
|
||||
return execute(player, args);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<String> 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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<UUID> 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<UUID> 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;
|
||||
|
||||
@@ -29,11 +29,11 @@ mob-drops:
|
||||
# 允许末影人掉落搬运的方块。
|
||||
allow-drops: true
|
||||
|
||||
# TPA 传送请求,行为参考 HuskHomes。
|
||||
# TPA 传送请求
|
||||
tpa:
|
||||
# 请求有效时间,单位为秒。
|
||||
timeout-seconds: 60
|
||||
# 每名接收者最多保留的待处理请求数;参考 EssentialsX,超出时移除最早请求。
|
||||
# 每名接收者最多保留的待处理请求数;超出时移除最早请求。
|
||||
max-pending-requests: 5
|
||||
|
||||
# 位置规则
|
||||
|
||||
@@ -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><bold>[EssentialsC]</bold></#00FB9A><gray>: </gray>'
|
||||
|
||||
messages:
|
||||
no-permission: '<#FF3300>Error:</#FF3300> <#FF7E5E>You do not have permission to use this command.</#FF7E5E> <gray>Required: {permission}</gray>'
|
||||
player-only: '<#FF3300>Error:</#FF3300> <#FF7E5E>This command can only be run by players.</#FF7E5E>'
|
||||
console-name: 'Console'
|
||||
config-reloaded: '<#00FB9A>Configuration reloaded.</#00FB9A>'
|
||||
version: '<#00FB9A>EssentialsC v{version}</#00FB9A>'
|
||||
paper-version: '<gray>Running on Paper {version}</gray>'
|
||||
@@ -49,9 +50,11 @@ messages:
|
||||
heal-self: '<#00FB9A>Your health and hunger have been restored.</#00FB9A>'
|
||||
heal-other: '<#00FB9A>You healed <bold>{player}</bold>.</#00FB9A>'
|
||||
heal-by-other: '<#00FB9A>You were healed by <bold>{admin}</bold>.</#00FB9A>'
|
||||
heal-usage-console: '<#FF3300>Error:</#FF3300> <#FF7E5E>Usage: /heal [player]</#FF7E5E>'
|
||||
feed-self: '<#00FB9A>Your hunger has been restored.</#00FB9A>'
|
||||
feed-other: '<#00FB9A>You fed <bold>{player}</bold>.</#00FB9A>'
|
||||
feed-by-other: '<#00FB9A>You were fed by <bold>{admin}</bold>.</#00FB9A>'
|
||||
feed-usage-console: '<#FF3300>Error:</#FF3300> <#FF7E5E>Usage: /feed [player]</#FF7E5E>'
|
||||
repair-hand-success: '<#00FB9A>Repaired the item in your hand.</#00FB9A>'
|
||||
repair-all-success: '<#00FB9A>Repaired <bold>{count}</bold> item(s).</#00FB9A>'
|
||||
repair-not-damaged: '<#FF3300>Error:</#FF3300> <#FF7E5E>This item is not damaged.</#FF7E5E>'
|
||||
@@ -76,6 +79,8 @@ help:
|
||||
footer: '<gray>Each command requires the corresponding permission.</gray>'
|
||||
|
||||
commands:
|
||||
reload: '<white>/essc reload</white> <gray>- Reload plugin configuration</gray>'
|
||||
version: '<white>/essc version</white> <gray>- View plugin and server versions</gray>'
|
||||
workbench: '<white>/workbench</white> <gray>- Open a crafting table</gray>'
|
||||
anvil: '<white>/anvil</white> <gray>- Open an anvil</gray>'
|
||||
enchantingtable: '<white>/enchantingtable</white> <gray>- Open an enchanting table</gray>'
|
||||
@@ -91,10 +96,10 @@ help:
|
||||
fly: '<white>/fly</white> <gray>- Toggle flight mode</gray>'
|
||||
nightvision: '<white>/nightvision</white> <gray>- Toggle night vision</gray>'
|
||||
glow: '<white>/glow</white> <gray>- Toggle glowing</gray>'
|
||||
heal: '<white>/heal</white> <gray>- Restore health and hunger</gray>'
|
||||
heal: '<white>/heal [player]</white> <gray>- Restore health and hunger</gray>'
|
||||
vanish: '<white>/vanish</white> <gray>- Toggle vanish mode</gray>'
|
||||
seen: '<white>/seen</white> <gray>- View player information</gray>'
|
||||
feed: '<white>/feed</white> <gray>- Restore hunger</gray>'
|
||||
seen: '<white>/seen [player]</white> <gray>- View player information</gray>'
|
||||
feed: '<white>/feed [player]</white> <gray>- Restore hunger</gray>'
|
||||
repair: '<white>/repair</white> <gray>- Repair held or all items</gray>'
|
||||
tpa: '<white>/tpa [player]</white> <gray>- Request to teleport to a player</gray>'
|
||||
tpahere: '<white>/tpahere [player]</white> <gray>- Request a player teleport to you</gray>'
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
# 简体中文语言文件
|
||||
# 文本风格参考 HuskHomes:MiniMessage、绿色成功、红色错误、灰色说明。
|
||||
# 支持 MiniMessage 格式,默认使用绿色成功、红色错误和灰色说明。
|
||||
|
||||
prefix: ''
|
||||
prefix: '<#00FB9A><bold>[EssentialsC]</bold></#00FB9A><gray>: </gray>'
|
||||
|
||||
messages:
|
||||
no-permission: '<#FF3300>错误:</#FF3300> <#FF7E5E>你没有权限执行此命令.</#FF7E5E> <gray>需要权限: {permission}</gray>'
|
||||
player-only: '<#FF3300>错误:</#FF3300> <#FF7E5E>此命令只能由玩家执行.</#FF7E5E>'
|
||||
console-name: '控制台'
|
||||
config-reloaded: '<#00FB9A>配置已重载.</#00FB9A>'
|
||||
version: '<#00FB9A>EssentialsC v{version}</#00FB9A>'
|
||||
paper-version: '<gray>当前运行于 Paper {version}</gray>'
|
||||
@@ -49,9 +50,11 @@ messages:
|
||||
heal-self: '<#00FB9A>你的生命值和饥饿值已恢复.</#00FB9A>'
|
||||
heal-other: '<#00FB9A>你已治疗玩家 <bold>{player}</bold>.</#00FB9A>'
|
||||
heal-by-other: '<#00FB9A>管理员 <bold>{admin}</bold> 治疗了你.</#00FB9A>'
|
||||
heal-usage-console: '<#FF3300>错误:</#FF3300> <#FF7E5E>用法: /heal [玩家]</#FF7E5E>'
|
||||
feed-self: '<#00FB9A>你的饥饿值已恢复.</#00FB9A>'
|
||||
feed-other: '<#00FB9A>你已喂饱玩家 <bold>{player}</bold>.</#00FB9A>'
|
||||
feed-by-other: '<#00FB9A>管理员 <bold>{admin}</bold> 喂饱了你.</#00FB9A>'
|
||||
feed-usage-console: '<#FF3300>错误:</#FF3300> <#FF7E5E>用法: /feed [玩家]</#FF7E5E>'
|
||||
repair-hand-success: '<#00FB9A>手中物品已修复.</#00FB9A>'
|
||||
repair-all-success: '<#00FB9A>已修复 <bold>{count}</bold> 个物品.</#00FB9A>'
|
||||
repair-not-damaged: '<#FF3300>错误:</#FF3300> <#FF7E5E>该物品没有损坏.</#FF7E5E>'
|
||||
@@ -76,6 +79,8 @@ help:
|
||||
footer: '<gray>每个命令都需要对应权限.</gray>'
|
||||
|
||||
commands:
|
||||
reload: '<white>/essc reload</white> <gray>- 重载插件配置</gray>'
|
||||
version: '<white>/essc version</white> <gray>- 查看插件与服务端版本</gray>'
|
||||
workbench: '<white>/workbench</white> <gray>- 打开工作台</gray>'
|
||||
anvil: '<white>/anvil</white> <gray>- 打开铁砧</gray>'
|
||||
enchantingtable: '<white>/enchantingtable</white> <gray>- 打开附魔台</gray>'
|
||||
@@ -91,10 +96,10 @@ help:
|
||||
fly: '<white>/fly</white> <gray>- 切换飞行模式</gray>'
|
||||
nightvision: '<white>/nightvision</white> <gray>- 切换夜视</gray>'
|
||||
glow: '<white>/glow</white> <gray>- 切换发光</gray>'
|
||||
heal: '<white>/heal</white> <gray>- 恢复生命值和饥饿值</gray>'
|
||||
heal: '<white>/heal [玩家]</white> <gray>- 恢复生命值和饥饿值</gray>'
|
||||
vanish: '<white>/vanish</white> <gray>- 切换隐身模式</gray>'
|
||||
seen: '<white>/seen</white> <gray>- 查看玩家信息</gray>'
|
||||
feed: '<white>/feed</white> <gray>- 恢复饥饿值</gray>'
|
||||
seen: '<white>/seen [玩家]</white> <gray>- 查看玩家信息</gray>'
|
||||
feed: '<white>/feed [玩家]</white> <gray>- 恢复饥饿值</gray>'
|
||||
repair: '<white>/repair</white> <gray>- 修复手中或全部物品</gray>'
|
||||
tpa: '<white>/tpa [玩家]</white> <gray>- 请求传送到玩家身边</gray>'
|
||||
tpahere: '<white>/tpahere [玩家]</white> <gray>- 请求玩家传送到你身边</gray>'
|
||||
|
||||
@@ -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("<gray>文本</gray>", LangManager.normalizeMiniMessageTags("<GRAY>文本</GRAY>"));
|
||||
assertEquals("\\<GRAY>", LangManager.normalizeMiniMessageTags("\\<GRAY>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void legacyFormattingCanBeNestedInMiniMessage() {
|
||||
String message = LangManager.applyPlaceholders(
|
||||
"<gray>维护状态: {status}</gray>",
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user