v1.5.0: Добавлены кастомные команды, алиасы, цвета чата, многоядерная сборка

- Система кастомных команд с алиасами (custom-commands.yml)
- Команда /chatcolor для установки цвета сообщений
- Исправлена ошибка с legacy кодами в PM
- Настройки объявлений (title, actionbar)
- Автообновление конфигов
- Отображение префикса над игроком
- Многоядерная сборка Gradle
- Добавлены алиасы для команд (русские и английские)
- Права на использование цветовых кодов
This commit is contained in:
loki5512344 2025-12-30 12:29:39 +01:00
parent dfc1c901ab
commit f52bd29e7d
17 changed files with 808 additions and 33 deletions

View file

@ -3,7 +3,7 @@ plugins {
} }
group = 'com.loki' group = 'com.loki'
version = '1.0.0' version = '1.5.0'
repositories { repositories {
mavenCentral() mavenCentral()
@ -29,14 +29,20 @@ java {
tasks.withType(JavaCompile).configureEach { tasks.withType(JavaCompile).configureEach {
options.encoding = 'UTF-8' options.encoding = 'UTF-8'
// Параллельная компиляция
options.fork = true
options.forkOptions.jvmArgs += ['-XX:+UseParallelGC']
} }
test { test {
useJUnitPlatform() useJUnitPlatform()
// Параллельное выполнение тестов
maxParallelForks = Runtime.runtime.availableProcessors()
forkEvery = 100
} }
jar { jar {
archiveFileName = 'LoChat-1.0.0.jar' archiveFileName = 'LoChat-1.5.0.jar'
from { from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
} }

13
gradle.properties Normal file
View file

@ -0,0 +1,13 @@
# Параллельная сборка
org.gradle.parallel=true
org.gradle.workers.max=8
org.gradle.configureondemand=true
# Кэширование
org.gradle.caching=true
# JVM настройки для Gradle
org.gradle.jvmargs=-Xmx2g -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError
# Kotlin DSL
kotlin.code.style=official

View file

@ -26,6 +26,8 @@ public final class LoChat extends JavaPlugin {
private MentionManager mentionManager; private MentionManager mentionManager;
private SpyManager spyManager; private SpyManager spyManager;
private EmojiManager emojiManager; private EmojiManager emojiManager;
private ChatColorManager chatColorManager;
private CustomCommandManager customCommandManager;
private GradientModule gradientModule; private GradientModule gradientModule;
@Override @Override
@ -45,6 +47,8 @@ public final class LoChat extends JavaPlugin {
cooldownManager = new CooldownManager(); cooldownManager = new CooldownManager();
mentionManager = new MentionManager(this); mentionManager = new MentionManager(this);
spyManager = new SpyManager(this); spyManager = new SpyManager(this);
chatColorManager = new ChatColorManager(this);
customCommandManager = new CustomCommandManager(this);
chatManager = new ChatManager(this); chatManager = new ChatManager(this);
autoMessageManager = new AutoMessageManager(this); autoMessageManager = new AutoMessageManager(this);
@ -80,6 +84,9 @@ public final class LoChat extends JavaPlugin {
if (ignoreManager != null) { if (ignoreManager != null) {
ignoreManager.save(); ignoreManager.save();
} }
if (chatColorManager != null) {
chatColorManager.saveData();
}
if (gradientModule != null) { if (gradientModule != null) {
gradientModule.shutdown(); gradientModule.shutdown();
} }
@ -97,6 +104,7 @@ public final class LoChat extends JavaPlugin {
getCommand("announce").setExecutor(new AnnounceCommand(this)); getCommand("announce").setExecutor(new AnnounceCommand(this));
getCommand("chatspy").setExecutor(new ChatSpyCommand(this)); getCommand("chatspy").setExecutor(new ChatSpyCommand(this));
getCommand("clearchat").setExecutor(new ClearChatCommand(this)); getCommand("clearchat").setExecutor(new ClearChatCommand(this));
getCommand("chatcolor").setExecutor(new ChatColorCommand(this));
LoChatCommand loChatCommand = new LoChatCommand(this); LoChatCommand loChatCommand = new LoChatCommand(this);
getCommand("lochat").setExecutor(loChatCommand); getCommand("lochat").setExecutor(loChatCommand);
@ -112,6 +120,9 @@ public final class LoChat extends JavaPlugin {
if (gradientModule != null) { if (gradientModule != null) {
gradientModule.reload(); gradientModule.reload();
} }
if (customCommandManager != null) {
customCommandManager.reload();
}
} }
public static LoChat getInstance() { public static LoChat getInstance() {
@ -169,4 +180,12 @@ public final class LoChat extends JavaPlugin {
public GradientModule getGradientModule() { public GradientModule getGradientModule() {
return gradientModule; return gradientModule;
} }
public ChatColorManager getChatColorManager() {
return chatColorManager;
}
public CustomCommandManager getCustomCommandManager() {
return customCommandManager;
}
} }

View file

@ -43,17 +43,25 @@ public class AnnounceCommand implements CommandExecutor {
for (Player player : Bukkit.getOnlinePlayers()) { for (Player player : Bukkit.getOnlinePlayers()) {
player.sendMessage(formatted); player.sendMessage(formatted);
// ActionBar // ActionBar (если включен)
if (plugin.getConfigManager().isAnnouncementActionBarEnabled()) {
player.sendActionBar(formatted); player.sendActionBar(formatted);
}
// Title // Title (если включен)
if (plugin.getConfigManager().isAnnouncementTitleEnabled()) {
Title title = Title.title( Title title = Title.title(
ChatFormatter.parse("<gold>ОБЪЯВЛЕНИЕ</gold>"), ChatFormatter.parse(plugin.getConfigManager().getAnnouncementTitleHeader()),
ChatFormatter.parse(message), ChatFormatter.parse(message),
Title.Times.times(Duration.ofMillis(500), Duration.ofSeconds(3), Duration.ofMillis(500)) Title.Times.times(
Duration.ofMillis(500),
Duration.ofSeconds(plugin.getConfigManager().getAnnouncementTitleDuration()),
Duration.ofMillis(500)
)
); );
player.showTitle(title); player.showTitle(title);
} }
}
plugin.getLogger().info("[ANNOUNCE] " + sender.getName() + ": " + ChatFormatter.stripTags(message)); plugin.getLogger().info("[ANNOUNCE] " + sender.getName() + ": " + ChatFormatter.stripTags(message));
return true; return true;

View file

@ -0,0 +1,86 @@
package com.loki.lochat.commands;
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.command.TabCompleter;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ChatColorCommand implements CommandExecutor, TabCompleter {
private final LoChat plugin;
public ChatColorCommand(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("Только для игроков!");
return true;
}
if (!player.hasPermission("lochat.chatcolor")) {
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getNoPermission()));
return true;
}
if (args.length == 0) {
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getInvalidUsage("/chatcolor <цвет>")));
player.sendMessage(ChatFormatter.parse("<gray>Доступные цвета: <red>red</red>, <blue>blue</blue>, <green>green</green>, <yellow>yellow</yellow>, <gold>gold</gold>, <aqua>aqua</aqua>, <light_purple>light_purple</light_purple>, <white>white</white>, <gray>gray</gray>, <dark_gray>dark_gray</dark_gray>"));
return true;
}
String color = args[0].toLowerCase();
// Проверяем валидность цвета
if (!isValidColor(color)) {
player.sendMessage(ChatFormatter.parse("<red>Неверный цвет! Используйте один из доступных цветов.</red>"));
return true;
}
// Сохраняем цвет чата игрока
plugin.getChatColorManager().setChatColor(player.getUniqueId(), color);
player.sendMessage(ChatFormatter.parse("<green>Цвет чата установлен на <" + color + ">" + color + "</color>!"));
return true;
}
private boolean isValidColor(String color) {
List<String> validColors = Arrays.asList(
"red", "blue", "green", "yellow", "gold", "aqua",
"light_purple", "white", "gray", "dark_gray", "black",
"dark_red", "dark_green", "dark_blue", "dark_aqua", "dark_purple"
);
return validColors.contains(color);
}
@Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
if (args.length == 1) {
List<String> colors = Arrays.asList(
"red", "blue", "green", "yellow", "gold", "aqua",
"light_purple", "white", "gray", "dark_gray"
);
List<String> completions = new ArrayList<>();
String input = args[0].toLowerCase();
for (String color : colors) {
if (color.startsWith(input)) {
completions.add(color);
}
}
return completions;
}
return new ArrayList<>();
}
}

View file

@ -0,0 +1,38 @@
package com.loki.lochat.commands;
import com.loki.lochat.managers.CustomCommandManager;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class CustomCommand extends Command {
private final CustomCommandManager manager;
public CustomCommand(String name, CustomCommandManager manager) {
super(name);
this.manager = manager;
this.setDescription("Кастомная команда LoChat");
this.setUsage("/" + name);
}
@Override
public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
if (!(sender instanceof Player player)) {
sender.sendMessage("Только для игроков!");
return true;
}
return manager.executeCustomCommand(commandLabel, player, args);
}
@Override
public @NotNull List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException {
// Базовое автодополнение - можно расширить при необходимости
return new ArrayList<>();
}
}

View file

@ -1,6 +1,7 @@
package com.loki.lochat.commands; package com.loki.lochat.commands;
import com.loki.lochat.LoChat; import com.loki.lochat.LoChat;
import com.loki.lochat.utils.ChatFormatter;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -8,6 +9,7 @@ import org.bukkit.command.TabCompleter;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List; import java.util.List;
public class LoChatCommand implements CommandExecutor, TabCompleter { public class LoChatCommand implements CommandExecutor, TabCompleter {
@ -21,35 +23,75 @@ public class LoChatCommand implements CommandExecutor, TabCompleter {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command,
@NotNull String label, @NotNull String[] args) { @NotNull String label, @NotNull String[] args) {
if (args.length == 0) {
sender.sendMessage("§e=== LoChat v" + plugin.getDescription().getVersion() + " ===");
sender.sendMessage("§e/lochat reload §7— перезагрузить конфиги");
return true;
}
if (args[0].equalsIgnoreCase("reload")) {
if (!sender.hasPermission("lochat.admin")) { if (!sender.hasPermission("lochat.admin")) {
sender.sendMessage(plugin.getMessageConfig().get("errors.no-permission")); sender.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getNoPermission()));
return true; return true;
} }
if (args.length == 0) {
sender.sendMessage(ChatFormatter.parse("<gold>LoChat v" + plugin.getDescription().getVersion() + "</gold>"));
sender.sendMessage(ChatFormatter.parse("<gray>Команды:</gray>"));
sender.sendMessage(ChatFormatter.parse("<yellow>/lochat reload</yellow> - Перезагрузить конфиги"));
sender.sendMessage(ChatFormatter.parse("<yellow>/lochat commands</yellow> - Список кастомных команд"));
sender.sendMessage(ChatFormatter.parse("<yellow>/lochat commands reload</yellow> - Перезагрузить кастомные команды"));
return true;
}
switch (args[0].toLowerCase()) {
case "reload" -> {
plugin.reload(); plugin.reload();
sender.sendMessage("§aLoChat перезагружен!"); sender.sendMessage(ChatFormatter.parse("<green>Конфигурация перезагружена!</green>"));
return true; }
case "commands" -> {
if (args.length > 1 && args[1].equalsIgnoreCase("reload")) {
plugin.getCustomCommandManager().reload();
sender.sendMessage(ChatFormatter.parse("<green>Кастомные команды перезагружены!</green>"));
} else {
var commands = plugin.getCustomCommandManager().getCommands();
sender.sendMessage(ChatFormatter.parse("<gold>Кастомные команды (" + commands.size() + "):</gold>"));
for (var cmd : commands.values()) {
sender.sendMessage(ChatFormatter.parse("<yellow>/" + cmd.name + "</yellow> <gray>(" + cmd.type + ")</gray>"));
if (!cmd.aliases.isEmpty()) {
sender.sendMessage(ChatFormatter.parse("<gray> Алиасы: " + String.join(", ", cmd.aliases) + "</gray>"));
}
}
}
}
default -> sender.sendMessage(ChatFormatter.parse("<red>Неизвестная команда. Используйте /lochat для помощи.</red>"));
} }
sender.sendMessage("§cНеизвестная команда. Используй /lochat reload");
return true; return true;
} }
@Override @Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
@NotNull String alias, @NotNull String[] args) { @NotNull String alias, @NotNull String[] args) {
if (args.length == 1 && sender.hasPermission("lochat.admin")) { if (!sender.hasPermission("lochat.admin")) {
return List.of("reload").stream() return new ArrayList<>();
.filter(s -> s.startsWith(args[0].toLowerCase()))
.toList();
} }
return List.of();
if (args.length == 1) {
List<String> completions = new ArrayList<>();
String input = args[0].toLowerCase();
for (String cmd : List.of("reload", "commands")) {
if (cmd.startsWith(input)) {
completions.add(cmd);
}
}
return completions;
}
if (args.length == 2 && args[0].equalsIgnoreCase("commands")) {
List<String> completions = new ArrayList<>();
String input = args[1].toLowerCase();
if ("reload".startsWith(input)) {
completions.add("reload");
}
return completions;
}
return new ArrayList<>();
} }
} }

View file

@ -14,6 +14,76 @@ public class ConfigManager {
this.plugin = plugin; this.plugin = plugin;
plugin.saveDefaultConfig(); plugin.saveDefaultConfig();
this.config = plugin.getConfig(); this.config = plugin.getConfig();
// Проверяем версию конфига и обновляем при необходимости
updateConfig();
}
private void updateConfig() {
int currentVersion = config.getInt("config-version", 1);
int requiredVersion = 2; // Текущая версия конфига
if (currentVersion < requiredVersion) {
plugin.getLogger().info("Обновление конфига с версии " + currentVersion + " до " + requiredVersion);
// Добавляем новые настройки если их нет
addMissingConfigOptions();
// Обновляем версию
config.set("config-version", requiredVersion);
plugin.saveConfig();
plugin.getLogger().info("Конфиг успешно обновлен!");
}
}
private void addMissingConfigOptions() {
// Добавляем настройки объявлений если их нет
if (!config.contains("announcements")) {
config.set("announcements.show-title", true);
config.set("announcements.title-header", "<gold>ОБЪЯВЛЕНИЕ</gold>");
config.set("announcements.show-actionbar", true);
config.set("announcements.title-duration", 3);
}
// Можно добавить другие недостающие настройки
if (!config.contains("chat.pm.enabled")) {
config.set("chat.pm.enabled", true);
}
if (!config.contains("mentions.enabled")) {
config.set("mentions.enabled", true);
config.set("mentions.sound", true);
config.set("mentions.sound-type", "BLOCK_NOTE_BLOCK_PLING");
config.set("mentions.highlight", "<yellow>@{player}</yellow>");
config.set("mentions.self-highlight", "<gold><bold>{player}</bold></gold>");
}
if (!config.contains("filter.enabled")) {
config.set("filter.enabled", true);
config.set("filter.words", java.util.Arrays.asList("хуй", "пизд", "блять", "ебать", "сука"));
config.set("filter.action", "censor");
config.set("filter.replacement", "***");
}
if (!config.contains("antispam.enabled")) {
config.set("antispam.enabled", true);
config.set("antispam.max-caps-percent", 70);
config.set("antispam.max-repeat-chars", 5);
config.set("antispam.similar-message-delay", 30);
}
if (!config.contains("automessages.enabled")) {
config.set("automessages.enabled", true);
config.set("automessages.interval", 300);
config.set("automessages.random", false);
}
if (!config.contains("lopreff.enabled")) {
config.set("lopreff.enabled", true);
config.set("lopreff.use-gradient-name", true);
config.set("lopreff.use-custom-prefix", true);
}
} }
public void reload() { public void reload() {
@ -153,6 +223,23 @@ public class ConfigManager {
return config.getString("formats.announcement", "<gold>[ОБЪЯВЛЕНИЕ]</gold> <message>"); return config.getString("formats.announcement", "<gold>[ОБЪЯВЛЕНИЕ]</gold> <message>");
} }
// Announcements
public boolean isAnnouncementTitleEnabled() {
return config.getBoolean("announcements.show-title", true);
}
public String getAnnouncementTitleHeader() {
return config.getString("announcements.title-header", "<gold>ОБЪЯВЛЕНИЕ</gold>");
}
public boolean isAnnouncementActionBarEnabled() {
return config.getBoolean("announcements.show-actionbar", true);
}
public int getAnnouncementTitleDuration() {
return config.getInt("announcements.title-duration", 3);
}
// LoPreff integration // LoPreff integration
public boolean isLopreffEnabled() { public boolean isLopreffEnabled() {
return config.getBoolean("lopreff.enabled", true); return config.getBoolean("lopreff.enabled", true);

View file

@ -79,4 +79,8 @@ public class GradientConfig {
return getPrefixBlacklist().stream() return getPrefixBlacklist().stream()
.anyMatch(blacklisted -> lowerPrefix.contains(blacklisted.toLowerCase())); .anyMatch(blacklisted -> lowerPrefix.contains(blacklisted.toLowerCase()));
} }
// Display settings
public boolean isUpdateDisplayName() { return config.getBoolean("display.update-display-name", true); }
public boolean isUpdateTabList() { return config.getBoolean("display.update-tab-list", true); }
} }

View file

@ -32,7 +32,10 @@ public class GradientPlayerListener implements Listener {
FoliaUtil.runEntityTask(module.getPlugin(), player, () -> { FoliaUtil.runEntityTask(module.getPlugin(), player, () -> {
if (!player.isOnline()) return; if (!player.isOnline()) return;
// Обновляем display name если включено
if (module.getConfig().isUpdateDisplayName()) {
DisplayNameUtil.updateDisplayName(module, player, data); DisplayNameUtil.updateDisplayName(module, player, data);
}
if (data.hasPrefix() && data.isPrefixEnabled()) { if (data.hasPrefix() && data.isPrefixEnabled()) {
module.getLuckPermsHook().setPrefix(player, module.getLuckPermsHook().setPrefix(player,

View file

@ -0,0 +1,93 @@
package com.loki.lochat.managers;
import com.loki.lochat.LoChat;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class ChatColorManager {
private final LoChat plugin;
private final Map<UUID, String> chatColors;
private File dataFile;
private FileConfiguration dataConfig;
public ChatColorManager(LoChat plugin) {
this.plugin = plugin;
this.chatColors = new HashMap<>();
loadData();
}
private void loadData() {
dataFile = new File(plugin.getDataFolder(), "chatcolors.yml");
if (!dataFile.exists()) {
try {
dataFile.createNewFile();
} catch (IOException e) {
plugin.getLogger().severe("Не удалось создать файл chatcolors.yml: " + e.getMessage());
}
}
dataConfig = YamlConfiguration.loadConfiguration(dataFile);
// Загружаем цвета из файла
for (String key : dataConfig.getKeys(false)) {
try {
UUID uuid = UUID.fromString(key);
String color = dataConfig.getString(key);
if (color != null) {
chatColors.put(uuid, color);
}
} catch (IllegalArgumentException ignored) {
// Неверный UUID, пропускаем
}
}
}
public void saveData() {
for (Map.Entry<UUID, String> entry : chatColors.entrySet()) {
dataConfig.set(entry.getKey().toString(), entry.getValue());
}
try {
dataConfig.save(dataFile);
} catch (IOException e) {
plugin.getLogger().severe("Не удалось сохранить chatcolors.yml: " + e.getMessage());
}
}
public void setChatColor(UUID playerId, String color) {
chatColors.put(playerId, color);
saveData();
}
public String getChatColor(UUID playerId) {
return chatColors.get(playerId);
}
public boolean hasChatColor(UUID playerId) {
return chatColors.containsKey(playerId);
}
public void removeChatColor(UUID playerId) {
chatColors.remove(playerId);
dataConfig.set(playerId.toString(), null);
saveData();
}
/**
* Применяет цвет чата к сообщению
*/
public String applyChatColor(UUID playerId, String message) {
String color = getChatColor(playerId);
if (color != null) {
return "<" + color + ">" + message + "</" + color + ">";
}
return message;
}
}

View file

@ -0,0 +1,278 @@
package com.loki.lochat.managers;
import com.loki.lochat.LoChat;
import com.loki.lochat.commands.CustomCommand;
import com.loki.lochat.utils.ChatFormatter;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.title.Title;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandMap;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import java.io.File;
import java.lang.reflect.Field;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class CustomCommandManager {
private final LoChat plugin;
private final Map<String, CustomCommandData> commands;
private File commandsFile;
private FileConfiguration commandsConfig;
public CustomCommandManager(LoChat plugin) {
this.plugin = plugin;
this.commands = new HashMap<>();
loadCommands();
}
private void loadCommands() {
commandsFile = new File(plugin.getDataFolder(), "custom-commands.yml");
if (!commandsFile.exists()) {
plugin.saveResource("custom-commands.yml", false);
}
commandsConfig = YamlConfiguration.loadConfiguration(commandsFile);
// Очищаем старые команды
commands.clear();
// Загружаем команды из конфига
for (String commandName : commandsConfig.getKeys(false)) {
ConfigurationSection section = commandsConfig.getConfigurationSection(commandName);
if (section == null) continue;
CustomCommandData data = new CustomCommandData(
commandName,
section.getString("permission"),
section.getString("message", ""),
section.getStringList("aliases"),
section.getString("type", "chat"),
section.getString("target", "sender")
);
commands.put(commandName, data);
// Регистрируем команду
registerCommand(data);
}
plugin.getLogger().info("Загружено " + commands.size() + " кастомных команд");
}
private void registerCommand(CustomCommandData data) {
try {
// Получаем CommandMap через рефлексию
Field commandMapField = Bukkit.getServer().getClass().getDeclaredField("commandMap");
commandMapField.setAccessible(true);
CommandMap commandMap = (CommandMap) commandMapField.get(Bukkit.getServer());
// Создаем и регистрируем команду
CustomCommand command = new CustomCommand(data.name, this);
commandMap.register("lochat", command);
// Регистрируем алиасы
for (String alias : data.aliases) {
CustomCommand aliasCommand = new CustomCommand(alias, this);
commandMap.register("lochat", aliasCommand);
}
} catch (Exception e) {
plugin.getLogger().severe("Ошибка регистрации команды " + data.name + ": " + e.getMessage());
}
}
public void reload() {
loadCommands();
}
public boolean executeCustomCommand(String commandName, Player player, String[] args) {
CustomCommandData data = commands.get(commandName);
if (data == null) {
// Проверяем алиасы
for (CustomCommandData cmd : commands.values()) {
if (cmd.aliases.contains(commandName)) {
data = cmd;
break;
}
}
}
if (data == null) return false;
// Проверяем права
if (data.permission != null && !player.hasPermission(data.permission)) {
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getNoPermission()));
return true;
}
// Обрабатываем плейсхолдеры
String message = processPlaceholders(data.message, player, args);
// Выполняем команду в зависимости от типа
switch (data.type.toLowerCase()) {
case "chat" -> sendChatMessage(data.target, player, message);
case "broadcast" -> sendBroadcast(data.target, message);
case "title" -> sendTitle(data.target, player, message);
case "actionbar" -> sendActionBar(data.target, player, message);
default -> sendChatMessage(data.target, player, message);
}
return true;
}
private String processPlaceholders(String message, Player player, String[] args) {
String result = message;
// Базовые плейсхолдеры
result = result.replace("{player}", player.getName());
result = result.replace("{x}", String.valueOf(player.getLocation().getBlockX()));
result = result.replace("{y}", String.valueOf(player.getLocation().getBlockY()));
result = result.replace("{z}", String.valueOf(player.getLocation().getBlockZ()));
result = result.replace("{world}", player.getWorld().getName());
// Аргументы команды
for (int i = 0; i < args.length; i++) {
result = result.replace("{arg" + i + "}", args[i]);
}
result = result.replace("{args}", String.join(" ", args));
// PlaceholderAPI если доступен
if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) {
result = me.clip.placeholderapi.PlaceholderAPI.setPlaceholders(player, result);
}
return result;
}
private void sendChatMessage(String target, Player sender, String message) {
Component component = ChatFormatter.parse(message);
switch (target.toLowerCase()) {
case "sender" -> sender.sendMessage(component);
case "all" -> {
for (Player player : Bukkit.getOnlinePlayers()) {
player.sendMessage(component);
}
}
default -> {
if (target.startsWith("permission:")) {
String permission = target.substring(11);
for (Player player : Bukkit.getOnlinePlayers()) {
if (player.hasPermission(permission)) {
player.sendMessage(component);
}
}
} else {
sender.sendMessage(component);
}
}
}
}
private void sendBroadcast(String target, String message) {
Component component = ChatFormatter.parse(message);
switch (target.toLowerCase()) {
case "all" -> {
for (Player player : Bukkit.getOnlinePlayers()) {
player.sendMessage(component);
}
}
default -> {
if (target.startsWith("permission:")) {
String permission = target.substring(11);
for (Player player : Bukkit.getOnlinePlayers()) {
if (player.hasPermission(permission)) {
player.sendMessage(component);
}
}
}
}
}
}
private void sendTitle(String target, Player sender, String message) {
String[] parts = message.split("\\n", 2);
Component title = ChatFormatter.parse(parts[0]);
Component subtitle = parts.length > 1 ? ChatFormatter.parse(parts[1]) : Component.empty();
Title titleObj = Title.title(title, subtitle,
Title.Times.times(Duration.ofMillis(500), Duration.ofSeconds(3), Duration.ofMillis(500)));
switch (target.toLowerCase()) {
case "sender" -> sender.showTitle(titleObj);
case "all" -> {
for (Player player : Bukkit.getOnlinePlayers()) {
player.showTitle(titleObj);
}
}
default -> {
if (target.startsWith("permission:")) {
String permission = target.substring(11);
for (Player player : Bukkit.getOnlinePlayers()) {
if (player.hasPermission(permission)) {
player.showTitle(titleObj);
}
}
} else {
sender.showTitle(titleObj);
}
}
}
}
private void sendActionBar(String target, Player sender, String message) {
Component component = ChatFormatter.parse(message);
switch (target.toLowerCase()) {
case "sender" -> sender.sendActionBar(component);
case "all" -> {
for (Player player : Bukkit.getOnlinePlayers()) {
player.sendActionBar(component);
}
}
default -> {
if (target.startsWith("permission:")) {
String permission = target.substring(11);
for (Player player : Bukkit.getOnlinePlayers()) {
if (player.hasPermission(permission)) {
player.sendActionBar(component);
}
}
} else {
sender.sendActionBar(component);
}
}
}
}
public Map<String, CustomCommandData> getCommands() {
return commands;
}
public static class CustomCommandData {
public final String name;
public final String permission;
public final String message;
public final List<String> aliases;
public final String type;
public final String target;
public CustomCommandData(String name, String permission, String message,
List<String> aliases, String type, String target) {
this.name = name;
this.permission = permission;
this.message = message;
this.aliases = aliases;
this.type = type;
this.target = target;
}
}
}

View file

@ -123,7 +123,7 @@ public class ChatFormatter {
/** /**
* Полная обработка сообщения игрока * Полная обработка сообщения игрока
* Цвета + смайлики * Цвета + смайлики + цвет чата
*/ */
public static String processPlayerMessage(String message, Player player, boolean hasColorPermission) { public static String processPlayerMessage(String message, Player player, boolean hasColorPermission) {
String result = message; String result = message;
@ -137,6 +137,12 @@ public class ChatFormatter {
} else { } else {
// Убираем цветовые коды // Убираем цветовые коды
result = stripLegacyColors(result); result = stripLegacyColors(result);
// Применяем цвет чата если установлен
LoChat plugin = LoChat.getInstance();
if (plugin != null && plugin.getChatColorManager() != null) {
result = plugin.getChatColorManager().applyChatColor(player.getUniqueId(), result);
}
} }
return result; return result;
@ -235,6 +241,10 @@ public class ChatFormatter {
&& plugin.getGradientModule() != null && plugin.getGradientModule().isEnabled()) { && plugin.getGradientModule() != null && plugin.getGradientModule().isEnabled()) {
senderDisplay = plugin.getGradientModule().getFormattedName(sender); senderDisplay = plugin.getGradientModule().getFormattedName(sender);
receiverDisplay = plugin.getGradientModule().getFormattedName(receiver); receiverDisplay = plugin.getGradientModule().getFormattedName(receiver);
// Конвертируем legacy коды в MiniMessage формат
senderDisplay = convertAllColors(senderDisplay);
receiverDisplay = convertAllColors(receiverDisplay);
} }
return parse(format return parse(format
@ -258,6 +268,10 @@ public class ChatFormatter {
&& plugin.getGradientModule() != null && plugin.getGradientModule().isEnabled()) { && plugin.getGradientModule() != null && plugin.getGradientModule().isEnabled()) {
senderDisplay = plugin.getGradientModule().getFormattedName(sender); senderDisplay = plugin.getGradientModule().getFormattedName(sender);
receiverDisplay = plugin.getGradientModule().getFormattedName(receiver); receiverDisplay = plugin.getGradientModule().getFormattedName(receiver);
// Конвертируем legacy коды в MiniMessage формат
senderDisplay = convertAllColors(senderDisplay);
receiverDisplay = convertAllColors(receiverDisplay);
} }
return parse(format return parse(format

View file

@ -1,5 +1,8 @@
# LoChat Configuration # LoChat Configuration
# Author: loki # Author: loki
# Config Version: 2
config-version: 2
chat: chat:
global: global:
@ -67,3 +70,14 @@ automessages:
formats: formats:
announcement: "<gold>[ОБЪЯВЛЕНИЕ]</gold> <message>" announcement: "<gold>[ОБЪЯВЛЕНИЕ]</gold> <message>"
# Настройки объявлений
announcements:
# Показывать title при объявлении
show-title: true
# Заголовок title
title-header: "<gold>ОБЪЯВЛЕНИЕ</gold>"
# Показывать actionbar при объявлении
show-actionbar: true
# Время показа title (в секундах)
title-duration: 3

View file

@ -0,0 +1,51 @@
# Кастомные команды LoChat
# Здесь можно создавать свои команды с сообщениями и алиасами
# Пример структуры:
# command-name:
# permission: "lochat.command.name" # Право для использования (опционально)
# message: "Текст сообщения" # Сообщение которое отправится
# aliases: ["alias1", "alias2"] # Алиасы команды (опционально)
# type: "chat" # Тип: chat, broadcast, title, actionbar
# target: "sender" # Кому отправить: sender, all, permission:право
# Примеры команд:
help:
message: "<gold>Добро пожаловать на сервер!</gold>\n<gray>Используйте /rules для просмотра правил</gray>"
aliases: ["помощь", "хелп"]
type: "chat"
target: "sender"
rules:
message: "<red><bold>ПРАВИЛА СЕРВЕРА:</bold></red>\n<gray>1. Не гриферить\n2. Не читерить\n3. Уважать других игроков</gray>"
aliases: ["правила", "rule"]
type: "chat"
target: "sender"
discord:
message: "<blue>Наш Discord сервер: discord.gg/example</blue>"
aliases: ["дискорд", "ds"]
type: "chat"
target: "sender"
# Команда для админов
restart-warning:
permission: "lochat.admin"
message: "<red><bold>ВНИМАНИЕ!</bold></red> <yellow>Сервер будет перезагружен через 5 минут!</yellow>"
aliases: ["рестарт", "reboot"]
type: "broadcast"
target: "all"
# Команда с title
welcome:
message: "Добро пожаловать!"
aliases: ["привет"]
type: "title"
target: "sender"
# Команда с actionbar
coords:
message: "Ваши координаты: {x}, {y}, {z}"
aliases: ["координаты", "pos"]
type: "actionbar"
target: "sender"

View file

@ -43,6 +43,13 @@ continuous-gradient: true
# Использовать &#RRGGBB формат (true) или <#RRGGBB> (false) # Использовать &#RRGGBB формат (true) или <#RRGGBB> (false)
use-legacy-rgb-format: true use-legacy-rgb-format: true
# Отображение над игроком
display:
# Обновлять display name игрока (показывает префикс+ник над головой)
update-display-name: true
# Обновлять tab list
update-tab-list: true
# Тип хранилища: YAML или SQLITE # Тип хранилища: YAML или SQLITE
storage-type: YAML storage-type: YAML

View file

@ -1,5 +1,5 @@
name: LoChat name: LoChat
version: '1.0.0' version: '1.5.0'
main: com.loki.lochat.LoChat main: com.loki.lochat.LoChat
api-version: '1.21' api-version: '1.21'
folia-supported: true folia-supported: true
@ -31,28 +31,31 @@ commands:
description: Send private message description: Send private message
usage: /msg <player> <message> usage: /msg <player> <message>
permission: chat.pm.use permission: chat.pm.use
aliases: [tell, w, pm] aliases: [tell, w, pm, m, ьып]
reply: reply:
description: Reply to last message description: Reply to last message
usage: /reply <message> usage: /reply <message>
permission: chat.pm.use permission: chat.pm.use
aliases: [r] aliases: [r, ответить]
ignore: ignore:
description: Ignore player description: Ignore player
usage: /ignore <player> usage: /ignore <player>
permission: chat.pm.ignore permission: chat.pm.ignore
aliases: [игнор]
unignore: unignore:
description: Unignore player description: Unignore player
usage: /unignore <player> usage: /unignore <player>
permission: chat.pm.ignore permission: chat.pm.ignore
aliases: [разигнор]
announce: announce:
description: Send announcement description: Send announcement
usage: /announce <message> usage: /announce <message>
permission: chat.announce.use permission: chat.announce.use
aliases: [объявление, анонс]
chatspy: chatspy:
description: Toggle chat spy mode description: Toggle chat spy mode
@ -66,6 +69,12 @@ commands:
permission: chat.clear permission: chat.clear
aliases: [cc] aliases: [cc]
chatcolor:
description: Set chat color
usage: /chatcolor <color>
permission: lochat.chatcolor
aliases: [цветчата, chatcolour]
lochat: lochat:
description: LoChat admin command description: LoChat admin command
usage: /lochat [reload] usage: /lochat [reload]
@ -75,12 +84,13 @@ commands:
description: Set gradient nick color description: Set gradient nick color
usage: /color <hex1> [hex2] ... usage: /color <hex1> [hex2] ...
permission: gradient.color permission: gradient.color
aliases: [gradient] aliases: [gradient, цвет, градиент]
prefix: prefix:
description: Set custom prefix description: Set custom prefix
usage: /prefix <text> usage: /prefix <text>
permission: gradient.prefix permission: gradient.prefix
aliases: [префикс]
aprefix: aprefix:
description: Admin gradient commands description: Admin gradient commands
@ -119,6 +129,8 @@ permissions:
default: op default: op
chat.mention.use: chat.mention.use:
default: true default: true
lochat.chatcolor:
default: true
gradient.color: gradient.color:
default: true default: true
gradient.prefix: gradient.prefix: