chore: update .gitignore and build.gradle.kts for local server testing
- Added entries to .gitignore for local Paper test server and API documentation. - Included the run-paper plugin in build.gradle.kts for local server execution. - Configured runServer task with Minecraft version 1.21.4 for testing.
This commit is contained in:
parent
d1d2b93742
commit
2d79d2a91d
260 changed files with 4808 additions and 3847 deletions
|
|
@ -1,20 +1,14 @@
|
|||
package com.loki.lochat;
|
||||
|
||||
import com.loki.lochat.api.service.IgnoreService;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.api.service.NickService;
|
||||
import com.loki.lochat.commands.*;
|
||||
import com.loki.lochat.api.service.PunishmentService;
|
||||
import com.loki.lochat.commands.CommandManager;
|
||||
import com.loki.lochat.config.ConfigManager;
|
||||
import com.loki.lochat.config.MessageConfig;
|
||||
import com.loki.lochat.core.PluginInitializer;
|
||||
import com.loki.lochat.core.PluginShutdown;
|
||||
import com.loki.lochat.core.registry.ServiceRegistry;
|
||||
import com.loki.lochat.gradient.GradientModule;
|
||||
import com.loki.lochat.integrations.DiscordIntegration;
|
||||
import com.loki.lochat.integrations.LibertyBansHook;
|
||||
import com.loki.lochat.integrations.PlaceholderAPIHook;
|
||||
import com.loki.lochat.core.filter.AdvancedMessageFilter;
|
||||
import com.loki.lochat.listener.ChatEventListener;
|
||||
import com.loki.lochat.listener.DiscordEventListener;
|
||||
import com.loki.lochat.listener.PlayerEventListener;
|
||||
import com.loki.lochat.managers.AutoMessageManager;
|
||||
import com.loki.lochat.managers.CustomCommandManager;
|
||||
import org.bukkit.Bukkit;
|
||||
|
|
@ -27,6 +21,8 @@ import org.bukkit.plugin.java.JavaPlugin;
|
|||
public final class LoChat extends JavaPlugin {
|
||||
|
||||
private static LoChat instance;
|
||||
|
||||
// Основные компоненты
|
||||
private ServiceRegistry serviceRegistry;
|
||||
private ConfigManager configManager;
|
||||
private MessageConfig messageConfig;
|
||||
|
|
@ -35,115 +31,51 @@ public final class LoChat extends JavaPlugin {
|
|||
private CustomCommandManager customCommandManager;
|
||||
private DiscordIntegration discordIntegration;
|
||||
private CommandManager commandManager;
|
||||
private AdvancedMessageFilter advancedMessageFilter;
|
||||
|
||||
// Геттеры
|
||||
public static LoChat getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
// Вспомогательные классы
|
||||
private PluginInitializer initializer;
|
||||
private PluginShutdown shutdown;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
instance = this;
|
||||
getLogger().info("Загрузка LoChat v" + getPluginMeta().getVersion() + " для Paper/Folia " + Bukkit.getMinecraftVersion() + "...");
|
||||
|
||||
// Инициализация
|
||||
initializer = new PluginInitializer(this);
|
||||
shutdown = new PluginShutdown(this);
|
||||
|
||||
// Конфиги
|
||||
configManager = new ConfigManager(this);
|
||||
messageConfig = new MessageConfig(this);
|
||||
|
||||
// Service Registry (SOLID DI) - с зависимостями
|
||||
serviceRegistry = ServiceRegistry.createWithDeps(this, configManager, messageConfig);
|
||||
getLogger().info("ServiceRegistry инициализирован (SOLID DI)");
|
||||
|
||||
configManager = initializer.initConfigs();
|
||||
messageConfig = initializer.initMessageConfig();
|
||||
|
||||
// Service Registry (SOLID DI)
|
||||
serviceRegistry = initializer.initServiceRegistry(configManager, messageConfig);
|
||||
|
||||
// Менеджеры
|
||||
customCommandManager = CustomCommandManager.create(this);
|
||||
autoMessageManager = new AutoMessageManager(this);
|
||||
|
||||
customCommandManager = initializer.initCustomCommandManager();
|
||||
autoMessageManager = initializer.initAutoMessageManager();
|
||||
|
||||
// Интеграции
|
||||
initIntegrations();
|
||||
|
||||
discordIntegration = initializer.initDiscordIntegration();
|
||||
gradientModule = initializer.initGradientModule();
|
||||
initializer.initIntegrations(discordIntegration, gradientModule);
|
||||
|
||||
// Команды и слушатели
|
||||
commandManager = new CommandManager(this);
|
||||
commandManager.registerCommands();
|
||||
registerListeners();
|
||||
registerPluginChannels();
|
||||
|
||||
initializer.registerListeners(serviceRegistry, discordIntegration);
|
||||
initializer.registerPluginChannels();
|
||||
|
||||
// Автосообщения
|
||||
autoMessageManager.start();
|
||||
|
||||
|
||||
getLogger().info("LoChat успешно запущен!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (autoMessageManager != null) autoMessageManager.stop();
|
||||
|
||||
// Сохраняем статистику
|
||||
com.loki.lochat.api.service.PlayerDataService pds = serviceRegistry.get(com.loki.lochat.api.service.PlayerDataService.class);
|
||||
if (pds != null) pds.saveAll();
|
||||
|
||||
// Сохранение данных через сервисы
|
||||
IgnoreService ignoreService = serviceRegistry.get(IgnoreService.class);
|
||||
if (ignoreService != null) ignoreService.save();
|
||||
|
||||
MuteService muteService = serviceRegistry.get(MuteService.class);
|
||||
if (muteService != null) muteService.save();
|
||||
|
||||
NickService nickService = serviceRegistry.get(NickService.class);
|
||||
if (nickService != null) nickService.save();
|
||||
|
||||
// Останавливаем Discord executor
|
||||
if (discordIntegration != null) discordIntegration.shutdown();
|
||||
|
||||
if (gradientModule != null) gradientModule.shutdown();
|
||||
getLogger().info("LoChat отключен!");
|
||||
}
|
||||
|
||||
private void initIntegrations() {
|
||||
// Discord интеграция
|
||||
discordIntegration = new DiscordIntegration(this);
|
||||
|
||||
// Gradient модуль
|
||||
gradientModule = new GradientModule(this);
|
||||
if (gradientModule.init()) {
|
||||
getLogger().info("Gradient модуль (LoPreff) загружен!");
|
||||
}
|
||||
|
||||
// LibertyBans
|
||||
new LibertyBansHook(this);
|
||||
|
||||
// PlaceholderAPI
|
||||
if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) {
|
||||
new PlaceholderAPIHook(this).register();
|
||||
getLogger().info("PlaceholderAPI подключен!");
|
||||
}
|
||||
}
|
||||
|
||||
// Старый метод регистрации команд - заменен на CommandManager
|
||||
/*
|
||||
private void registerCommands() {
|
||||
// Код перенесен в CommandManager для лучшей организации
|
||||
}
|
||||
*/
|
||||
|
||||
private void registerListeners() {
|
||||
// Создаём один экземпляр фильтра и передаём в оба листенера — flood/spam трекеры общие
|
||||
advancedMessageFilter = new AdvancedMessageFilter(getConfig(), this);
|
||||
getServer().getPluginManager().registerEvents(new ChatEventListener(this, serviceRegistry, advancedMessageFilter), this);
|
||||
getServer().getPluginManager().registerEvents(new PlayerEventListener(serviceRegistry, advancedMessageFilter), this);
|
||||
|
||||
// Discord listener
|
||||
if (discordIntegration.isEnabled()) {
|
||||
getServer().getPluginManager().registerEvents(new DiscordEventListener(discordIntegration), this);
|
||||
getLogger().info("Discord event listener registered");
|
||||
}
|
||||
|
||||
getLogger().info("Слушатели зарегистрированы (новая архитектура)");
|
||||
}
|
||||
|
||||
private void registerPluginChannels() {
|
||||
getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
|
||||
getLogger().info("BungeeCord plugin messaging channel registered");
|
||||
shutdown.shutdown(serviceRegistry, autoMessageManager, discordIntegration, gradientModule);
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
|
|
@ -153,6 +85,17 @@ public final class LoChat extends JavaPlugin {
|
|||
if (gradientModule != null) gradientModule.reload();
|
||||
if (customCommandManager != null) customCommandManager.reload();
|
||||
if (discordIntegration != null) discordIntegration.reload();
|
||||
if (serviceRegistry != null) {
|
||||
try {
|
||||
serviceRegistry.get(PunishmentService.class).save();
|
||||
} catch (IllegalStateException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Геттеры
|
||||
public static LoChat getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public ServiceRegistry getServiceRegistry() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
package com.loki.lochat.api.service;
|
||||
|
||||
import com.loki.lochat.data.model.BanRecord;
|
||||
import com.loki.lochat.data.model.WarnEntry;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Варны и баны LoChat
|
||||
*/
|
||||
public interface PunishmentService {
|
||||
|
||||
void addWarn(UUID uuid, String playerName, String moderator, String reason, boolean silent);
|
||||
|
||||
List<WarnEntry> getWarns(UUID uuid);
|
||||
|
||||
int getWarnCount(UUID uuid);
|
||||
|
||||
/**
|
||||
* @param durationMs длительность; 0 = навсегда
|
||||
*/
|
||||
void ban(UUID uuid, String playerName, long durationMs, String reason, String bannedBy);
|
||||
|
||||
boolean unban(UUID uuid);
|
||||
|
||||
boolean isBanned(UUID uuid);
|
||||
|
||||
BanRecord getActiveBan(UUID uuid);
|
||||
|
||||
/** Сообщение при кике (MiniMessage + legacy &#) */
|
||||
Component buildBanKickMessage(BanRecord ban);
|
||||
|
||||
void save();
|
||||
|
||||
void reload();
|
||||
}
|
||||
|
|
@ -43,10 +43,14 @@ public class CommandManager {
|
|||
reg("lmutelist", new MuteListCommand(plugin));
|
||||
regTab("lmutehistory", new MuteHistoryCommand(plugin));
|
||||
regTab("lmuteblame", new MuteBlameCommand(plugin));
|
||||
regTab("warn", new WarnCommand(plugin));
|
||||
regTab("silentwarn", new SilentWarnCommand(plugin));
|
||||
regTab("lban", new BanCommand(plugin));
|
||||
regTab("lunban", new UnbanCommand(plugin));
|
||||
|
||||
// ── Ник / профиль ─────────────────────────────────────────────────────
|
||||
regTab("nick", new NickCommand(plugin));
|
||||
reg("playerinfo", new PlayerInfoCommand(plugin));
|
||||
regTab("playerinfo", new PlayerInfoCommand(plugin));
|
||||
|
||||
// ── Админ ─────────────────────────────────────────────────────────────
|
||||
reg("announce", new AnnounceCommand(plugin));
|
||||
|
|
|
|||
|
|
@ -35,4 +35,4 @@ public final class CustomCommand extends Command {
|
|||
// Базовое автодополнение - можно расширить при необходимости
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ 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 java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
|
@ -125,4 +124,4 @@ public class CustomCommandsCommand implements CommandExecutor, TabCompleter {
|
|||
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public class ChatSpyCommand implements CommandExecutor {
|
|||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(plugin.getConfigManager().getHardcodedMessages().getPlayerOnly());
|
||||
sender.sendMessage(plugin.getConfigManager().getMessagesConfig().getPlayerOnly());
|
||||
return true;
|
||||
}
|
||||
boolean enabled = spyService.toggleSpy(player.getUniqueId());
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ public class ReloadConfigCommand implements CommandExecutor {
|
|||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!sender.hasPermission("lochat.admin.reload")) {
|
||||
sender.sendMessage(ChatFormatter.parse(plugin.getConfigManager().getHardcodedMessages().getNoPermission()));
|
||||
sender.sendMessage(ChatFormatter.parse(plugin.getConfigManager().getMessagesConfig().getNoPermission()));
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -32,4 +32,4 @@ public abstract class AdminCommand extends BaseCommand {
|
|||
*/
|
||||
protected abstract boolean executeAdminCommand(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ public abstract class BaseCommand implements CommandExecutor, TabCompleter {
|
|||
*/
|
||||
protected boolean requirePlayer(CommandSender sender) {
|
||||
if (!(sender instanceof Player)) {
|
||||
sender.sendMessage(plugin.getConfigManager().getHardcodedMessages().getPlayerOnly());
|
||||
sender.sendMessage(plugin.getConfigManager().getMessagesConfig().getPlayerOnly());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -79,7 +79,7 @@ public abstract class BaseCommand implements CommandExecutor, TabCompleter {
|
|||
*/
|
||||
protected boolean requirePermission(CommandSender sender, String permission) {
|
||||
if (!hasPermission(sender, permission)) {
|
||||
sender.sendMessage(plugin.getConfigManager().getHardcodedMessages().getNoPermission());
|
||||
sender.sendMessage(plugin.getConfigManager().getMessagesConfig().getNoPermission());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -98,4 +98,4 @@ public abstract class BaseCommand implements CommandExecutor, TabCompleter {
|
|||
protected void sendUsage(CommandSender sender, String usage) {
|
||||
sender.sendMessage("§cИспользование: " + usage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,4 +31,4 @@ public abstract class PlayerCommand extends BaseCommand {
|
|||
*/
|
||||
protected abstract boolean executePlayerCommand(@NotNull Player player, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.loki.lochat.commands.chat;
|
|||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.ChatService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
|
|
@ -25,7 +24,7 @@ public class GlobalChatCommand implements CommandExecutor {
|
|||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(plugin.getConfigManager().getHardcodedMessages().getPlayerOnly());
|
||||
sender.sendMessage(plugin.getConfigManager().getMessagesConfig().getPlayerOnly());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public class LocalChatCommand implements CommandExecutor {
|
|||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(plugin.getConfigManager().getHardcodedMessages().getPlayerOnly());
|
||||
sender.sendMessage(plugin.getConfigManager().getMessagesConfig().getPlayerOnly());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public class IgnoreCommand implements CommandExecutor {
|
|||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) { sender.sendMessage(plugin.getConfigManager().getHardcodedMessages().getPlayerOnly()); return true; }
|
||||
if (!(sender instanceof Player player)) { sender.sendMessage(plugin.getConfigManager().getMessagesConfig().getPlayerOnly()); return true; }
|
||||
if (args.length == 0) { player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getInvalidUsage("/ignore <ник>"))); return true; }
|
||||
|
||||
Player target = Bukkit.getPlayer(args[0]);
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ public class IgnoreListCommand implements CommandExecutor {
|
|||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) { sender.sendMessage(plugin.getConfigManager().getHardcodedMessages().getPlayerOnly()); return true; }
|
||||
if (!(sender instanceof Player player)) { sender.sendMessage(plugin.getConfigManager().getMessagesConfig().getPlayerOnly()); return true; }
|
||||
|
||||
Set<UUID> ignored = ignoreService.getIgnoredPlayers(player.getUniqueId());
|
||||
if (ignored.isEmpty()) { player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("ignore.list-empty"))); return true; }
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ public class MsgCommand implements CommandExecutor {
|
|||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(plugin.getConfigManager().getHardcodedMessages().getPlayerOnly());
|
||||
sender.sendMessage(plugin.getConfigManager().getMessagesConfig().getPlayerOnly());
|
||||
return true;
|
||||
}
|
||||
if (!plugin.getConfigManager().isPmEnabled()) return true;
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ public class ReplyCommand implements CommandExecutor {
|
|||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(plugin.getConfigManager().getHardcodedMessages().getPlayerOnly());
|
||||
sender.sendMessage(plugin.getConfigManager().getMessagesConfig().getPlayerOnly());
|
||||
return true;
|
||||
}
|
||||
if (!plugin.getConfigManager().isPmEnabled()) return true;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public class UnignoreCommand implements CommandExecutor {
|
|||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) { sender.sendMessage(plugin.getConfigManager().getHardcodedMessages().getPlayerOnly()); return true; }
|
||||
if (!(sender instanceof Player player)) { sender.sendMessage(plugin.getConfigManager().getMessagesConfig().getPlayerOnly()); return true; }
|
||||
if (args.length == 0) { player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getInvalidUsage("/unignore <ник>"))); return true; }
|
||||
|
||||
Player target = Bukkit.getPlayer(args[0]);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,136 @@
|
|||
package com.loki.lochat.commands.moderation;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.api.service.PunishmentService;
|
||||
import com.loki.lochat.config.MessagesConfig;
|
||||
import com.loki.lochat.data.model.BanRecord;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import com.loki.lochat.utils.PlayerUtil;
|
||||
import org.bukkit.Bukkit;
|
||||
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.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* /lban — бан с красивым киком
|
||||
*/
|
||||
public class BanCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final PunishmentService punishmentService;
|
||||
private final MuteService muteService;
|
||||
|
||||
public BanCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.punishmentService = plugin.getServiceRegistry().get(PunishmentService.class);
|
||||
this.muteService = plugin.getServiceRegistry().get(MuteService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args) {
|
||||
MessagesConfig msg = plugin.getConfigManager().getMessagesConfig();
|
||||
if (!sender.hasPermission("lochat.ban")) {
|
||||
sender.sendMessage(ChatFormatter.parse(msg.getNoPermission()));
|
||||
return true;
|
||||
}
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage(ChatFormatter.parse(msg.getBanUsage()));
|
||||
sender.sendMessage(ChatFormatter.parse(msg.getMuteTimeHelp()));
|
||||
return true;
|
||||
}
|
||||
|
||||
String targetName = args[0];
|
||||
UUID targetUuid = PlayerUtil.findPlayerUUID(targetName);
|
||||
if (targetUuid == null) {
|
||||
sender.sendMessage(ChatFormatter.parse(msg.getPlayerNotFound()));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sender instanceof Player p && p.getUniqueId().equals(targetUuid)) {
|
||||
sender.sendMessage(ChatFormatter.parse("&#CF6679Нельзя забанить самого себя"));
|
||||
return true;
|
||||
}
|
||||
|
||||
Player onlineTarget = Bukkit.getPlayer(targetUuid);
|
||||
if (onlineTarget != null && onlineTarget.hasPermission("lochat.bypass.ban") && !sender.hasPermission("lochat.ban.override")) {
|
||||
sender.sendMessage(ChatFormatter.parse("&#CF6679Нельзя забанить этого игрока"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (punishmentService.isBanned(targetUuid)) {
|
||||
sender.sendMessage(ChatFormatter.parse(msg.getAlreadyBanned()));
|
||||
return true;
|
||||
}
|
||||
|
||||
String timeStr = null;
|
||||
StringBuilder reasonBuilder = new StringBuilder();
|
||||
for (int i = 1; i < args.length; i++) {
|
||||
if (timeStr == null && isTimeFormat(args[i])) {
|
||||
timeStr = args[i];
|
||||
} else {
|
||||
if (reasonBuilder.length() > 0) reasonBuilder.append(" ");
|
||||
reasonBuilder.append(args[i]);
|
||||
}
|
||||
}
|
||||
|
||||
long durationMs;
|
||||
if (timeStr == null || timeStr.isEmpty()) {
|
||||
durationMs = 0;
|
||||
} else if (timeStr.equalsIgnoreCase("perm") || timeStr.equals("0")) {
|
||||
durationMs = 0;
|
||||
} else {
|
||||
durationMs = muteService.parseTime(timeStr);
|
||||
if (durationMs < 0) {
|
||||
sender.sendMessage(ChatFormatter.parse(msg.getInvalidTime()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
String reason = reasonBuilder.length() > 0 ? reasonBuilder.toString() : msg.getModerationDefaultReason();
|
||||
String finalName = PlayerUtil.getPlayerName(targetUuid);
|
||||
if (finalName == null) finalName = targetName;
|
||||
|
||||
punishmentService.ban(targetUuid, finalName, durationMs, reason, sender.getName());
|
||||
|
||||
String durationLabel = durationMs == 0
|
||||
? msg.getModerationDurationPermanent()
|
||||
: muteService.formatTime(durationMs);
|
||||
sender.sendMessage(ChatFormatter.parse(msg.getBanConfirm().replace("{player}", finalName).replace("{duration}", durationLabel)));
|
||||
|
||||
BanRecord ban = punishmentService.getActiveBan(targetUuid);
|
||||
if (ban != null) {
|
||||
Player kicked = Bukkit.getPlayer(targetUuid);
|
||||
if (kicked != null && kicked.isOnline()) {
|
||||
kicked.kick(punishmentService.buildBanKickMessage(ban));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isTimeFormat(String s) {
|
||||
return s.equalsIgnoreCase("perm") || s.matches("\\d+[dhms]?");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
List<String> c = new ArrayList<>();
|
||||
if (args.length == 1) {
|
||||
for (Player p : Bukkit.getOnlinePlayers())
|
||||
if (p.getName().toLowerCase().startsWith(args[0].toLowerCase())) c.add(p.getName());
|
||||
} else if (args.length == 2) {
|
||||
c.addAll(List.of("1h", "1d", "7d", "30d", "perm"));
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ package com.loki.lochat.commands.moderation;
|
|||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.config.HardcodedMessages;
|
||||
import com.loki.lochat.config.MessagesConfig;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
|
|
@ -34,7 +34,7 @@ public class MuteCommand implements CommandExecutor, TabCompleter {
|
|||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args) {
|
||||
|
||||
HardcodedMessages hm = plugin.getConfigManager().getHardcodedMessages();
|
||||
MessagesConfig hm = plugin.getConfigManager().getMessagesConfig();
|
||||
|
||||
if (!sender.hasPermission("lochat.mute")) {
|
||||
sender.sendMessage(plugin.getMessageConfig().getComponent("errors.no-permission"));
|
||||
|
|
@ -68,7 +68,7 @@ public class MuteCommand implements CommandExecutor, TabCompleter {
|
|||
}
|
||||
|
||||
if (silent && !sender.hasPermission("lochat.mute.silent")) {
|
||||
sender.sendMessage(ChatFormatter.parse(hm.getNoSilentPermission()));
|
||||
sender.sendMessage(ChatFormatter.parse(hm.getNoSilentMutePermission()));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -123,6 +123,12 @@ public class MuteCommand implements CommandExecutor, TabCompleter {
|
|||
Bukkit.broadcast(ChatFormatter.parse(broadcastMsg));
|
||||
}
|
||||
|
||||
String voiceCmd = plugin.getConfig().getString("moderation.voice-mute-console-command", "");
|
||||
if (target != null && voiceCmd != null && !voiceCmd.isEmpty()) {
|
||||
String c = voiceCmd.replace("{player}", finalName).replace("{uuid}", targetUUID.toString());
|
||||
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), c);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import com.loki.lochat.LoChat;
|
|||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.data.model.MuteData;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
package com.loki.lochat.commands.moderation;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.PunishmentService;
|
||||
import com.loki.lochat.config.MessagesConfig;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import com.loki.lochat.utils.PlayerUtil;
|
||||
import org.bukkit.Bukkit;
|
||||
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.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* /silentwarn — варн в базе, цель не видит; модераторы с lochat.warn.notify видят
|
||||
*/
|
||||
public class SilentWarnCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final PunishmentService punishmentService;
|
||||
|
||||
public SilentWarnCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.punishmentService = plugin.getServiceRegistry().get(PunishmentService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args) {
|
||||
MessagesConfig msg = plugin.getConfigManager().getMessagesConfig();
|
||||
if (!sender.hasPermission("lochat.warn.silent")) {
|
||||
sender.sendMessage(ChatFormatter.parse(msg.getNoPermission()));
|
||||
return true;
|
||||
}
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage(ChatFormatter.parse(msg.getSilentWarnUsage()));
|
||||
return true;
|
||||
}
|
||||
|
||||
UUID targetUuid = PlayerUtil.findPlayerUUID(args[0]);
|
||||
if (targetUuid == null) {
|
||||
sender.sendMessage(ChatFormatter.parse(msg.getPlayerNotFound()));
|
||||
return true;
|
||||
}
|
||||
|
||||
String mod = sender.getName();
|
||||
String reason = args.length > 1
|
||||
? String.join(" ", java.util.Arrays.copyOfRange(args, 1, args.length))
|
||||
: msg.getModerationDefaultReason();
|
||||
String name = PlayerUtil.getPlayerName(targetUuid);
|
||||
if (name == null) name = args[0];
|
||||
|
||||
punishmentService.addWarn(targetUuid, name, mod, reason, true);
|
||||
|
||||
sender.sendMessage(ChatFormatter.parse(msg.getWarnStaffConfirm().replace("{player}", name).replace("{reason}", reason)));
|
||||
|
||||
String staffLine = msg.getSilentWarnStaff().replace("{player}", name).replace("{reason}", reason);
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
if (p.hasPermission("lochat.warn.notify")) {
|
||||
p.sendMessage(ChatFormatter.parse(staffLine));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
List<String> out = new ArrayList<>();
|
||||
if (args.length == 1) {
|
||||
String p = args[0].toLowerCase();
|
||||
for (Player pl : Bukkit.getOnlinePlayers()) {
|
||||
if (pl.getName().toLowerCase().startsWith(p)) out.add(pl.getName());
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
package com.loki.lochat.commands.moderation;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.PunishmentService;
|
||||
import com.loki.lochat.config.MessagesConfig;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import com.loki.lochat.utils.PlayerUtil;
|
||||
import org.bukkit.Bukkit;
|
||||
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.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* /lunban — снять бан
|
||||
*/
|
||||
public class UnbanCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final PunishmentService punishmentService;
|
||||
|
||||
public UnbanCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.punishmentService = plugin.getServiceRegistry().get(PunishmentService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args) {
|
||||
MessagesConfig msg = plugin.getConfigManager().getMessagesConfig();
|
||||
if (!sender.hasPermission("lochat.unban")) {
|
||||
sender.sendMessage(ChatFormatter.parse(msg.getNoPermission()));
|
||||
return true;
|
||||
}
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage(ChatFormatter.parse("&#B798A8Использование: ẲE9/lunban <ник>"));
|
||||
return true;
|
||||
}
|
||||
|
||||
UUID uuid = PlayerUtil.findPlayerUUID(args[0]);
|
||||
if (uuid == null) {
|
||||
sender.sendMessage(ChatFormatter.parse(msg.getPlayerNotFound()));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!punishmentService.isBanned(uuid)) {
|
||||
sender.sendMessage(ChatFormatter.parse(msg.getNotBanned()));
|
||||
return true;
|
||||
}
|
||||
|
||||
String name = PlayerUtil.getPlayerName(uuid);
|
||||
if (name == null) name = args[0];
|
||||
|
||||
punishmentService.unban(uuid);
|
||||
sender.sendMessage(ChatFormatter.parse(msg.getUnbanConfirm().replace("{player}", name)));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
List<String> out = new ArrayList<>();
|
||||
if (args.length == 1) {
|
||||
String p = args[0].toLowerCase();
|
||||
for (Player pl : Bukkit.getOnlinePlayers()) {
|
||||
if (pl.getName().toLowerCase().startsWith(p)) out.add(pl.getName());
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
|
@ -38,7 +38,7 @@ public class UnmuteCommand implements CommandExecutor, TabCompleter {
|
|||
else if (targetName == null) targetName = a;
|
||||
}
|
||||
if (silent && !sender.hasPermission("lochat.mute.silent")) {
|
||||
sender.sendMessage(ChatFormatter.parse(plugin.getConfigManager().getHardcodedMessages().getNoSilentUnmutePermission()));
|
||||
sender.sendMessage(ChatFormatter.parse(plugin.getConfigManager().getMessagesConfig().getNoSilentUnmutePermission()));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ public class UnmuteCommand implements CommandExecutor, TabCompleter {
|
|||
Bukkit.broadcast(ChatFormatter.parse("⚖C9Игрок ẲE9" + name + " ⚖C9размучен модератором ẲE9" + op));
|
||||
}
|
||||
} else {
|
||||
sender.sendMessage(ChatFormatter.parse(plugin.getConfigManager().getHardcodedMessages().getPlayerNotMutedSimple()));
|
||||
sender.sendMessage(ChatFormatter.parse(plugin.getConfigManager().getMessagesConfig().getPlayerNotMutedSimple()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,103 @@
|
|||
package com.loki.lochat.commands.moderation;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.PunishmentService;
|
||||
import com.loki.lochat.config.MessagesConfig;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import com.loki.lochat.utils.PlayerUtil;
|
||||
import org.bukkit.Bukkit;
|
||||
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.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* /warn — игрок видит предупреждение в чате
|
||||
*/
|
||||
public class WarnCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final PunishmentService punishmentService;
|
||||
|
||||
public WarnCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.punishmentService = plugin.getServiceRegistry().get(PunishmentService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args) {
|
||||
MessagesConfig msg = plugin.getConfigManager().getMessagesConfig();
|
||||
if (!sender.hasPermission("lochat.warn")) {
|
||||
sender.sendMessage(ChatFormatter.parse(msg.getNoPermission()));
|
||||
return true;
|
||||
}
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage(ChatFormatter.parse(msg.getWarnUsage()));
|
||||
return true;
|
||||
}
|
||||
|
||||
UUID targetUuid = PlayerUtil.findPlayerUUID(args[0]);
|
||||
if (targetUuid == null) {
|
||||
sender.sendMessage(ChatFormatter.parse(msg.getPlayerNotFound()));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sender instanceof Player p && p.getUniqueId().equals(targetUuid)) {
|
||||
sender.sendMessage(ChatFormatter.parse("&#CF6679Нельзя выдать варн самому себе"));
|
||||
return true;
|
||||
}
|
||||
|
||||
String mod = sender.getName();
|
||||
String reason = buildReason(msg, args, 1);
|
||||
String name = PlayerUtil.getPlayerName(targetUuid);
|
||||
if (name == null) name = args[0];
|
||||
|
||||
Player targetOnline = Bukkit.getPlayer(targetUuid);
|
||||
if (targetOnline != null && targetOnline.hasPermission("lochat.bypass.warn")) {
|
||||
sender.sendMessage(ChatFormatter.parse("&#CF6679У этого игрока защита от варнов"));
|
||||
return true;
|
||||
}
|
||||
|
||||
punishmentService.addWarn(targetUuid, name, mod, reason, false);
|
||||
|
||||
sender.sendMessage(ChatFormatter.parse(msg.getWarnStaffConfirm().replace("{player}", name).replace("{reason}", reason)));
|
||||
|
||||
if (targetOnline != null && targetOnline.isOnline()) {
|
||||
targetOnline.sendMessage(ChatFormatter.parse(msg.getWarnReceived().replace("{reason}", reason)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static String buildReason(MessagesConfig msg, String[] args, int from) {
|
||||
if (args.length <= from) {
|
||||
return msg.getModerationDefaultReason();
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = from; i < args.length; i++) {
|
||||
if (sb.length() > 0) sb.append(" ");
|
||||
sb.append(args[i]);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
List<String> out = new ArrayList<>();
|
||||
if (args.length == 1) {
|
||||
String p = args[0].toLowerCase();
|
||||
for (Player pl : Bukkit.getOnlinePlayers()) {
|
||||
if (pl.getName().toLowerCase().startsWith(p)) out.add(pl.getName());
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
|
@ -27,11 +27,11 @@ public class NickCommand implements CommandExecutor, TabCompleter {
|
|||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(Component.text(plugin.getConfigManager().getHardcodedMessages().getPlayerOnly(), NamedTextColor.RED));
|
||||
sender.sendMessage(Component.text(plugin.getConfigManager().getMessagesConfig().getPlayerOnly(), NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
if (!player.hasPermission("lochat.nick")) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getConfigManager().getHardcodedMessages().getNoPermission()));
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getConfigManager().getMessagesConfig().getNoPermission()));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,39 +1,137 @@
|
|||
package com.loki.lochat.commands.nick;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.api.service.PunishmentService;
|
||||
import com.loki.lochat.data.model.BanRecord;
|
||||
import com.loki.lochat.data.model.MuteData;
|
||||
import com.loki.lochat.data.model.WarnEntry;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import com.loki.lochat.utils.PlayerUtil;
|
||||
import org.bukkit.Bukkit;
|
||||
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;
|
||||
|
||||
public class PlayerInfoCommand implements CommandExecutor {
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* /playerinfo — статистика варнов, банов и мутов
|
||||
*/
|
||||
public class PlayerInfoCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final MuteService muteService;
|
||||
private final PunishmentService punishmentService;
|
||||
|
||||
public PlayerInfoCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.muteService = plugin.getServiceRegistry().get(MuteService.class);
|
||||
this.punishmentService = plugin.getServiceRegistry().get(PunishmentService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (args.length < 1) return false;
|
||||
|
||||
Player target = Bukkit.getPlayer(args[0]);
|
||||
if (target == null) {
|
||||
sender.sendMessage(ChatFormatter.parse("&#CF6679Игрок не найден или не в сети"));
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args) {
|
||||
if (!sender.hasPermission("lochat.playerinfo")) {
|
||||
sender.sendMessage(ChatFormatter.parse(plugin.getConfigManager().getMessagesConfig().getNoPermission()));
|
||||
return true;
|
||||
}
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage(ChatFormatter.parse("&#B798A8Использование: ẲE9/playerinfo <ник>"));
|
||||
return true;
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatFormatter.parse("ẲE9▬▬▬▬▬ &#B798A8" + target.getName() + " ẲE9▬▬▬▬▬"));
|
||||
sender.sendMessage(ChatFormatter.parse("⚖C9❤ &fЗдоровье: ẲE9" + Math.round(target.getHealth()) + "/20"));
|
||||
sender.sendMessage(ChatFormatter.parse("⚖C9✦ &fРежим: ẲE9" + target.getGameMode().name().toLowerCase()));
|
||||
sender.sendMessage(ChatFormatter.parse("⚖C9◆ &fМир: ẲE9" + target.getWorld().getName()));
|
||||
if (plugin.getGradientModule() != null) {
|
||||
UUID uuid = PlayerUtil.findPlayerUUID(args[0]);
|
||||
if (uuid == null) {
|
||||
sender.sendMessage(ChatFormatter.parse("&#CF6679Игрок не найден"));
|
||||
return true;
|
||||
}
|
||||
|
||||
String name = PlayerUtil.getPlayerName(uuid);
|
||||
if (name == null) name = args[0];
|
||||
|
||||
Player target = Bukkit.getPlayer(uuid);
|
||||
|
||||
sender.sendMessage(ChatFormatter.parse("<gradient:#7858E9:#B798A8><bold>═══════ Профиль: " + name + " ═══════</bold></gradient>"));
|
||||
|
||||
if (target != null) {
|
||||
sender.sendMessage(ChatFormatter.parse("⚖C9❤ &fЗдоровье: ẲE9" + Math.round(target.getHealth()) + "/20"));
|
||||
sender.sendMessage(ChatFormatter.parse("⚖C9✦ &fРежим: ẲE9" + target.getGameMode().name().toLowerCase()));
|
||||
sender.sendMessage(ChatFormatter.parse("⚖C9◆ &fМир: ẲE9" + target.getWorld().getName()));
|
||||
} else {
|
||||
sender.sendMessage(ChatFormatter.parse("&#B798A8Игрок ẲE9не в сети"));
|
||||
}
|
||||
|
||||
if (plugin.getGradientModule() != null && target != null) {
|
||||
sender.sendMessage(ChatFormatter.parse("⚖C9⚡ &fГрадиент: " + plugin.getGradientModule().getFormattedName(target)));
|
||||
}
|
||||
|
||||
int warns = punishmentService.getWarnCount(uuid);
|
||||
sender.sendMessage(ChatFormatter.parse("&#CF6679⚠ &fВарнов всего: ẲE9" + warns));
|
||||
|
||||
List<WarnEntry> recent = punishmentService.getWarns(uuid);
|
||||
int show = Math.min(5, recent.size());
|
||||
for (int i = recent.size() - show; i < recent.size(); i++) {
|
||||
WarnEntry w = recent.get(i);
|
||||
String tag = w.silent ? "&#B798A8[тихий] " : "";
|
||||
String reason = w.reason != null ? w.reason : "—";
|
||||
long t = w.time;
|
||||
sender.sendMessage(ChatFormatter.parse(tag + "⚖C9• &7" + formatTime(t) + " &#B798A8от ẲE9" + w.moderator + "⚖C9: &f" + reason));
|
||||
}
|
||||
|
||||
BanRecord ban = punishmentService.getActiveBan(uuid);
|
||||
if (ban != null) {
|
||||
String until = ban.isPermanent()
|
||||
? "&#CF6679навсегда"
|
||||
: "ẲE9" + muteService.formatTime(Math.max(0, ban.until - System.currentTimeMillis()));
|
||||
sender.sendMessage(ChatFormatter.parse("&#CF6679🚫 &fБан: &cактивен &7(" + until + "&7)"));
|
||||
sender.sendMessage(ChatFormatter.parse("&#B798A8Причина: &f" + (ban.reason != null ? ban.reason : "—")));
|
||||
} else {
|
||||
sender.sendMessage(ChatFormatter.parse("⚖C9🚫 &fБан: &aнет"));
|
||||
}
|
||||
|
||||
if (muteService.isMuted(uuid)) {
|
||||
MuteData md = muteService.getMuteData(uuid);
|
||||
long rem = muteService.getRemainingTime(uuid);
|
||||
String remStr = rem < 0 ? "навсегда" : muteService.formatTime(rem);
|
||||
sender.sendMessage(ChatFormatter.parse("&#CF6679🔇 &fМут: &cда &7(" + remStr + "&7)"));
|
||||
if (md != null) {
|
||||
sender.sendMessage(ChatFormatter.parse("&#B798A8Причина: &f" + (md.getReason() != null ? md.getReason() : "—")));
|
||||
}
|
||||
} else {
|
||||
sender.sendMessage(ChatFormatter.parse("⚖C9🔇 &fМут: &aнет"));
|
||||
}
|
||||
|
||||
var muteHist = muteService.getPlayerHistory(uuid);
|
||||
if (muteHist != null && !muteHist.isEmpty()) {
|
||||
sender.sendMessage(ChatFormatter.parse("&#B798A8Записей в истории мутов: ẲE9" + muteHist.size()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static String formatTime(long ms) {
|
||||
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("dd.MM.yyyy HH:mm");
|
||||
return sdf.format(new java.util.Date(ms));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
List<String> out = new ArrayList<>();
|
||||
if (args.length == 1) {
|
||||
String p = args[0].toLowerCase();
|
||||
for (Player pl : Bukkit.getOnlinePlayers()) {
|
||||
if (pl.getName().toLowerCase().startsWith(p)) out.add(pl.getName());
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ public class DoCommand extends PlayerCommand {
|
|||
int cooldown = plugin.getConfigManager().getInt("rp.cooldowns.do", 0);
|
||||
if (cooldown > 0 && cooldownService.isOnCooldown(player.getUniqueId(), "rp_do", cooldown)) {
|
||||
int remaining = cooldownService.getRemainingCooldown(player.getUniqueId(), "rp_do", cooldown);
|
||||
String msg = plugin.getConfigManager().getHardcodedMessages().getCooldownMessage().replace("{remaining}", String.valueOf(remaining));
|
||||
String msg = plugin.getConfigManager().getMessagesConfig().getCooldownMessage().replace("{remaining}", String.valueOf(remaining));
|
||||
player.sendMessage(ChatFormatter.parse(msg));
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ public class MeCommand extends PlayerCommand {
|
|||
int cooldown = plugin.getConfigManager().getInt("rp.cooldowns.me", 0);
|
||||
if (cooldown > 0 && cooldownService.isOnCooldown(player.getUniqueId(), "rp_me", cooldown)) {
|
||||
int remaining = cooldownService.getRemainingCooldown(player.getUniqueId(), "rp_me", cooldown);
|
||||
String msg = plugin.getConfigManager().getHardcodedMessages().getCooldownMessage().replace("{remaining}", String.valueOf(remaining));
|
||||
String msg = plugin.getConfigManager().getMessagesConfig().getCooldownMessage().replace("{remaining}", String.valueOf(remaining));
|
||||
player.sendMessage(ChatFormatter.parse(msg));
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import com.loki.lochat.commands.base.PlayerCommand;
|
|||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
|
@ -18,7 +17,7 @@ import java.util.Random;
|
|||
* Бросает кубик от 1 до максимума (по умолчанию 100).
|
||||
* Формат: * ИгрокИмя бросает кубик и получает N из M
|
||||
*/
|
||||
public class RollCommand extends PlayerCommand implements TabCompleter {
|
||||
public class RollCommand extends PlayerCommand {
|
||||
|
||||
private static final Random RANDOM = new Random();
|
||||
private static final int DEFAULT_MAX = 100;
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ public class TryCommand extends PlayerCommand {
|
|||
int cooldown = plugin.getConfigManager().getInt("rp.cooldowns.try", 0);
|
||||
if (cooldown > 0 && cooldownService.isOnCooldown(player.getUniqueId(), "rp_try", cooldown)) {
|
||||
int remaining = cooldownService.getRemainingCooldown(player.getUniqueId(), "rp_try", cooldown);
|
||||
String msg = plugin.getConfigManager().getHardcodedMessages().getCooldownMessage().replace("{remaining}", String.valueOf(remaining));
|
||||
String msg = plugin.getConfigManager().getMessagesConfig().getCooldownMessage().replace("{remaining}", String.valueOf(remaining));
|
||||
player.sendMessage(ChatFormatter.parse(msg));
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,51 +1,33 @@
|
|||
package com.loki.lochat.config;
|
||||
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Конфигурация внешнего вида чата
|
||||
* Конфигурация внешнего вида чата (config/appearance.yml)
|
||||
*/
|
||||
public class AppearanceConfig {
|
||||
private final Plugin plugin;
|
||||
private YamlConfiguration config;
|
||||
public class AppearanceConfig extends BaseConfig {
|
||||
private final Map<String, String> emojiCache = new HashMap<>();
|
||||
|
||||
public AppearanceConfig(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
// Не вызываем loadConfig() в конструкторе для избежания this-escape
|
||||
super((org.bukkit.plugin.java.JavaPlugin) plugin, "appearance.yml", true); // true = в папке config/
|
||||
// init() вызывается отдельно в методе init()
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициализировать конфигурацию (вызывать после создания объекта)
|
||||
*/
|
||||
public void init() {
|
||||
loadConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Загрузить конфигурацию
|
||||
*/
|
||||
public void loadConfig() {
|
||||
File configFile = new File(plugin.getDataFolder(), "config/chat-appearance.yml");
|
||||
if (!configFile.exists()) {
|
||||
plugin.saveResource("config/chat-appearance.yml", false);
|
||||
}
|
||||
|
||||
config = YamlConfiguration.loadConfiguration(configFile);
|
||||
super.init(); // ✅ Вызываем init() родителя
|
||||
loadEmojiCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Перезагрузить конфигурацию
|
||||
*/
|
||||
public void reload() {
|
||||
loadConfig();
|
||||
@Override
|
||||
protected void onLoad() {
|
||||
loadEmojiCache();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -146,6 +128,22 @@ public class AppearanceConfig {
|
|||
return config.getString("prefixes.local.message-color", "#FFFFFF");
|
||||
}
|
||||
|
||||
// ========== ФОРМАТ СООБЩЕНИЙ ==========
|
||||
|
||||
/**
|
||||
* Получить формат сообщения для глобального чата
|
||||
*/
|
||||
public String getGlobalChatFormat() {
|
||||
return config.getString("chat-format.global", "{emoji} {prefix} {separator} {player} : {message}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить формат сообщения для локального чата
|
||||
*/
|
||||
public String getLocalChatFormat() {
|
||||
return config.getString("chat-format.local", "{emoji} {prefix} {separator} {player} : {message}");
|
||||
}
|
||||
|
||||
// ========== HOVER ЭФФЕКТЫ ==========
|
||||
|
||||
/**
|
||||
|
|
@ -279,4 +277,4 @@ public class AppearanceConfig {
|
|||
public String getDiscordEventTitle(String event) {
|
||||
return config.getString("discord.event_titles." + event, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
121
src/main/java/com/loki/lochat/config/BaseConfig.java
Normal file
121
src/main/java/com/loki/lochat/config/BaseConfig.java
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
package com.loki.lochat.config;
|
||||
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
|
||||
/**
|
||||
* Базовый класс для всех конфигураций
|
||||
* Избегает дублирования кода загрузки конфигов
|
||||
*/
|
||||
public abstract class BaseConfig {
|
||||
protected final JavaPlugin plugin;
|
||||
protected File configFile;
|
||||
protected FileConfiguration config;
|
||||
private final String fileName;
|
||||
private final boolean inConfigFolder;
|
||||
|
||||
/**
|
||||
* Конструктор для конфигов в корне папки плагина
|
||||
* @param plugin плагин
|
||||
* @param fileName имя файла (например "config.yml")
|
||||
*/
|
||||
protected BaseConfig(JavaPlugin plugin, String fileName) {
|
||||
this(plugin, fileName, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Конструктор для конфигов в подпапке config/
|
||||
* @param plugin плагин
|
||||
* @param fileName имя файла (например "messages.yml")
|
||||
* @param inConfigFolder true если файл в папке config/
|
||||
*/
|
||||
protected BaseConfig(JavaPlugin plugin, String fileName, boolean inConfigFolder) {
|
||||
this.plugin = plugin;
|
||||
this.fileName = fileName;
|
||||
this.inConfigFolder = inConfigFolder;
|
||||
// ✅ FIX: Не вызываем loadConfig() здесь чтобы избежать this-escape
|
||||
// Вызывается явно после создания объекта
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициализация конфига (вызывать после создания)
|
||||
*/
|
||||
public void init() {
|
||||
loadConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Загрузить конфигурацию
|
||||
*/
|
||||
protected void loadConfig() {
|
||||
// Определяем путь к файлу
|
||||
if (inConfigFolder) {
|
||||
File configFolder = new File(plugin.getDataFolder(), "config");
|
||||
if (!configFolder.exists()) {
|
||||
configFolder.mkdirs();
|
||||
}
|
||||
configFile = new File(configFolder, fileName);
|
||||
} else {
|
||||
configFile = new File(plugin.getDataFolder(), fileName);
|
||||
}
|
||||
|
||||
// Копируем из ресурсов если не существует
|
||||
if (!configFile.exists()) {
|
||||
try {
|
||||
String resourcePath = inConfigFolder ? "config/" + fileName : fileName;
|
||||
InputStream in = plugin.getResource(resourcePath);
|
||||
if (in != null) {
|
||||
Files.copy(in, configFile.toPath());
|
||||
in.close();
|
||||
plugin.getLogger().info("Created config file: " + (inConfigFolder ? "config/" : "") + fileName);
|
||||
} else {
|
||||
plugin.getLogger().warning("Resource not found: " + resourcePath);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().warning("Failed to copy " + fileName + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
config = YamlConfiguration.loadConfiguration(configFile);
|
||||
onLoad();
|
||||
}
|
||||
|
||||
/**
|
||||
* Перезагрузить конфигурацию
|
||||
*/
|
||||
public void reload() {
|
||||
config = YamlConfiguration.loadConfiguration(configFile);
|
||||
onLoad();
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранить конфигурацию
|
||||
*/
|
||||
public void save() {
|
||||
try {
|
||||
config.save(configFile);
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().warning("Failed to save " + fileName + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Вызывается после загрузки конфига
|
||||
* Переопределите для дополнительной инициализации
|
||||
*/
|
||||
protected void onLoad() {
|
||||
// Переопределяется в подклассах
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить FileConfiguration
|
||||
*/
|
||||
public FileConfiguration getConfig() {
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
|
@ -9,9 +9,15 @@ import java.util.List;
|
|||
public class ConfigManager {
|
||||
|
||||
private final LoChat plugin;
|
||||
private FileConfiguration config;
|
||||
private AppearanceConfig appearanceConfig;
|
||||
private HardcodedMessages hardcodedMessages;
|
||||
|
||||
// ✅ THREAD-SAFETY: volatile для безопасного reload из разных потоков
|
||||
// volatile гарантирует что все потоки видят актуальную версию конфига после reload
|
||||
private volatile FileConfiguration config;
|
||||
private volatile AppearanceConfig appearanceConfig;
|
||||
private volatile MessagesConfig messagesConfig;
|
||||
private volatile MuteConfig muteConfig;
|
||||
private volatile FiltersConfig filtersConfig;
|
||||
private volatile SoundsConfig soundsConfig;
|
||||
|
||||
public ConfigManager(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
|
|
@ -20,11 +26,13 @@ public class ConfigManager {
|
|||
|
||||
// Инициализируем новые конфигурации
|
||||
this.appearanceConfig = new AppearanceConfig(plugin);
|
||||
this.hardcodedMessages = new HardcodedMessages(plugin);
|
||||
this.messagesConfig = new MessagesConfig(plugin);
|
||||
this.muteConfig = new MuteConfig(plugin);
|
||||
this.filtersConfig = new FiltersConfig(plugin);
|
||||
this.soundsConfig = new SoundsConfig(plugin);
|
||||
|
||||
// Инициализируем их после создания
|
||||
this.appearanceConfig.init();
|
||||
this.hardcodedMessages.init();
|
||||
|
||||
// Проверяем версию конфига и обновляем при необходимости
|
||||
updateConfig();
|
||||
|
|
@ -38,32 +46,47 @@ public class ConfigManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Получить конфигурацию хардкодированных сообщений
|
||||
* Получить конфигурацию системных сообщений
|
||||
*/
|
||||
public HardcodedMessages getHardcodedMessages() {
|
||||
return hardcodedMessages;
|
||||
public MessagesConfig getMessagesConfig() {
|
||||
return messagesConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить конфигурацию мутов
|
||||
*/
|
||||
public MuteConfig getMuteConfig() {
|
||||
return muteConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить конфигурацию фильтров
|
||||
*/
|
||||
public FiltersConfig getFiltersConfig() {
|
||||
return filtersConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить конфигурацию звуков
|
||||
*/
|
||||
public SoundsConfig getSoundsConfig() {
|
||||
return soundsConfig;
|
||||
}
|
||||
|
||||
private void updateConfig() {
|
||||
int currentVersion = config.getInt("config-version", 1);
|
||||
int requiredVersion = 2; // Текущая версия конфига
|
||||
int requiredVersion = 5;
|
||||
|
||||
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", "&#FFD700ОБЪЯВЛЕНИЕ");
|
||||
|
|
@ -71,7 +94,6 @@ public class ConfigManager {
|
|||
config.set("announcements.title-duration", 3);
|
||||
}
|
||||
|
||||
// Можно добавить другие недостающие настройки
|
||||
if (!config.contains("chat.pm.enabled")) {
|
||||
config.set("chat.pm.enabled", true);
|
||||
}
|
||||
|
|
@ -100,48 +122,47 @@ public class ConfigManager {
|
|||
config.set("lopreff.use-gradient-name", true);
|
||||
config.set("lopreff.use-custom-prefix", true);
|
||||
}
|
||||
|
||||
if (!config.contains("moderation.voice-mute-console-command")) {
|
||||
config.set("moderation.voice-mute-console-command", "");
|
||||
}
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
/**
|
||||
* Перезагрузить все конфигурации
|
||||
* ✅ THREAD-SAFE: использует volatile для безопасного обновления
|
||||
*/
|
||||
public synchronized void reload() {
|
||||
plugin.reloadConfig();
|
||||
this.config = plugin.getConfig();
|
||||
|
||||
// Перезагружаем новые конфигурации
|
||||
// volatile гарантирует что изменения видны всем потокам
|
||||
this.appearanceConfig.reload();
|
||||
this.hardcodedMessages.reload();
|
||||
this.messagesConfig.reload();
|
||||
this.muteConfig.reload();
|
||||
this.filtersConfig.reload();
|
||||
this.soundsConfig.reload();
|
||||
|
||||
plugin.getLogger().info("Все конфигурации успешно перезагружены");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить строку из конфига с дефолтным значением
|
||||
*/
|
||||
public String getString(String path, String defaultValue) {
|
||||
return config.getString(path, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить строку из конфига
|
||||
*/
|
||||
public String getString(String path) {
|
||||
return config.getString(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить int из конфига
|
||||
*/
|
||||
public int getInt(String path, int defaultValue) {
|
||||
return config.getInt(path, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить boolean из конфига
|
||||
*/
|
||||
public boolean getBoolean(String path, boolean defaultValue) {
|
||||
return config.getBoolean(path, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить список строк из конфига
|
||||
*/
|
||||
public List<String> getStringList(String path) {
|
||||
return config.getStringList(path);
|
||||
}
|
||||
|
|
@ -151,18 +172,6 @@ public class ConfigManager {
|
|||
return config.getBoolean("chat.global.enabled", true);
|
||||
}
|
||||
|
||||
public String getGlobalPrefix() {
|
||||
return ChatFormatter.convertAllColors(config.getString("chat.global.prefix", "[G]"));
|
||||
}
|
||||
|
||||
public String getGlobalFormat() {
|
||||
return ChatFormatter.convertAllColors(config.getString("chat.global.format", "<prefix><player>: <message>"));
|
||||
}
|
||||
|
||||
public String getGlobalSymbol() {
|
||||
return config.getString("chat.global.symbol", "!");
|
||||
}
|
||||
|
||||
public int getGlobalCooldown() {
|
||||
return config.getInt("chat.global.cooldown", 3);
|
||||
}
|
||||
|
|
@ -176,14 +185,6 @@ public class ConfigManager {
|
|||
return appearanceConfig.getLocalRadius();
|
||||
}
|
||||
|
||||
public String getLocalPrefix() {
|
||||
return ChatFormatter.convertAllColors(config.getString("chat.local.prefix", "[L]"));
|
||||
}
|
||||
|
||||
public String getLocalFormat() {
|
||||
return ChatFormatter.convertAllColors(config.getString("chat.local.format", "<player>: <message>"));
|
||||
}
|
||||
|
||||
public int getLocalCooldown() {
|
||||
return config.getInt("chat.local.cooldown", 1);
|
||||
}
|
||||
|
|
@ -198,11 +199,13 @@ public class ConfigManager {
|
|||
}
|
||||
|
||||
public boolean isPmSoundEnabled() {
|
||||
return config.getBoolean("chat.pm.sound", true);
|
||||
// ✅ Используем SoundsConfig если доступен
|
||||
return soundsConfig != null ? soundsConfig.isPmSoundEnabled() : config.getBoolean("chat.pm.sound", true);
|
||||
}
|
||||
|
||||
public String getPmSoundType() {
|
||||
return config.getString("chat.pm.sound-type", "ENTITY_EXPERIENCE_ORB_PICKUP");
|
||||
// ✅ Используем SoundsConfig если доступен
|
||||
return soundsConfig != null ? soundsConfig.getPmSound() : config.getString("chat.pm.sound-type", "ENTITY_EXPERIENCE_ORB_PICKUP");
|
||||
}
|
||||
|
||||
// Mentions
|
||||
|
|
@ -211,11 +214,13 @@ public class ConfigManager {
|
|||
}
|
||||
|
||||
public boolean isMentionSoundEnabled() {
|
||||
return config.getBoolean("mentions.sound", true);
|
||||
// ✅ Используем SoundsConfig если доступен
|
||||
return soundsConfig != null ? soundsConfig.isMentionSoundEnabled() : config.getBoolean("mentions.sound", true);
|
||||
}
|
||||
|
||||
public String getMentionSoundType() {
|
||||
return config.getString("mentions.sound-type", "BLOCK_NOTE_BLOCK_PLING");
|
||||
// ✅ Используем SoundsConfig если доступен
|
||||
return soundsConfig != null ? soundsConfig.getMentionSound() : config.getString("mentions.sound-type", "BLOCK_NOTE_BLOCK_PLING");
|
||||
}
|
||||
|
||||
public String getMentionHighlight() {
|
||||
|
|
|
|||
162
src/main/java/com/loki/lochat/config/FiltersConfig.java
Normal file
162
src/main/java/com/loki/lochat/config/FiltersConfig.java
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
package com.loki.lochat.config;
|
||||
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Конфигурация фильтров (config/filters.yml)
|
||||
*/
|
||||
public class FiltersConfig extends BaseConfig {
|
||||
|
||||
public FiltersConfig(JavaPlugin plugin) {
|
||||
super(plugin, "filters.yml", true);
|
||||
init(); // ✅ Явная инициализация
|
||||
}
|
||||
|
||||
// ── Капс ────────────────────────────────────────────────────────────────────
|
||||
|
||||
public boolean isCapsEnabled() {
|
||||
return config.getBoolean("caps.enabled", true);
|
||||
}
|
||||
|
||||
public int getCapsMaxPercent() {
|
||||
return config.getInt("caps.max-percent", 70);
|
||||
}
|
||||
|
||||
public int getCapsMinLength() {
|
||||
return config.getInt("caps.min-length", 5);
|
||||
}
|
||||
|
||||
public boolean isCapsAutoLowercase() {
|
||||
return config.getBoolean("caps.auto-lowercase", true);
|
||||
}
|
||||
|
||||
public boolean isCapsBlock() {
|
||||
return config.getBoolean("caps.block", false);
|
||||
}
|
||||
|
||||
// ── Мат ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
public boolean isSwearEnabled() {
|
||||
return config.getBoolean("swear.enabled", true);
|
||||
}
|
||||
|
||||
public String getSwearAction() {
|
||||
return config.getString("swear.action", "replace");
|
||||
}
|
||||
|
||||
public String getSwearReplacementChar() {
|
||||
return config.getString("swear.replacement-char", "*");
|
||||
}
|
||||
|
||||
public boolean isSwearCheckFragments() {
|
||||
return config.getBoolean("swear.check-fragments", true);
|
||||
}
|
||||
|
||||
public boolean isSwearIgnoreCase() {
|
||||
return config.getBoolean("swear.ignore-case", true);
|
||||
}
|
||||
|
||||
public boolean isSwearUseExternalFile() {
|
||||
return config.getBoolean("swear.use-external-file", true);
|
||||
}
|
||||
|
||||
public List<String> getSwearWords() {
|
||||
return config.getStringList("swear.words");
|
||||
}
|
||||
|
||||
public String getSwearBlockMessage() {
|
||||
return config.getString("swear.block-message", "&#CF6679Мат запрещён");
|
||||
}
|
||||
|
||||
// ── Реклама ─────────────────────────────────────────────────────────────────
|
||||
|
||||
public boolean isAdvertisingEnabled() {
|
||||
return config.getBoolean("advertising.enabled", true);
|
||||
}
|
||||
|
||||
public boolean isBlockHiddenUrls() {
|
||||
return config.getBoolean("advertising.block-hidden-urls", true);
|
||||
}
|
||||
|
||||
public boolean isBlockDomains() {
|
||||
return config.getBoolean("advertising.block-domains", true);
|
||||
}
|
||||
|
||||
public List<String> getWhitelistedDomains() {
|
||||
return config.getStringList("advertising.whitelist");
|
||||
}
|
||||
|
||||
public List<String> getBlacklistedDomains() {
|
||||
return config.getStringList("advertising.blacklist");
|
||||
}
|
||||
|
||||
public String getAdvertisingBlockMessage() {
|
||||
return config.getString("advertising.blocked-message", "&#CF6679Реклама запрещена!");
|
||||
}
|
||||
|
||||
// ── IP адреса ───────────────────────────────────────────────────────────────
|
||||
|
||||
public boolean isIpEnabled() {
|
||||
return config.getBoolean("ip.enabled", true);
|
||||
}
|
||||
|
||||
public boolean isIpBlock() {
|
||||
return config.getBoolean("ip.block", false);
|
||||
}
|
||||
|
||||
public String getIpReplacement() {
|
||||
return config.getString("ip.replacement", "[IP скрыт]");
|
||||
}
|
||||
|
||||
public String getIpBlockMessage() {
|
||||
return config.getString("ip.blocked-message", "&#CF6679IP адреса запрещены!");
|
||||
}
|
||||
|
||||
// ── Повторяющиеся символы ───────────────────────────────────────────────────
|
||||
|
||||
public boolean isRepeatEnabled() {
|
||||
return config.getBoolean("repeat.enabled", true);
|
||||
}
|
||||
|
||||
public int getRepeatMaxRepeats() {
|
||||
return config.getInt("repeat.max-repeats", 3);
|
||||
}
|
||||
|
||||
// ── Флуд ────────────────────────────────────────────────────────────────────
|
||||
|
||||
public boolean isFloodEnabled() {
|
||||
return config.getBoolean("flood.enabled", true);
|
||||
}
|
||||
|
||||
public int getFloodMaxMessages() {
|
||||
return config.getInt("flood.max-messages", 5);
|
||||
}
|
||||
|
||||
public int getFloodTimePeriod() {
|
||||
return config.getInt("flood.time-period", 10);
|
||||
}
|
||||
|
||||
public String getFloodBlockMessage() {
|
||||
return config.getString("flood.block-message", "&#CF6679Не флудите!");
|
||||
}
|
||||
|
||||
// ── Спам ────────────────────────────────────────────────────────────────────
|
||||
|
||||
public boolean isSpamEnabled() {
|
||||
return config.getBoolean("spam.enabled", true);
|
||||
}
|
||||
|
||||
public int getSpamMaxSimilarMessages() {
|
||||
return config.getInt("spam.max-similar-messages", 3);
|
||||
}
|
||||
|
||||
public int getSpamSimilarityThreshold() {
|
||||
return config.getInt("spam.similarity-threshold", 80);
|
||||
}
|
||||
|
||||
public String getSpamBlockMessage() {
|
||||
return config.getString("spam.block-message", "&#CF6679Не отправляйте одинаковые сообщения");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,261 +0,0 @@
|
|||
package com.loki.lochat.config;
|
||||
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Конфигурация хардкодированных сообщений
|
||||
*/
|
||||
public class HardcodedMessages {
|
||||
private final Plugin plugin;
|
||||
private YamlConfiguration config;
|
||||
|
||||
public HardcodedMessages(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
// Не вызываем loadConfig() в конструкторе для избежания this-escape
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициализировать конфигурацию (вызывать после создания объекта)
|
||||
*/
|
||||
public void init() {
|
||||
loadConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Загрузить конфигурацию
|
||||
*/
|
||||
public void loadConfig() {
|
||||
File configFile = new File(plugin.getDataFolder(), "config/hardcoded-messages.yml");
|
||||
if (!configFile.exists()) {
|
||||
plugin.saveResource("config/hardcoded-messages.yml", false);
|
||||
}
|
||||
|
||||
config = YamlConfiguration.loadConfiguration(configFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Перезагрузить конфигурацию
|
||||
*/
|
||||
public void reload() {
|
||||
loadConfig();
|
||||
}
|
||||
|
||||
// ========== КОМАНДЫ ==========
|
||||
|
||||
/**
|
||||
* Получить сообщение об использовании команды мута
|
||||
*/
|
||||
public String getMuteUsage() {
|
||||
return config.getString("commands.mute.usage", "§cИспользование: /mute <ник> [время] [-s] [причина]");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить помощь по времени мута
|
||||
*/
|
||||
public String getMuteTimeHelp() {
|
||||
return config.getString("commands.mute.time_help", "§7Время: 1d, 2h, 30m, 60s или 0/perm для перманентного");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить помощь по тихому муту
|
||||
*/
|
||||
public String getMuteSilentHelp() {
|
||||
return config.getString("commands.mute.silent_help", "§7-s - тихий мут (требует право lochat.mute.silent)");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение об отсутствии прав на тихий мут
|
||||
*/
|
||||
public String getNoSilentPermission() {
|
||||
return config.getString("commands.mute.no_silent_permission", "§cУ вас нет права на тихий мут!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить причину мута по умолчанию
|
||||
*/
|
||||
public String getDefaultMuteReason() {
|
||||
return config.getString("commands.mute.default_reason", "Нету.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить длительность мута по умолчанию
|
||||
*/
|
||||
public String getDefaultMuteDuration() {
|
||||
return config.getString("commands.mute.default_duration", "7d");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение кулдауна
|
||||
*/
|
||||
public String getCooldownMessage() {
|
||||
return config.getString("commands.cooldown.message", "§cПодождите {remaining} сек.");
|
||||
}
|
||||
|
||||
// ========== ЛОКАЛЬНЫЙ ЧАТ ==========
|
||||
|
||||
/**
|
||||
* Получить сообщение "никто не услышал"
|
||||
*/
|
||||
public String getNobodyHeard() {
|
||||
return config.getString("local_chat.nobody_heard", "&#FFA726Вас никто не услышал - рядом нет игроков");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение "рядом нет игроков"
|
||||
*/
|
||||
public String getNoPlayersNearby() {
|
||||
return config.getString("local_chat.no_players_nearby", "&#FFA726Рядом нет игроков");
|
||||
}
|
||||
|
||||
// ========== ФИЛЬТРЫ ==========
|
||||
|
||||
/**
|
||||
* Получить минимальную длину для проверки капса
|
||||
*/
|
||||
public int getCapsMinLength() {
|
||||
return config.getInt("filters.caps.min_length", 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение о блокировке капса
|
||||
*/
|
||||
public String getCapsBlockedMessage() {
|
||||
return config.getString("filters.caps.blocked_message", "§cСлишком много заглавных букв!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить уведомление об автоисправлении
|
||||
*/
|
||||
public String getAutoLowercaseNotice() {
|
||||
return config.getString("filters.caps.auto_lowercase_notice", "§7Ваше сообщение было автоматически исправлено");
|
||||
}
|
||||
|
||||
// ========== МУТИРОВАНИЕ ==========
|
||||
|
||||
/**
|
||||
* Получить сообщение о том что игрок замучен
|
||||
*/
|
||||
public String getMutedMessage() {
|
||||
return config.getString("mute.muted_message", "§cВы замучены! Причина: {reason}. Осталось: {time}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение о перманентном муте
|
||||
*/
|
||||
public String getMutedPermanent() {
|
||||
return config.getString("mute.muted_permanent", "§cВы замучены навсегда! Причина: {reason}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить уведомление о муте игрока
|
||||
*/
|
||||
public String getPlayerMuted() {
|
||||
return config.getString("mute.player_muted", "§aИгрок {player} замучен на {time}. Причина: {reason}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить уведомление о перманентном муте игрока
|
||||
*/
|
||||
public String getPlayerMutedPermanent() {
|
||||
return config.getString("mute.player_muted_permanent", "§aИгрок {player} замучен навсегда. Причина: {reason}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить уведомление о тихом муте
|
||||
*/
|
||||
public String getPlayerMutedSilent() {
|
||||
return config.getString("mute.player_muted_silent", "§7[ТИХИЙ МУТ] {player} замучен на {time}");
|
||||
}
|
||||
|
||||
// ========== ИГРОВЫЕ РЕЖИМЫ ==========
|
||||
|
||||
/**
|
||||
* Получить название игрового режима
|
||||
*/
|
||||
public String getGamemodeName(String gamemode) {
|
||||
return config.getString("gamemodes." + gamemode.toLowerCase(), gamemode);
|
||||
}
|
||||
|
||||
// ========== ОБЩИЕ СООБЩЕНИЯ ==========
|
||||
|
||||
/**
|
||||
* Получить сообщение об отсутствии прав
|
||||
*/
|
||||
public String getNoPermission() {
|
||||
return config.getString("general.no_permission", "§cУ вас нет прав для выполнения этой команды!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение "только для игроков"
|
||||
*/
|
||||
public String getPlayerOnly() {
|
||||
return config.getString("general.player_only", "§cЭта команда доступна только игрокам!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение о неверных аргументах
|
||||
*/
|
||||
public String getInvalidArguments() {
|
||||
return config.getString("general.invalid_arguments", "§cНеверные аргументы команды!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение об отключенной команде
|
||||
*/
|
||||
public String getCommandDisabled() {
|
||||
return config.getString("general.command_disabled", "§cЭта команда отключена!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение об отключенной функции
|
||||
*/
|
||||
public String getFeatureDisabled() {
|
||||
return config.getString("general.feature_disabled", "§cЭта функция отключена в конфигурации!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить формат времени
|
||||
*/
|
||||
public String getTimeFormat(String unit) {
|
||||
return config.getString("general.time_formats." + unit, unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение об игроке не найден
|
||||
*/
|
||||
public String getPlayerNotFound() {
|
||||
return config.getString("general.player_not_found", "§cИгрок не найден!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение игнорирования
|
||||
*/
|
||||
public String getIgnore(String key) {
|
||||
return config.getString("ignore." + key, "§cОшибка игнорирования!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение личных сообщений
|
||||
*/
|
||||
public String getPrivateMessage(String key) {
|
||||
return config.getString("private_messages." + key, "§cОшибка личных сообщений!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение об отсутствии прав на тихий размут
|
||||
*/
|
||||
public String getNoSilentUnmutePermission() {
|
||||
return config.getString("mute.no_silent_unmute_permission", "&#CF6679Нет права на тихий размут!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сообщение о том что игрок не замучен
|
||||
*/
|
||||
public String getPlayerNotMutedSimple() {
|
||||
return config.getString("mute.player_not_muted_simple", "&#CF6679Игрок не замучен!");
|
||||
}
|
||||
}
|
||||
220
src/main/java/com/loki/lochat/config/MessagesConfig.java
Normal file
220
src/main/java/com/loki/lochat/config/MessagesConfig.java
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
package com.loki.lochat.config;
|
||||
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
/**
|
||||
* Конфигурация системных сообщений (config/messages.yml)
|
||||
*/
|
||||
public class MessagesConfig extends BaseConfig {
|
||||
|
||||
public MessagesConfig(JavaPlugin plugin) {
|
||||
super(plugin, "messages.yml", true); // true = в папке config/
|
||||
init(); // ✅ Явная инициализация
|
||||
}
|
||||
|
||||
// ── Общие сообщения ────────────────────────────────────────────────────────
|
||||
|
||||
public String getNoPermission() {
|
||||
return config.getString("general.no_permission", "&#CF6679Нет прав!");
|
||||
}
|
||||
|
||||
public String getPlayerOnly() {
|
||||
return config.getString("general.player_only", "&#CF6679Только для игроков!");
|
||||
}
|
||||
|
||||
public String getPlayerNotFound() {
|
||||
return config.getString("general.player_not_found", "&#CF6679Игрок не найден!");
|
||||
}
|
||||
|
||||
// ── Кулдаун ────────────────────────────────────────────────────────────────
|
||||
|
||||
public String getCooldownMessage() {
|
||||
return config.getString("commands.cooldown.message", "&#B798A8Подождите ẲE9{remaining} &#B798A8сек");
|
||||
}
|
||||
|
||||
// ── Локальный чат ──────────────────────────────────────────────────────────
|
||||
|
||||
public String getNobodyHeard() {
|
||||
return config.getString("local_chat.nobody_heard", "⚖C9Вас никто не услышал &#B798A8— рядом нет игроков");
|
||||
}
|
||||
|
||||
// ── Мут ────────────────────────────────────────────────────────────────────
|
||||
|
||||
public String getMutedMessage() {
|
||||
return config.getString("mute.muted_message", "&#CF6679Вы замучены! &#B798A8Причина: ẲE9{reason}&#B798A8. Осталось: ẲE9{time}");
|
||||
}
|
||||
|
||||
public String getMutedPermanent() {
|
||||
return config.getString("mute.muted_permanent", "&#CF6679Вы замучены навсегда! &#B798A8Причина: ẲE9{reason}");
|
||||
}
|
||||
|
||||
public String getPlayerMuted() {
|
||||
return config.getString("mute.player_muted", "⚖C9Игрок ẲE9{player} ⚖C9замучен на ẲE9{time}⚖C9. Причина: &#B798A8{reason}");
|
||||
}
|
||||
|
||||
public String getPlayerMutedPermanent() {
|
||||
return config.getString("mute.player_muted_permanent", "⚖C9Игрок ẲE9{player} ⚖C9замучен навсегда. Причина: &#B798A8{reason}");
|
||||
}
|
||||
|
||||
public String getPlayerMutedSilent() {
|
||||
return config.getString("mute.player_muted_silent", "&#B798A8[⚖C9ТИХИЙ МУТ&#B798A8] ẲE9{player} &#B798A8замучен на ẲE9{time}");
|
||||
}
|
||||
|
||||
public String getPlayerUnmuted() {
|
||||
return config.getString("mute.player_unmuted", "⚖C9Игрок ẲE9{player} ⚖C9размучен");
|
||||
}
|
||||
|
||||
public String getPlayerNotMuted() {
|
||||
return config.getString("mute.not_muted", "&#B798A8Игрок ẲE9{player} &#B798A8не замучен");
|
||||
}
|
||||
|
||||
public String getPlayerNotMutedSimple() {
|
||||
return config.getString("mute.player_not_muted_simple", "&#CF6679Игрок не замучен!");
|
||||
}
|
||||
|
||||
public String getInvalidTime() {
|
||||
return config.getString("mute.invalid_time", "&#CF6679Неверный формат времени!");
|
||||
}
|
||||
|
||||
public String getAlreadyMuted() {
|
||||
return config.getString("mute.already_muted", "&#CF6679Игрок уже замучен!");
|
||||
}
|
||||
|
||||
public String getNoSilentUnmutePermission() {
|
||||
return config.getString("mute.no_silent_unmute_permission", "&#CF6679Нет права на тихий размут!");
|
||||
}
|
||||
|
||||
public String getNoSilentMutePermission() {
|
||||
return config.getString("commands.mute.no_silent_permission", "&#CF6679Нет права на тихий мут");
|
||||
}
|
||||
|
||||
public String getMuteUsage() {
|
||||
return config.getString("commands.mute.usage", "&#B798A8Использование: ẲE9/mute <ник> [время] [-s] [причина]");
|
||||
}
|
||||
|
||||
public String getMuteTimeHelp() {
|
||||
return config.getString("commands.mute.time_help", "⚖C9Время: ẲE91d⚖C9, ẲE92h⚖C9, ẲE930m⚖C9, ẲE960s⚖C9, ẲE9perm");
|
||||
}
|
||||
|
||||
public String getMuteSilentHelp() {
|
||||
return config.getString("commands.mute.silent_help", "&#B798A8-s ⚖C9— тихий мут (право ẲE9lochat.mute.silent⚖C9)");
|
||||
}
|
||||
|
||||
public String getDefaultMuteReason() {
|
||||
return config.getString("commands.mute.default_reason", "Без причины");
|
||||
}
|
||||
|
||||
public String getDefaultMuteDuration() {
|
||||
return config.getString("commands.mute.default_duration", "7d");
|
||||
}
|
||||
|
||||
// ── ЛС ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
public String getNoReplyTarget() {
|
||||
return config.getString("private_messages.no_reply_target", "&#CF6679Нет цели для ответа");
|
||||
}
|
||||
|
||||
public String getPlayerOffline() {
|
||||
return config.getString("private_messages.player_offline", "&#CF6679Игрок ẲE9{player} &#CF6679не в сети");
|
||||
}
|
||||
|
||||
public String getCannotMessageSelf() {
|
||||
return config.getString("private_messages.cannot_message_self", "&#CF6679Нельзя писать самому себе");
|
||||
}
|
||||
|
||||
// ── Игнор ──────────────────────────────────────────────────────────────────
|
||||
|
||||
public String getPlayerIgnored() {
|
||||
return config.getString("ignore.player_ignored", "⚖C9Игрок ẲE9{player} ⚖C9добавлен в игнор");
|
||||
}
|
||||
|
||||
public String getPlayerUnignored() {
|
||||
return config.getString("ignore.player_unignored", "⚖C9Игрок ẲE9{player} ⚖C9убран из игнора");
|
||||
}
|
||||
|
||||
public String getAlreadyIgnored() {
|
||||
return config.getString("ignore.already_ignored", "&#B798A8Игрок ẲE9{player} &#B798A8уже в игноре");
|
||||
}
|
||||
|
||||
public String getNotIgnored() {
|
||||
return config.getString("ignore.not_ignored", "&#B798A8Игрок ẲE9{player} &#B798A8не в игноре");
|
||||
}
|
||||
|
||||
public String getCannotIgnoreSelf() {
|
||||
return config.getString("ignore.cannot_ignore_self", "&#CF6679Нельзя игнорировать самого себя");
|
||||
}
|
||||
|
||||
public String getIgnoreListEmpty() {
|
||||
return config.getString("ignore.ignore_list_empty", "&#B798A8Список игнора пуст");
|
||||
}
|
||||
|
||||
public String getIgnoreListHeader() {
|
||||
return config.getString("ignore.ignore_list_header", "&#B798A8Игнорируемые игроки⚖C9:");
|
||||
}
|
||||
|
||||
// ── Модерация (варны / баны) ────────────────────────────────────────────────
|
||||
|
||||
public String getModerationDefaultReason() {
|
||||
return config.getString("moderation.default_reason", "Без причины");
|
||||
}
|
||||
|
||||
public String getModerationDurationPermanent() {
|
||||
return config.getString("moderation.duration_permanent", "навсегда");
|
||||
}
|
||||
|
||||
/**
|
||||
* MiniMessage + legacy &# цвета; плейсхолдеры {reason},{moderator},{duration},{player}; перевод строки \\n
|
||||
*/
|
||||
public String getBanKickMessage() {
|
||||
return config.getString("moderation.ban_kick_message", defaultBanKick());
|
||||
}
|
||||
|
||||
private String defaultBanKick() {
|
||||
return "<gradient:#FF5555:#8B0000><bold>═══════ ДОСТУП ЗАПРЕЩЁН ═══════</bold></gradient>\n"
|
||||
+ "<gray>Вы заблокированы на этом сервере.</gray>\n"
|
||||
+ "<white>Причина: <gradient:#7858E9:#B798A8>{reason}</gradient></white>\n"
|
||||
+ "<gray>Модератор: <white>{moderator}</white></gray>\n"
|
||||
+ "<gray>Срок: <#7858E9>{duration}</#7858E9></gray>\n"
|
||||
+ "<gradient:#FF5555:#8B0000><bold>════════════════════════</bold></gradient>";
|
||||
}
|
||||
|
||||
public String getWarnReceived() {
|
||||
return config.getString("moderation.warn_received", "&#CF6679⚠ Вам выдано предупреждение! &#B798A8Причина: ẲE9{reason}");
|
||||
}
|
||||
|
||||
public String getWarnStaffConfirm() {
|
||||
return config.getString("moderation.warn_staff_confirm", "⚖C9Игроку ẲE9{player} ⚖C9выдан варн. &#B798A8Причина: ẲE9{reason}");
|
||||
}
|
||||
|
||||
public String getSilentWarnStaff() {
|
||||
return config.getString("moderation.silent_warn_staff", "&#B798A8[⚖C9ТИХИЙ ВАРН&#B798A8] ẲE9{player}⚖C9: &#B798A8{reason}");
|
||||
}
|
||||
|
||||
public String getBanUsage() {
|
||||
return config.getString("moderation.ban_usage", "&#B798A8Использование: ẲE9/lban <ник> [время|perm] [причина]");
|
||||
}
|
||||
|
||||
public String getBanConfirm() {
|
||||
return config.getString("moderation.ban_confirm", "⚖C9Игрок ẲE9{player} ⚖C9забанен. &#B798A8Срок: ẲE9{duration}");
|
||||
}
|
||||
|
||||
public String getUnbanConfirm() {
|
||||
return config.getString("moderation.unban_confirm", "⚖C9Бан снят с ẲE9{player}");
|
||||
}
|
||||
|
||||
public String getNotBanned() {
|
||||
return config.getString("moderation.not_banned", "&#CF6679Игрок не забанен!");
|
||||
}
|
||||
|
||||
public String getAlreadyBanned() {
|
||||
return config.getString("moderation.already_banned", "&#CF6679Игрок уже забанен!");
|
||||
}
|
||||
|
||||
public String getWarnUsage() {
|
||||
return config.getString("moderation.warn_usage", "&#B798A8Использование: ẲE9/warn <ник> [причина]");
|
||||
}
|
||||
|
||||
public String getSilentWarnUsage() {
|
||||
return config.getString("moderation.silent_warn_usage", "&#B798A8Использование: ẲE9/silentwarn <ник> [причина]");
|
||||
}
|
||||
}
|
||||
62
src/main/java/com/loki/lochat/config/MuteConfig.java
Normal file
62
src/main/java/com/loki/lochat/config/MuteConfig.java
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
package com.loki.lochat.config;
|
||||
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
/**
|
||||
* Конфигурация мутов (config/mute.yml)
|
||||
*/
|
||||
public class MuteConfig extends BaseConfig {
|
||||
|
||||
public MuteConfig(JavaPlugin plugin) {
|
||||
super(plugin, "mute.yml", true);
|
||||
init(); // ✅ Явная инициализация
|
||||
}
|
||||
|
||||
// ── Основные настройки ──────────────────────────────────────────────────────
|
||||
|
||||
public boolean isEnabled() {
|
||||
return config.getBoolean("enabled", true);
|
||||
}
|
||||
|
||||
public String getDefaultDuration() {
|
||||
return config.getString("default-duration", "7d");
|
||||
}
|
||||
|
||||
public String getDefaultReason() {
|
||||
return config.getString("default-reason", "Без причины");
|
||||
}
|
||||
|
||||
public int getMaxHistoryPerPlayer() {
|
||||
return config.getInt("max-history-per-player", 50);
|
||||
}
|
||||
|
||||
// ── Права на длительность ───────────────────────────────────────────────────
|
||||
|
||||
public boolean isPermissionBasedDuration() {
|
||||
return config.getBoolean("permission-based-duration", true);
|
||||
}
|
||||
|
||||
// ── Уведомления ─────────────────────────────────────────────────────────────
|
||||
|
||||
public boolean isNotifyOnMute() {
|
||||
return config.getBoolean("notify.on-mute", true);
|
||||
}
|
||||
|
||||
public boolean isNotifyOnUnmute() {
|
||||
return config.getBoolean("notify.on-unmute", true);
|
||||
}
|
||||
|
||||
public boolean isNotifyOnExpire() {
|
||||
return config.getBoolean("notify.on-expire", true);
|
||||
}
|
||||
|
||||
// ── Тихий мут ───────────────────────────────────────────────────────────────
|
||||
|
||||
public boolean isSilentMuteEnabled() {
|
||||
return config.getBoolean("silent-mute.enabled", true);
|
||||
}
|
||||
|
||||
public boolean isSilentMuteLogToConsole() {
|
||||
return config.getBoolean("silent-mute.log-to-console", true);
|
||||
}
|
||||
}
|
||||
68
src/main/java/com/loki/lochat/config/SoundsConfig.java
Normal file
68
src/main/java/com/loki/lochat/config/SoundsConfig.java
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
package com.loki.lochat.config;
|
||||
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
/**
|
||||
* Конфигурация звуков (config/sounds.yml)
|
||||
*/
|
||||
public class SoundsConfig extends BaseConfig {
|
||||
|
||||
public SoundsConfig(JavaPlugin plugin) {
|
||||
super(plugin, "sounds.yml", true);
|
||||
init(); // ✅ Явная инициализация
|
||||
}
|
||||
|
||||
// ── Звуки упоминаний ────────────────────────────────────────────────────────
|
||||
|
||||
public boolean isMentionSoundEnabled() {
|
||||
return config.getBoolean("mention.enabled", true);
|
||||
}
|
||||
|
||||
public String getMentionSound() {
|
||||
return config.getString("mention.sound", "BLOCK_NOTE_BLOCK_PLING");
|
||||
}
|
||||
|
||||
public float getMentionVolume() {
|
||||
return (float) config.getDouble("mention.volume", 1.0);
|
||||
}
|
||||
|
||||
public float getMentionPitch() {
|
||||
return (float) config.getDouble("mention.pitch", 1.0);
|
||||
}
|
||||
|
||||
// ── Звуки личных сообщений ──────────────────────────────────────────────────
|
||||
|
||||
public boolean isPmSoundEnabled() {
|
||||
return config.getBoolean("pm.enabled", true);
|
||||
}
|
||||
|
||||
public String getPmSound() {
|
||||
return config.getString("pm.sound", "ENTITY_EXPERIENCE_ORB_PICKUP");
|
||||
}
|
||||
|
||||
public float getPmVolume() {
|
||||
return (float) config.getDouble("pm.volume", 1.0);
|
||||
}
|
||||
|
||||
public float getPmPitch() {
|
||||
return (float) config.getDouble("pm.pitch", 1.0);
|
||||
}
|
||||
|
||||
// ── Звуки команд ────────────────────────────────────────────────────────────
|
||||
|
||||
public boolean isCommandSoundEnabled() {
|
||||
return config.getBoolean("command.enabled", false);
|
||||
}
|
||||
|
||||
public String getCommandSound() {
|
||||
return config.getString("command.sound", "UI_BUTTON_CLICK");
|
||||
}
|
||||
|
||||
public float getCommandVolume() {
|
||||
return (float) config.getDouble("command.volume", 0.5);
|
||||
}
|
||||
|
||||
public float getCommandPitch() {
|
||||
return (float) config.getDouble("command.pitch", 1.0);
|
||||
}
|
||||
}
|
||||
131
src/main/java/com/loki/lochat/core/PluginInitializer.java
Normal file
131
src/main/java/com/loki/lochat/core/PluginInitializer.java
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
package com.loki.lochat.core;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.config.ConfigManager;
|
||||
import com.loki.lochat.config.MessageConfig;
|
||||
import com.loki.lochat.core.filter.AdvancedMessageFilter;
|
||||
import com.loki.lochat.core.registry.ServiceRegistry;
|
||||
import com.loki.lochat.gradient.GradientModule;
|
||||
import com.loki.lochat.integrations.DiscordIntegration;
|
||||
import com.loki.lochat.integrations.LibertyBansHook;
|
||||
import com.loki.lochat.integrations.PlaceholderAPIHook;
|
||||
import com.loki.lochat.listener.ChatEventListener;
|
||||
import com.loki.lochat.listener.DiscordEventListener;
|
||||
import com.loki.lochat.listener.ModerationListener;
|
||||
import com.loki.lochat.listener.PlayerEventListener;
|
||||
import com.loki.lochat.managers.AutoMessageManager;
|
||||
import com.loki.lochat.managers.CustomCommandManager;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
/**
|
||||
* Инициализатор компонентов плагина
|
||||
* Выносит логику инициализации из главного класса
|
||||
*/
|
||||
public class PluginInitializer {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public PluginInitializer(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициализировать конфигурации
|
||||
*/
|
||||
public ConfigManager initConfigs() {
|
||||
return new ConfigManager(plugin);
|
||||
}
|
||||
|
||||
public MessageConfig initMessageConfig() {
|
||||
return new MessageConfig(plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициализировать Service Registry
|
||||
*/
|
||||
public ServiceRegistry initServiceRegistry(ConfigManager configManager, MessageConfig messageConfig) {
|
||||
ServiceRegistry registry = ServiceRegistry.createWithDeps(plugin, configManager, messageConfig);
|
||||
plugin.getLogger().info("ServiceRegistry инициализирован (SOLID DI)");
|
||||
return registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициализировать менеджеры
|
||||
*/
|
||||
public CustomCommandManager initCustomCommandManager() {
|
||||
return CustomCommandManager.create(plugin);
|
||||
}
|
||||
|
||||
public AutoMessageManager initAutoMessageManager() {
|
||||
return new AutoMessageManager(plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициализировать интеграции
|
||||
*/
|
||||
public void initIntegrations(DiscordIntegration discordIntegration, GradientModule gradientModule) {
|
||||
// LibertyBans
|
||||
new LibertyBansHook(plugin);
|
||||
|
||||
// PlaceholderAPI
|
||||
if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) {
|
||||
new PlaceholderAPIHook(plugin).register();
|
||||
plugin.getLogger().info("PlaceholderAPI подключен!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициализировать Discord интеграцию
|
||||
*/
|
||||
public DiscordIntegration initDiscordIntegration() {
|
||||
return new DiscordIntegration(plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициализировать Gradient модуль
|
||||
*/
|
||||
public GradientModule initGradientModule() {
|
||||
GradientModule module = new GradientModule(plugin);
|
||||
if (module.init()) {
|
||||
plugin.getLogger().info("Gradient модуль (LoPreff) загружен!");
|
||||
}
|
||||
return module;
|
||||
}
|
||||
|
||||
/**
|
||||
* Зарегистрировать слушатели событий
|
||||
*/
|
||||
public AdvancedMessageFilter registerListeners(ServiceRegistry serviceRegistry, DiscordIntegration discordIntegration) {
|
||||
// Создаём один экземпляр фильтра и передаём в оба листенера
|
||||
AdvancedMessageFilter filter = new AdvancedMessageFilter(plugin.getConfig(), plugin);
|
||||
|
||||
plugin.getServer().getPluginManager().registerEvents(
|
||||
new ChatEventListener(plugin, serviceRegistry, filter), plugin
|
||||
);
|
||||
plugin.getServer().getPluginManager().registerEvents(
|
||||
new PlayerEventListener(serviceRegistry, filter), plugin
|
||||
);
|
||||
plugin.getServer().getPluginManager().registerEvents(
|
||||
new ModerationListener(serviceRegistry), plugin
|
||||
);
|
||||
|
||||
// Discord listener
|
||||
if (discordIntegration.isEnabled()) {
|
||||
plugin.getServer().getPluginManager().registerEvents(
|
||||
new DiscordEventListener(discordIntegration), plugin
|
||||
);
|
||||
plugin.getLogger().info("Discord event listener registered");
|
||||
}
|
||||
|
||||
plugin.getLogger().info("Слушатели зарегистрированы");
|
||||
return filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Зарегистрировать plugin channels
|
||||
*/
|
||||
public void registerPluginChannels() {
|
||||
plugin.getServer().getMessenger().registerOutgoingPluginChannel(plugin, "BungeeCord");
|
||||
plugin.getLogger().info("BungeeCord plugin messaging channel registered");
|
||||
}
|
||||
}
|
||||
93
src/main/java/com/loki/lochat/core/PluginShutdown.java
Normal file
93
src/main/java/com/loki/lochat/core/PluginShutdown.java
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
package com.loki.lochat.core;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.IgnoreService;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.api.service.PunishmentService;
|
||||
import com.loki.lochat.api.service.NickService;
|
||||
import com.loki.lochat.api.service.PlayerDataService;
|
||||
import com.loki.lochat.core.registry.ServiceRegistry;
|
||||
import com.loki.lochat.gradient.GradientModule;
|
||||
import com.loki.lochat.integrations.DiscordIntegration;
|
||||
import com.loki.lochat.managers.AutoMessageManager;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
/**
|
||||
* Обработчик выключения плагина
|
||||
* Выносит логику shutdown из главного класса
|
||||
*/
|
||||
public class PluginShutdown {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
public PluginShutdown(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполнить полное выключение плагина
|
||||
*/
|
||||
public void shutdown(ServiceRegistry serviceRegistry,
|
||||
AutoMessageManager autoMessageManager,
|
||||
DiscordIntegration discordIntegration,
|
||||
GradientModule gradientModule) {
|
||||
|
||||
// ✅ ПЕРВЫМ ДЕЛОМ отменяем все event listeners
|
||||
HandlerList.unregisterAll(plugin);
|
||||
|
||||
// Останавливаем менеджеры
|
||||
if (autoMessageManager != null) {
|
||||
autoMessageManager.stop();
|
||||
}
|
||||
|
||||
// Сохраняем данные через сервисы
|
||||
saveAllData(serviceRegistry);
|
||||
|
||||
// Останавливаем интеграции
|
||||
if (discordIntegration != null) {
|
||||
discordIntegration.shutdown();
|
||||
}
|
||||
|
||||
if (gradientModule != null) {
|
||||
gradientModule.shutdown();
|
||||
}
|
||||
|
||||
plugin.getLogger().info("LoChat отключен!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранить все данные через сервисы
|
||||
*/
|
||||
private void saveAllData(ServiceRegistry serviceRegistry) {
|
||||
if (serviceRegistry == null) return;
|
||||
|
||||
// Статистика игроков
|
||||
PlayerDataService playerDataService = serviceRegistry.get(PlayerDataService.class);
|
||||
if (playerDataService != null) {
|
||||
playerDataService.saveAll();
|
||||
}
|
||||
|
||||
// Игнорирования
|
||||
IgnoreService ignoreService = serviceRegistry.get(IgnoreService.class);
|
||||
if (ignoreService != null) {
|
||||
ignoreService.save();
|
||||
}
|
||||
|
||||
// Муты
|
||||
MuteService muteService = serviceRegistry.get(MuteService.class);
|
||||
if (muteService != null) {
|
||||
muteService.save();
|
||||
}
|
||||
|
||||
PunishmentService punishmentService = serviceRegistry.get(PunishmentService.class);
|
||||
if (punishmentService != null) {
|
||||
punishmentService.save();
|
||||
}
|
||||
|
||||
// Ники
|
||||
NickService nickService = serviceRegistry.get(NickService.class);
|
||||
if (nickService != null) {
|
||||
nickService.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -57,18 +57,42 @@ public class AdvancedMessageFilter {
|
|||
|
||||
public AdvancedMessageFilter(FileConfiguration config, JavaPlugin plugin) {
|
||||
this.config = config;
|
||||
this.whitelistedDomains = config.getStringList("filters.advertising.whitelist");
|
||||
this.blacklistedDomains = config.getStringList("filters.advertising.blacklist");
|
||||
|
||||
// ✅ Используем FiltersConfig если доступен
|
||||
com.loki.lochat.config.FiltersConfig filtersConfig = null;
|
||||
if (plugin instanceof com.loki.lochat.LoChat loChat) {
|
||||
filtersConfig = loChat.getConfigManager().getFiltersConfig();
|
||||
}
|
||||
|
||||
// Читаем настройки из FiltersConfig или fallback на config.yml
|
||||
if (filtersConfig != null) {
|
||||
this.whitelistedDomains = filtersConfig.getWhitelistedDomains();
|
||||
this.blacklistedDomains = filtersConfig.getBlacklistedDomains();
|
||||
|
||||
int maxCapsPercent = filtersConfig.getCapsMaxPercent();
|
||||
int minLength = filtersConfig.getCapsMinLength();
|
||||
boolean autoLower = filtersConfig.isCapsAutoLowercase();
|
||||
boolean blockCaps = filtersConfig.isCapsBlock();
|
||||
this.capsFilter = new CapsFilter(maxCapsPercent, minLength, autoLower, blockCaps);
|
||||
|
||||
this.replacementChar = filtersConfig.getSwearReplacementChar().charAt(0);
|
||||
this.checkFragments = filtersConfig.isSwearCheckFragments();
|
||||
this.ignoreCase = filtersConfig.isSwearIgnoreCase();
|
||||
} else {
|
||||
// Fallback на config.yml
|
||||
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 autoLower = config.getBoolean("filters.caps.auto-lowercase", true);
|
||||
boolean blockCaps = config.getBoolean("filters.caps.block", false);
|
||||
this.capsFilter = new CapsFilter(maxCapsPercent, minLength, autoLower, blockCaps);
|
||||
int maxCapsPercent = config.getInt("filters.caps.max-percent", 70);
|
||||
int minLength = config.getInt("filters.caps.min-length", 5);
|
||||
boolean autoLower = config.getBoolean("filters.caps.auto-lowercase", true);
|
||||
boolean blockCaps = config.getBoolean("filters.caps.block", false);
|
||||
this.capsFilter = new CapsFilter(maxCapsPercent, minLength, autoLower, blockCaps);
|
||||
|
||||
this.replacementChar = config.getString("filters.swear.replacement-char", "*").charAt(0);
|
||||
this.checkFragments = config.getBoolean("filters.swear.check-fragments", true);
|
||||
this.ignoreCase = config.getBoolean("filters.swear.ignore-case", true);
|
||||
this.replacementChar = config.getString("filters.swear.replacement-char", "*").charAt(0);
|
||||
this.checkFragments = config.getBoolean("filters.swear.check-fragments", true);
|
||||
this.ignoreCase = config.getBoolean("filters.swear.ignore-case", true);
|
||||
}
|
||||
|
||||
loadWordFilter(plugin);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ public class CooldownFilter implements MessageFilter {
|
|||
|
||||
// Получаем сообщение из конфигурации
|
||||
com.loki.lochat.LoChat loChat = (com.loki.lochat.LoChat) plugin;
|
||||
String cooldownMessage = loChat.getConfigManager().getHardcodedMessages().getCooldownMessage();
|
||||
String cooldownMessage = loChat.getConfigManager().getMessagesConfig().getCooldownMessage();
|
||||
cooldownMessage = cooldownMessage.replace("{remaining}", String.valueOf(remaining));
|
||||
|
||||
player.sendMessage(cooldownMessage);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.loki.lochat.core.filter;
|
|||
|
||||
import com.loki.lochat.api.filter.MessageFilter;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.config.HardcodedMessages;
|
||||
import com.loki.lochat.config.MessagesConfig;
|
||||
import com.loki.lochat.data.model.ChatMessage;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.entity.Player;
|
||||
|
|
@ -13,13 +13,13 @@ import org.bukkit.plugin.java.JavaPlugin;
|
|||
*/
|
||||
public class MuteFilter implements MessageFilter {
|
||||
private final MuteService muteService;
|
||||
private final HardcodedMessages hardcodedMessages;
|
||||
private final MessagesConfig messagesConfig;
|
||||
|
||||
public MuteFilter(MuteService muteService, JavaPlugin plugin) {
|
||||
this.muteService = muteService;
|
||||
// Получаем HardcodedMessages через LoChat
|
||||
// Получаем MessagesConfig через LoChat
|
||||
com.loki.lochat.LoChat loChat = (com.loki.lochat.LoChat) plugin;
|
||||
this.hardcodedMessages = loChat.getConfigManager().getHardcodedMessages();
|
||||
this.messagesConfig = loChat.getConfigManager().getMessagesConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -39,11 +39,11 @@ public class MuteFilter implements MessageFilter {
|
|||
String msg;
|
||||
if (remaining < 0) {
|
||||
// Permanent mute
|
||||
msg = hardcodedMessages.getMutedPermanent()
|
||||
msg = messagesConfig.getMutedPermanent()
|
||||
.replace("{reason}", reason);
|
||||
} else {
|
||||
String timeStr = muteService.formatTime(remaining);
|
||||
msg = hardcodedMessages.getMutedMessage()
|
||||
msg = messagesConfig.getMutedMessage()
|
||||
.replace("{reason}", reason)
|
||||
.replace("{time}", timeStr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,9 +38,11 @@ public class ServiceRegistry {
|
|||
|
||||
register(PMService.class, new PMServiceImpl());
|
||||
register(IgnoreService.class, new IgnoreServiceImpl(plugin));
|
||||
register(SpyService.class, new SpyServiceImpl(plugin, messageConfig));
|
||||
SpyService spyService = new SpyServiceImpl(messageConfig);
|
||||
register(SpyService.class, spyService);
|
||||
register(MentionService.class, new MentionServiceImpl(configManager));
|
||||
register(NickService.class, new NickServiceImpl(plugin));
|
||||
register(PunishmentService.class, new PunishmentServiceImpl(plugin, configManager.getMessagesConfig()));
|
||||
}
|
||||
|
||||
public <T> void register(Class<T> serviceClass, T implementation) {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ public class ChatServiceImpl implements ChatService {
|
|||
@Override
|
||||
public void sendGlobalMessage(Player sender, Object message) {
|
||||
Component msg = toComponent(message);
|
||||
EnhancedChatRenderer renderer = new EnhancedChatRenderer(plugin, sender, msg, true);
|
||||
EnhancedChatRenderer renderer = new EnhancedChatRenderer(plugin, true);
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
if (!isGlobalChatDisabled(p.getUniqueId())) {
|
||||
p.sendMessage(renderer.render(sender, sender.displayName(), msg, p));
|
||||
|
|
@ -53,7 +53,7 @@ public class ChatServiceImpl implements ChatService {
|
|||
public void sendLocalMessage(Player sender, Object message) {
|
||||
int radius = plugin.getConfig().getInt("chat.local.radius", 100);
|
||||
Component msg = toComponent(message);
|
||||
EnhancedChatRenderer renderer = new EnhancedChatRenderer(plugin, sender, msg, false);
|
||||
EnhancedChatRenderer renderer = new EnhancedChatRenderer(plugin, false);
|
||||
|
||||
int count = 0;
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.loki.lochat.core.service;
|
||||
|
||||
import com.loki.lochat.api.filter.MessageFilter;
|
||||
import com.loki.lochat.api.service.ChatService;
|
||||
import com.loki.lochat.api.service.CooldownService;
|
||||
import com.loki.lochat.api.service.MessageService;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
|
|
@ -19,13 +18,9 @@ import java.util.List;
|
|||
* Реализация сервиса обработки сообщений (Chain of Responsibility)
|
||||
*/
|
||||
public class MessageServiceImpl implements MessageService {
|
||||
private final JavaPlugin plugin;
|
||||
private final List<MessageFilter> filters;
|
||||
private final ChatService chatService;
|
||||
|
||||
public MessageServiceImpl(JavaPlugin plugin, ServiceRegistry registry) {
|
||||
this.plugin = plugin;
|
||||
this.chatService = registry.get(ChatService.class);
|
||||
this.filters = new ArrayList<>();
|
||||
|
||||
// Регистрируем фильтры в порядке применения
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
|
||||
public class MuteHistoryManager {
|
||||
|
||||
// ✅ FIX: Ограничиваем размер истории для предотвращения утечки памяти
|
||||
private static final int MAX_HISTORY_PER_PLAYER = 50;
|
||||
|
||||
private final Map<UUID, List<MuteData.MuteHistoryEntry>> history = new ConcurrentHashMap<>();
|
||||
private final File historyFile;
|
||||
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
|
@ -36,6 +39,11 @@ public class MuteHistoryManager {
|
|||
null,
|
||||
0
|
||||
));
|
||||
|
||||
// ✅ FIX: Удаляем старые записи если превышен лимит
|
||||
if (entries.size() > MAX_HISTORY_PER_PLAYER) {
|
||||
entries.remove(0); // Удаляем самую старую запись
|
||||
}
|
||||
}
|
||||
|
||||
public void updateUnmute(UUID uuid, String unmutedBy) {
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ public class PlayerDataServiceImpl implements PlayerDataService {
|
|||
|
||||
/** Вызывать при каждом сообщении в чате */
|
||||
public void recordMessage(UUID player, String chatType) {
|
||||
playerMessages.merge(player, 1L, Long::sum);
|
||||
playerMessages.compute(player, (k, v) -> (v == null ? 0L : v) + 1L);
|
||||
totalMessages.incrementAndGet();
|
||||
switch (chatType) {
|
||||
case "global" -> globalChatCount.incrementAndGet();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,192 @@
|
|||
package com.loki.lochat.core.service;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.loki.lochat.api.service.PunishmentService;
|
||||
import com.loki.lochat.config.MessagesConfig;
|
||||
import com.loki.lochat.data.model.BanRecord;
|
||||
import com.loki.lochat.data.model.WarnEntry;
|
||||
import com.loki.lochat.util.FoliaUtil;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import com.loki.lochat.utils.TimeFormatter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Варны и баны в punishments.json
|
||||
*/
|
||||
public class PunishmentServiceImpl implements PunishmentService {
|
||||
|
||||
private final JavaPlugin plugin;
|
||||
private final MessagesConfig messages;
|
||||
private final File dataFile;
|
||||
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
||||
private final Map<UUID, List<WarnEntry>> warns = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, BanRecord> bans = new ConcurrentHashMap<>();
|
||||
|
||||
public PunishmentServiceImpl(JavaPlugin plugin, MessagesConfig messagesConfig) {
|
||||
this.plugin = plugin;
|
||||
this.messages = messagesConfig;
|
||||
this.dataFile = new File(plugin.getDataFolder(), "punishments.json");
|
||||
load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWarn(UUID uuid, String playerName, String moderator, String reason, boolean silent) {
|
||||
WarnEntry entry = new WarnEntry(System.currentTimeMillis(), moderator, reason, silent);
|
||||
warns.computeIfAbsent(uuid, k -> Collections.synchronizedList(new ArrayList<>())).add(entry);
|
||||
saveAsync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<WarnEntry> getWarns(UUID uuid) {
|
||||
List<WarnEntry> list = warns.get(uuid);
|
||||
if (list == null) return List.of();
|
||||
synchronized (list) {
|
||||
return List.copyOf(list);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWarnCount(UUID uuid) {
|
||||
List<WarnEntry> list = warns.get(uuid);
|
||||
return list == null ? 0 : list.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ban(UUID uuid, String playerName, long durationMs, String reason, String bannedBy) {
|
||||
long until = durationMs <= 0 ? 0 : System.currentTimeMillis() + durationMs;
|
||||
BanRecord record = new BanRecord(uuid, playerName, reason, bannedBy, until, System.currentTimeMillis());
|
||||
bans.put(uuid, record);
|
||||
saveAsync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unban(UUID uuid) {
|
||||
BanRecord removed = bans.remove(uuid);
|
||||
if (removed != null) {
|
||||
saveAsync();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBanned(UUID uuid) {
|
||||
BanRecord b = bans.get(uuid);
|
||||
if (b == null) return false;
|
||||
if (b.isPermanent()) return true;
|
||||
if (b.isExpired()) {
|
||||
bans.remove(uuid, b);
|
||||
saveAsync();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BanRecord getActiveBan(UUID uuid) {
|
||||
if (!isBanned(uuid)) return null;
|
||||
return bans.get(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component buildBanKickMessage(BanRecord ban) {
|
||||
String reason = ban.reason != null ? ban.reason : messages.getModerationDefaultReason();
|
||||
String mod = ban.bannedBy != null ? ban.bannedBy : "?";
|
||||
String duration;
|
||||
if (ban.isPermanent()) {
|
||||
duration = messages.getModerationDurationPermanent();
|
||||
} else {
|
||||
long left = Math.max(0, ban.until - System.currentTimeMillis());
|
||||
duration = TimeFormatter.format(left);
|
||||
}
|
||||
String template = messages.getBanKickMessage();
|
||||
String filled = template
|
||||
.replace("{reason}", reason)
|
||||
.replace("{moderator}", mod)
|
||||
.replace("{duration}", duration)
|
||||
.replace("{player}", ban.playerName != null ? ban.playerName : "?");
|
||||
return ChatFormatter.parse(filled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
try {
|
||||
if (!dataFile.getParentFile().exists() && !dataFile.getParentFile().mkdirs()) {
|
||||
plugin.getLogger().warning("Could not create data folder for punishments");
|
||||
}
|
||||
PunishmentSnapshot snap = new PunishmentSnapshot();
|
||||
for (Map.Entry<UUID, List<WarnEntry>> e : warns.entrySet()) {
|
||||
List<WarnEntry> list = e.getValue();
|
||||
synchronized (list) {
|
||||
snap.warns.put(e.getKey().toString(), new ArrayList<>(list));
|
||||
}
|
||||
}
|
||||
for (Map.Entry<UUID, BanRecord> e : bans.entrySet()) {
|
||||
snap.bans.put(e.getKey().toString(), e.getValue());
|
||||
}
|
||||
try (Writer w = new OutputStreamWriter(new FileOutputStream(dataFile), java.nio.charset.StandardCharsets.UTF_8)) {
|
||||
gson.toJson(snap, w);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("Failed to save punishments: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
warns.clear();
|
||||
bans.clear();
|
||||
load();
|
||||
}
|
||||
|
||||
private void saveAsync() {
|
||||
FoliaUtil.runAsync(plugin, this::save);
|
||||
}
|
||||
|
||||
private void load() {
|
||||
if (!dataFile.exists()) return;
|
||||
try (Reader r = new InputStreamReader(new FileInputStream(dataFile), java.nio.charset.StandardCharsets.UTF_8)) {
|
||||
Type type = new TypeToken<PunishmentSnapshot>() {}.getType();
|
||||
PunishmentSnapshot snap = gson.fromJson(r, type);
|
||||
if (snap == null) return;
|
||||
if (snap.warns != null) {
|
||||
for (Map.Entry<String, List<WarnEntry>> e : snap.warns.entrySet()) {
|
||||
try {
|
||||
warns.put(UUID.fromString(e.getKey()), Collections.synchronizedList(new ArrayList<>(e.getValue())));
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (snap.bans != null) {
|
||||
for (Map.Entry<String, BanRecord> e : snap.bans.entrySet()) {
|
||||
try {
|
||||
UUID id = UUID.fromString(e.getKey());
|
||||
BanRecord br = e.getValue();
|
||||
if (br != null) {
|
||||
br.uuid = id;
|
||||
if (!br.isPermanent() && br.isExpired()) continue;
|
||||
bans.put(id, br);
|
||||
}
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("Failed to load punishments: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static class PunishmentSnapshot {
|
||||
Map<String, List<WarnEntry>> warns = new HashMap<>();
|
||||
Map<String, BanRecord> bans = new HashMap<>();
|
||||
}
|
||||
}
|
||||
|
|
@ -7,8 +7,6 @@ import net.kyori.adventure.text.Component;
|
|||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
|
@ -17,12 +15,10 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
* Реализация сервиса шпионажа за чатом
|
||||
*/
|
||||
public class SpyServiceImpl implements SpyService {
|
||||
private final JavaPlugin plugin;
|
||||
private final MessageConfig messageConfig;
|
||||
private final Set<UUID> spyEnabled = ConcurrentHashMap.newKeySet();
|
||||
|
||||
public SpyServiceImpl(JavaPlugin plugin, MessageConfig messageConfig) {
|
||||
this.plugin = plugin;
|
||||
public SpyServiceImpl(MessageConfig messageConfig) {
|
||||
this.messageConfig = messageConfig;
|
||||
}
|
||||
|
||||
|
|
|
|||
37
src/main/java/com/loki/lochat/data/model/BanRecord.java
Normal file
37
src/main/java/com/loki/lochat/data/model/BanRecord.java
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package com.loki.lochat.data.model;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Активная блокировка
|
||||
*/
|
||||
public class BanRecord {
|
||||
public UUID uuid;
|
||||
public String playerName;
|
||||
public String reason;
|
||||
public String bannedBy;
|
||||
/** 0 = перманент; иначе timestamp окончания бана */
|
||||
public long until;
|
||||
public long createdAt;
|
||||
|
||||
public BanRecord() {
|
||||
}
|
||||
|
||||
public BanRecord(UUID uuid, String playerName, String reason, String bannedBy, long until, long createdAt) {
|
||||
this.uuid = uuid;
|
||||
this.playerName = playerName;
|
||||
this.reason = reason;
|
||||
this.bannedBy = bannedBy;
|
||||
this.until = until;
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public boolean isPermanent() {
|
||||
return until == 0;
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
if (isPermanent()) return false;
|
||||
return System.currentTimeMillis() >= until;
|
||||
}
|
||||
}
|
||||
21
src/main/java/com/loki/lochat/data/model/WarnEntry.java
Normal file
21
src/main/java/com/loki/lochat/data/model/WarnEntry.java
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package com.loki.lochat.data.model;
|
||||
|
||||
/**
|
||||
* Запись о варне (для JSON)
|
||||
*/
|
||||
public class WarnEntry {
|
||||
public long time;
|
||||
public String moderator;
|
||||
public String reason;
|
||||
public boolean silent;
|
||||
|
||||
public WarnEntry() {
|
||||
}
|
||||
|
||||
public WarnEntry(long time, String moderator, String reason, boolean silent) {
|
||||
this.time = time;
|
||||
this.moderator = moderator;
|
||||
this.reason = reason;
|
||||
this.silent = silent;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,8 @@
|
|||
package com.loki.lochat.gradient.config;
|
||||
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
|
@ -19,7 +13,6 @@ public class GradientConfig {
|
|||
private final JavaPlugin plugin;
|
||||
private FileConfiguration config;
|
||||
private FileConfiguration mainConfig;
|
||||
private File configFile;
|
||||
|
||||
public GradientConfig(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
|
|
|
|||
|
|
@ -121,7 +121,8 @@ public final class DisplayNameUtil {
|
|||
if (!data.hasPrefix() || !data.isPrefixEnabled()) return null;
|
||||
|
||||
GradientConfig cfg = module.getConfig();
|
||||
String prefix = cfg.getPrefixFormat().replace("{prefix}", data.getPrefix()).stripTrailing();
|
||||
// ✅ FIX: Убрал stripTrailing() чтобы сохранить пробел из prefix-format
|
||||
String prefix = cfg.getPrefixFormat().replace("{prefix}", data.getPrefix());
|
||||
|
||||
// Применяем градиент только если цвета включены И есть цвета
|
||||
if (data.hasColors() && data.isColorEnabled() && cfg.isGradientOnPrefix()) {
|
||||
|
|
|
|||
|
|
@ -157,6 +157,7 @@ public final class GradientUtil {
|
|||
String prefixFormat, boolean useLegacyFormat) {
|
||||
if (colors == null || colors.isEmpty()) {
|
||||
if (prefix != null && !prefix.isEmpty()) {
|
||||
// ✅ FIX: Не используем stripTrailing() чтобы сохранить пробел из prefixFormat
|
||||
return prefixFormat.replace("{prefix}", prefix) + nick;
|
||||
}
|
||||
return nick;
|
||||
|
|
@ -167,13 +168,16 @@ public final class GradientUtil {
|
|||
}
|
||||
|
||||
if (continuousGradient && gradientOnPrefix) {
|
||||
// ✅ FIX: Сохраняем пробел между префиксом и ником
|
||||
String fullText = prefixFormat.replace("{prefix}", prefix) + nick;
|
||||
return applyGradient(fullText, colors, useLegacyFormat);
|
||||
} else if (gradientOnPrefix) {
|
||||
// ✅ FIX: Применяем градиент к префиксу с форматом (включая пробел)
|
||||
String coloredPrefix = applyGradient(prefixFormat.replace("{prefix}", prefix), colors, useLegacyFormat);
|
||||
String coloredNick = applyGradient(nick, colors, useLegacyFormat);
|
||||
return coloredPrefix + coloredNick;
|
||||
} else {
|
||||
// ✅ FIX: Префикс без градиента, но с пробелом из формата
|
||||
String formattedPrefix = prefixFormat.replace("{prefix}", prefix);
|
||||
return formattedPrefix + applyGradient(nick, colors, useLegacyFormat);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.loki.lochat.integrations;
|
||||
|
||||
import com.loki.lochat.util.FoliaUtil;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
|
@ -32,9 +31,24 @@ public class DiscordIntegration {
|
|||
* Загрузить конфигурацию
|
||||
*/
|
||||
private void loadConfig() {
|
||||
File configFile = new File(plugin.getDataFolder(), "config/discord.yml");
|
||||
// Создаём папку config если её нет
|
||||
File configFolder = new File(plugin.getDataFolder(), "config");
|
||||
if (!configFolder.exists()) {
|
||||
configFolder.mkdirs();
|
||||
}
|
||||
|
||||
File configFile = new File(configFolder, "discord.yml");
|
||||
if (!configFile.exists()) {
|
||||
plugin.saveResource("config/discord.yml", false);
|
||||
try {
|
||||
// Копируем из resources/config/discord.yml
|
||||
java.io.InputStream in = plugin.getResource("config/discord.yml");
|
||||
if (in != null) {
|
||||
java.nio.file.Files.copy(in, configFile.toPath());
|
||||
in.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().warning("Failed to copy config/discord.yml: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
config = YamlConfiguration.loadConfiguration(configFile);
|
||||
|
|
@ -278,4 +292,4 @@ public class DiscordIntegration {
|
|||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,9 @@ import org.bukkit.entity.Player;
|
|||
*/
|
||||
public class LibertyBansHook {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final MuteService muteService;
|
||||
|
||||
public LibertyBansHook(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.muteService = plugin.getServiceRegistry().get(MuteService.class);
|
||||
|
||||
// Проверяем наличие LibertyBans
|
||||
|
|
|
|||
|
|
@ -81,4 +81,4 @@ public class SkinsRestorerHook {
|
|||
// Используем стандартные сервисы для получения голов
|
||||
return "https://minotar.net/helm/" + playerName + "/8.png";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,8 +33,35 @@ public class ChatEventListener implements Listener {
|
|||
// plain нужен только для фильтров — цвета игрока обрабатываем ниже отдельно
|
||||
String rawMessage = PlainTextComponentSerializer.plainText().serialize(event.message());
|
||||
|
||||
boolean isGlobal = rawMessage.startsWith("!");
|
||||
String plainMessage = isGlobal ? rawMessage.substring(1).stripLeading() : rawMessage;
|
||||
// ✅ РЕЖИМ ЕДИНОГО ЧАТА: если локальный отключен, весь чат = глобальный
|
||||
LoChat loChat = (LoChat) plugin;
|
||||
boolean localEnabled = loChat.getConfigManager().isLocalEnabled();
|
||||
|
||||
boolean isGlobal;
|
||||
String plainMessage;
|
||||
|
||||
if (localEnabled) {
|
||||
// Старая логика: "!" = глобальный, без "!" = локальный
|
||||
isGlobal = rawMessage.startsWith("!");
|
||||
plainMessage = isGlobal ? rawMessage.substring(1).stripLeading() : rawMessage;
|
||||
} else {
|
||||
// Новая логика: весь чат глобальный, "!" не нужен
|
||||
isGlobal = true;
|
||||
plainMessage = rawMessage;
|
||||
}
|
||||
|
||||
// ✅ Проверка: включен ли глобальный/локальный чат
|
||||
if (isGlobal && !loChat.getConfigManager().isGlobalEnabled()) {
|
||||
event.setCancelled(true);
|
||||
sender.sendMessage(ChatFormatter.parse("&#CF6679Глобальный чат отключен!"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isGlobal && !localEnabled) {
|
||||
event.setCancelled(true);
|
||||
sender.sendMessage(ChatFormatter.parse("&#CF6679Локальный чат отключен!"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Мут, кулдаун
|
||||
if (!messageService.processMessage(sender, plainMessage)) {
|
||||
|
|
@ -52,14 +79,14 @@ public class ChatEventListener implements Listener {
|
|||
|
||||
String filteredMessage = filterResult.filteredMessage();
|
||||
|
||||
// ── Фиксируем цвет текста ──────────────────────────────────────────────
|
||||
// Проблема: Paper наследует цвет от предыдущего компонента если явный не задан.
|
||||
// Синий цвет в LOCAL — это цвет последней буквы градиента "LOCAL" (#4169E1),
|
||||
// который просачивается в текст сообщения.
|
||||
// Решение: всегда явно задаём цвет сообщения.
|
||||
// • lochat.chat.colors → парсим &7 / &#RRGGBB / MiniMessage из самого текста
|
||||
// • без права → берём message-color из конфига
|
||||
LoChat loChat = (LoChat) plugin;
|
||||
// ── Цвет текста сообщения ({message}) ─────────────────────────────────
|
||||
// Если стиль не задан явно, Paper может унаследовать цвет последнего символа градиента
|
||||
// префикса в строке чата (например у LOCAL последняя буква давала синеватый #4169E1) —
|
||||
// из‑за этого весь текст сообщения «подтягивался» под этот тон. Намеренный цвет текста
|
||||
// задаётся в appearance.yml: prefixes.global.message-color / prefixes.local.message-color (#E8E0F0 по умолчанию).
|
||||
// Решение: всегда выставлять стиль тела сообщения:
|
||||
// • lochat.chat.colors → MiniMessage / &# / & из текста
|
||||
// • иначе → parseWithDefaultMessageColor(..., message-color) + экранирование < (см. ChatFormatter)
|
||||
Component messageComponent;
|
||||
|
||||
if (sender.hasPermission("lochat.chat.colors")) {
|
||||
|
|
@ -68,32 +95,42 @@ public class ChatEventListener implements Listener {
|
|||
String defaultColor = isGlobal
|
||||
? loChat.getConfigManager().getAppearanceConfig().getGlobalMessageColor()
|
||||
: loChat.getConfigManager().getAppearanceConfig().getLocalMessageColor();
|
||||
String hex = defaultColor.startsWith("#") ? defaultColor : "#" + defaultColor;
|
||||
messageComponent = ChatFormatter.parse("<color:" + hex + ">" + filteredMessage + "</color>");
|
||||
messageComponent = ChatFormatter.parseWithDefaultMessageColor(filteredMessage, defaultColor);
|
||||
}
|
||||
|
||||
event.message(messageComponent);
|
||||
|
||||
// Радиус для локального чата
|
||||
// ✅ FIX: Радиус для локального чата - кэшируем локации в async потоке
|
||||
// Paper позволяет читать Location в async, но не модифицировать World
|
||||
if (!isGlobal) {
|
||||
int radius = loChat.getConfigManager().getAppearanceConfig().getLocalRadius();
|
||||
event.viewers().removeIf(v ->
|
||||
v instanceof Player p && !com.loki.lochat.util.DistanceUtil.isInRange(sender, p, radius)
|
||||
);
|
||||
|
||||
// Безопасно: AsyncChatEvent.viewers() thread-safe, Location.distance() тоже
|
||||
event.viewers().removeIf(v -> {
|
||||
if (!(v instanceof Player p)) return false;
|
||||
|
||||
// Проверяем мир и расстояние (чтение Location безопасно в async)
|
||||
try {
|
||||
return !com.loki.lochat.util.DistanceUtil.isInRange(sender, p, radius);
|
||||
} catch (Exception e) {
|
||||
// На всякий случай ловим исключения
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
long recipients = event.viewers().stream()
|
||||
.filter(v -> v instanceof Player p && !p.equals(sender))
|
||||
.count();
|
||||
|
||||
if (recipients == 0) {
|
||||
String msg = loChat.getConfigManager().getHardcodedMessages().getNobodyHeard();
|
||||
String msg = loChat.getConfigManager().getMessagesConfig().getNobodyHeard();
|
||||
sender.getScheduler().run(plugin, t ->
|
||||
sender.sendMessage(ChatFormatter.parse(msg)), null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
event.renderer(new EnhancedChatRenderer(plugin, sender, messageComponent, isGlobal));
|
||||
event.renderer(new EnhancedChatRenderer(plugin, isGlobal));
|
||||
|
||||
loChat.getDiscordIntegration().sendChatMessage(sender, filteredMessage, isGlobal);
|
||||
|
||||
|
|
|
|||
|
|
@ -78,4 +78,4 @@ public class DiscordEventListener implements Listener {
|
|||
|
||||
discord.sendPlayerDeath(player, deathMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
package com.loki.lochat.listener;
|
||||
|
||||
import com.loki.lochat.api.service.PunishmentService;
|
||||
import com.loki.lochat.data.model.BanRecord;
|
||||
import com.loki.lochat.core.registry.ServiceRegistry;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
|
||||
|
||||
/**
|
||||
* Блокировка входа забаненным игрокам
|
||||
*/
|
||||
public class ModerationListener implements Listener {
|
||||
|
||||
private final PunishmentService punishmentService;
|
||||
|
||||
public ModerationListener(ServiceRegistry registry) {
|
||||
this.punishmentService = registry.get(PunishmentService.class);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onPreLogin(AsyncPlayerPreLoginEvent event) {
|
||||
if (!punishmentService.isBanned(event.getUniqueId())) {
|
||||
return;
|
||||
}
|
||||
BanRecord ban = punishmentService.getActiveBan(event.getUniqueId());
|
||||
if (ban == null) {
|
||||
return;
|
||||
}
|
||||
event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_BANNED, punishmentService.buildBanKickMessage(ban));
|
||||
}
|
||||
}
|
||||
|
|
@ -3,13 +3,13 @@ package com.loki.lochat.managers;
|
|||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.util.FoliaUtil;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
|
|
@ -18,7 +18,9 @@ public class AutoMessageManager {
|
|||
private final LoChat plugin;
|
||||
private final Random random = new Random();
|
||||
private final AtomicInteger currentIndex = new AtomicInteger(0);
|
||||
private boolean running = false;
|
||||
|
||||
// ✅ FIX: Сохраняем ссылку на задачу для отмены
|
||||
private Object scheduledTask; // BukkitTask или ScheduledTask
|
||||
|
||||
private Map<String, List<String>> messages;
|
||||
private List<String> messageOrder;
|
||||
|
|
@ -75,18 +77,45 @@ public class AutoMessageManager {
|
|||
return;
|
||||
}
|
||||
|
||||
// ✅ FIX: Отменяем старую задачу перед созданием новой
|
||||
stop();
|
||||
|
||||
int intervalSeconds = plugin.getConfigManager().getAutoMessagesInterval();
|
||||
long intervalTicks = intervalSeconds * 20L;
|
||||
|
||||
// Используем FoliaUtil для совместимости с Paper и Folia
|
||||
FoliaUtil.runTimerAsync(plugin, this::broadcastNextMessage, intervalTicks, intervalTicks);
|
||||
running = true;
|
||||
if (FoliaUtil.isFolia()) {
|
||||
long delayMs = intervalTicks * 50;
|
||||
long periodMs = intervalTicks * 50;
|
||||
scheduledTask = Bukkit.getAsyncScheduler().runAtFixedRate(
|
||||
plugin,
|
||||
task -> broadcastNextMessage(),
|
||||
delayMs,
|
||||
periodMs,
|
||||
java.util.concurrent.TimeUnit.MILLISECONDS
|
||||
);
|
||||
} else {
|
||||
scheduledTask = Bukkit.getScheduler().runTaskTimerAsynchronously(
|
||||
plugin,
|
||||
this::broadcastNextMessage,
|
||||
intervalTicks,
|
||||
intervalTicks
|
||||
);
|
||||
}
|
||||
|
||||
plugin.getLogger().info("Автосообщения запущены (интервал: " + intervalSeconds + " сек, режим: " + mode + ")");
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
running = false;
|
||||
// ✅ FIX: Отменяем scheduled task
|
||||
if (scheduledTask != null) {
|
||||
if (scheduledTask instanceof ScheduledTask) {
|
||||
((ScheduledTask) scheduledTask).cancel();
|
||||
} else if (scheduledTask instanceof BukkitTask) {
|
||||
((BukkitTask) scheduledTask).cancel();
|
||||
}
|
||||
scheduledTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void broadcastNextMessage() {
|
||||
|
|
|
|||
|
|
@ -10,12 +10,9 @@ 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.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;
|
||||
|
|
@ -318,4 +315,4 @@ public class CustomCommandManager {
|
|||
public record CustomCommandData(String name, String permission, String message, List<String> aliases, String type,
|
||||
String target, boolean enabled) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,29 +25,22 @@ public class EnhancedChatRenderer implements ChatRenderer {
|
|||
private static final TextColor FALLBACK_COLOR = NamedTextColor.WHITE;
|
||||
|
||||
private final JavaPlugin plugin;
|
||||
private final Player sender;
|
||||
private final boolean isGlobal;
|
||||
private final MentionHandler mentionHandler;
|
||||
private final com.loki.lochat.config.AppearanceConfig cfg;
|
||||
private final com.loki.lochat.config.HardcodedMessages hm;
|
||||
private final com.loki.lochat.config.ConfigManager cfg;
|
||||
|
||||
public EnhancedChatRenderer(JavaPlugin plugin, Player sender, Component originalMessage, boolean isGlobal) {
|
||||
public EnhancedChatRenderer(JavaPlugin plugin, boolean isGlobal) {
|
||||
this.plugin = plugin;
|
||||
this.sender = sender;
|
||||
this.isGlobal = isGlobal;
|
||||
this.mentionHandler = new MentionHandler(plugin);
|
||||
|
||||
com.loki.lochat.LoChat loChat = (com.loki.lochat.LoChat) plugin;
|
||||
this.cfg = loChat.getConfigManager().getAppearanceConfig();
|
||||
this.hm = loChat.getConfigManager().getHardcodedMessages();
|
||||
this.cfg = loChat.getConfigManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component render(Player source, Component sourceDisplayName, Component message, Audience viewer) {
|
||||
Component prefix = buildPrefix();
|
||||
Component separator = buildSeparator();
|
||||
Component player = buildPlayerComponent(source);
|
||||
|
||||
// Обрабатываем сообщение
|
||||
Component processed = message;
|
||||
processed = TextFormatter.formatMarkdown(processed);
|
||||
processed = processUrls(processed);
|
||||
|
|
@ -57,11 +50,94 @@ public class EnhancedChatRenderer implements ChatRenderer {
|
|||
processed = mentionHandler.processMentions(processed, source, vp);
|
||||
}
|
||||
|
||||
return prefix
|
||||
.append(separator)
|
||||
.append(player)
|
||||
.append(Component.text(": ", NamedTextColor.WHITE))
|
||||
.append(processed);
|
||||
// ✅ Получаем кастомный формат из конфига
|
||||
com.loki.lochat.config.AppearanceConfig appearanceCfg = cfg.getAppearanceConfig();
|
||||
String format = isGlobal ? appearanceCfg.getGlobalChatFormat() : appearanceCfg.getLocalChatFormat();
|
||||
|
||||
// Строим компоненты
|
||||
Component emoji = buildEmojiComponent();
|
||||
Component prefix = buildPrefix();
|
||||
Component separator = buildSeparator();
|
||||
Component playerPrefix = buildPlayerPrefix(source);
|
||||
Component player = buildPlayerComponent(source);
|
||||
|
||||
// Парсим формат и заменяем плейсхолдеры
|
||||
return buildFormattedMessage(format, emoji, prefix, separator, playerPrefix, player, processed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Строит финальное сообщение по формату с плейсхолдерами
|
||||
*/
|
||||
private Component buildFormattedMessage(String format, Component emoji, Component prefix,
|
||||
Component separator, Component playerPrefix,
|
||||
Component player, Component message) {
|
||||
Component result = Component.empty();
|
||||
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("\\{([^}]+)\\}");
|
||||
java.util.regex.Matcher matcher = pattern.matcher(format);
|
||||
|
||||
int lastEnd = 0;
|
||||
|
||||
while (matcher.find()) {
|
||||
// Добавляем текст до плейсхолдера
|
||||
if (matcher.start() > lastEnd) {
|
||||
result = result.append(Component.text(format.substring(lastEnd, matcher.start())));
|
||||
}
|
||||
|
||||
// Заменяем плейсхолдер
|
||||
String placeholder = matcher.group(1);
|
||||
switch (placeholder) {
|
||||
case "emoji" -> result = result.append(emoji);
|
||||
case "prefix" -> result = result.append(prefix);
|
||||
case "separator" -> result = result.append(separator);
|
||||
case "player_prefix" -> result = result.append(playerPrefix);
|
||||
case "player" -> result = result.append(player);
|
||||
case "message" -> result = result.append(message);
|
||||
default -> result = result.append(Component.text("{" + placeholder + "}"));
|
||||
}
|
||||
|
||||
lastEnd = matcher.end();
|
||||
}
|
||||
|
||||
// Добавляем оставшийся текст
|
||||
if (lastEnd < format.length()) {
|
||||
result = result.append(Component.text(format.substring(lastEnd)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Строит компонент эмодзи
|
||||
*/
|
||||
private Component buildEmojiComponent() {
|
||||
com.loki.lochat.config.AppearanceConfig appearanceCfg = cfg.getAppearanceConfig();
|
||||
String emoji = isGlobal ? appearanceCfg.getGlobalEmoji() : appearanceCfg.getLocalEmoji();
|
||||
return emoji.isEmpty() ? Component.empty() : Component.text(emoji);
|
||||
}
|
||||
|
||||
/**
|
||||
* Строит префикс игрока из LuckPerms/градиента
|
||||
*/
|
||||
private Component buildPlayerPrefix(Player player) {
|
||||
// Получаем градиентный модуль
|
||||
com.loki.lochat.LoChat loChat = (com.loki.lochat.LoChat) plugin;
|
||||
com.loki.lochat.gradient.GradientModule gradientModule = loChat.getGradientModule();
|
||||
|
||||
if (gradientModule != null) {
|
||||
com.loki.lochat.gradient.data.GradientPlayerData data =
|
||||
gradientModule.getDataManager().getPlayerData(player.getUniqueId());
|
||||
|
||||
if (data != null && data.isPrefixEnabled() && data.hasPrefix()) {
|
||||
String prefixText = data.getPrefix();
|
||||
if (data.hasColors() && data.isColorEnabled()) {
|
||||
// Применяем градиент к префиксу
|
||||
return buildGradientText(prefixText, data.getColors());
|
||||
}
|
||||
return Component.text(prefixText);
|
||||
}
|
||||
}
|
||||
|
||||
return Component.empty();
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
|
@ -69,18 +145,13 @@ public class EnhancedChatRenderer implements ChatRenderer {
|
|||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private Component buildPrefix() {
|
||||
String emoji = isGlobal ? cfg.getGlobalEmoji() : cfg.getLocalEmoji();
|
||||
String text = isGlobal ? cfg.getGlobalText() : cfg.getLocalText();
|
||||
List<String> colors = isGlobal ? cfg.getGlobalColors() : cfg.getLocalColors();
|
||||
// ✅ FIX: Получаем AppearanceConfig через ConfigManager
|
||||
com.loki.lochat.config.AppearanceConfig appearanceCfg = cfg.getAppearanceConfig();
|
||||
|
||||
String text = isGlobal ? appearanceCfg.getGlobalText() : appearanceCfg.getLocalText();
|
||||
List<String> colors = isGlobal ? appearanceCfg.getGlobalColors() : appearanceCfg.getLocalColors();
|
||||
|
||||
Component prefix = Component.empty();
|
||||
|
||||
if (!emoji.isEmpty()) {
|
||||
prefix = prefix.append(Component.text(emoji + " "));
|
||||
}
|
||||
|
||||
prefix = prefix.append(buildGradientText(text, colors));
|
||||
return prefix;
|
||||
return buildGradientText(text, colors);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -145,8 +216,9 @@ public class EnhancedChatRenderer implements ChatRenderer {
|
|||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private Component buildSeparator() {
|
||||
String text = isGlobal ? cfg.getGlobalSeparatorText() : cfg.getLocalSeparatorText();
|
||||
String color = isGlobal ? cfg.getGlobalSeparatorColor() : cfg.getLocalSeparatorColor();
|
||||
com.loki.lochat.config.AppearanceConfig appearanceCfg = cfg.getAppearanceConfig();
|
||||
String text = isGlobal ? appearanceCfg.getGlobalSeparatorText() : appearanceCfg.getLocalSeparatorText();
|
||||
String color = isGlobal ? appearanceCfg.getGlobalSeparatorColor() : appearanceCfg.getLocalSeparatorColor();
|
||||
return Component.text(text, parseColor(color));
|
||||
}
|
||||
|
||||
|
|
@ -157,11 +229,28 @@ public class EnhancedChatRenderer implements ChatRenderer {
|
|||
private Component buildPlayerComponent(Player player) {
|
||||
Component displayName = player.displayName();
|
||||
|
||||
if (!cfg.isHoverEnabled()) {
|
||||
// ✅ FIX: Получаем AppearanceConfig для проверки hover
|
||||
com.loki.lochat.config.AppearanceConfig appearanceCfg = cfg.getAppearanceConfig();
|
||||
|
||||
if (!appearanceCfg.isHoverEnabled()) {
|
||||
return displayName.clickEvent(ClickEvent.suggestCommand("/msg " + player.getName() + " "));
|
||||
}
|
||||
|
||||
List<String> hoverLines = cfg.getHoverFormat();
|
||||
List<String> hoverLines = appearanceCfg.getHoverFormat();
|
||||
if (hoverLines.isEmpty()) {
|
||||
hoverLines = List.of(
|
||||
"ẲE9▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬",
|
||||
"&#B798A8✦ &f{player}",
|
||||
"",
|
||||
"⚖C9⏱ &fПинг: ẲE9{ping}ms",
|
||||
"⚖C9♥ &fЗдоровье: ẲE9{health}/20",
|
||||
"⚖C9🍖 &fГолод: ẲE9{food}/20",
|
||||
"",
|
||||
"ẲE9▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬",
|
||||
"&#B798A8✦ &fНажми чтобы написать"
|
||||
);
|
||||
}
|
||||
|
||||
net.kyori.adventure.text.TextComponent.Builder hoverBuilder = Component.text();
|
||||
|
||||
for (int i = 0; i < hoverLines.size(); i++) {
|
||||
|
|
@ -169,7 +258,7 @@ public class EnhancedChatRenderer implements ChatRenderer {
|
|||
.replace("{player}", player.getName())
|
||||
.replace("{world}", player.getWorld().getName())
|
||||
.replace("{ping}", String.valueOf(player.getPing()))
|
||||
.replace("{gamemode}", hm.getGamemodeName(player.getGameMode().name().toLowerCase()))
|
||||
.replace("{gamemode}", player.getGameMode().name())
|
||||
.replace("{health}", String.valueOf(Math.round(player.getHealth())))
|
||||
.replace("{food}", String.valueOf(player.getFoodLevel()));
|
||||
|
||||
|
|
@ -204,8 +293,11 @@ public class EnhancedChatRenderer implements ChatRenderer {
|
|||
}
|
||||
|
||||
private Component processEmojis(Component message) {
|
||||
// ✅ FIX: Получаем AppearanceConfig для эмодзи
|
||||
com.loki.lochat.config.AppearanceConfig appearanceCfg = cfg.getAppearanceConfig();
|
||||
|
||||
Component result = message;
|
||||
for (java.util.Map.Entry<String, String> e : cfg.getEmojis().entrySet()) {
|
||||
for (java.util.Map.Entry<String, String> e : appearanceCfg.getEmojis().entrySet()) {
|
||||
result = result.replaceText(b -> b.matchLiteral(e.getKey()).replacement(e.getValue()));
|
||||
}
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -85,4 +85,4 @@ public class AdventureManager {
|
|||
public static boolean isAdventureAvailable() {
|
||||
return adventureAvailable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,33 @@ public final class ChatFormatter {
|
|||
return MM.deserialize(converted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default message color for players without {@code lochat.chat.colors}.
|
||||
* Escapes {@code \} and {@code <} in the raw text so a literal {@code </color>} (or any {@code <})
|
||||
* cannot close the outer MiniMessage wrapper early — which used to leave a stray {@code </color>}
|
||||
* visible in-game and in the server console log.
|
||||
* <p>
|
||||
* Legacy hex ({@code &#RRGGBB}) and ampersand color codes in the message are still applied after escaping
|
||||
* (see {@link #convertLegacyFormats} inside {@link #parse}).
|
||||
*/
|
||||
public static Component parseWithDefaultMessageColor(String message, String hexCode) {
|
||||
if (message == null) message = "";
|
||||
String hex = (hexCode == null || hexCode.isBlank())
|
||||
? "#FFFFFF"
|
||||
: (hexCode.startsWith("#") ? hexCode : "#" + hexCode);
|
||||
String escaped = escapeForMiniMessageOuterWrap(message);
|
||||
return parse("<color:" + hex + ">" + escaped + "</color>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape user text before it is embedded in a MiniMessage template with outer tags.
|
||||
* MiniMessage treats {@code \<} as a literal angle bracket.
|
||||
*/
|
||||
public static String escapeForMiniMessageOuterWrap(String text) {
|
||||
if (text == null) return "";
|
||||
return text.replace("\\", "\\\\").replace("<", "\\<");
|
||||
}
|
||||
|
||||
public static Component parse(String message, String... replacements) {
|
||||
if (message == null) return Component.empty();
|
||||
String result = message;
|
||||
|
|
|
|||
|
|
@ -23,4 +23,4 @@ public final class Constants {
|
|||
|
||||
private Constants() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ public final class PlayerUtil {
|
|||
|
||||
/**
|
||||
* Safely parses a sound name from config.
|
||||
* Uses Registry API for modern sound parsing with fallback to reflection.
|
||||
* Uses Paper 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
|
||||
|
|
@ -29,8 +29,9 @@ public final class PlayerUtil {
|
|||
}
|
||||
|
||||
try {
|
||||
// Try to get sound by key using Registry (modern way)
|
||||
Sound sound = org.bukkit.Registry.SOUNDS.match(soundName);
|
||||
// ✅ FIX: Use Paper's NamespacedKey instead of deprecated match()
|
||||
org.bukkit.NamespacedKey key = org.bukkit.NamespacedKey.minecraft(soundName.toLowerCase());
|
||||
Sound sound = org.bukkit.Registry.SOUNDS.get(key);
|
||||
if (sound != null) {
|
||||
return sound;
|
||||
}
|
||||
|
|
@ -105,4 +106,4 @@ public final class PlayerUtil {
|
|||
Player player = Bukkit.getPlayer(uuid);
|
||||
return player != null && player.isOnline();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,181 +1,30 @@
|
|||
# ╔══════════════════════════════════════════════════════════════════════════════╗
|
||||
# ║ LoChat Configuration ║
|
||||
# ║ Версия: 1.5.5-1.20.1 ║
|
||||
# ╚══════════════════════════════════════════════════════════════════════════════╝
|
||||
|
||||
# LoChat Configuration v1.5.5
|
||||
config-version: 4
|
||||
prefix: "�D4FF[LoChat]"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ДОПОЛНИТЕЛЬНЫЕ КОНФИГИ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
# Загружать дополнительные конфигурационные файлы
|
||||
load-additional-configs: true
|
||||
|
||||
# Список дополнительных конфигов для загрузки
|
||||
additional-configs:
|
||||
- "config/chat-formats.yml" # Форматы чата и сообщений
|
||||
- "config/filters.yml" # Настройки фильтров
|
||||
- "config/gradient-config.yml" # Конфигурация градиентов
|
||||
- "config/automessages.yml" # Автосообщения
|
||||
- "config/mute-config.yml" # Система мутов
|
||||
- "config/sounds.yml" # Звуковые эффекты
|
||||
- "config/database.yml" # База данных
|
||||
- "config/integrations.yml" # Интеграции с другими плагинами
|
||||
- "config/custom-commands.yml" # Кастомные команды
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ СТРУКТУРА ДАННЫХ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
# Настройки хранения данных
|
||||
data-storage:
|
||||
# Основная папка для данных
|
||||
data-folder: "data/"
|
||||
|
||||
# Использовать отдельные файлы для разных типов данных
|
||||
separate-files: true
|
||||
|
||||
# Файлы данных
|
||||
files:
|
||||
main: "data.yml" # Основные данные (если separate-files: false)
|
||||
players: "players.yml" # Данные игроков
|
||||
gradients: "gradients.yml" # Градиентные ники
|
||||
mutes: "mutes.yml" # Муты и история
|
||||
ignores: "ignores.yml" # Игнорирование
|
||||
statistics: "statistics.yml" # Статистика
|
||||
filter-words: "filter-words.json" # Запрещенные слова
|
||||
|
||||
# Автосохранение (секунды)
|
||||
auto-save-interval: 300
|
||||
|
||||
# Создавать резервные копии
|
||||
backup:
|
||||
enabled: true
|
||||
interval: 3600 # Каждый час
|
||||
max-backups: 24
|
||||
folder: "backups/"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ HUB COMMAND │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
hub_command:
|
||||
# Enable/disable /hub command
|
||||
enabled: true
|
||||
|
||||
# Target server name for /hub command
|
||||
# Sends player to this server via BungeeCord
|
||||
server: "hub"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ КАСТОМНЫЕ КОМАНДЫ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
custom-commands:
|
||||
# Включить кастомные команды
|
||||
enabled: true
|
||||
|
||||
# Настройки Discord команды
|
||||
discord:
|
||||
invite-link: "discord.gg/example"
|
||||
|
||||
# Настройки Website команды
|
||||
website:
|
||||
url: "https://example.com"
|
||||
|
||||
# Настройки координат
|
||||
coords:
|
||||
# Показывать координаты в actionbar
|
||||
show-in-actionbar: true
|
||||
# Время показа в actionbar (тики)
|
||||
actionbar-duration: 60
|
||||
|
||||
# Настройки /me команды
|
||||
me:
|
||||
# Радиус для локального действия
|
||||
radius: 100
|
||||
# Цвет сообщения
|
||||
color: "gray"
|
||||
|
||||
# Настройки звуков
|
||||
sounds:
|
||||
# Разрешить игрокам переключать звуки
|
||||
allow-toggle: true
|
||||
# Звук при включении/выключении
|
||||
toggle-sound: "UI_BUTTON_CLICK"
|
||||
|
||||
# Настройки статистики
|
||||
stats:
|
||||
# Показывать статистику сервера
|
||||
show-server-stats: true
|
||||
# Показывать статистику игроков
|
||||
show-player-stats: true
|
||||
# Обновлять статистику каждые N секунд
|
||||
update-interval: 300
|
||||
|
||||
# Настройки пинга
|
||||
ping:
|
||||
# Цвета для разных значений пинга
|
||||
colors:
|
||||
excellent: "green" # < 50ms
|
||||
good: "yellow" # 50-100ms
|
||||
fair: "gold" # 100-200ms
|
||||
poor: "red" # > 200ms
|
||||
|
||||
# Настройки времени
|
||||
time:
|
||||
# Формат времени (12 или 24 часа)
|
||||
format: 24
|
||||
# Показывать дату
|
||||
show-date: true
|
||||
|
||||
# Настройки списка игроков
|
||||
list:
|
||||
# Группировать по мирам
|
||||
group-by-world: false
|
||||
# Показывать AFK игроков
|
||||
show-afk: true
|
||||
# Максимум игроков в списке
|
||||
max-players: 50
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ НАСТРОЙКИ ЧАТА │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
# Базовые настройки чата
|
||||
# Детальные настройки (префиксы, цвета, форматы) в config/*.yml
|
||||
|
||||
chat:
|
||||
# Режим чата:
|
||||
# - Оба включены = локальный (по умолчанию) + глобальный (с "!")
|
||||
# - Только global = единый чат без префиксов для всех
|
||||
global:
|
||||
enabled: true
|
||||
cooldown: 3
|
||||
prefix: "&#FFD700[G]"
|
||||
|
||||
local:
|
||||
enabled: true
|
||||
radius: 100
|
||||
cooldown: 1
|
||||
prefix: "WCEEB[L]"
|
||||
|
||||
pm:
|
||||
enabled: true
|
||||
cooldown: 2
|
||||
sound: true
|
||||
sound-type: "BLOCK_NOTE_BLOCK_PLING"
|
||||
log: false
|
||||
|
||||
hover:
|
||||
enabled: true
|
||||
format:
|
||||
- "<gray>Игрок: <white>{player}"
|
||||
- "<gray>Мир: <green>{world}"
|
||||
- "<gray>Пинг: <yellow>{ping}ms"
|
||||
- ""
|
||||
- "<yellow>▶ Клик: написать ЛС"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ГРАДИЕНТНЫЕ НИКИ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
# Hub команда
|
||||
hub_command:
|
||||
enabled: true
|
||||
server: "hub"
|
||||
|
||||
# Градиентные ники
|
||||
lopreff:
|
||||
enabled: true
|
||||
use-gradient-name: true
|
||||
|
|
@ -185,107 +34,7 @@ display:
|
|||
update-display-name: true
|
||||
update-tab-list: true
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ДОПОЛНИТЕЛЬНЫЕ ФУНКЦИИ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
mentions:
|
||||
enabled: true
|
||||
sound: true
|
||||
sound-type: "BLOCK_NOTE_BLOCK_PLING"
|
||||
highlight: "&#FFFF00@{player}"
|
||||
self-highlight: "&#FFD700{player}"
|
||||
everyone:
|
||||
enabled: true
|
||||
permission: "lochat.mention.everyone"
|
||||
here:
|
||||
enabled: true
|
||||
permission: "lochat.mention.here"
|
||||
role:
|
||||
enabled: true
|
||||
permission: "lochat.mention.role"
|
||||
|
||||
automessages:
|
||||
enabled: true
|
||||
interval: 300
|
||||
random: false
|
||||
|
||||
announcements:
|
||||
show-title: true
|
||||
title-header: "&#FF6B6BОБЪЯВЛЕНИЕ"
|
||||
show-actionbar: true
|
||||
title-duration: 3
|
||||
|
||||
clearchat:
|
||||
message:
|
||||
enabled: true
|
||||
text: "&#FF6B6BЧат очищен администратором {player}"
|
||||
|
||||
head-emoji:
|
||||
enabled: true
|
||||
allow-player-heads: true
|
||||
max-heads-per-message: 3
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ СИСТЕМА МУТОВ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
mute:
|
||||
default-reason: "Нарушение правил"
|
||||
default-duration: "7d"
|
||||
durations:
|
||||
- "10m"
|
||||
- "30m"
|
||||
- "1h"
|
||||
- "6h"
|
||||
- "12h"
|
||||
- "1d"
|
||||
- "3d"
|
||||
- "7d"
|
||||
- "14d"
|
||||
- "30d"
|
||||
- "perm"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ФИЛЬТРЫ СООБЩЕНИЙ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
filters:
|
||||
caps:
|
||||
enabled: true
|
||||
max-percent: 70
|
||||
min-length: 5
|
||||
auto-lowercase: true
|
||||
|
||||
advertising:
|
||||
enabled: true
|
||||
block-hidden-urls: true
|
||||
block-domains: true
|
||||
whitelist:
|
||||
- "youtube.com"
|
||||
- "youtu.be"
|
||||
- "discord.gg"
|
||||
- "twitch.tv"
|
||||
blacklist:
|
||||
- "scam-site.com"
|
||||
|
||||
ip:
|
||||
enabled: true
|
||||
block: false
|
||||
replacement: "[IP скрыт]"
|
||||
|
||||
repeat:
|
||||
enabled: true
|
||||
max-repeats: 3
|
||||
|
||||
# Форматы (не трогать)
|
||||
formats:
|
||||
announcement: "&#FF6B6B[ОБЪЯВЛЕНИЕ] <message>"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ НАСТРОЙКИ ГРАДИЕНТОВ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
# Градиенты (детали в config/gradient.yml)
|
||||
gradient:
|
||||
storage-type: "file"
|
||||
min-colors: 1
|
||||
|
|
@ -297,66 +46,21 @@ gradient:
|
|||
max-prefix-length: 16
|
||||
color-cooldown: 60
|
||||
prefix-cooldown: 120
|
||||
|
||||
# Форматирование
|
||||
prefix-format: "[{prefix}] "
|
||||
gradient-on-prefix: true
|
||||
gradient-on-luckperms-prefix: true
|
||||
continuous-gradient: true
|
||||
use-legacy-rgb-format: true
|
||||
|
||||
presets:
|
||||
fire: [ "#FF4500", "#FF6347", "#FFD700" ]
|
||||
ocean: [ "#00CED1", "#1E90FF", "#4169E1" ]
|
||||
forest: [ "#228B22", "#32CD32", "#90EE90" ]
|
||||
sunset: [ "#FF69B4", "#FF1493", "#DC143C" ]
|
||||
rainbow: [ "#FF0000", "#FF7F00", "#FFFF00", "#00FF00", "#0000FF", "#4B0082", "#9400D3" ]
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ АВТОСООБЩЕНИЯ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ROLEPLAY НАСТРОЙКИ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
# RP команды (детали в config/rp.yml)
|
||||
rp:
|
||||
# Радиус видимости RP-команд (в блоках)
|
||||
radius: 100
|
||||
|
||||
# Кулдауны для RP-команд (в секундах, 0 = отключено)
|
||||
cooldowns:
|
||||
me: 3
|
||||
try: 5
|
||||
do: 3
|
||||
roll: 2
|
||||
|
||||
# Форматы сообщений (поддерживают MiniMessage + цвета)
|
||||
# {player} — отображаемое имя игрока
|
||||
# {action} — текст действия (/me)
|
||||
# {description} — текст сцены (/do)
|
||||
# {result} — итог (/try)
|
||||
# {max} — максимум кубика, {result} — результат (/roll)
|
||||
me-format: "<gray>* {player} {action}</gray>"
|
||||
try-format: "<gray>* {player} пытается {action}...</gray> {result}"
|
||||
try-success: "<green><bold>[Успех!]</bold></green>"
|
||||
try-fail: "<red><bold>[Провал!]</bold></red>"
|
||||
do-format: "<dark_gray>[<gray>Сцена</gray>]</dark_gray> <white>{description}</white> <dark_gray>({player})</dark_gray>"
|
||||
roll-format: "<gray>* {player} бросает кубик и получает </gray><yellow><bold>{result}</bold></yellow><gray> из </gray><yellow>{max}</yellow>"
|
||||
|
||||
automessages-list:
|
||||
welcome:
|
||||
- "&#FFD700Добро пожаловать на наш сервер!"
|
||||
- "WCEEBИспользуйте /help для получения помощи"
|
||||
|
||||
rules:
|
||||
- "&#FF6B6BСоблюдайте правила сервера!"
|
||||
- "WCEEBПодробнее: /rules"
|
||||
|
||||
discord:
|
||||
- "ᱹDAПрисоединяйтесь к нашему Discord!"
|
||||
- "WCEEBСсылка: discord.gg/example"
|
||||
|
||||
website:
|
||||
- "�FF88Посетите наш сайт!"
|
||||
- "WCEEBАдрес: example.com"
|
||||
# Модерация: после /lmute выполнить команду от консоли (SimpleVoiceChat: voice mute {player})
|
||||
moderation:
|
||||
voice-mute-console-command: ""
|
||||
|
|
|
|||
|
|
@ -1,22 +1,20 @@
|
|||
# ╔══════════════════════════════════════════════════════════════════════════════╗
|
||||
# ║ LoChat Chat Appearance Config ║
|
||||
# ║ Настройки внешнего вида чата ║
|
||||
# ║ Цветовая гамма: Spacelegacy ║
|
||||
# ║ #B798A8 → #7858E9 (розово-фиолетовый) ║
|
||||
# ║ LoChat — config/appearance.yml ║
|
||||
# ║ In-game chat look: prefixes, format, hover, emoji shortcuts, Discord UI ║
|
||||
# ║ Palette (Spacelegacy): #B798A8 (dusty rose) → #7858E9 (violet) ║
|
||||
# ╚══════════════════════════════════════════════════════════════════════════════╝
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ПРЕФИКСЫ ЧАТА │
|
||||
# │ Chat prefixes (gradient label + separator + default message color) │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
#
|
||||
# Градиент: ровно 2 цвета — от и до. Интерполяция плавная между ними.
|
||||
# Вся гамма сервера: светлый #B798A8 (пыльная роза) → тёмный #7858E9 (фиолет)
|
||||
# Gradient: use 2+ hex colors; letters are blended smoothly along the label.
|
||||
# message-color: used when the player does NOT have lochat.chat.colors (see plugin.yml).
|
||||
|
||||
prefixes:
|
||||
global:
|
||||
emoji: "✦"
|
||||
text: "GLOBAL"
|
||||
# 2 цвета — светлый конец гаммы → тёмный
|
||||
colors:
|
||||
- "#B798A8"
|
||||
- "#7858E9"
|
||||
|
|
@ -28,7 +26,6 @@ prefixes:
|
|||
local:
|
||||
emoji: "◆"
|
||||
text: "LOCAL"
|
||||
# Для локала — чуть темнее, акцент на фиолетовом конце
|
||||
colors:
|
||||
- "#9878C9"
|
||||
- "#7858E9"
|
||||
|
|
@ -38,7 +35,28 @@ prefixes:
|
|||
message-color: "#E8E0F0"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ HOVER ЭФФЕКТЫ │
|
||||
# │ Rendered chat line layout │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
#
|
||||
# Placeholders:
|
||||
# {emoji} — prefix emoji (✦ / ◆)
|
||||
# {prefix} — gradient prefix text (GLOBAL / LOCAL)
|
||||
# {separator} — separator (e.g. ▶)
|
||||
# {player_prefix} — LuckPerms / gradient prefix from LoPreff
|
||||
# {player} — display name + hover (click to /msg)
|
||||
# {message} — message body
|
||||
#
|
||||
# Examples:
|
||||
# Default: {emoji} {prefix} {separator} {player} : {message}
|
||||
# Minimal: {player_prefix} {player} ▶ {message}
|
||||
# Custom: ✦ {player_prefix} {player} ▶ {message}
|
||||
|
||||
chat-format:
|
||||
global: "{emoji} {prefix} {separator} {player} : {message}"
|
||||
local: "{emoji} {prefix} {separator} {player} : {message}"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ Hover shown when pointing at the player name in chat │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
hover:
|
||||
|
|
@ -55,7 +73,7 @@ hover:
|
|||
- "&#B798A8✦ &fНажми чтобы написать"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ЭМОДЗИ ЗАМЕНЫ │
|
||||
# │ Text shortcuts → unicode (applied to outgoing chat before URLs / mentions) │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
emojis:
|
||||
|
|
@ -79,7 +97,7 @@ emojis:
|
|||
":shrug:": "¯\\_(ツ)_/¯"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ НАСТРОЙКИ ЧАТА │
|
||||
# │ Radius, length limits, and misc chat UI colors (plugin internals) │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
chat:
|
||||
|
|
@ -95,7 +113,7 @@ chat:
|
|||
info: "#A485BC"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ DISCORD ИНТЕГРАЦИЯ │
|
||||
# │ Discord webhook embeds (see also config/discord.yml for webhooks) │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
discord:
|
||||
|
|
@ -1,255 +0,0 @@
|
|||
# ╔══════════════════════════════════════════════════════════════════════════════╗
|
||||
# ║ LoChat Auto Messages ║
|
||||
# ║ Автосообщения ║
|
||||
# ║ Версия: 1.5.5-1.20.1 ║
|
||||
# ╚══════════════════════════════════════════════════════════════════════════════╝
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ОСНОВНЫЕ НАСТРОЙКИ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
settings:
|
||||
# Включить автосообщения
|
||||
enabled: true
|
||||
|
||||
# Интервал между сообщениями (секунды)
|
||||
interval: 300
|
||||
|
||||
# Случайный порядок сообщений
|
||||
random: true
|
||||
|
||||
# Минимальное количество игроков для отправки
|
||||
min-players: 1
|
||||
|
||||
# Префикс для автосообщений
|
||||
prefix: "�D4FF[Авто]"
|
||||
|
||||
# Отправлять в определенные миры
|
||||
worlds:
|
||||
- "world"
|
||||
- "world_nether"
|
||||
- "world_the_end"
|
||||
|
||||
# Тип отправки: chat, actionbar, title, bossbar
|
||||
type: "chat"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ СООБЩЕНИЯ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
messages:
|
||||
welcome:
|
||||
enabled: true
|
||||
weight: 10 # Вес для случайного выбора (больше = чаще)
|
||||
permission: "" # Пустое = для всех
|
||||
worlds: [] # Пустое = во всех мирах
|
||||
content:
|
||||
- "&#FFD700Добро пожаловать на наш сервер!"
|
||||
- "WCEEBИспользуйте /help для получения помощи"
|
||||
- "BCB77Приятной игры!"
|
||||
|
||||
rules:
|
||||
enabled: true
|
||||
weight: 8
|
||||
permission: ""
|
||||
worlds: []
|
||||
content:
|
||||
- "&#FF6B6BСоблюдайте правила сервера!"
|
||||
- "WCEEBПодробнее: /rules"
|
||||
- "&#FFA726Нарушения караются мутом или баном"
|
||||
|
||||
discord:
|
||||
enabled: true
|
||||
weight: 7
|
||||
permission: ""
|
||||
worlds: []
|
||||
content:
|
||||
- "ᱹDAПрисоединяйтесь к нашему Discord!"
|
||||
- "WCEEBСсылка: discord.gg/example"
|
||||
- "BCB77Общайтесь с игроками вне игры!"
|
||||
|
||||
website:
|
||||
enabled: true
|
||||
weight: 6
|
||||
permission: ""
|
||||
worlds: []
|
||||
content:
|
||||
- "�FF88Посетите наш сайт!"
|
||||
- "WCEEBАдрес: example.com"
|
||||
- "BCB77Карта сервера, статистика и многое другое!"
|
||||
|
||||
donate:
|
||||
enabled: true
|
||||
weight: 5
|
||||
permission: ""
|
||||
worlds: []
|
||||
content:
|
||||
- "&#FFD700Поддержите сервер донатом!"
|
||||
- "WCEEBПолучите VIP статус и привилегии"
|
||||
- "BCB77Подробнее: /donate"
|
||||
|
||||
tips:
|
||||
enabled: true
|
||||
weight: 9
|
||||
permission: ""
|
||||
worlds: []
|
||||
content:
|
||||
- "WCEEB💡 Совет: Используйте /coords для просмотра координат"
|
||||
- "WCEEB💡 Совет: /ignore <игрок> - игнорировать игрока"
|
||||
- "WCEEB💡 Совет: /msg <игрок> <сообщение> - личное сообщение"
|
||||
- "WCEEB💡 Совет: /reply <сообщение> - ответить на ЛС"
|
||||
- "WCEEB💡 Совет: /color <цвет> - изменить цвет ника"
|
||||
|
||||
events:
|
||||
enabled: false
|
||||
weight: 3
|
||||
permission: ""
|
||||
worlds: []
|
||||
content:
|
||||
- "&#FF69B4🎉 Скоро событие на сервере!"
|
||||
- "WCEEBСледите за новостями в Discord"
|
||||
- "BCB77Участвуйте и получайте призы!"
|
||||
|
||||
economy:
|
||||
enabled: false
|
||||
weight: 4
|
||||
permission: ""
|
||||
worlds: []
|
||||
content:
|
||||
- "&#FFD700💰 Зарабатывайте деньги на сервере!"
|
||||
- "WCEEBПродавайте ресурсы в магазине"
|
||||
- "BCB77Команды: /shop, /sell, /balance"
|
||||
|
||||
pvp:
|
||||
enabled: false
|
||||
weight: 2
|
||||
permission: ""
|
||||
worlds: ["world_pvp"]
|
||||
content:
|
||||
- "&#FF6B6B⚔️ Вы в PvP зоне!"
|
||||
- "&#FFA726Будьте осторожны с другими игроками"
|
||||
- "WCEEBИспользуйте /spawn для выхода"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ СПЕЦИАЛЬНЫЕ СООБЩЕНИЯ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
special:
|
||||
# Сообщения для новичков (первый вход)
|
||||
newbie:
|
||||
enabled: true
|
||||
delay: 30 # Задержка после входа (секунды)
|
||||
content:
|
||||
- "BCB77Добро пожаловать на сервер, {player}!"
|
||||
- "WCEEBЭто ваш первый визит. Изучите /help"
|
||||
- "&#FFD700Желаем приятной игры!"
|
||||
|
||||
# Сообщения при входе
|
||||
join:
|
||||
enabled: false
|
||||
delay: 5
|
||||
content:
|
||||
- "BCB77С возвращением, {player}!"
|
||||
- "WCEEBНе забудьте проверить новости: /news"
|
||||
|
||||
# Сообщения при достижениях
|
||||
achievement:
|
||||
enabled: false
|
||||
content:
|
||||
- "&#FFD700🏆 Поздравляем с достижением!"
|
||||
- "WCEEBПродолжайте исследовать мир"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ НАСТРОЙКИ ОТОБРАЖЕНИЯ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
display:
|
||||
# Настройки для title сообщений
|
||||
title:
|
||||
fade-in: 10
|
||||
stay: 40
|
||||
fade-out: 10
|
||||
title: "&#FFD700Информация"
|
||||
subtitle: "{message}"
|
||||
|
||||
# Настройки для actionbar сообщений
|
||||
actionbar:
|
||||
duration: 60 # Время показа (тики)
|
||||
|
||||
# Настройки для bossbar сообщений
|
||||
bossbar:
|
||||
color: "BLUE" # BLUE, GREEN, PINK, PURPLE, RED, WHITE, YELLOW
|
||||
style: "SOLID" # SOLID, SEGMENTED_6, SEGMENTED_10, SEGMENTED_12, SEGMENTED_20
|
||||
duration: 100 # Время показа (тики)
|
||||
|
||||
# Звуковые эффекты
|
||||
sound:
|
||||
enabled: false
|
||||
type: "BLOCK_NOTE_BLOCK_PLING"
|
||||
volume: 0.5
|
||||
pitch: 1.0
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ УСЛОВИЯ ОТПРАВКИ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
conditions:
|
||||
# Время суток (24-часовой формат)
|
||||
time:
|
||||
enabled: false
|
||||
start: "09:00"
|
||||
end: "23:00"
|
||||
|
||||
# Дни недели (1-7, где 1 = понедельник)
|
||||
days:
|
||||
enabled: false
|
||||
allowed: [1, 2, 3, 4, 5, 6, 7]
|
||||
|
||||
# Исключить игроков в AFK
|
||||
exclude-afk: false
|
||||
|
||||
# Исключить игроков в творческом режиме
|
||||
exclude-creative: false
|
||||
|
||||
# Исключить игроков с определенными правами
|
||||
exclude-permissions:
|
||||
- "lochat.automessage.ignore"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ПЕРСОНАЛИЗАЦИЯ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
personalization:
|
||||
# Использовать имя игрока в сообщениях
|
||||
use-player-name: true
|
||||
|
||||
# Адаптировать сообщения под группу игрока
|
||||
group-specific: false
|
||||
|
||||
# Сообщения для разных групп
|
||||
groups:
|
||||
vip:
|
||||
prefix: "&#FFD700[VIP Авто]"
|
||||
messages:
|
||||
- "&#FFD700Спасибо за поддержку сервера, {player}!"
|
||||
- "WCEEBВы получаете эксклюзивные привилегии"
|
||||
|
||||
newbie:
|
||||
prefix: "BCB77[Помощь]"
|
||||
messages:
|
||||
- "BCB77Нужна помощь, {player}? Спросите в чате!"
|
||||
- "WCEEBИспользуйте /help для списка команд"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ СТАТИСТИКА │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
statistics:
|
||||
# Вести статистику отправленных сообщений
|
||||
enabled: true
|
||||
|
||||
# Файл для сохранения статистики
|
||||
file: "plugins/LoChat/automessage-stats.yml"
|
||||
|
||||
# Сбрасывать статистику каждый месяц
|
||||
reset-monthly: true
|
||||
|
|
@ -1,230 +0,0 @@
|
|||
# ╔══════════════════════════════════════════════════════════════════════════════╗
|
||||
# ║ LoChat Beautiful Chat Formats ║
|
||||
# ║ Красивые форматы чата ║
|
||||
# ║ Версия: 1.5.5-1.20.1 ║
|
||||
# ╚══════════════════════════════════════════════════════════════════════════════╝
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ КРАСИВЫЕ ФОРМАТЫ ЧАТА │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
# Настройки отображения чата
|
||||
chat:
|
||||
# Включить красивые префиксы
|
||||
beautiful_prefixes: true
|
||||
|
||||
# Включить hover эффекты на никах
|
||||
hover:
|
||||
enabled: true
|
||||
# Формат hover подсказки (поддерживает MiniMessage)
|
||||
format:
|
||||
- "<gradient:#FFD700:#FFA500><bold>▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬</bold></gradient>"
|
||||
- "<gradient:#87CEEB:#4169E1><bold>👤 Информация об игроке</bold></gradient>"
|
||||
- ""
|
||||
- "<gray>🏷️ Имя: <white><bold>{player}</bold>"
|
||||
- "<gray>📶 Пинг: <yellow><bold>{ping}ms</bold>"
|
||||
- "<gray>❤️ Здоровье: <red><bold>{health}/20</bold>"
|
||||
- "<gray>🍖 Голод: <gold><bold>{food}/20</bold>"
|
||||
- ""
|
||||
- "<gradient:#FFD700:#FFA500><bold>▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬</bold></gradient>"
|
||||
- "<yellow>💬 <bold>Клик:</bold> <gray>написать личное сообщение"
|
||||
|
||||
# Настройки глобального чата
|
||||
global:
|
||||
# Префикс глобального чата
|
||||
prefix:
|
||||
# Использовать эмодзи
|
||||
emoji: "🌍"
|
||||
# Текст с градиентом
|
||||
text: "GLOBAL"
|
||||
# Цвета градиента (от -> к)
|
||||
gradient:
|
||||
from: "#FFD700"
|
||||
to: "#FF4500"
|
||||
|
||||
# Разделитель между префиксом и ником
|
||||
separator:
|
||||
text: " ▶ "
|
||||
color: "#FFD700"
|
||||
|
||||
# Настройки локального чата
|
||||
local:
|
||||
# Префикс локального чата
|
||||
prefix:
|
||||
# Использовать эмодзи
|
||||
emoji: "📍"
|
||||
# Текст с градиентом
|
||||
text: "LOCAL"
|
||||
# Цвета градиента (от -> к)
|
||||
gradient:
|
||||
from: "#87CEEB"
|
||||
to: "#4169E1"
|
||||
|
||||
# Разделитель между префиксом и ником
|
||||
separator:
|
||||
text: " ▶ "
|
||||
color: "#87CEEB"
|
||||
|
||||
# Радиус локального чата (блоки)
|
||||
radius: 100
|
||||
|
||||
# Настройки эмодзи
|
||||
emojis:
|
||||
# Включить обработку эмодзи
|
||||
enabled: true
|
||||
|
||||
# Список доступных эмодзи (символы для Minecraft)
|
||||
list:
|
||||
# Основные
|
||||
":heart:": "♥"
|
||||
":star:": "★"
|
||||
":check:": "✓"
|
||||
":cross:": "✗"
|
||||
":arrow:": "→"
|
||||
":fire:": "▲"
|
||||
":snowflake:": "❄"
|
||||
":sun:": "☀"
|
||||
":moon:": "☽"
|
||||
|
||||
# Minecraft
|
||||
":diamond:": "◆"
|
||||
":sword:": "⚔"
|
||||
":pickaxe:": "⛏"
|
||||
":bow:": "⌐"
|
||||
":shield:": "▣"
|
||||
":potion:": "⚗"
|
||||
":book:": "▤"
|
||||
":crown:": "♔"
|
||||
":emerald:": "◈"
|
||||
":gold:": "◉"
|
||||
":iron:": "◎"
|
||||
|
||||
# Эмоции (текстовые)
|
||||
":thumbsup:": "▲"
|
||||
":thumbsdown:": "▼"
|
||||
":clap:": "※"
|
||||
":wave:": "~"
|
||||
":ok:": "◯"
|
||||
":thinking:": "?"
|
||||
":laugh:": "xD"
|
||||
":cry:": ":'("
|
||||
":rage:": ">:("
|
||||
":cool:": "B)"
|
||||
":wink:": ";)"
|
||||
|
||||
# Настройки URL обработки
|
||||
urls:
|
||||
# Включить кликабельные ссылки
|
||||
enabled: true
|
||||
# Цвет ссылок
|
||||
color: "#00BFFF"
|
||||
# Подчеркивание
|
||||
underlined: true
|
||||
# Hover текст
|
||||
hover_text: "🔗 Открыть ссылку: {url}"
|
||||
|
||||
# Настройки упоминаний (@player)
|
||||
mentions:
|
||||
# Включить упоминания
|
||||
enabled: true
|
||||
# Цвет упоминания
|
||||
color: "#FFD700"
|
||||
# Звук при упоминании
|
||||
sound:
|
||||
enabled: true
|
||||
type: "BLOCK_NOTE_BLOCK_PLING"
|
||||
volume: 1.0
|
||||
pitch: 2.0
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ФОРМАТЫ ПО ГРУППАМ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
groups:
|
||||
owner:
|
||||
priority: 1000
|
||||
prefix: "&#FF0000[OWNER]"
|
||||
suffix: ""
|
||||
name-color: "&#FF0000"
|
||||
|
||||
admin:
|
||||
priority: 900
|
||||
prefix: "&#FF6B6B[ADMIN]"
|
||||
suffix: ""
|
||||
name-color: "&#FF6B6B"
|
||||
|
||||
moderator:
|
||||
priority: 800
|
||||
prefix: "&#FFA726[MOD]"
|
||||
suffix: ""
|
||||
name-color: "&#FFA726"
|
||||
|
||||
helper:
|
||||
priority: 700
|
||||
prefix: "WCEEB[HELPER]"
|
||||
suffix: ""
|
||||
name-color: "WCEEB"
|
||||
|
||||
vip:
|
||||
priority: 600
|
||||
prefix: "&#FFD700[VIP]"
|
||||
suffix: ""
|
||||
name-color: "&#FFD700"
|
||||
|
||||
premium:
|
||||
priority: 500
|
||||
prefix: "BCB77[PREMIUM]"
|
||||
suffix: ""
|
||||
name-color: "BCB77"
|
||||
|
||||
default:
|
||||
priority: 0
|
||||
prefix: ""
|
||||
suffix: ""
|
||||
name-color: "&#FFFFFF"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ СПЕЦИАЛЬНЫЕ ФОРМАТЫ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
special:
|
||||
# Формат для мутов
|
||||
mute:
|
||||
message: "&#FF6B6BВы замучены! Осталось: {time}"
|
||||
permanent: "&#FF6B6BВы замучены навсегда!"
|
||||
|
||||
# Формат для упоминаний
|
||||
mention:
|
||||
format: "&#FFFF00@{player}"
|
||||
sound: "BLOCK_NOTE_BLOCK_PLING"
|
||||
volume: 1.0
|
||||
pitch: 1.0
|
||||
|
||||
# Формат для ссылок
|
||||
url:
|
||||
format: "<click:open_url:'{url}'><hover:show_text:'<gray>🔗 Открыть ссылку'><u><#00BFFF>{url}</u></hover></click>"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ НАСТРОЙКИ ОТОБРАЖЕНИЯ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
display:
|
||||
# Максимальная длина сообщения
|
||||
max-message-length: 256
|
||||
|
||||
# Показывать время в чате
|
||||
show-time: false
|
||||
time-format: "[HH:mm]"
|
||||
|
||||
# Показывать мир в чате
|
||||
show-world: false
|
||||
world-format: "[{world}]"
|
||||
|
||||
# Компактный режим (убирает лишние пробелы)
|
||||
compact-mode: false
|
||||
|
||||
# Автоматически переносить длинные сообщения
|
||||
word-wrap: true
|
||||
|
||||
# Цвет сообщений по умолчанию
|
||||
default-message-color: "&#FFFFFF"
|
||||
|
|
@ -1,346 +0,0 @@
|
|||
# ╔══════════════════════════════════════════════════════════════════════════════╗
|
||||
# ║ LoChat Database Configuration ║
|
||||
# ║ Конфигурация базы данных ║
|
||||
# ║ Версия: 1.5.5-1.20.1 ║
|
||||
# ╚══════════════════════════════════════════════════════════════════════════════╝
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ОСНОВНЫЕ НАСТРОЙКИ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
settings:
|
||||
# Тип базы данных: file, sqlite, mysql, postgresql
|
||||
type: "file"
|
||||
|
||||
# Префикс для таблиц
|
||||
table-prefix: "lochat_"
|
||||
|
||||
# Автоматически создавать таблицы
|
||||
auto-create-tables: true
|
||||
|
||||
# Автоматически обновлять структуру таблиц
|
||||
auto-update-tables: true
|
||||
|
||||
# Кодировка для MySQL
|
||||
charset: "utf8mb4"
|
||||
|
||||
# Collation для MySQL
|
||||
collation: "utf8mb4_unicode_ci"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ФАЙЛОВОЕ ХРАНЕНИЕ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
file:
|
||||
# Папка для хранения файлов данных
|
||||
data-folder: "plugins/LoChat/data/"
|
||||
|
||||
# Формат файлов: yaml, json
|
||||
format: "yaml"
|
||||
|
||||
# Автоматическое сохранение (секунды)
|
||||
auto-save-interval: 300
|
||||
|
||||
# Создавать резервные копии
|
||||
backup:
|
||||
enabled: true
|
||||
interval: 3600 # Каждый час
|
||||
max-backups: 24 # Хранить 24 копии
|
||||
folder: "plugins/LoChat/backups/"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ SQLITE │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
sqlite:
|
||||
# Путь к файлу базы данных
|
||||
file: "plugins/LoChat/database.db"
|
||||
|
||||
# Настройки соединения
|
||||
connection:
|
||||
# Таймаут соединения (миллисекунды)
|
||||
timeout: 30000
|
||||
|
||||
# Включить WAL режим (Write-Ahead Logging)
|
||||
wal-mode: true
|
||||
|
||||
# Размер кэша (KB)
|
||||
cache-size: 2000
|
||||
|
||||
# Синхронизация: OFF, NORMAL, FULL
|
||||
synchronous: "NORMAL"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ MYSQL │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
mysql:
|
||||
# Хост базы данных
|
||||
host: "localhost"
|
||||
|
||||
# Порт базы данных
|
||||
port: 3306
|
||||
|
||||
# Имя базы данных
|
||||
database: "lochat"
|
||||
|
||||
# Имя пользователя
|
||||
username: "root"
|
||||
|
||||
# Пароль
|
||||
password: "password"
|
||||
|
||||
# Дополнительные параметры подключения
|
||||
properties:
|
||||
useSSL: false
|
||||
allowPublicKeyRetrieval: true
|
||||
serverTimezone: "UTC"
|
||||
characterEncoding: "utf8"
|
||||
useUnicode: true
|
||||
autoReconnect: true
|
||||
failOverReadOnly: false
|
||||
maxReconnects: 3
|
||||
initialTimeout: 1
|
||||
|
||||
# Настройки пула соединений (HikariCP)
|
||||
pool:
|
||||
# Максимальное количество соединений
|
||||
maximum-pool-size: 10
|
||||
|
||||
# Минимальное количество простаивающих соединений
|
||||
minimum-idle: 2
|
||||
|
||||
# Таймаут получения соединения (миллисекунды)
|
||||
connection-timeout: 30000
|
||||
|
||||
# Время жизни простаивающего соединения (миллисекунды)
|
||||
idle-timeout: 600000
|
||||
|
||||
# Максимальное время жизни соединения (миллисекунды)
|
||||
max-lifetime: 1800000
|
||||
|
||||
# Интервал проверки соединений (миллисекунды)
|
||||
leak-detection-threshold: 60000
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ POSTGRESQL │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
postgresql:
|
||||
# Хост базы данных
|
||||
host: "localhost"
|
||||
|
||||
# Порт базы данных
|
||||
port: 5432
|
||||
|
||||
# Имя базы данных
|
||||
database: "lochat"
|
||||
|
||||
# Имя пользователя
|
||||
username: "postgres"
|
||||
|
||||
# Пароль
|
||||
password: "password"
|
||||
|
||||
# Схема базы данных
|
||||
schema: "public"
|
||||
|
||||
# Дополнительные параметры подключения
|
||||
properties:
|
||||
ssl: false
|
||||
sslmode: "disable"
|
||||
serverTimezone: "UTC"
|
||||
characterEncoding: "utf8"
|
||||
autoReconnect: true
|
||||
|
||||
# Настройки пула соединений
|
||||
pool:
|
||||
maximum-pool-size: 10
|
||||
minimum-idle: 2
|
||||
connection-timeout: 30000
|
||||
idle-timeout: 600000
|
||||
max-lifetime: 1800000
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ СТРУКТУРА ТАБЛИЦ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
tables:
|
||||
# Таблица игроков
|
||||
players:
|
||||
name: "players"
|
||||
columns:
|
||||
uuid: "VARCHAR(36) PRIMARY KEY"
|
||||
name: "VARCHAR(16) NOT NULL"
|
||||
display_name: "TEXT"
|
||||
first_join: "BIGINT NOT NULL"
|
||||
last_join: "BIGINT NOT NULL"
|
||||
last_quit: "BIGINT"
|
||||
total_playtime: "BIGINT DEFAULT 0"
|
||||
created_at: "TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
|
||||
updated_at: "TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"
|
||||
|
||||
# Таблица градиентов
|
||||
gradients:
|
||||
name: "gradients"
|
||||
columns:
|
||||
uuid: "VARCHAR(36) PRIMARY KEY"
|
||||
colors: "TEXT"
|
||||
prefix: "TEXT"
|
||||
enabled: "BOOLEAN DEFAULT TRUE"
|
||||
created_at: "TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
|
||||
updated_at: "TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"
|
||||
|
||||
# Таблица мутов
|
||||
mutes:
|
||||
name: "mutes"
|
||||
columns:
|
||||
id: "INT AUTO_INCREMENT PRIMARY KEY"
|
||||
uuid: "VARCHAR(36) NOT NULL"
|
||||
moderator_uuid: "VARCHAR(36)"
|
||||
reason: "TEXT NOT NULL"
|
||||
start_time: "BIGINT NOT NULL"
|
||||
end_time: "BIGINT"
|
||||
active: "BOOLEAN DEFAULT TRUE"
|
||||
silent: "BOOLEAN DEFAULT FALSE"
|
||||
created_at: "TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
|
||||
|
||||
# История мутов
|
||||
mute_history:
|
||||
name: "mute_history"
|
||||
columns:
|
||||
id: "INT AUTO_INCREMENT PRIMARY KEY"
|
||||
uuid: "VARCHAR(36) NOT NULL"
|
||||
moderator_uuid: "VARCHAR(36)"
|
||||
reason: "TEXT NOT NULL"
|
||||
duration: "BIGINT NOT NULL"
|
||||
start_time: "BIGINT NOT NULL"
|
||||
end_time: "BIGINT"
|
||||
unmute_reason: "TEXT"
|
||||
unmute_moderator: "VARCHAR(36)"
|
||||
created_at: "TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
|
||||
|
||||
# Таблица игнорирования
|
||||
ignores:
|
||||
name: "ignores"
|
||||
columns:
|
||||
id: "INT AUTO_INCREMENT PRIMARY KEY"
|
||||
player_uuid: "VARCHAR(36) NOT NULL"
|
||||
ignored_uuid: "VARCHAR(36) NOT NULL"
|
||||
created_at: "TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
|
||||
|
||||
# Таблица сообщений (если включено логирование)
|
||||
messages:
|
||||
name: "messages"
|
||||
columns:
|
||||
id: "INT AUTO_INCREMENT PRIMARY KEY"
|
||||
uuid: "VARCHAR(36) NOT NULL"
|
||||
type: "VARCHAR(20) NOT NULL"
|
||||
content: "TEXT NOT NULL"
|
||||
server: "VARCHAR(50)"
|
||||
world: "VARCHAR(50)"
|
||||
created_at: "TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
|
||||
|
||||
# Настройки игроков
|
||||
player_settings:
|
||||
name: "player_settings"
|
||||
columns:
|
||||
uuid: "VARCHAR(36) PRIMARY KEY"
|
||||
sounds_enabled: "BOOLEAN DEFAULT TRUE"
|
||||
sound_volume: "FLOAT DEFAULT 1.0"
|
||||
auto_messages: "BOOLEAN DEFAULT TRUE"
|
||||
mentions_enabled: "BOOLEAN DEFAULT TRUE"
|
||||
pm_sounds: "BOOLEAN DEFAULT TRUE"
|
||||
created_at: "TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
|
||||
updated_at: "TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ИНДЕКСЫ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
indexes:
|
||||
# Индексы для оптимизации запросов
|
||||
players:
|
||||
- "CREATE INDEX idx_players_name ON {prefix}players(name)"
|
||||
- "CREATE INDEX idx_players_last_join ON {prefix}players(last_join)"
|
||||
|
||||
mutes:
|
||||
- "CREATE INDEX idx_mutes_uuid ON {prefix}mutes(uuid)"
|
||||
- "CREATE INDEX idx_mutes_active ON {prefix}mutes(active)"
|
||||
- "CREATE INDEX idx_mutes_end_time ON {prefix}mutes(end_time)"
|
||||
|
||||
mute_history:
|
||||
- "CREATE INDEX idx_mute_history_uuid ON {prefix}mute_history(uuid)"
|
||||
- "CREATE INDEX idx_mute_history_moderator ON {prefix}mute_history(moderator_uuid)"
|
||||
- "CREATE INDEX idx_mute_history_start_time ON {prefix}mute_history(start_time)"
|
||||
|
||||
ignores:
|
||||
- "CREATE INDEX idx_ignores_player ON {prefix}ignores(player_uuid)"
|
||||
- "CREATE INDEX idx_ignores_ignored ON {prefix}ignores(ignored_uuid)"
|
||||
- "CREATE UNIQUE INDEX idx_ignores_unique ON {prefix}ignores(player_uuid, ignored_uuid)"
|
||||
|
||||
messages:
|
||||
- "CREATE INDEX idx_messages_uuid ON {prefix}messages(uuid)"
|
||||
- "CREATE INDEX idx_messages_type ON {prefix}messages(type)"
|
||||
- "CREATE INDEX idx_messages_created_at ON {prefix}messages(created_at)"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ОЧИСТКА ДАННЫХ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
cleanup:
|
||||
# Автоматическая очистка старых данных
|
||||
enabled: true
|
||||
|
||||
# Интервал очистки (секунды)
|
||||
interval: 86400 # Раз в день
|
||||
|
||||
# Настройки очистки для каждой таблицы
|
||||
tables:
|
||||
mute_history:
|
||||
# Удалять записи старше N дней
|
||||
max-age-days: 90
|
||||
# Оставлять минимум записей на игрока
|
||||
min-records-per-player: 5
|
||||
|
||||
messages:
|
||||
max-age-days: 30
|
||||
# Максимальное количество записей
|
||||
max-records: 100000
|
||||
|
||||
players:
|
||||
# Удалять неактивных игроков (дни)
|
||||
inactive-days: 365
|
||||
# Не удалять игроков с определенными данными
|
||||
keep-with-data: true
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ МИГРАЦИИ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
migrations:
|
||||
# Автоматически выполнять миграции
|
||||
auto-migrate: true
|
||||
|
||||
# Создавать резервные копии перед миграцией
|
||||
backup-before-migrate: true
|
||||
|
||||
# Версия схемы базы данных
|
||||
schema-version: 1
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ОТЛАДКА │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
debug:
|
||||
# Логировать SQL запросы
|
||||
log-queries: false
|
||||
|
||||
# Логировать медленные запросы (миллисекунды)
|
||||
slow-query-threshold: 1000
|
||||
|
||||
# Показывать статистику соединений
|
||||
show-connection-stats: false
|
||||
|
||||
# Интервал вывода статистики (секунды)
|
||||
stats-interval: 300
|
||||
|
|
@ -1,243 +0,0 @@
|
|||
# ╔══════════════════════════════════════════════════════════════════════════════╗
|
||||
# ║ LoChat Gradient Configuration ║
|
||||
# ║ Конфигурация градиентов ║
|
||||
# ║ Версия: 1.5.5-1.20.1 ║
|
||||
# ╚══════════════════════════════════════════════════════════════════════════════╝
|
||||
#
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ СИСТЕМА ОПЛАТЫ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
#
|
||||
# Есть два способа контролировать доступ к градиентам:
|
||||
#
|
||||
# 1. ЧЕРЕЗ PLAYERPOINTS (по умолчанию):
|
||||
# - Установите use-permission-instead-of-cost: false
|
||||
# - Игроки платят поинтами за установку градиентов
|
||||
# - price-per-color: стоимость за каждый цвет
|
||||
# - prefix-price: стоимость префикса
|
||||
# - Игроки с правом "gradient.bypass.cost" могут использовать бесплатно
|
||||
#
|
||||
# 2. ЧЕРЕЗ ПЕРМИШЕНЫ (бесплатно):
|
||||
# - Установите use-permission-instead-of-cost: true
|
||||
# - Установите price-per-color: 0 и prefix-price: 0
|
||||
# - Выдайте игрокам право "gradient.bypass.cost" для бесплатного использования
|
||||
# - Или выдайте "gradient.color" для доступа к командам
|
||||
#
|
||||
# ОСНОВНЫЕ ПЕРМИШЕНЫ:
|
||||
# - gradient.color - доступ к команде /color
|
||||
# - gradient.prefix - доступ к команде /prefix
|
||||
# - gradient.bypass.cost - бесплатное использование (обход стоимости)
|
||||
# - gradient.bypass.cooldown - обход кулдауна
|
||||
# - gradient.admin - админские команды
|
||||
#
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ОСНОВНЫЕ НАСТРОЙКИ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
gradient:
|
||||
# Тип хранения данных: file, mysql, sqlite
|
||||
storage-type: "file"
|
||||
|
||||
# Минимальное количество цветов в градиенте
|
||||
min-colors: 1
|
||||
|
||||
# Максимальное количество цветов в градиенте
|
||||
max-colors: 7
|
||||
|
||||
# Стоимость за каждый цвет (PlayerPoints)
|
||||
# Установите 0 для бесплатного использования
|
||||
price-per-color: 50
|
||||
|
||||
# Стоимость префикса
|
||||
# Установите 0 для бесплатного использования
|
||||
prefix-price: 500
|
||||
|
||||
# Одноразовая покупка префикса (true) или за каждое изменение (false)
|
||||
prefix-one-time-purchase: true
|
||||
|
||||
# Использовать пермишены вместо поинтов
|
||||
# Если true, игроки с правом gradient.bypass.cost могут использовать бесплатно
|
||||
# Если false, используется система PlayerPoints
|
||||
use-permission-instead-of-cost: false
|
||||
|
||||
# Максимальная длина префикса
|
||||
max-prefix-length: 16
|
||||
|
||||
# Кулдаун на смену цвета (секунды)
|
||||
color-cooldown: 60
|
||||
|
||||
# Кулдаун на смену префикса (секунды)
|
||||
prefix-cooldown: 120
|
||||
|
||||
# Автоматически применять градиент к нику
|
||||
auto-apply-gradient: true
|
||||
|
||||
# Обновлять отображаемое имя
|
||||
update-display-name: true
|
||||
|
||||
# Обновлять таб-лист
|
||||
update-tab-list: true
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ГОТОВЫЕ ПРЕСЕТЫ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
presets:
|
||||
fire:
|
||||
name: "Огонь"
|
||||
colors: [ "#FF4500", "#FF6347", "#FFD700" ]
|
||||
permission: "gradient.preset.fire"
|
||||
price: 100
|
||||
|
||||
ocean:
|
||||
name: "Океан"
|
||||
colors: [ "#00CED1", "#1E90FF", "#4169E1" ]
|
||||
permission: "gradient.preset.ocean"
|
||||
price: 100
|
||||
|
||||
forest:
|
||||
name: "Лес"
|
||||
colors: [ "#228B22", "#32CD32", "#90EE90" ]
|
||||
permission: "gradient.preset.forest"
|
||||
price: 100
|
||||
|
||||
sunset:
|
||||
name: "Закат"
|
||||
colors: [ "#FF69B4", "#FF1493", "#DC143C" ]
|
||||
permission: "gradient.preset.sunset"
|
||||
price: 150
|
||||
|
||||
rainbow:
|
||||
name: "Радуга"
|
||||
colors: [ "#FF0000", "#FF7F00", "#FFFF00", "#00FF00", "#0000FF", "#4B0082", "#9400D3" ]
|
||||
permission: "gradient.preset.rainbow"
|
||||
price: 300
|
||||
|
||||
gold:
|
||||
name: "Золото"
|
||||
colors: [ "#FFD700", "#FFA500", "#FF8C00" ]
|
||||
permission: "gradient.preset.gold"
|
||||
price: 200
|
||||
|
||||
silver:
|
||||
name: "Серебро"
|
||||
colors: [ "#C0C0C0", "#A9A9A9", "#808080" ]
|
||||
permission: "gradient.preset.silver"
|
||||
price: 150
|
||||
|
||||
purple:
|
||||
name: "Фиолетовый"
|
||||
colors: [ "#9400D3", "#8A2BE2", "#DA70D6" ]
|
||||
permission: "gradient.preset.purple"
|
||||
price: 120
|
||||
|
||||
ice:
|
||||
name: "Лёд"
|
||||
colors: [ "#B0E0E6", "#87CEEB", "#4682B4" ]
|
||||
permission: "gradient.preset.ice"
|
||||
price: 120
|
||||
|
||||
lava:
|
||||
name: "Лава"
|
||||
colors: [ "#DC143C", "#B22222", "#8B0000" ]
|
||||
permission: "gradient.preset.lava"
|
||||
price: 150
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ НАСТРОЙКИ БАЗЫ ДАННЫХ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
database:
|
||||
mysql:
|
||||
host: "localhost"
|
||||
port: 3306
|
||||
database: "lochat"
|
||||
username: "root"
|
||||
password: "password"
|
||||
table-prefix: "lochat_"
|
||||
|
||||
# Настройки пула соединений
|
||||
pool:
|
||||
maximum-pool-size: 10
|
||||
minimum-idle: 5
|
||||
connection-timeout: 30000
|
||||
idle-timeout: 600000
|
||||
max-lifetime: 1800000
|
||||
|
||||
sqlite:
|
||||
file: "plugins/LoChat/gradient-data.db"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ДОПОЛНИТЕЛЬНЫЕ ФУНКЦИИ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
features:
|
||||
# Анимированные градиенты
|
||||
animated-gradients:
|
||||
enabled: false
|
||||
speed: 1000 # миллисекунды
|
||||
|
||||
# Случайные градиенты
|
||||
random-gradients:
|
||||
enabled: true
|
||||
change-interval: 3600 # секунды (1 час)
|
||||
|
||||
# Градиенты в чате
|
||||
chat-gradients:
|
||||
enabled: true
|
||||
apply-to-messages: true
|
||||
|
||||
# Градиенты в табе
|
||||
tab-gradients:
|
||||
enabled: true
|
||||
update-interval: 20 # тики
|
||||
|
||||
# Сохранение градиентов при выходе
|
||||
save-on-quit: true
|
||||
|
||||
# Восстановление градиентов при входе
|
||||
restore-on-join: true
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ПРАВА ДОСТУПА │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
permissions:
|
||||
# Базовые права
|
||||
use-gradient: "gradient.use" # Право использовать градиенты
|
||||
use-color: "gradient.color" # Право использовать команду /color
|
||||
use-prefix: "gradient.prefix" # Право использовать команду /prefix
|
||||
admin: "gradient.admin" # Админские права
|
||||
|
||||
# Обход ограничений
|
||||
bypass-cooldown: "gradient.bypass.cooldown" # Обход кулдауна
|
||||
bypass-cost: "gradient.bypass.cost" # Бесплатное использование (обход стоимости)
|
||||
bypass-length: "gradient.bypass.length" # Обход ограничения длины префикса
|
||||
|
||||
# Дополнительные цвета
|
||||
# Игроки с этими правами могут использовать больше цветов
|
||||
extra-colors:
|
||||
"gradient.colors.5": 5
|
||||
"gradient.colors.10": 10
|
||||
"gradient.colors.15": 15
|
||||
"gradient.colors.unlimited": 999
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ФОРМАТИРОВАНИЕ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
formatting:
|
||||
# Формат ника с градиентом
|
||||
nick-format: "{gradient}{nick}"
|
||||
|
||||
# Формат префикса с градиентом
|
||||
prefix-format: "{gradient}{prefix}"
|
||||
|
||||
# Формат в табе
|
||||
tab-format: "{prefix} {gradient}{nick}"
|
||||
|
||||
# Формат в чате
|
||||
chat-format: "{prefix} {gradient}{nick}&r: {message}"
|
||||
|
||||
# Разделитель между префиксом и ником
|
||||
separator: " "
|
||||
|
|
@ -1,380 +0,0 @@
|
|||
# ╔══════════════════════════════════════════════════════════════════════════════╗
|
||||
# ║ LoChat Integrations ║
|
||||
# ║ Интеграции ║
|
||||
# ║ Версия: 1.5.5-1.20.1 ║
|
||||
# ╚══════════════════════════════════════════════════════════════════════════════╝
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ PLACEHOLDERAPI │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
placeholderapi:
|
||||
# Включить интеграцию с PlaceholderAPI
|
||||
enabled: true
|
||||
|
||||
# Регистрировать собственные плейсхолдеры
|
||||
register-placeholders: true
|
||||
|
||||
# Доступные плейсхолдеры LoChat:
|
||||
# %lochat_player_muted% - замучен ли игрок (true/false)
|
||||
# %lochat_player_mute_time% - оставшееся время мута
|
||||
# %lochat_player_mute_reason% - причина мута
|
||||
# %lochat_player_gradient% - градиентный ник
|
||||
# %lochat_player_prefix% - кастомный префикс
|
||||
# %lochat_player_ignored_count% - количество игнорируемых игроков
|
||||
# %lochat_player_messages_sent% - отправлено сообщений
|
||||
# %lochat_player_last_message% - последнее сообщение
|
||||
# %lochat_server_muted_count% - количество замученных игроков
|
||||
# %lochat_server_messages_today% - сообщений сегодня
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ LUCKPERMS │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
luckperms:
|
||||
# Включить интеграцию с LuckPerms
|
||||
enabled: true
|
||||
|
||||
# Использовать префиксы из LuckPerms
|
||||
use-prefix: true
|
||||
|
||||
# Использовать суффиксы из LuckPerms
|
||||
use-suffix: true
|
||||
|
||||
# Использовать мета-данные из LuckPerms
|
||||
use-meta: true
|
||||
|
||||
# Приоритет групп для форматирования чата
|
||||
group-priority: true
|
||||
|
||||
# Кэшировать данные LuckPerms
|
||||
cache-data: true
|
||||
|
||||
# Время кэширования (секунды)
|
||||
cache-duration: 300
|
||||
|
||||
# Синхронизировать изменения прав
|
||||
sync-changes: true
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ VAULT │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
vault:
|
||||
# Включить интеграцию с Vault
|
||||
enabled: true
|
||||
|
||||
# Использовать Vault для прав
|
||||
use-permissions: true
|
||||
|
||||
# Использовать Vault для экономики
|
||||
use-economy: true
|
||||
|
||||
# Использовать Vault для чата (префиксы/суффиксы)
|
||||
use-chat: true
|
||||
|
||||
# Приоритет Vault над другими системами
|
||||
priority: 1
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ PLAYERPOINTS │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
playerpoints:
|
||||
# Включить интеграцию с PlayerPoints
|
||||
enabled: true
|
||||
|
||||
# Использовать поинты для покупки градиентов
|
||||
use-for-gradients: true
|
||||
|
||||
# Использовать поинты для покупки префиксов
|
||||
use-for-prefixes: true
|
||||
|
||||
# Автоматически списывать поинты
|
||||
auto-deduct: true
|
||||
|
||||
# Уведомлять о списании поинтов
|
||||
notify-deduction: true
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ LIBERTYBANS │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
libertybans:
|
||||
# Включить интеграцию с LibertyBans
|
||||
enabled: false
|
||||
|
||||
# Синхронизировать муты с LibertyBans
|
||||
sync-mutes: true
|
||||
|
||||
# Использовать LibertyBans для мутов вместо встроенной системы
|
||||
use-for-mutes: false
|
||||
|
||||
# Импортировать существующие муты из LibertyBans
|
||||
import-existing: false
|
||||
|
||||
# Экспортировать муты в LibertyBans
|
||||
export-mutes: true
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ SKINSRESTORER │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
skinsrestorer:
|
||||
# Включить интеграцию с SkinsRestorer
|
||||
enabled: true
|
||||
|
||||
# Обновлять отображение при смене скина
|
||||
update-on-skin-change: true
|
||||
|
||||
# Использовать скины в head emoji
|
||||
use-in-head-emoji: true
|
||||
|
||||
# Кэшировать данные скинов
|
||||
cache-skins: true
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ DISCORDSRV │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
discordsrv:
|
||||
# Включить интеграцию с DiscordSRV
|
||||
enabled: false
|
||||
|
||||
# Отправлять сообщения чата в Discord
|
||||
send-chat-to-discord: true
|
||||
|
||||
# Отправлять сообщения из Discord в игру
|
||||
send-discord-to-chat: true
|
||||
|
||||
# Канал для глобального чата
|
||||
global-chat-channel: "global-chat"
|
||||
|
||||
# Канал для локального чата
|
||||
local-chat-channel: "local-chat"
|
||||
|
||||
# Канал для объявлений
|
||||
announcement-channel: "announcements"
|
||||
|
||||
# Формат сообщений в Discord
|
||||
discord-format: "**{player}**: {message}"
|
||||
|
||||
# Формат сообщений из Discord
|
||||
minecraft-format: "&7[Discord] &b{user}&7: {message}"
|
||||
|
||||
# Использовать градиентные ники в Discord
|
||||
use-gradients-in-discord: false
|
||||
|
||||
# Webhook для отправки сообщений
|
||||
webhook:
|
||||
enabled: false
|
||||
url: ""
|
||||
username: "LoChat"
|
||||
avatar-url: ""
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ DYNMAP │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
dynmap:
|
||||
# Включить интеграцию с Dynmap
|
||||
enabled: false
|
||||
|
||||
# Отправлять сообщения чата на веб-карту
|
||||
send-chat-to-web: true
|
||||
|
||||
# Отправлять сообщения с веб-карты в игру
|
||||
send-web-to-chat: true
|
||||
|
||||
# Формат сообщений на веб-карте
|
||||
web-format: "{player}: {message}"
|
||||
|
||||
# Формат сообщений с веб-карты
|
||||
minecraft-format: "&7[Web] &a{player}&7: {message}"
|
||||
|
||||
# Использовать градиентные ники на веб-карте
|
||||
use-gradients-on-web: false
|
||||
|
||||
# Фильтровать сообщения для веб-карты
|
||||
filter-messages: true
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ BLUEMAP │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
bluemap:
|
||||
# Включить интеграцию с BlueMap
|
||||
enabled: false
|
||||
|
||||
# Отправлять сообщения чата на карту
|
||||
send-chat-to-map: true
|
||||
|
||||
# Формат сообщений на карте
|
||||
map-format: "{player}: {message}"
|
||||
|
||||
# Использовать градиентные ники на карте
|
||||
use-gradients-on-map: false
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ PLAN │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
plan:
|
||||
# Включить интеграцию с Plan
|
||||
enabled: false
|
||||
|
||||
# Отправлять статистику чата в Plan
|
||||
send-chat-stats: true
|
||||
|
||||
# Отправлять статистику мутов в Plan
|
||||
send-mute-stats: true
|
||||
|
||||
# Интервал отправки статистики (секунды)
|
||||
stats-interval: 300
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ COREPROTECT │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
coreprotect:
|
||||
# Включить интеграцию с CoreProtect
|
||||
enabled: false
|
||||
|
||||
# Логировать сообщения чата
|
||||
log-chat-messages: true
|
||||
|
||||
# Логировать команды
|
||||
log-commands: true
|
||||
|
||||
# Логировать муты
|
||||
log-mutes: true
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ AUTHME │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
authme:
|
||||
# Включить интеграцию с AuthMe
|
||||
enabled: false
|
||||
|
||||
# Блокировать чат для неавторизованных игроков
|
||||
block-chat-for-unregistered: true
|
||||
|
||||
# Сообщение для неавторизованных игроков
|
||||
unregistered-message: "&#FF6B6BВойдите в аккаунт для использования чата!"
|
||||
|
||||
# Разрешенные команды для неавторизованных
|
||||
allowed-commands:
|
||||
- "/login"
|
||||
- "/register"
|
||||
- "/l"
|
||||
- "/reg"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ TOWNY │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
towny:
|
||||
# Включить интеграцию с Towny
|
||||
enabled: false
|
||||
|
||||
# Чат города
|
||||
town-chat:
|
||||
enabled: true
|
||||
format: "&6[Town] {prefix}{player}&r: {message}"
|
||||
command: "/tc"
|
||||
|
||||
# Чат нации
|
||||
nation-chat:
|
||||
enabled: true
|
||||
format: "&5[Nation] {prefix}{player}&r: {message}"
|
||||
command: "/nc"
|
||||
|
||||
# Показывать город в чате
|
||||
show-town-in-chat: true
|
||||
|
||||
# Формат города в чате
|
||||
town-format: "&7[{town}]"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ FACTIONS │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
factions:
|
||||
# Включить интеграцию с Factions
|
||||
enabled: false
|
||||
|
||||
# Чат фракции
|
||||
faction-chat:
|
||||
enabled: true
|
||||
format: "&c[Faction] {prefix}{player}&r: {message}"
|
||||
command: "/fc"
|
||||
|
||||
# Чат альянса
|
||||
alliance-chat:
|
||||
enabled: true
|
||||
format: "&a[Alliance] {prefix}{player}&r: {message}"
|
||||
command: "/ac"
|
||||
|
||||
# Показывать фракцию в чате
|
||||
show-faction-in-chat: true
|
||||
|
||||
# Формат фракции в чате
|
||||
faction-format: "&7[{faction}]"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ MULTIVERSE │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
multiverse:
|
||||
# Включить интеграцию с Multiverse
|
||||
enabled: false
|
||||
|
||||
# Разделять чат по мирам
|
||||
separate-chat-by-world: false
|
||||
|
||||
# Показывать мир в чате
|
||||
show-world-in-chat: true
|
||||
|
||||
# Формат мира в чате
|
||||
world-format: "&7[{world}]"
|
||||
|
||||
# Настройки чата для конкретных миров
|
||||
world-settings:
|
||||
world_nether:
|
||||
chat-color: "&c"
|
||||
prefix: "&4[Nether]"
|
||||
|
||||
world_the_end:
|
||||
chat-color: "&5"
|
||||
prefix: "&d[End]"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ОБЩИЕ НАСТРОЙКИ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
general:
|
||||
# Автоматически определять доступные плагины
|
||||
auto-detect-plugins: true
|
||||
|
||||
# Интервал проверки плагинов (секунды)
|
||||
plugin-check-interval: 60
|
||||
|
||||
# Логировать интеграции
|
||||
log-integrations: true
|
||||
|
||||
# Уведомлять о подключении/отключении интеграций
|
||||
notify-integration-changes: true
|
||||
|
||||
# Приоритет интеграций (чем выше число, тем выше приоритет)
|
||||
integration-priority:
|
||||
luckperms: 100
|
||||
vault: 90
|
||||
placeholderapi: 80
|
||||
playerpoints: 70
|
||||
libertybans: 60
|
||||
skinsrestorer: 50
|
||||
discordsrv: 40
|
||||
dynmap: 30
|
||||
plan: 20
|
||||
coreprotect: 10
|
||||
|
|
@ -116,3 +116,22 @@ general:
|
|||
disabled: "&#B798A8Отключено"
|
||||
online: "⚖C9В сети"
|
||||
offline: "&#B798A8Не в сети"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ВАРНЫ / БАНЫ (LoChat) │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
moderation:
|
||||
default_reason: "Без причины"
|
||||
duration_permanent: "навсегда"
|
||||
warn_received: "&#CF6679⚠ Вам выдано предупреждение! &#B798A8Причина: ẲE9{reason}"
|
||||
warn_staff_confirm: "⚖C9Игроку ẲE9{player} ⚖C9выдан варн. &#B798A8Причина: ẲE9{reason}"
|
||||
silent_warn_staff: "&#B798A8[⚖C9ТИХИЙ ВАРН&#B798A8] ẲE9{player}⚖C9: &#B798A8{reason}"
|
||||
ban_usage: "&#B798A8Использование: ẲE9/lban <ник> [время|perm] [причина]"
|
||||
ban_confirm: "⚖C9Игрок ẲE9{player} ⚖C9забанен. &#B798A8Срок: ẲE9{duration}"
|
||||
unban_confirm: "⚖C9Бан снят с ẲE9{player}"
|
||||
not_banned: "&#CF6679Игрок не забанен!"
|
||||
already_banned: "&#CF6679Игрок уже забанен!"
|
||||
warn_usage: "&#B798A8Использование: ẲE9/warn <ник> [причина]"
|
||||
silent_warn_usage: "&#B798A8Использование: ẲE9/silentwarn <ник> [причина]"
|
||||
ban_kick_message: "<gradient:#FF5555:#8B0000><bold>═══════ ДОСТУП ЗАПРЕЩЁН ═══════</bold></gradient>\n<gray>Вы заблокированы на этом сервере.</gray>\n<white>Причина: <gradient:#7858E9:#B798A8>{reason}</gradient></white>\n<gray>Модератор: <white>{moderator}</white></gray>\n<gray>Срок: <#7858E9>{duration}</#7858E9></gray>\n<gradient:#FF5555:#8B0000><bold>════════════════════════</bold></gradient>"
|
||||
|
|
@ -1,20 +1,19 @@
|
|||
# ╔══════════════════════════════════════════════════════════════════════════════╗
|
||||
# ║ LoChat Messages ║
|
||||
# ║ Цветовая гамма: Spacelegacy ║
|
||||
# ║ #B798A8 (роза) → #7858E9 (фиолет) ║
|
||||
# ║ LoChat — messages.yml ║
|
||||
# ║ Default palette (Spacelegacy): #B798A8 (dusty rose) → #7858E9 (violet) ║
|
||||
# ╚══════════════════════════════════════════════════════════════════════════════╝
|
||||
|
||||
# Роли цветов:
|
||||
# &#B798A8 — основной текст / лейблы (пыльная роза, светлый)
|
||||
# ⚖C9 — акцент / разделители (средний фиолет)
|
||||
# ẲE9 — важные данные / значения (насыщенный фиолет, тёмный)
|
||||
# &f / #E8E0F0 — белый текст сообщений
|
||||
# &#CF6679 — ошибки (приглушённый красный, вписывается в гамму)
|
||||
#
|
||||
# Color roles (reference when editing strings below):
|
||||
# &#B798A8 — primary labels / body (dusty rose)
|
||||
# ⚖C9 — accents / separators (mid violet)
|
||||
# ẲE9 — emphasis / values (deep violet)
|
||||
# &f / #E8E0F0 — message body white
|
||||
# &#CF6679 — errors (muted red, fits the palette)
|
||||
|
||||
prefix: "&#B798A8[⚖C9LB6BD6oẲE9CⅵDChB6BD6a⚖C9t&#B798A8]"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ГЛОБАЛЬНЫЙ ЧАТ │
|
||||
# │ Global chat (toggle messages, server-disabled notice) │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
global:
|
||||
|
|
@ -23,7 +22,7 @@ global:
|
|||
disabled: "&#CF6679Глобальный чат отключён на этом сервере"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ЛОКАЛЬНЫЙ ЧАТ │
|
||||
# │ Local chat (no recipients nearby) │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
local:
|
||||
|
|
@ -31,7 +30,7 @@ local:
|
|||
nobody-heard: "⚖C9Вас никто не услышал &#B798A8— рядом нет игроков"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ЛИЧНЫЕ СООБЩЕНИЯ │
|
||||
# │ Private messages (/msg, /reply) │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
pm:
|
||||
|
|
@ -46,7 +45,7 @@ pm:
|
|||
self: "&#CF6679Нельзя писать самому себе"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ИГНОРИРОВАНИЕ │
|
||||
# │ Ignore list │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
ignore:
|
||||
|
|
@ -60,14 +59,14 @@ ignore:
|
|||
list-footer: "&#B798A8Всего: ẲE9{count} &#B798A8игроков"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ОБЪЯВЛЕНИЯ │
|
||||
# │ Announcements │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
announce:
|
||||
reloaded: "⚖C9Конфигурация ẲE9перезагружена"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ШПИОН РЕЖИМ │
|
||||
# │ Chat spy (moderation) │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
spy:
|
||||
|
|
@ -77,7 +76,7 @@ spy:
|
|||
chat-format: "&#B798A8[⚖C9SPY&#B798A8] &#B798A8{type} ẲE9{sender}&#B798A8: &f{message}"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ФИЛЬТРЫ │
|
||||
# │ Message filters │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
filter:
|
||||
|
|
@ -85,14 +84,14 @@ filter:
|
|||
warning: "&#B798A8Предупреждение"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ КУЛДАУНЫ │
|
||||
# │ Cooldowns │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
cooldown:
|
||||
wait: "&#B798A8Подождите ẲE9{time} &#B798A8сек"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ АНТИ-СПАМ │
|
||||
# │ Anti-spam (caps, repeat, similar messages) │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
antispam:
|
||||
|
|
@ -101,14 +100,14 @@ antispam:
|
|||
similar: "&#CF6679Не отправляйте одинаковые сообщения"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ОЧИСТКА ЧАТА │
|
||||
# │ Clear chat │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
clearchat:
|
||||
cleared: "⚖C9Чат очищен &#B798A8администратором ẲE9{player}"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ОШИБКИ │
|
||||
# │ Command / usage errors │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
errors:
|
||||
|
|
@ -117,7 +116,7 @@ errors:
|
|||
invalid-usage: "&#B798A8Использование: ẲE9{usage}"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ОБЩИЕ СООБЩЕНИЯ │
|
||||
# │ General (permissions, reload, hub, etc.) │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
general:
|
||||
|
|
@ -132,7 +131,7 @@ hub:
|
|||
connecting: "⚖C9Подключение к ẲE9{server}⚖C9..."
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ СИСТЕМА МУТОВ │
|
||||
# │ Mutes │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
mute:
|
||||
|
|
@ -144,7 +143,7 @@ mute:
|
|||
permanent: "&#CF6679Вы замучены навсегда!"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ГРАДИЕНТНЫЕ НИКИ │
|
||||
# │ Gradient nick module (LoPreff) │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
gradient:
|
||||
|
|
@ -191,7 +190,7 @@ gradient:
|
|||
info-prefix-purchased: "&#B798A8Куплен: &f{purchased}"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ КАСТОМНЫЕ НИКИ │
|
||||
# │ Custom nicknames (/nick) │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
nick:
|
||||
|
|
@ -211,7 +210,7 @@ nick:
|
|||
error: "&#CF6679Не удалось установить ник!"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ROLEPLAY КОМАНДЫ │
|
||||
# │ Roleplay commands (/me, /try, /do, /roll) │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
rp:
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ commands:
|
|||
description: Send private message
|
||||
usage: /msg <player> <message>
|
||||
permission: chat.pm.use
|
||||
aliases: [ tell, w, pm, m, ьып, ееддж, цж, зьж, ьж ]
|
||||
aliases: [ tell, pm, m, ьып, ееддж, цж, зьж, ьж ]
|
||||
|
||||
reply:
|
||||
description: Reply to last message
|
||||
|
|
@ -146,6 +146,36 @@ commands:
|
|||
permission: lochat.muteblame
|
||||
aliases: [ muteblame, мутблейм, ьгеуидфьу ]
|
||||
|
||||
warn:
|
||||
description: Warn a player (visible to them)
|
||||
usage: /warn <player> [reason]
|
||||
permission: lochat.warn
|
||||
aliases: [ w, варн ]
|
||||
|
||||
silentwarn:
|
||||
description: Silent warn (logged; staff with lochat.warn.notify see)
|
||||
usage: /silentwarn <player> [reason]
|
||||
permission: lochat.warn.silent
|
||||
aliases: [ siw ]
|
||||
|
||||
lban:
|
||||
description: Ban a player
|
||||
usage: /lban <player> [time|perm] [reason]
|
||||
permission: lochat.ban
|
||||
aliases: [ ban, бан ]
|
||||
|
||||
lunban:
|
||||
description: Unban a player
|
||||
usage: /lunban <player>
|
||||
permission: lochat.unban
|
||||
aliases: [ unban, разбан ]
|
||||
|
||||
playerinfo:
|
||||
description: Player info and moderation stats
|
||||
usage: /playerinfo <player>
|
||||
permission: lochat.playerinfo
|
||||
aliases: [ pi, инфо ]
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# КАСТОМНЫЕ КОМАНДЫ
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
|
@ -284,6 +314,34 @@ permissions:
|
|||
lochat.mute.dur.perm:
|
||||
description: Can mute permanently
|
||||
default: op
|
||||
|
||||
lochat.warn:
|
||||
description: Issue a visible warning
|
||||
default: op
|
||||
lochat.warn.silent:
|
||||
description: Issue a silent warning
|
||||
default: op
|
||||
lochat.warn.notify:
|
||||
description: See silent warn notifications
|
||||
default: op
|
||||
lochat.ban:
|
||||
description: Ban players
|
||||
default: op
|
||||
lochat.ban.override:
|
||||
description: Ban players with lochat.bypass.ban
|
||||
default: op
|
||||
lochat.unban:
|
||||
description: Unban players
|
||||
default: op
|
||||
lochat.bypass.warn:
|
||||
description: Immune to /warn
|
||||
default: false
|
||||
lochat.bypass.ban:
|
||||
description: Immune to /lban (unless staff has lochat.ban.override)
|
||||
default: false
|
||||
lochat.playerinfo:
|
||||
description: View player moderation stats
|
||||
default: op
|
||||
lochat.clearchat.config:
|
||||
description: Can configure clear chat message
|
||||
default: op
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue