fix command and refactoring config
This commit is contained in:
parent
b922f588d0
commit
fe64b3d51f
109 changed files with 4103 additions and 5403 deletions
|
|
@ -59,6 +59,7 @@ public final class LoChat extends JavaPlugin {
|
|||
// Команды и слушатели
|
||||
registerCommands();
|
||||
registerListeners();
|
||||
registerPluginChannels();
|
||||
|
||||
// Автосообщения
|
||||
autoMessageManager.start();
|
||||
|
|
@ -108,9 +109,11 @@ public final class LoChat extends JavaPlugin {
|
|||
getCommand("reply").setExecutor(new ReplyCommand(this));
|
||||
getCommand("ignore").setExecutor(new IgnoreCommand(this));
|
||||
getCommand("unignore").setExecutor(new UnignoreCommand(this));
|
||||
getCommand("ignorelist").setExecutor(new IgnoreListCommand(this));
|
||||
getCommand("announce").setExecutor(new AnnounceCommand(this));
|
||||
getCommand("chatspy").setExecutor(new ChatSpyCommand(this));
|
||||
getCommand("clearchat").setExecutor(new ClearChatCommand(this));
|
||||
getCommand("hub").setExecutor(new HubCommand(this));
|
||||
|
||||
NickCommand nickCmd = new NickCommand(this);
|
||||
getCommand("nick").setExecutor(nickCmd);
|
||||
|
|
@ -149,6 +152,11 @@ public final class LoChat extends JavaPlugin {
|
|||
getLogger().info("Слушатели зарегистрированы (новая архитектура)");
|
||||
}
|
||||
|
||||
private void registerPluginChannels() {
|
||||
getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
|
||||
getLogger().info("BungeeCord plugin messaging channel registered");
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
configManager.reload();
|
||||
messageConfig.reload();
|
||||
|
|
|
|||
107
src/main/java/com/loki/lochat/commands/CustomCommandManager.java
Normal file
107
src/main/java/com/loki/lochat/commands/CustomCommandManager.java
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.commands.custom.*;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Менеджер кастомных команд
|
||||
*/
|
||||
public class CustomCommandManager {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final Map<String, CommandExecutor> commands;
|
||||
|
||||
public CustomCommandManager(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.commands = new HashMap<>();
|
||||
registerCommands();
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрирует все кастомные команды
|
||||
*/
|
||||
private void registerCommands() {
|
||||
// Информационные команды
|
||||
commands.put("help", new HelpCommand(plugin));
|
||||
commands.put("rules", new RulesCommand(plugin));
|
||||
commands.put("discord", new DiscordCommand(plugin));
|
||||
|
||||
// Утилиты
|
||||
commands.put("coords", new CoordsCommand(plugin));
|
||||
commands.put("ping", new PingCommand(plugin));
|
||||
commands.put("stats", new StatsCommand(plugin));
|
||||
|
||||
// Чат команды
|
||||
commands.put("me", new MeCommand(plugin));
|
||||
commands.put("sounds", new SoundsCommand(plugin));
|
||||
|
||||
// TODO: Добавить остальные команды
|
||||
// commands.put("website", new WebsiteCommand(plugin));
|
||||
// commands.put("time", new TimeCommand(plugin));
|
||||
// commands.put("list", new ListCommand(plugin));
|
||||
// commands.put("top", new TopCommand(plugin));
|
||||
// commands.put("afk", new AfkCommand(plugin));
|
||||
// commands.put("back", new BackCommand(plugin));
|
||||
// commands.put("broadcast", new BroadcastCommand(plugin));
|
||||
// commands.put("maintenance", new MaintenanceCommand(plugin));
|
||||
// commands.put("motd", new MotdCommand(plugin));
|
||||
// commands.put("tps", new TpsCommand(plugin));
|
||||
// commands.put("memory", new MemoryCommand(plugin));
|
||||
// commands.put("version", new VersionCommand(plugin));
|
||||
// commands.put("reload", new ReloadCommand(plugin));
|
||||
// commands.put("automessage", new AutoMessageCommand(plugin));
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрирует команды в Bukkit
|
||||
*/
|
||||
public void registerBukkitCommands() {
|
||||
for (Map.Entry<String, CommandExecutor> entry : commands.entrySet()) {
|
||||
String commandName = entry.getKey();
|
||||
CommandExecutor executor = entry.getValue();
|
||||
|
||||
if (plugin.getCommand(commandName) != null) {
|
||||
plugin.getCommand(commandName).setExecutor(executor);
|
||||
plugin.getLogger().info("Зарегистрирована кастомная команда: /" + commandName);
|
||||
} else {
|
||||
plugin.getLogger().warning("Не удалось зарегистрировать команду: /" + commandName +
|
||||
" (не найдена в plugin.yml)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить исполнителя команды
|
||||
*/
|
||||
public CommandExecutor getCommandExecutor(String command) {
|
||||
return commands.get(command.toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверить, является ли команда кастомной
|
||||
*/
|
||||
public boolean isCustomCommand(String command) {
|
||||
return commands.containsKey(command.toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить все зарегистрированные команды
|
||||
*/
|
||||
public Map<String, CommandExecutor> getCommands() {
|
||||
return new HashMap<>(commands);
|
||||
}
|
||||
|
||||
/**
|
||||
* Перезагрузить команды
|
||||
*/
|
||||
public void reload() {
|
||||
commands.clear();
|
||||
registerCommands();
|
||||
registerBukkitCommands();
|
||||
plugin.getLogger().info("Кастомные команды перезагружены!");
|
||||
}
|
||||
}
|
||||
52
src/main/java/com/loki/lochat/commands/HubCommand.java
Normal file
52
src/main/java/com/loki/lochat/commands/HubCommand.java
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class HubCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public HubCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("general.players-only")));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if hub command is enabled
|
||||
if (!plugin.getConfigManager().getBoolean("hub_command.enabled", true)) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("general.command-disabled")));
|
||||
return true;
|
||||
}
|
||||
|
||||
String hubServer = plugin.getConfigManager().getString("hub_command.server", "hub");
|
||||
|
||||
// Send player to hub server via BungeeCord
|
||||
sendToServer(player, hubServer);
|
||||
|
||||
String message = plugin.getMessageConfig().get("hub.connecting")
|
||||
.replace("%server%", hubServer);
|
||||
player.sendMessage(ChatFormatter.parse(message));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void sendToServer(Player player, String server) {
|
||||
ByteArrayDataOutput out = ByteStreams.newDataOutput();
|
||||
out.writeUTF("Connect");
|
||||
out.writeUTF(server);
|
||||
|
||||
player.sendPluginMessage(plugin, "BungeeCord", out.toByteArray());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.IgnoreService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Команда /ignorelist - показывает список игнорируемых игроков
|
||||
*/
|
||||
public class IgnoreListCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final IgnoreService ignoreService;
|
||||
|
||||
public IgnoreListCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.ignoreService = plugin.getServiceRegistry().get(IgnoreService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage("Только для игроков!");
|
||||
return true;
|
||||
}
|
||||
|
||||
Set<UUID> ignoredPlayers = ignoreService.getIgnoredPlayers(player.getUniqueId());
|
||||
|
||||
if (ignoredPlayers.isEmpty()) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("ignore.list-empty")));
|
||||
return true;
|
||||
}
|
||||
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("ignore.list-header")));
|
||||
|
||||
for (UUID ignoredUuid : ignoredPlayers) {
|
||||
String playerName = com.loki.lochat.utils.PlayerUtil.getPlayerName(ignoredUuid);
|
||||
if (playerName != null) {
|
||||
player.sendMessage(ChatFormatter.parse("WCEEB• &#FFFFFF" + playerName));
|
||||
}
|
||||
}
|
||||
|
||||
player.sendMessage(ChatFormatter.parse(
|
||||
plugin.getMessageConfig().get("ignore.list-footer")
|
||||
.replace("{count}", String.valueOf(ignoredPlayers.size()))
|
||||
));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package com.loki.lochat.commands.custom;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда координат игрока
|
||||
*/
|
||||
public class CoordsCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public CoordsCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(ChatFormatter.parse("<red>Эта команда только для игроков!</red>"));
|
||||
return true;
|
||||
}
|
||||
|
||||
int x = (int) player.getLocation().getX();
|
||||
int y = (int) player.getLocation().getY();
|
||||
int z = (int) player.getLocation().getZ();
|
||||
String world = player.getWorld().getName();
|
||||
|
||||
String message = String.format("""
|
||||
<gradient:#FFA726:#FF9800>Ваши координаты:</gradient>
|
||||
<gradient:#87CEEB:#4682B4>X:</gradient> <white>%d</white>
|
||||
<gradient:#87CEEB:#4682B4>Y:</gradient> <white>%d</white>
|
||||
<gradient:#87CEEB:#4682B4>Z:</gradient> <white>%d</white>
|
||||
<gradient:#87CEEB:#4682B4>Мир:</gradient> <white>%s</white>
|
||||
""", x, y, z, world);
|
||||
|
||||
player.sendMessage(ChatFormatter.parse(message));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package com.loki.lochat.commands.custom;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда Discord сервера
|
||||
*/
|
||||
public class DiscordCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public DiscordCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
String discordLink = plugin.getConfig().getString("discord.invite-link", "discord.gg/example");
|
||||
|
||||
String message = String.format("""
|
||||
<gradient:#7289DA:#5865F2>═══════ Наш Discord ═══════</gradient>
|
||||
<gray>Присоединяйтесь к нашему сообществу!</gray>
|
||||
|
||||
<click:open_url:'https://%s'><gradient:#87CEEB:#4682B4>🔗 %s</gradient></click>
|
||||
|
||||
<gray>В Discord вы можете:</gray>
|
||||
<white>•</white> <gray>Общаться с игроками</gray>
|
||||
<white>•</white> <gray>Получать новости сервера</gray>
|
||||
<white>•</white> <gray>Участвовать в событиях</gray>
|
||||
<white>•</white> <gray>Получать поддержку</gray>
|
||||
""", discordLink, discordLink);
|
||||
|
||||
sender.sendMessage(ChatFormatter.parse(message));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package com.loki.lochat.commands.custom;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда помощи сервера
|
||||
*/
|
||||
public class HelpCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public HelpCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
String message = """
|
||||
<gradient:#FFD700:#FFA500>═══════ Помощь по серверу ═══════</gradient>
|
||||
<gradient:#87CEEB:#4682B4>/rules</gradient> <gray>- правила сервера</gray>
|
||||
<gradient:#87CEEB:#4682B4>/discord</gradient> <gray>- наш Discord</gray>
|
||||
<gradient:#87CEEB:#4682B4>/website</gradient> <gray>- официальный сайт</gray>
|
||||
<gradient:#87CEEB:#4682B4>/coords</gradient> <gray>- ваши координаты</gray>
|
||||
<gradient:#87CEEB:#4682B4>/time</gradient> <gray>- время на сервере</gray>
|
||||
<gradient:#87CEEB:#4682B4>/ping</gradient> <gray>- ваш пинг</gray>
|
||||
<gradient:#87CEEB:#4682B4>/list</gradient> <gray>- список игроков</gray>
|
||||
<gradient:#87CEEB:#4682B4>/me <действие></gradient> <gray>- действие от третьего лица</gray>
|
||||
<gradient:#87CEEB:#4682B4>/msg <игрок> <сообщение></gradient> <gray>- личное сообщение</gray>
|
||||
<gradient:#87CEEB:#4682B4>/reply <сообщение></gradient> <gray>- ответить на ЛС</gray>
|
||||
<gradient:#87CEEB:#4682B4>/ignore <игрок></gradient> <gray>- игнорировать игрока</gray>
|
||||
<gradient:#87CEEB:#4682B4>/color <цвета></gradient> <gray>- градиентный ник</gray>
|
||||
<gradient:#87CEEB:#4682B4>/prefix <текст></gradient> <gray>- кастомный префикс</gray>
|
||||
""";
|
||||
|
||||
sender.sendMessage(ChatFormatter.parse(message));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
75
src/main/java/com/loki/lochat/commands/custom/MeCommand.java
Normal file
75
src/main/java/com/loki/lochat/commands/custom/MeCommand.java
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
package com.loki.lochat.commands.custom;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда действия от третьего лица
|
||||
*/
|
||||
public class MeCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public MeCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(ChatFormatter.parse("<red>Эта команда только для игроков!</red>"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!player.hasPermission("chat.me")) {
|
||||
player.sendMessage(ChatFormatter.parse("<red>У вас нет прав для использования этой команды!</red>"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
player.sendMessage(ChatFormatter.parse("<red>Использование: /me <действие></red>"));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Проверяем мут
|
||||
if (plugin.getMuteService() != null && plugin.getMuteService().isMuted(player.getUniqueId())) {
|
||||
String muteMessage = plugin.getMessagesConfig().getString("mute.you-muted",
|
||||
"<red>Вы замучены!</red>");
|
||||
player.sendMessage(ChatFormatter.parse(muteMessage));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Собираем действие из аргументов
|
||||
String action = String.join(" ", args);
|
||||
|
||||
// Получаем отображаемое имя игрока (с градиентом если есть)
|
||||
String displayName = player.getDisplayName();
|
||||
if (plugin.getGradientModule() != null) {
|
||||
displayName = plugin.getGradientModule().getFormattedName(player);
|
||||
}
|
||||
|
||||
// Форматируем сообщение действия
|
||||
String message = String.format("<gray>* %s %s</gray>", displayName, action);
|
||||
|
||||
// Отправляем всем игрокам в радиусе (как локальный чат)
|
||||
int radius = plugin.getConfig().getInt("chat.local.radius", 100);
|
||||
|
||||
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
|
||||
if (onlinePlayer.getWorld().equals(player.getWorld()) &&
|
||||
onlinePlayer.getLocation().distance(player.getLocation()) <= radius) {
|
||||
onlinePlayer.sendMessage(ChatFormatter.parse(message));
|
||||
}
|
||||
}
|
||||
|
||||
// Логируем действие
|
||||
plugin.getLogger().info(String.format("[ME] %s: %s", player.getName(), action));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
package com.loki.lochat.commands.custom;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда пинга игрока
|
||||
*/
|
||||
public class PingCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public PingCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(ChatFormatter.parse("<red>Эта команда только для игроков!</red>"));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Если указан аргумент - показать пинг другого игрока
|
||||
if (args.length > 0) {
|
||||
if (!player.hasPermission("lochat.ping.others")) {
|
||||
player.sendMessage(ChatFormatter.parse("<red>У вас нет прав для просмотра пинга других игроков!</red>"));
|
||||
return true;
|
||||
}
|
||||
|
||||
Player target = Bukkit.getPlayer(args[0]);
|
||||
if (target == null || !target.isOnline()) {
|
||||
player.sendMessage(ChatFormatter.parse("<red>Игрок не найден или не в сети!</red>"));
|
||||
return true;
|
||||
}
|
||||
|
||||
int ping = target.getPing();
|
||||
String color = getPingColor(ping);
|
||||
|
||||
String message = String.format(
|
||||
"<gradient:#87CEEB:#4682B4>Пинг игрока %s:</gradient> <%s>%dмс</%s>",
|
||||
target.getName(), color, ping, color
|
||||
);
|
||||
|
||||
player.sendMessage(ChatFormatter.parse(message));
|
||||
} else {
|
||||
// Показать свой пинг
|
||||
int ping = player.getPing();
|
||||
String color = getPingColor(ping);
|
||||
|
||||
String message = String.format(
|
||||
"<gradient:#87CEEB:#4682B4>Ваш пинг:</gradient> <%s>%dмс</%s>",
|
||||
color, ping, color
|
||||
);
|
||||
|
||||
player.sendMessage(ChatFormatter.parse(message));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private String getPingColor(int ping) {
|
||||
if (ping < 50) return "green";
|
||||
if (ping < 100) return "yellow";
|
||||
if (ping < 200) return "gold";
|
||||
return "red";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package com.loki.lochat.commands.custom;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда правил сервера
|
||||
*/
|
||||
public class RulesCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public RulesCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
String message = """
|
||||
<gradient:#FF6B6B:#FF4757>═══════ Правила сервера ═══════</gradient>
|
||||
<white>1.</white> <gray>Не используйте читы и моды</gray>
|
||||
<white>2.</white> <gray>Уважайте других игроков</gray>
|
||||
<white>3.</white> <gray>Не спамьте в чате</gray>
|
||||
<white>4.</white> <gray>Не стройте рядом с чужими постройками</gray>
|
||||
<white>5.</white> <gray>Не используйте мат и оскорбления</gray>
|
||||
<white>6.</white> <gray>Не рекламируйте другие серверы</gray>
|
||||
<white>7.</white> <gray>Слушайтесь администрацию</gray>
|
||||
<white>8.</white> <gray>Не злоупотребляйте багами</gray>
|
||||
|
||||
<gradient:#FFA726:#FF9800>Нарушение правил карается мутом или баном!</gradient>
|
||||
<gradient:#87CEEB:#4682B4>За помощью обращайтесь к администрации</gradient>
|
||||
""";
|
||||
|
||||
sender.sendMessage(ChatFormatter.parse(message));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
package com.loki.lochat.commands.custom;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда управления звуками
|
||||
*/
|
||||
public class SoundsCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public SoundsCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(ChatFormatter.parse("<red>Эта команда только для игроков!</red>"));
|
||||
return true;
|
||||
}
|
||||
|
||||
String playerUUID = player.getUniqueId().toString();
|
||||
boolean currentState = plugin.getPlayerData().getBoolean("players." + playerUUID + ".settings.sounds-enabled", true);
|
||||
|
||||
if (args.length == 0) {
|
||||
// Показать текущее состояние
|
||||
String status = currentState ? "<green>включены</green>" : "<red>отключены</red>";
|
||||
String message = String.format(
|
||||
"<gradient:#87CEEB:#4682B4>Звуки сейчас %s</gradient>\n" +
|
||||
"<gray>Используйте /sounds on или /sounds off для изменения</gray>",
|
||||
status
|
||||
);
|
||||
player.sendMessage(ChatFormatter.parse(message));
|
||||
return true;
|
||||
}
|
||||
|
||||
String action = args[0].toLowerCase();
|
||||
boolean newState;
|
||||
|
||||
switch (action) {
|
||||
case "on", "включить", "вкл" -> {
|
||||
newState = true;
|
||||
player.sendMessage(ChatFormatter.parse("<green>Звуки включены!</green>"));
|
||||
}
|
||||
case "off", "отключить", "выкл" -> {
|
||||
newState = false;
|
||||
player.sendMessage(ChatFormatter.parse("<red>Звуки отключены!</red>"));
|
||||
}
|
||||
case "toggle", "переключить" -> {
|
||||
newState = !currentState;
|
||||
String status = newState ? "<green>включены</green>" : "<red>отключены</red>";
|
||||
player.sendMessage(ChatFormatter.parse(String.format("Звуки %s!", status)));
|
||||
}
|
||||
default -> {
|
||||
player.sendMessage(ChatFormatter.parse("""
|
||||
<red>Неверное использование!</red>
|
||||
<gray>Доступные команды:</gray>
|
||||
<white>/sounds on</white> <gray>- включить звуки</gray>
|
||||
<white>/sounds off</white> <gray>- отключить звуки</gray>
|
||||
<white>/sounds toggle</white> <gray>- переключить</gray>
|
||||
"""));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Сохраняем настройку
|
||||
plugin.getPlayerData().set("players." + playerUUID + ".settings.sounds-enabled", newState);
|
||||
plugin.savePlayerData();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
114
src/main/java/com/loki/lochat/commands/custom/StatsCommand.java
Normal file
114
src/main/java/com/loki/lochat/commands/custom/StatsCommand.java
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
package com.loki.lochat.commands.custom;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда статистики чата
|
||||
*/
|
||||
public class StatsCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public StatsCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!sender.hasPermission("lochat.stats")) {
|
||||
sender.sendMessage(ChatFormatter.parse("<red>У вас нет прав для просмотра статистики!</red>"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length > 0) {
|
||||
// Статистика конкретного игрока
|
||||
Player target = Bukkit.getPlayer(args[0]);
|
||||
if (target == null) {
|
||||
sender.sendMessage(ChatFormatter.parse("<red>Игрок не найден!</red>"));
|
||||
return true;
|
||||
}
|
||||
|
||||
showPlayerStats(sender, target);
|
||||
} else {
|
||||
// Общая статистика сервера
|
||||
showServerStats(sender);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void showPlayerStats(CommandSender sender, Player target) {
|
||||
// Получаем статистику игрока из конфига или базы данных
|
||||
int messagesSent = plugin.getPlayerData().getInt("players." + target.getUniqueId() + ".messages-sent", 0);
|
||||
int commandsUsed = plugin.getPlayerData().getInt("players." + target.getUniqueId() + ".commands-used", 0);
|
||||
long playtime = plugin.getPlayerData().getLong("players." + target.getUniqueId() + ".total-playtime", 0);
|
||||
|
||||
String playtimeFormatted = formatTime(playtime);
|
||||
|
||||
String message = String.format("""
|
||||
<gradient:#FFD700:#FFA500>═══ Статистика игрока %s ═══</gradient>
|
||||
<gradient:#87CEEB:#4682B4>Сообщений отправлено:</gradient> <white>%d</white>
|
||||
<gradient:#87CEEB:#4682B4>Команд использовано:</gradient> <white>%d</white>
|
||||
<gradient:#87CEEB:#4682B4>Время в игре:</gradient> <white>%s</white>
|
||||
<gradient:#87CEEB:#4682B4>Статус:</gradient> <green>В сети</green>
|
||||
<gradient:#87CEEB:#4682B4>Пинг:</gradient> <yellow>%dмс</yellow>
|
||||
""", target.getName(), messagesSent, commandsUsed, playtimeFormatted, target.getPing());
|
||||
|
||||
sender.sendMessage(ChatFormatter.parse(message));
|
||||
}
|
||||
|
||||
private void showServerStats(CommandSender sender) {
|
||||
// Получаем общую статистику сервера
|
||||
int totalMessages = plugin.getStatisticsData().getInt("global.total-messages", 0);
|
||||
int messagesToday = plugin.getStatisticsData().getInt("global.messages-today", 0);
|
||||
int onlinePlayers = Bukkit.getOnlinePlayers().size();
|
||||
int maxPlayers = Bukkit.getMaxPlayers();
|
||||
|
||||
String message = String.format("""
|
||||
<gradient:#FFD700:#FFA500>═══ Статистика сервера ═══</gradient>
|
||||
<gradient:#87CEEB:#4682B4>Всего сообщений:</gradient> <white>%d</white>
|
||||
<gradient:#87CEEB:#4682B4>Сообщений сегодня:</gradient> <white>%d</white>
|
||||
<gradient:#87CEEB:#4682B4>Игроков онлайн:</gradient> <white>%d/%d</white>
|
||||
<gradient:#87CEEB:#4682B4>Активных мутов:</gradient> <white>%d</white>
|
||||
<gradient:#87CEEB:#4682B4>TPS:</gradient> <green>%.1f</green>
|
||||
""", totalMessages, messagesToday, onlinePlayers, maxPlayers,
|
||||
getActiveMutesCount(), getCurrentTPS());
|
||||
|
||||
sender.sendMessage(ChatFormatter.parse(message));
|
||||
}
|
||||
|
||||
private String formatTime(long milliseconds) {
|
||||
long seconds = milliseconds / 1000;
|
||||
long minutes = seconds / 60;
|
||||
long hours = minutes / 60;
|
||||
long days = hours / 24;
|
||||
|
||||
if (days > 0) {
|
||||
return String.format("%dд %dч %dм", days, hours % 24, minutes % 60);
|
||||
} else if (hours > 0) {
|
||||
return String.format("%dч %dм", hours, minutes % 60);
|
||||
} else {
|
||||
return String.format("%dм", minutes);
|
||||
}
|
||||
}
|
||||
|
||||
private int getActiveMutesCount() {
|
||||
return plugin.getMuteData().getConfigurationSection("active-mutes") != null ?
|
||||
plugin.getMuteData().getConfigurationSection("active-mutes").getKeys(false).size() : 0;
|
||||
}
|
||||
|
||||
private double getCurrentTPS() {
|
||||
try {
|
||||
return Bukkit.getTPS()[0];
|
||||
} catch (Exception e) {
|
||||
return 20.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,25 +22,69 @@ public class AdvancedMessageFilter {
|
|||
"(.)\\1{3,}"
|
||||
);
|
||||
|
||||
// Паттерн для скрытых ссылок (h t t p, w w w и т.д.)
|
||||
private static final Pattern HIDDEN_URL_PATTERN = Pattern.compile(
|
||||
"(?i)(h\\s*t\\s*t\\s*p|w\\s*w\\s*w|d\\s*o\\s*t\\s*c\\s*o\\s*m)",
|
||||
Pattern.CASE_INSENSITIVE
|
||||
);
|
||||
|
||||
// Паттерн для доменов без http
|
||||
private static final Pattern DOMAIN_PATTERN = Pattern.compile(
|
||||
"\\b([a-zA-Z0-9-]+\\.(com|net|org|ru|gg|me|io|xyz|tk|ml|ga|cf|gq))\\b",
|
||||
Pattern.CASE_INSENSITIVE
|
||||
);
|
||||
|
||||
private final FileConfiguration config;
|
||||
private final List<String> whitelistedDomains;
|
||||
private final List<String> blacklistedDomains;
|
||||
private final CapsFilter capsFilter;
|
||||
|
||||
public AdvancedMessageFilter(FileConfiguration config) {
|
||||
this.config = config;
|
||||
this.whitelistedDomains = config.getStringList("filters.url.whitelist");
|
||||
this.blacklistedDomains = config.getStringList("filters.url.blacklist");
|
||||
this.whitelistedDomains = config.getStringList("filters.advertising.whitelist");
|
||||
this.blacklistedDomains = config.getStringList("filters.advertising.blacklist");
|
||||
|
||||
// Инициализация капс фильтра
|
||||
int maxCapsPercent = config.getInt("filters.caps.max-percent", 70);
|
||||
int minLength = config.getInt("filters.caps.min-length", 5);
|
||||
boolean autoLowercase = config.getBoolean("filters.caps.auto-lowercase", true);
|
||||
boolean blockMessage = config.getBoolean("filters.caps.block", false);
|
||||
this.capsFilter = new CapsFilter(maxCapsPercent, minLength, autoLowercase, blockMessage);
|
||||
}
|
||||
|
||||
public FilterResult filterMessage(Player player, String message) {
|
||||
if (config.getBoolean("filters.url.enabled", true)) {
|
||||
// Капс фильтр
|
||||
if (config.getBoolean("filters.caps.enabled", true)) {
|
||||
String filtered = capsFilter.filter(player, message);
|
||||
if (filtered == null) {
|
||||
return new FilterResult(false, message, "&#FF6B6BНе кричите в чате!");
|
||||
}
|
||||
message = filtered;
|
||||
}
|
||||
|
||||
// Анти-реклама (объединенная проверка)
|
||||
if (config.getBoolean("filters.advertising.enabled", true)) {
|
||||
// URL фильтр
|
||||
FilterResult urlResult = filterUrls(player, message);
|
||||
if (!urlResult.allowed()) {
|
||||
return urlResult;
|
||||
}
|
||||
message = urlResult.filteredMessage();
|
||||
|
||||
// Скрытые ссылки
|
||||
FilterResult hiddenResult = filterHiddenUrls(player, message);
|
||||
if (!hiddenResult.allowed()) {
|
||||
return hiddenResult;
|
||||
}
|
||||
|
||||
// Домены без http
|
||||
FilterResult domainResult = filterDomains(player, message);
|
||||
if (!domainResult.allowed()) {
|
||||
return domainResult;
|
||||
}
|
||||
}
|
||||
|
||||
// IP фильтр
|
||||
if (config.getBoolean("filters.ip.enabled", true)) {
|
||||
FilterResult ipResult = filterIPs(player, message);
|
||||
if (!ipResult.allowed()) {
|
||||
|
|
@ -49,6 +93,7 @@ public class AdvancedMessageFilter {
|
|||
message = ipResult.filteredMessage();
|
||||
}
|
||||
|
||||
// Повторяющиеся символы
|
||||
if (config.getBoolean("filters.repeat.enabled", true)) {
|
||||
message = filterRepeatingChars(message);
|
||||
}
|
||||
|
|
@ -138,6 +183,70 @@ public class AdvancedMessageFilter {
|
|||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Фильтрует скрытые ссылки (h t t p, w w w и т.д.)
|
||||
*/
|
||||
private FilterResult filterHiddenUrls(Player player, String message) {
|
||||
if (!config.getBoolean("filters.advertising.block-hidden-urls", true)) {
|
||||
return new FilterResult(true, message, null);
|
||||
}
|
||||
|
||||
if (player.hasPermission("lochat.bypass.hiddenurls")) {
|
||||
return new FilterResult(true, message, null);
|
||||
}
|
||||
|
||||
Matcher matcher = HIDDEN_URL_PATTERN.matcher(message);
|
||||
if (matcher.find()) {
|
||||
return new FilterResult(false, message,
|
||||
config.getString("filters.advertising.blocked-message", "&#FF6B6BРеклама запрещена!"));
|
||||
}
|
||||
|
||||
return new FilterResult(true, message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Фильтрует домены без http (example.com, discord.gg и т.д.)
|
||||
*/
|
||||
private FilterResult filterDomains(Player player, String message) {
|
||||
if (!config.getBoolean("filters.advertising.block-domains", true)) {
|
||||
return new FilterResult(true, message, null);
|
||||
}
|
||||
|
||||
if (player.hasPermission("lochat.bypass.domains")) {
|
||||
return new FilterResult(true, message, null);
|
||||
}
|
||||
|
||||
Matcher matcher = DOMAIN_PATTERN.matcher(message);
|
||||
|
||||
while (matcher.find()) {
|
||||
String domain = matcher.group();
|
||||
|
||||
// Проверяем whitelist
|
||||
if (!whitelistedDomains.isEmpty()) {
|
||||
boolean isWhitelisted = whitelistedDomains.stream()
|
||||
.anyMatch(d -> domain.toLowerCase().contains(d.toLowerCase()));
|
||||
|
||||
if (!isWhitelisted) {
|
||||
return new FilterResult(false, message,
|
||||
config.getString("filters.advertising.blocked-message", "&#FF6B6BРеклама запрещена!"));
|
||||
}
|
||||
}
|
||||
|
||||
// Проверяем blacklist
|
||||
if (!blacklistedDomains.isEmpty()) {
|
||||
boolean isBlacklisted = blacklistedDomains.stream()
|
||||
.anyMatch(d -> domain.toLowerCase().contains(d.toLowerCase()));
|
||||
|
||||
if (isBlacklisted) {
|
||||
return new FilterResult(false, message,
|
||||
config.getString("filters.advertising.blocked-message", "&#FF6B6BРеклама запрещена!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new FilterResult(true, message, null);
|
||||
}
|
||||
|
||||
public record FilterResult(boolean allowed, String filteredMessage, String blockReason) {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
83
src/main/java/com/loki/lochat/core/filter/CapsFilter.java
Normal file
83
src/main/java/com/loki/lochat/core/filter/CapsFilter.java
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
package com.loki.lochat.core.filter;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Фильтр капса (заглавных букв)
|
||||
*/
|
||||
public class CapsFilter {
|
||||
private final int maxCapsPercent;
|
||||
private final int minLength;
|
||||
private final boolean autoLowercase;
|
||||
private final boolean blockMessage;
|
||||
|
||||
public CapsFilter(int maxCapsPercent, int minLength, boolean autoLowercase, boolean blockMessage) {
|
||||
this.maxCapsPercent = maxCapsPercent;
|
||||
this.minLength = minLength;
|
||||
this.autoLowercase = autoLowercase;
|
||||
this.blockMessage = blockMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет и обрабатывает сообщение с капсом
|
||||
*
|
||||
* @return обработанное сообщение или null если заблокировано
|
||||
*/
|
||||
public String filter(Player player, String message) {
|
||||
// Bypass для админов
|
||||
if (player.hasPermission("lochat.bypass.caps")) {
|
||||
return message;
|
||||
}
|
||||
|
||||
// Игнорируем короткие сообщения
|
||||
if (message.length() < minLength) {
|
||||
return message;
|
||||
}
|
||||
|
||||
// Подсчитываем процент заглавных букв
|
||||
int totalLetters = 0;
|
||||
int capsLetters = 0;
|
||||
|
||||
for (char c : message.toCharArray()) {
|
||||
if (Character.isLetter(c)) {
|
||||
totalLetters++;
|
||||
if (Character.isUpperCase(c)) {
|
||||
capsLetters++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Если букв мало, пропускаем
|
||||
if (totalLetters < 3) {
|
||||
return message;
|
||||
}
|
||||
|
||||
int capsPercent = (capsLetters * 100) / totalLetters;
|
||||
|
||||
// Если капса больше лимита
|
||||
if (capsPercent > maxCapsPercent) {
|
||||
if (blockMessage) {
|
||||
return null; // Блокируем сообщение
|
||||
} else if (autoLowercase) {
|
||||
// Понижаем регистр (первая буква заглавная)
|
||||
return capitalizeFirst(message.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Делает первую букву заглавной
|
||||
*/
|
||||
private String capitalizeFirst(String text) {
|
||||
if (text == null || text.isEmpty()) {
|
||||
return text;
|
||||
}
|
||||
return Character.toUpperCase(text.charAt(0)) + text.substring(1);
|
||||
}
|
||||
|
||||
public int getMaxCapsPercent() {
|
||||
return maxCapsPercent;
|
||||
}
|
||||
}
|
||||
|
|
@ -32,20 +32,40 @@ public class MuteServiceImpl implements MuteService {
|
|||
|
||||
@Override
|
||||
public void mute(UUID uuid, String playerName, long duration, String reason, String mutedBy) {
|
||||
if (uuid == null) {
|
||||
plugin.getLogger().warning("Attempted to mute player with null UUID");
|
||||
return;
|
||||
}
|
||||
|
||||
if (playerName == null || playerName.isEmpty()) {
|
||||
plugin.getLogger().warning("Attempted to mute player with null or empty name");
|
||||
return;
|
||||
}
|
||||
|
||||
long endTime = duration > 0 ? System.currentTimeMillis() + duration : 0;
|
||||
MuteData data = new MuteData(uuid, playerName, endTime, reason, mutedBy);
|
||||
mutes.put(uuid, data);
|
||||
|
||||
historyManager.addEntry(uuid, playerName, duration, reason, mutedBy);
|
||||
saveAsync();
|
||||
|
||||
plugin.getLogger().info("Player " + playerName + " muted by " + mutedBy +
|
||||
" for " + (duration > 0 ? formatTime(duration) : "permanent") +
|
||||
(reason != null ? " (reason: " + reason + ")" : ""));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unmute(UUID uuid, String unmutedBy) {
|
||||
if (uuid == null) {
|
||||
plugin.getLogger().warning("Attempted to unmute player with null UUID");
|
||||
return false;
|
||||
}
|
||||
|
||||
MuteData removed = mutes.remove(uuid);
|
||||
if (removed != null) {
|
||||
historyManager.updateUnmute(uuid, unmutedBy);
|
||||
saveAsync();
|
||||
plugin.getLogger().info("Player " + removed.getPlayerName() + " unmuted by " + unmutedBy);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -53,6 +73,10 @@ public class MuteServiceImpl implements MuteService {
|
|||
|
||||
@Override
|
||||
public boolean isMuted(UUID uuid) {
|
||||
if (uuid == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MuteData data = mutes.get(uuid);
|
||||
if (data == null) {
|
||||
return false;
|
||||
|
|
@ -61,6 +85,7 @@ public class MuteServiceImpl implements MuteService {
|
|||
if (isExpired(data)) {
|
||||
mutes.remove(uuid);
|
||||
saveAsync();
|
||||
plugin.getLogger().info("Mute expired for player " + data.getPlayerName());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ public class ChatMessage {
|
|||
}
|
||||
|
||||
public static ChatMessage create(Player sender, String rawMessage) {
|
||||
// Определяем тип чата
|
||||
boolean isGlobal = rawMessage.startsWith("!") &&
|
||||
sender.hasPermission("chat.global.use");
|
||||
// С "!" = глобальный чат
|
||||
// Без "!" = локальный чат
|
||||
boolean isGlobal = rawMessage.startsWith("!");
|
||||
|
||||
String content = isGlobal ?
|
||||
rawMessage.substring(1).trim() :
|
||||
|
|
|
|||
|
|
@ -28,100 +28,88 @@ public class GradientConfig {
|
|||
}
|
||||
|
||||
private void loadConfig() {
|
||||
configFile = new File(plugin.getDataFolder(), "gradient-config.yml");
|
||||
if (!configFile.exists()) {
|
||||
plugin.saveResource("gradient-config.yml", false);
|
||||
}
|
||||
config = YamlConfiguration.loadConfiguration(configFile);
|
||||
|
||||
// Загружаем дефолты
|
||||
InputStream defStream = plugin.getResource("gradient-config.yml");
|
||||
if (defStream != null) {
|
||||
YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(
|
||||
new InputStreamReader(defStream, StandardCharsets.UTF_8));
|
||||
config.setDefaults(defConfig);
|
||||
}
|
||||
// Читаем из главного config.yml вместо отдельного файла
|
||||
config = plugin.getConfig();
|
||||
plugin.getLogger().info("Gradient конфиг загружен из config.yml");
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
plugin.reloadConfig();
|
||||
this.mainConfig = plugin.getConfig();
|
||||
loadConfig();
|
||||
this.config = plugin.getConfig();
|
||||
}
|
||||
|
||||
public void save() {
|
||||
try {
|
||||
config.save(configFile);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("Ошибка сохранения gradient-config.yml: " + e.getMessage());
|
||||
}
|
||||
// Сохранение не требуется, используем главный конфиг
|
||||
plugin.saveConfig();
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return config.getBoolean("enabled", true);
|
||||
return config.getBoolean("gradient.enabled", true);
|
||||
}
|
||||
|
||||
public int getMaxColors() {
|
||||
return config.getInt("max-colors", 7);
|
||||
return config.getInt("gradient.max-colors", 7);
|
||||
}
|
||||
|
||||
public int getMinColors() {
|
||||
return config.getInt("min-colors", 1);
|
||||
return config.getInt("gradient.min-colors", 1);
|
||||
}
|
||||
|
||||
public int getPricePerColor() {
|
||||
return config.getInt("price-per-color", 50);
|
||||
return config.getInt("gradient.price-per-color", 50);
|
||||
}
|
||||
|
||||
public int getPrefixPrice() {
|
||||
return config.getInt("prefix-price", 500);
|
||||
return config.getInt("gradient.prefix-price", 500);
|
||||
}
|
||||
|
||||
public boolean isPrefixOneTimePurchase() {
|
||||
return config.getBoolean("prefix-one-time-purchase", true);
|
||||
return config.getBoolean("gradient.prefix-one-time-purchase", true);
|
||||
}
|
||||
|
||||
public int getColorCooldown() {
|
||||
return config.getInt("color-cooldown", 60);
|
||||
return config.getInt("gradient.color-cooldown", 60);
|
||||
}
|
||||
|
||||
public int getPrefixCooldown() {
|
||||
return config.getInt("prefix-cooldown", 300);
|
||||
return config.getInt("gradient.prefix-cooldown", 120);
|
||||
}
|
||||
|
||||
public int getMaxPrefixLength() {
|
||||
return config.getInt("max-prefix-length", 7);
|
||||
return config.getInt("gradient.max-prefix-length", 16);
|
||||
}
|
||||
|
||||
public String getPrefixFormat() {
|
||||
return config.getString("prefix-format", "[{prefix}] ");
|
||||
return config.getString("gradient.prefix-format", "[{prefix}] ");
|
||||
}
|
||||
|
||||
public boolean isGradientOnPrefix() {
|
||||
return config.getBoolean("gradient-on-prefix", true);
|
||||
return config.getBoolean("gradient.gradient-on-prefix", true);
|
||||
}
|
||||
|
||||
public boolean isGradientOnLuckPermsPrefix() {
|
||||
return config.getBoolean("gradient-on-luckperms-prefix", true);
|
||||
return config.getBoolean("gradient.gradient-on-luckperms-prefix", true);
|
||||
}
|
||||
|
||||
public boolean isContinuousGradient() {
|
||||
return config.getBoolean("continuous-gradient", true);
|
||||
return config.getBoolean("gradient.continuous-gradient", true);
|
||||
}
|
||||
|
||||
public boolean isUseLegacyRgbFormat() {
|
||||
return config.getBoolean("use-legacy-rgb-format", true);
|
||||
return config.getBoolean("gradient.use-legacy-rgb-format", true);
|
||||
}
|
||||
|
||||
public String getStorageType() {
|
||||
return config.getString("storage-type", "YAML");
|
||||
return config.getString("gradient.storage-type", "file");
|
||||
}
|
||||
|
||||
public String getSqliteFile() {
|
||||
return config.getString("sqlite-file", "gradient-data.db");
|
||||
return config.getString("gradient.sqlite-file", "gradient-data.db");
|
||||
}
|
||||
|
||||
public List<String> getPrefixBlacklist() {
|
||||
return config.getStringList("prefix-blacklist");
|
||||
return config.getStringList("gradient.prefix-blacklist");
|
||||
}
|
||||
|
||||
public boolean isPrefixBlacklisted(String prefix) {
|
||||
|
|
|
|||
|
|
@ -27,13 +27,13 @@ public class GradientMessages {
|
|||
}
|
||||
|
||||
private void loadMessages() {
|
||||
messagesFile = new File(plugin.getDataFolder(), "gradient-messages.yml");
|
||||
messagesFile = new File(plugin.getDataFolder(), "messages.yml");
|
||||
if (!messagesFile.exists()) {
|
||||
plugin.saveResource("gradient-messages.yml", false);
|
||||
plugin.saveResource("messages.yml", false);
|
||||
}
|
||||
messages = YamlConfiguration.loadConfiguration(messagesFile);
|
||||
|
||||
InputStream defStream = plugin.getResource("gradient-messages.yml");
|
||||
InputStream defStream = plugin.getResource("messages.yml");
|
||||
if (defStream != null) {
|
||||
YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(
|
||||
new InputStreamReader(defStream, StandardCharsets.UTF_8));
|
||||
|
|
@ -49,7 +49,7 @@ public class GradientMessages {
|
|||
* Получает сообщение как строку с MiniMessage тегами
|
||||
*/
|
||||
public String get(String key) {
|
||||
String message = messages.getString(key, "&#FF0000Сообщение не найдено: " + key);
|
||||
String message = messages.getString("gradient." + key, "&#FF0000Сообщение не найдено: " + key);
|
||||
return ChatFormatter.convertAllColors(message);
|
||||
}
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ public class GradientMessages {
|
|||
* Получает сообщение как строку с заменами
|
||||
*/
|
||||
public String get(String key, String... replacements) {
|
||||
String message = messages.getString(key, "&#FF0000Сообщение не найдено: " + key);
|
||||
String message = messages.getString("gradient." + key, "&#FF0000Сообщение не найдено: " + key);
|
||||
for (int i = 0; i < replacements.length; i += 2) {
|
||||
if (i + 1 < replacements.length) {
|
||||
message = message.replace("{" + replacements[i] + "}", replacements[i + 1]);
|
||||
|
|
|
|||
|
|
@ -54,23 +54,44 @@ public class ChatEventListener implements Listener {
|
|||
}
|
||||
|
||||
String filteredMessage = filterResult.filteredMessage();
|
||||
event.message(Component.text(filteredMessage));
|
||||
|
||||
// Определяем тип чата
|
||||
boolean isGlobal = message.startsWith("!");
|
||||
|
||||
event.message(com.loki.lochat.utils.ChatFormatter.parse(filteredMessage));
|
||||
|
||||
event.renderer(new EnhancedChatRenderer(sender, event.message()));
|
||||
event.renderer(new EnhancedChatRenderer(sender, event.message(), isGlobal));
|
||||
}
|
||||
|
||||
private class EnhancedChatRenderer implements ChatRenderer {
|
||||
private final Player sender;
|
||||
private final Component originalMessage;
|
||||
private final boolean isGlobal;
|
||||
|
||||
public EnhancedChatRenderer(Player sender, Component originalMessage) {
|
||||
public EnhancedChatRenderer(Player sender, Component originalMessage, boolean isGlobal) {
|
||||
this.sender = sender;
|
||||
this.originalMessage = originalMessage;
|
||||
this.isGlobal = isGlobal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component render(Player source, Component sourceDisplayName, Component message, Audience viewer) {
|
||||
String prefix = plugin.getConfig().getString("chat.global.prefix", "[G]");
|
||||
// Префикс в зависимости от типа чата
|
||||
Component prefixComponent;
|
||||
if (isGlobal) {
|
||||
// [G] с желтой буквой G
|
||||
prefixComponent = Component.text("[")
|
||||
.color(net.kyori.adventure.text.format.NamedTextColor.GRAY)
|
||||
.append(Component.text("G").color(net.kyori.adventure.text.format.NamedTextColor.GOLD))
|
||||
.append(Component.text("]").color(net.kyori.adventure.text.format.NamedTextColor.GRAY));
|
||||
} else {
|
||||
// [L] с голубой буквой L
|
||||
prefixComponent = Component.text("[")
|
||||
.color(net.kyori.adventure.text.format.NamedTextColor.GRAY)
|
||||
.append(Component.text("L").color(net.kyori.adventure.text.format.NamedTextColor.AQUA))
|
||||
.append(Component.text("]").color(net.kyori.adventure.text.format.NamedTextColor.GRAY));
|
||||
}
|
||||
|
||||
Component playerComponent = createPlayerComponent(source, sourceDisplayName);
|
||||
Component processedMessage = message;
|
||||
|
||||
|
|
@ -82,9 +103,10 @@ public class ChatEventListener implements Listener {
|
|||
processedMessage = mentionHandler.processMentions(processedMessage, source, viewerPlayer);
|
||||
}
|
||||
|
||||
return Component.text(prefix + " ")
|
||||
return prefixComponent
|
||||
.append(Component.text(" "))
|
||||
.append(playerComponent)
|
||||
.append(Component.text(": "))
|
||||
.append(Component.text(": ").color(net.kyori.adventure.text.format.NamedTextColor.GRAY))
|
||||
.append(processedMessage);
|
||||
}
|
||||
|
||||
|
|
@ -129,7 +151,8 @@ public class ChatEventListener implements Listener {
|
|||
|
||||
private Component processUrls(Component message) {
|
||||
String plainText = PlainTextComponentSerializer.plainText().serialize(message);
|
||||
String urlPattern = "(https?://[\\w\\-._~:/?#\\[\\]@!35538117-c5a8-4bfe-b9f2-b0dcf9374222'()*+,;=%]+)";
|
||||
// Fixed regex: escape the dash in character class to avoid illegal range
|
||||
String urlPattern = "(https?://[\\w\\-._~:/?#\\[\\]@!$&'()*+,;=%]+)";
|
||||
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(urlPattern);
|
||||
java.util.regex.Matcher matcher = pattern.matcher(plainText);
|
||||
|
||||
|
|
|
|||
|
|
@ -31,20 +31,15 @@ public class AutoMessageManager {
|
|||
}
|
||||
|
||||
private void loadMessages() {
|
||||
File file = new File(plugin.getDataFolder(), "automessages.yml");
|
||||
if (!file.exists()) {
|
||||
plugin.saveResource("automessages.yml", false);
|
||||
}
|
||||
|
||||
FileConfiguration config = YamlConfiguration.loadConfiguration(file);
|
||||
FileConfiguration config = plugin.getConfig();
|
||||
|
||||
// Настройки
|
||||
prefix = config.getString("settings.prefix", "&6[&eINFO&6]&r");
|
||||
mode = config.getString("settings.mode", "sequential").toLowerCase();
|
||||
prefix = plugin.getConfigManager().getString("prefix", "�D4FF[LoChat]");
|
||||
mode = "sequential"; // Всегда последовательно
|
||||
|
||||
// Загружаем сообщения
|
||||
// Загружаем сообщения из config.yml
|
||||
messages = new LinkedHashMap<>();
|
||||
ConfigurationSection msgSection = config.getConfigurationSection("messages");
|
||||
ConfigurationSection msgSection = config.getConfigurationSection("automessages-list");
|
||||
|
||||
if (msgSection != null) {
|
||||
for (String key : msgSection.getKeys(false)) {
|
||||
|
|
@ -65,32 +60,7 @@ public class AutoMessageManager {
|
|||
}
|
||||
}
|
||||
|
||||
// Определяем порядок
|
||||
messageOrder = new ArrayList<>();
|
||||
|
||||
if (mode.equals("custom")) {
|
||||
String orderStr = config.getString("settings.order", "");
|
||||
for (String item : orderStr.split(",")) {
|
||||
String trimmed = item.trim();
|
||||
if (!trimmed.isEmpty()) {
|
||||
try {
|
||||
int index = Integer.parseInt(trimmed) - 1;
|
||||
List<String> keys = new ArrayList<>(messages.keySet());
|
||||
if (index >= 0 && index < keys.size()) {
|
||||
messageOrder.add(keys.get(index));
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
if (messages.containsKey(trimmed)) {
|
||||
messageOrder.add(trimmed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (messageOrder.isEmpty()) {
|
||||
messageOrder.addAll(messages.keySet());
|
||||
}
|
||||
messageOrder = new ArrayList<>(messages.keySet());
|
||||
|
||||
plugin.getLogger().info("Загружено " + messages.size() + " автосообщений");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,51 +0,0 @@
|
|||
package com.loki.lochat.util;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
/**
|
||||
* Утилита для форматирования сообщений
|
||||
*/
|
||||
public class ChatFormatter {
|
||||
private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage();
|
||||
private static final LegacyComponentSerializer LEGACY = LegacyComponentSerializer.legacyAmpersand();
|
||||
|
||||
public static Object parse(String message) {
|
||||
try {
|
||||
return MINI_MESSAGE.deserialize(message);
|
||||
} catch (Exception e) {
|
||||
return LEGACY.deserialize(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static String toPlain(Object message) {
|
||||
if (message instanceof Component component) {
|
||||
return LegacyComponentSerializer.legacySection().serialize(component);
|
||||
}
|
||||
return message.toString();
|
||||
}
|
||||
|
||||
public static void sendMessage(CommandSender sender, Object message) {
|
||||
if (message instanceof Component component) {
|
||||
sender.sendMessage(component);
|
||||
} else {
|
||||
sender.sendMessage(message.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public static void broadcast(Object message) {
|
||||
if (message instanceof Component component) {
|
||||
org.bukkit.Bukkit.broadcast(component);
|
||||
}
|
||||
}
|
||||
|
||||
public static String stripTags(String message) {
|
||||
return message.replaceAll("<[^>]+>", "").replaceAll("§[0-9a-fk-or]", "");
|
||||
}
|
||||
|
||||
public static String convertAllColors(String message) {
|
||||
return message.replace("&", "§");
|
||||
}
|
||||
}
|
||||
|
|
@ -40,24 +40,24 @@ public final class ChatFormatter {
|
|||
private static String convertLegacyFormats(String message) {
|
||||
if (message == null) return "";
|
||||
|
||||
// Конвертируем &#RRGGBB в <#RRGGBB>
|
||||
// Конвертируем &#RRGGBB в <color:#RRGGBB>
|
||||
java.util.regex.Pattern pattern1 = java.util.regex.Pattern.compile("&#([0-9a-fA-F]{6})");
|
||||
java.util.regex.Matcher matcher1 = pattern1.matcher(message);
|
||||
StringBuffer sb1 = new StringBuffer();
|
||||
while (matcher1.find()) {
|
||||
String hex = matcher1.group(1).toLowerCase();
|
||||
matcher1.appendReplacement(sb1, "<#" + hex + ">");
|
||||
matcher1.appendReplacement(sb1, "<color:#" + hex + ">");
|
||||
}
|
||||
matcher1.appendTail(sb1);
|
||||
message = sb1.toString();
|
||||
|
||||
// Конвертируем #RRGGBB в <#RRGGBB> (только если не внутри тегов)
|
||||
// Конвертируем #RRGGBB в <color:#RRGGBB> (только если не внутри тегов)
|
||||
java.util.regex.Pattern pattern2 = java.util.regex.Pattern.compile("(?<!<)#([0-9a-fA-F]{6})(?![^<]*>)");
|
||||
java.util.regex.Matcher matcher2 = pattern2.matcher(message);
|
||||
StringBuffer sb2 = new StringBuffer();
|
||||
while (matcher2.find()) {
|
||||
String hex = matcher2.group(1).toLowerCase();
|
||||
matcher2.appendReplacement(sb2, "<#" + hex + ">");
|
||||
matcher2.appendReplacement(sb2, "<color:#" + hex + ">");
|
||||
}
|
||||
matcher2.appendTail(sb2);
|
||||
message = sb2.toString();
|
||||
|
|
|
|||
|
|
@ -17,20 +17,32 @@ public final class PlayerUtil {
|
|||
|
||||
/**
|
||||
* Safely parses a sound name from config.
|
||||
* Uses deprecated Sound.valueOf() for backward compatibility with string-based config values.
|
||||
* Uses Registry API for modern sound parsing with fallback to reflection.
|
||||
*
|
||||
* @param soundName the sound name to parse
|
||||
* @param defaultSound the default sound to return if parsing fails
|
||||
* @return the parsed Sound or the default if invalid
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public static Sound parseSound(String soundName, Sound defaultSound) {
|
||||
if (soundName == null || soundName.isEmpty()) {
|
||||
return defaultSound;
|
||||
}
|
||||
|
||||
try {
|
||||
return Sound.valueOf(soundName.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Try to get sound by key using Registry (modern way)
|
||||
Sound sound = org.bukkit.Registry.SOUNDS.match(soundName);
|
||||
if (sound != null) {
|
||||
return sound;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Registry failed, try fallback
|
||||
}
|
||||
|
||||
// Fallback: try direct field access via reflection
|
||||
try {
|
||||
java.lang.reflect.Field field = Sound.class.getField(soundName.toUpperCase().trim());
|
||||
return (Sound) field.get(null);
|
||||
} catch (Exception ex) {
|
||||
return defaultSound;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,13 @@ public final class TimeFormatter {
|
|||
}
|
||||
|
||||
public static String format(long millis) {
|
||||
if (millis <= 0) {
|
||||
if (millis < 0) {
|
||||
return "0с";
|
||||
}
|
||||
|
||||
if (millis == 0) {
|
||||
return "навсегда";
|
||||
}
|
||||
|
||||
long seconds = millis / 1000;
|
||||
long minutes = seconds / 60;
|
||||
|
|
@ -16,18 +20,21 @@ public final class TimeFormatter {
|
|||
long days = hours / 24;
|
||||
|
||||
if (days > 0) {
|
||||
return days + "д " + (hours % 24) + "ч";
|
||||
long remainingHours = hours % 24;
|
||||
return remainingHours > 0 ? days + "д " + remainingHours + "ч" : days + "д";
|
||||
} else if (hours > 0) {
|
||||
return hours + "ч " + (minutes % 60) + "м";
|
||||
long remainingMinutes = minutes % 60;
|
||||
return remainingMinutes > 0 ? hours + "ч " + remainingMinutes + "м" : hours + "ч";
|
||||
} else if (minutes > 0) {
|
||||
return minutes + "м " + (seconds % 60) + "с";
|
||||
long remainingSeconds = seconds % 60;
|
||||
return remainingSeconds > 0 ? minutes + "м " + remainingSeconds + "с" : minutes + "м";
|
||||
} else {
|
||||
return seconds + "с";
|
||||
}
|
||||
}
|
||||
|
||||
public static long parse(String timeStr) {
|
||||
if (timeStr == null || timeStr.isEmpty()) {
|
||||
if (timeStr == null || timeStr.trim().isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -38,8 +45,12 @@ public final class TimeFormatter {
|
|||
if (Character.isDigit(c)) {
|
||||
number.append(c);
|
||||
} else if (number.length() > 0) {
|
||||
long value = Long.parseLong(number.toString());
|
||||
totalMillis += getMillisForUnit(c, value);
|
||||
try {
|
||||
long value = Long.parseLong(number.toString());
|
||||
totalMillis += getMillisForUnit(c, value);
|
||||
} catch (NumberFormatException e) {
|
||||
// Skip invalid numbers
|
||||
}
|
||||
number.setLength(0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue