feat(lohub): Add complete configuration system with 6 config files

- Added messages.yml for all plugin messages with customizable prefix
- Added commands.yml for custom commands (discord, website, store, rules, heal, clearinventory)
- Added serverselector.yml for GUI server selector menu
- Added data.yml for persistent data storage (chat_locked, spawn location)
- Updated ConfigManager to load all config files
- Updated ChatLockManager to use data.yml instead of chatlock.dat
- Updated SpawnManager to use data.yml instead of spawn.yml
- Updated all commands to use messages.yml
- Updated listeners to use messages.yml
- Removed duplicate config files from root directory
- Added CONFIG_GUIDE.md documentation
This commit is contained in:
loki5512344 2026-02-24 13:27:18 +01:00
parent 102341bb2c
commit 1e5e8d8638
36 changed files with 1580 additions and 544 deletions

View file

@ -5,7 +5,9 @@ import com.loki.lohub.listeners.HubProtectionListener;
import com.loki.lohub.listeners.PlayerJoinListener;
import com.loki.lohub.managers.ActionManager;
import com.loki.lohub.managers.AnnouncementManager;
import com.loki.lohub.managers.ChatLockManager;
import com.loki.lohub.managers.CooldownManager;
import com.loki.lohub.managers.CustomCommandManager;
import com.loki.lohub.managers.HotbarManager;
import com.loki.lohub.managers.PlayerHiderManager;
import com.loki.lohub.managers.RegionManager;
@ -27,6 +29,8 @@ public class LoHub extends JavaPlugin {
private ScoreboardManager scoreboardManager;
private TablistManager tablistManager;
private AnnouncementManager announcementManager;
private ChatLockManager chatLockManager;
private CustomCommandManager customCommandManager;
@Override
public void onEnable() {
@ -46,6 +50,8 @@ public class LoHub extends JavaPlugin {
this.scoreboardManager = new ScoreboardManager(this);
this.tablistManager = new TablistManager(this);
this.announcementManager = new AnnouncementManager(this);
this.chatLockManager = new ChatLockManager(this);
this.customCommandManager = new CustomCommandManager(this);
registerListeners();
registerCommands();
@ -73,12 +79,15 @@ public class LoHub extends JavaPlugin {
getServer().getPluginManager().registerEvents(new com.loki.lohub.listeners.HotbarListener(this), this);
getServer().getPluginManager().registerEvents(new com.loki.lohub.listeners.LaunchpadListener(this), this);
getServer().getPluginManager().registerEvents(new com.loki.lohub.listeners.DoubleJumpListener(this), this);
getServer().getPluginManager().registerEvents(new com.loki.lohub.listeners.ChatLockListener(this), this);
getServer().getPluginManager().registerEvents(new com.loki.lohub.listeners.CustomCommandListener(this), this);
}
private void registerCommands() {
getCommand("hub").setExecutor(new com.loki.lohub.commands.HubCommand(this));
getCommand("sethub").setExecutor(new com.loki.lohub.commands.SetHubCommand(this));
getCommand("lohub").setExecutor(new com.loki.lohub.commands.LoHubCommand(this));
getCommand("chatlock").setExecutor(new com.loki.lohub.commands.ChatLockCommand(this));
}
public ConfigManager getConfigManager() {
@ -120,4 +129,12 @@ public class LoHub extends JavaPlugin {
public AnnouncementManager getAnnouncementManager() {
return announcementManager;
}
public ChatLockManager getChatLockManager() {
return chatLockManager;
}
public CustomCommandManager getCustomCommandManager() {
return customCommandManager;
}
}

View file

@ -0,0 +1,36 @@
package com.loki.lohub.commands;
import com.loki.lohub.LoHub;
import com.loki.lohub.utils.TextUtil;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public class ChatLockCommand implements CommandExecutor {
private final LoHub plugin;
public ChatLockCommand(LoHub plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command,
@NotNull String label, @NotNull String[] args) {
if (!sender.hasPermission("lohub.chatlock")) {
sender.sendMessage(TextUtil.colorize(plugin.getConfigManager().getMessage("general.no-permission")));
return true;
}
plugin.getChatLockManager().toggle();
boolean locked = plugin.getChatLockManager().isChatLocked();
String message = locked
? plugin.getConfigManager().getMessage("chat.lock-enabled")
: plugin.getConfigManager().getMessage("chat.lock-disabled");
sender.sendMessage(TextUtil.colorize(message));
return true;
}
}

View file

@ -18,17 +18,17 @@ public class HubCommand implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player player)) {
sender.sendMessage(TextUtil.colorize("&cThis command is only for players!"));
sender.sendMessage(TextUtil.colorize(plugin.getConfigManager().getMessage("general.players-only")));
return true;
}
if (plugin.getSpawnManager().getSpawn() == null) {
player.sendMessage(TextUtil.colorize(plugin.getConfig().getString("messages.no-spawn")));
player.sendMessage(TextUtil.colorize(plugin.getConfigManager().getMessage("spawn.no-spawn")));
return true;
}
plugin.getSpawnManager().teleportToSpawn(player);
player.sendMessage(TextUtil.colorize(plugin.getConfig().getString("messages.spawn-teleport")));
player.sendMessage(TextUtil.colorize(plugin.getConfigManager().getMessage("spawn.teleport")));
return true;
}

View file

@ -17,13 +17,13 @@ public class LoHubCommand implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!sender.hasPermission("lohub.admin")) {
sender.sendMessage(TextUtil.colorize(plugin.getConfig().getString("messages.no-permission")));
sender.sendMessage(TextUtil.colorize(plugin.getConfigManager().getMessage("general.no-permission")));
return true;
}
if (args.length == 0) {
sender.sendMessage(TextUtil.colorize("&7LoHub v" + plugin.getDescription().getVersion()));
sender.sendMessage(TextUtil.colorize("&7Use: &e/lohub reload"));
sender.sendMessage(TextUtil.colorize(plugin.getConfigManager().getMessage("commands.usage-lohub")));
return true;
}
@ -35,7 +35,7 @@ public class LoHubCommand implements CommandExecutor {
plugin.getTablistManager().stop();
plugin.getScoreboardManager().start();
plugin.getTablistManager().start();
sender.sendMessage(TextUtil.colorize("&aConfiguration reloaded!"));
sender.sendMessage(TextUtil.colorize(plugin.getConfigManager().getMessage("general.config-reloaded")));
return true;
}

View file

@ -18,25 +18,21 @@ public class SetHubCommand implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player player)) {
sender.sendMessage(TextUtil.colorize("&cThis command is only for players!"));
sender.sendMessage(TextUtil.colorize(plugin.getConfigManager().getMessage("general.players-only")));
return true;
}
if (!player.hasPermission("lohub.admin")) {
player.sendMessage(TextUtil.colorize(plugin.getConfig().getString("messages.no-permission")));
player.sendMessage(TextUtil.colorize(plugin.getConfigManager().getMessage("general.no-permission")));
return true;
}
plugin.getSpawnManager().setSpawn(player.getLocation());
player.sendMessage(TextUtil.colorize(plugin.getConfig().getString("messages.spawn-set")));
player.sendMessage(TextUtil.colorize(plugin.getConfigManager().getMessage("spawn.set")));
plugin.getRegionManager().deleteHubRegion();
plugin.getRegionManager().createHubRegion();
if (plugin.getConfig().getBoolean("worldguard.auto-create-region")) {
player.sendMessage(TextUtil.colorize(plugin.getConfig().getString("messages.region-created")));
}
return true;
}
}

View file

@ -0,0 +1,21 @@
package com.loki.lohub.common;
import org.bukkit.configuration.file.FileConfiguration;
public final class ConfigHelper {
private ConfigHelper() {
}
public static boolean isEnabled(FileConfiguration config, String path) {
return config.getBoolean(path + ".enabled", true);
}
public static double getPower(FileConfiguration config, String path, String key, double defaultValue) {
return config.getDouble(path + "." + key, defaultValue);
}
public static int getCooldown(FileConfiguration config, String path, int defaultValue) {
return config.getInt(path + ".cooldown", defaultValue);
}
}

View file

@ -0,0 +1,15 @@
package com.loki.lohub.common;
import org.bukkit.GameMode;
import org.bukkit.entity.Player;
public final class GameModeHelper {
private GameModeHelper() {
}
public static boolean isCreativeOrSpectator(Player player) {
GameMode mode = player.getGameMode();
return mode == GameMode.CREATIVE || mode == GameMode.SPECTATOR;
}
}

View file

@ -0,0 +1,16 @@
package com.loki.lohub.common;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
public final class LaunchHelper {
private LaunchHelper() {
}
public static void launch(Player player, double power, double powerY) {
Vector velocity = player.getLocation().getDirection().multiply(power);
velocity.setY(powerY);
player.setVelocity(velocity);
}
}

View file

@ -0,0 +1,24 @@
package com.loki.lohub.common;
import com.loki.lohub.utils.PlaceholderUtil;
import com.loki.lohub.utils.TextUtil;
import org.bukkit.entity.Player;
import java.util.List;
import java.util.stream.Collectors;
public final class TextFormatter {
private TextFormatter() {
}
public static String format(String text, Player player) {
return TextUtil.colorize(PlaceholderUtil.parse(text, player));
}
public static String joinLines(List<String> lines, Player player, String separator) {
return lines.stream()
.map(line -> format(line, player))
.collect(Collectors.joining(separator));
}
}

View file

@ -2,26 +2,155 @@ package com.loki.lohub.config;
import com.loki.lohub.LoHub;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
public class ConfigManager {
private final LoHub plugin;
private FileConfiguration config;
private FileConfiguration messages;
private FileConfiguration commands;
private FileConfiguration serverSelector;
private FileConfiguration data;
private File messagesFile;
private File commandsFile;
private File serverSelectorFile;
private File dataFile;
public ConfigManager(LoHub plugin) {
this.plugin = plugin;
this.config = plugin.getConfig();
loadMessages();
loadCommands();
loadServerSelector();
loadData();
}
private void loadMessages() {
messagesFile = new File(plugin.getDataFolder(), "messages.yml");
if (!messagesFile.exists()) {
try {
messagesFile.getParentFile().mkdirs();
InputStream in = plugin.getResource("messages.yml");
if (in != null) {
Files.copy(in, messagesFile.toPath());
}
} catch (IOException e) {
plugin.getLogger().severe("Could not create messages.yml!");
e.printStackTrace();
}
}
messages = YamlConfiguration.loadConfiguration(messagesFile);
}
private void loadCommands() {
commandsFile = new File(plugin.getDataFolder(), "commands.yml");
if (!commandsFile.exists()) {
try {
commandsFile.getParentFile().mkdirs();
InputStream in = plugin.getResource("commands.yml");
if (in != null) {
Files.copy(in, commandsFile.toPath());
}
} catch (IOException e) {
plugin.getLogger().severe("Could not create commands.yml!");
e.printStackTrace();
}
}
commands = YamlConfiguration.loadConfiguration(commandsFile);
}
private void loadServerSelector() {
serverSelectorFile = new File(plugin.getDataFolder(), "serverselector.yml");
if (!serverSelectorFile.exists()) {
try {
serverSelectorFile.getParentFile().mkdirs();
InputStream in = plugin.getResource("serverselector.yml");
if (in != null) {
Files.copy(in, serverSelectorFile.toPath());
}
} catch (IOException e) {
plugin.getLogger().severe("Could not create serverselector.yml!");
e.printStackTrace();
}
}
serverSelector = YamlConfiguration.loadConfiguration(serverSelectorFile);
}
private void loadData() {
dataFile = new File(plugin.getDataFolder(), "data.yml");
if (!dataFile.exists()) {
try {
dataFile.getParentFile().mkdirs();
InputStream in = plugin.getResource("data.yml");
if (in != null) {
Files.copy(in, dataFile.toPath());
}
} catch (IOException e) {
plugin.getLogger().severe("Could not create data.yml!");
e.printStackTrace();
}
}
data = YamlConfiguration.loadConfiguration(dataFile);
}
public void reload() {
plugin.reloadConfig();
this.config = plugin.getConfig();
loadMessages();
loadCommands();
loadServerSelector();
loadData();
}
public FileConfiguration getConfig() {
return config;
}
public FileConfiguration getMessages() {
return messages;
}
public FileConfiguration getCommands() {
return commands;
}
public FileConfiguration getServerSelector() {
return serverSelector;
}
public FileConfiguration getData() {
return data;
}
public void saveData() {
try {
data.save(dataFile);
} catch (IOException e) {
plugin.getLogger().severe("Could not save data.yml!");
e.printStackTrace();
}
}
public String getMessage(String path) {
String message = messages.getString(path, "&#FF6B6BMessage not found: " + path);
String prefix = messages.getString("prefix", "&#00D4FF[LoHub] ");
return message.replace("{prefix}", prefix);
}
public boolean isProtectionEnabled() {
return config.getBoolean("protection.enabled", true);
}

View file

@ -0,0 +1,104 @@
package com.loki.lohub.features;
import org.bukkit.Color;
import org.bukkit.FireworkEffect;
import org.bukkit.Location;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Firework;
import org.bukkit.entity.Player;
import org.bukkit.inventory.meta.FireworkMeta;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public final class FireworkHandler {
private static final Map<String, Color> COLOR_MAP = new HashMap<>();
private static final String CONFIG_PATH = "join_settings.firework";
static {
COLOR_MAP.put("AQUA", Color.AQUA);
COLOR_MAP.put("BLACK", Color.BLACK);
COLOR_MAP.put("BLUE", Color.BLUE);
COLOR_MAP.put("FUCHSIA", Color.FUCHSIA);
COLOR_MAP.put("GRAY", Color.GRAY);
COLOR_MAP.put("GREEN", Color.GREEN);
COLOR_MAP.put("LIME", Color.LIME);
COLOR_MAP.put("MAROON", Color.MAROON);
COLOR_MAP.put("NAVY", Color.NAVY);
COLOR_MAP.put("OLIVE", Color.OLIVE);
COLOR_MAP.put("ORANGE", Color.ORANGE);
COLOR_MAP.put("PURPLE", Color.PURPLE);
COLOR_MAP.put("RED", Color.RED);
COLOR_MAP.put("SILVER", Color.SILVER);
COLOR_MAP.put("TEAL", Color.TEAL);
COLOR_MAP.put("WHITE", Color.WHITE);
COLOR_MAP.put("YELLOW", Color.YELLOW);
}
private FireworkHandler() {
}
public static void spawn(Player player, FileConfiguration config) {
if (!config.getBoolean(CONFIG_PATH + ".enabled", false)) {
return;
}
if (shouldSkip(player, config)) {
return;
}
Location loc = player.getLocation();
Firework firework = (Firework) loc.getWorld().spawnEntity(loc, EntityType.FIREWORK_ROCKET);
FireworkMeta meta = firework.getFireworkMeta();
meta.addEffect(buildEffect(config));
meta.setPower(config.getInt(CONFIG_PATH + ".power", 1));
firework.setFireworkMeta(meta);
}
private static boolean shouldSkip(Player player, FileConfiguration config) {
boolean firstJoinOnly = config.getBoolean(CONFIG_PATH + ".first_join_only", true);
return firstJoinOnly && player.hasPlayedBefore();
}
private static FireworkEffect buildEffect(FileConfiguration config) {
FireworkEffect.Type type = getType(config);
List<Color> colors = getColors(config);
return FireworkEffect.builder()
.with(type)
.withColor(colors)
.flicker(config.getBoolean(CONFIG_PATH + ".flicker", true))
.trail(config.getBoolean(CONFIG_PATH + ".trail", true))
.build();
}
private static FireworkEffect.Type getType(FileConfiguration config) {
String typeStr = config.getString(CONFIG_PATH + ".type", "BALL_LARGE");
try {
return FireworkEffect.Type.valueOf(typeStr);
} catch (IllegalArgumentException e) {
return FireworkEffect.Type.BALL_LARGE;
}
}
private static List<Color> getColors(FileConfiguration config) {
List<Color> colors = new ArrayList<>();
for (String colorStr : config.getStringList(CONFIG_PATH + ".colors")) {
Color color = COLOR_MAP.get(colorStr.toUpperCase());
if (color != null) {
colors.add(color);
}
}
if (colors.isEmpty()) {
colors.add(Color.AQUA);
}
return colors;
}
}

View file

@ -0,0 +1,44 @@
package com.loki.lohub.features;
import com.loki.lohub.utils.TextUtil;
import net.kyori.adventure.text.Component;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
public final class JoinMessageHandler {
private static final String CONFIG_PATH = "join_leave_messages";
private JoinMessageHandler() {
}
public static void handleJoin(PlayerJoinEvent event, Player player, FileConfiguration config) {
if (!config.getBoolean(CONFIG_PATH + ".enabled", true)) {
return;
}
String message = config.getString(CONFIG_PATH + ".join_message", "");
if (message.isEmpty()) {
event.joinMessage(null);
} else {
String formatted = TextUtil.colorize(message.replace("%player%", player.getName()));
event.joinMessage(Component.text(formatted));
}
}
public static void handleQuit(PlayerQuitEvent event, Player player, FileConfiguration config) {
if (!config.getBoolean(CONFIG_PATH + ".enabled", true)) {
return;
}
String message = config.getString(CONFIG_PATH + ".quit_message", "");
if (message.isEmpty()) {
event.quitMessage(null);
} else {
String formatted = TextUtil.colorize(message.replace("%player%", player.getName()));
event.quitMessage(Component.text(formatted));
}
}
}

View file

@ -0,0 +1,62 @@
package com.loki.lohub.features;
import com.loki.lohub.LoHub;
import org.bukkit.Location;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
public final class JoinSettingsHandler {
private static final String CONFIG_PATH = "join_settings";
private JoinSettingsHandler() {
}
public static void apply(Player player, LoHub plugin) {
FileConfiguration config = plugin.getConfig();
teleportToSpawn(player, plugin, config);
healPlayer(player, config);
extinguishPlayer(player, config);
clearInventory(player, config);
giveItems(player, plugin);
}
private static void teleportToSpawn(Player player, LoHub plugin, FileConfiguration config) {
if (!config.getBoolean(CONFIG_PATH + ".spawn_join", true)) {
return;
}
Location spawn = plugin.getSpawnManager().getSpawn();
if (spawn != null) {
player.teleport(spawn);
}
}
private static void healPlayer(Player player, FileConfiguration config) {
if (!config.getBoolean(CONFIG_PATH + ".heal", true)) {
return;
}
player.setHealth(player.getMaxHealth());
player.setFoodLevel(20);
player.setSaturation(20);
}
private static void extinguishPlayer(Player player, FileConfiguration config) {
if (config.getBoolean(CONFIG_PATH + ".extinguish", true)) {
player.setFireTicks(0);
}
}
private static void clearInventory(Player player, FileConfiguration config) {
if (config.getBoolean(CONFIG_PATH + ".clear_inventory", false)) {
player.getInventory().clear();
}
}
private static void giveItems(Player player, LoHub plugin) {
plugin.getHotbarManager().giveItems(player);
plugin.getPlayerHiderManager().giveItem(player);
}
}

View file

@ -0,0 +1,68 @@
package com.loki.lohub.listeners;
import com.loki.lohub.LoHub;
import com.loki.lohub.utils.TextUtil;
import net.kyori.adventure.text.Component;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import java.util.Arrays;
import java.util.List;
public class ChatLockListener implements Listener {
private static final List<String> BLOCKED_COMMANDS = Arrays.asList(
"me", "msg", "tell", "w", "whisper", "pm", "dm",
"reply", "r", "say", "minecraft:me", "minecraft:msg",
"minecraft:tell", "bukkit:me", "bukkit:msg", "bukkit:tell"
);
private final LoHub plugin;
public ChatLockListener(LoHub plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPlayerChat(AsyncPlayerChatEvent event) {
if (!plugin.getChatLockManager().isChatLocked()) {
return;
}
Player player = event.getPlayer();
if (player.hasPermission("lohub.chatlock.bypass")) {
return;
}
event.setCancelled(true);
player.sendMessage(Component.text(TextUtil.colorize(plugin.getConfigManager().getMessage("chat.locked"))));
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPlayerCommand(PlayerCommandPreprocessEvent event) {
if (!plugin.getChatLockManager().isChatLocked()) {
return;
}
Player player = event.getPlayer();
if (player.hasPermission("lohub.chatlock.bypass")) {
return;
}
String command = event.getMessage().toLowerCase().substring(1);
String baseCommand = command.split(" ")[0];
if (isBlockedCommand(baseCommand)) {
event.setCancelled(true);
player.sendMessage(Component.text(TextUtil.colorize(plugin.getConfigManager().getMessage("chat.locked"))));
}
}
private boolean isBlockedCommand(String command) {
return BLOCKED_COMMANDS.stream().anyMatch(blocked -> command.equals(blocked));
}
}

View file

@ -0,0 +1,47 @@
package com.loki.lohub.listeners;
import com.loki.lohub.LoHub;
import com.loki.lohub.managers.CustomCommandManager;
import com.loki.lohub.utils.TextUtil;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
public class CustomCommandListener implements Listener {
private final LoHub plugin;
public CustomCommandListener(LoHub plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.NORMAL)
public void onPlayerCommand(PlayerCommandPreprocessEvent event) {
Player player = event.getPlayer();
String message = event.getMessage().toLowerCase();
if (!message.startsWith("/")) {
return;
}
String command = message.substring(1).split(" ")[0];
CustomCommandManager.CustomCommand customCommand = plugin.getCustomCommandManager().getCommand(command);
if (customCommand == null) {
return;
}
event.setCancelled(true);
if (customCommand.permission() != null && !customCommand.permission().isEmpty()) {
if (!player.hasPermission(customCommand.permission())) {
player.sendMessage(TextUtil.colorize(plugin.getConfig().getString("messages.no-permission")));
return;
}
}
plugin.getActionManager().executeActions(player, customCommand.actions(), false);
}
}

View file

@ -1,18 +1,22 @@
package com.loki.lohub.listeners;
import com.loki.lohub.LoHub;
import org.bukkit.GameMode;
import com.loki.lohub.common.ConfigHelper;
import com.loki.lohub.common.GameModeHelper;
import com.loki.lohub.common.LaunchHelper;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerToggleFlightEvent;
import org.bukkit.util.Vector;
import java.util.List;
public class DoubleJumpListener implements Listener {
private static final String CONFIG_PATH = "double_jump";
private static final String COOLDOWN_KEY = "double_jump";
private final LoHub plugin;
public DoubleJumpListener(LoHub plugin) {
@ -21,13 +25,13 @@ public class DoubleJumpListener implements Listener {
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
if (!plugin.getConfig().getBoolean("double_jump.enabled", true)) {
if (!ConfigHelper.isEnabled(plugin.getConfig(), CONFIG_PATH)) {
return;
}
Player player = event.getPlayer();
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) {
if (GameModeHelper.isCreativeOrSpectator(player)) {
return;
}
@ -41,38 +45,45 @@ public class DoubleJumpListener implements Listener {
@EventHandler
public void onPlayerToggleFlight(PlayerToggleFlightEvent event) {
if (!plugin.getConfig().getBoolean("double_jump.enabled", true)) {
if (!ConfigHelper.isEnabled(plugin.getConfig(), CONFIG_PATH)) {
return;
}
Player player = event.getPlayer();
if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) {
if (GameModeHelper.isCreativeOrSpectator(player)) {
return;
}
if (plugin.getCooldownManager().hasCooldown("double_jump", player.getUniqueId())) {
event.setCancelled(true);
player.setAllowFlight(false);
player.setFlying(false);
if (plugin.getCooldownManager().hasCooldown(COOLDOWN_KEY, player.getUniqueId())) {
cancelFlight(event, player);
return;
}
cancelFlight(event, player);
performDoubleJump(player);
}
private void cancelFlight(PlayerToggleFlightEvent event, Player player) {
event.setCancelled(true);
player.setAllowFlight(false);
player.setFlying(false);
}
double power = plugin.getConfig().getDouble("double_jump.launch_power", 1.0);
double powerY = plugin.getConfig().getDouble("double_jump.launch_power_y", 0.9);
private void performDoubleJump(Player player) {
double power = ConfigHelper.getPower(plugin.getConfig(), CONFIG_PATH, "launch_power", 1.0);
double powerY = ConfigHelper.getPower(plugin.getConfig(), CONFIG_PATH, "launch_power_y", 0.9);
Vector velocity = player.getLocation().getDirection().multiply(power);
velocity.setY(powerY);
player.setVelocity(velocity);
LaunchHelper.launch(player, power, powerY);
int cooldown = plugin.getConfig().getInt("double_jump.cooldown", 3);
plugin.getCooldownManager().setCooldown("double_jump", player.getUniqueId(), cooldown);
int cooldown = ConfigHelper.getCooldown(plugin.getConfig(), CONFIG_PATH, 3);
plugin.getCooldownManager().setCooldown(COOLDOWN_KEY, player.getUniqueId(), cooldown);
List<String> actions = plugin.getConfig().getStringList("double_jump.actions");
executeActions(player);
}
private void executeActions(Player player) {
List<String> actions = plugin.getConfig().getStringList(CONFIG_PATH + ".actions");
if (!actions.isEmpty()) {
plugin.getActionManager().executeActions(player, actions, false);
}

View file

@ -8,9 +8,11 @@ import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.FoodLevelChangeEvent;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerPickupItemEvent;
import org.bukkit.event.player.PlayerSwapHandItemsEvent;
public class HubProtectionListener implements Listener {
@ -131,4 +133,38 @@ public class HubProtectionListener implements Listener {
event.setCancelled(true);
}
}
@EventHandler
public void onPlayerSwapHandItems(PlayerSwapHandItemsEvent event) {
if (!plugin.getConfig().getBoolean("world_settings.disable_off_hand_swap", true)) {
return;
}
Player player = event.getPlayer();
if (player.hasPermission("lohub.bypass.protection")) {
return;
}
event.setCancelled(true);
}
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
if (!plugin.getConfig().getBoolean("world_settings.disable_off_hand_swap", true)) {
return;
}
if (!(event.getWhoClicked() instanceof Player)) {
return;
}
Player player = (Player) event.getWhoClicked();
if (player.hasPermission("lohub.bypass.protection")) {
return;
}
if (event.getRawSlot() != event.getSlot() && event.getSlot() == 40) {
event.setCancelled(true);
}
}
}

View file

@ -1,18 +1,21 @@
package com.loki.lohub.listeners;
import com.loki.lohub.LoHub;
import com.loki.lohub.common.ConfigHelper;
import com.loki.lohub.common.LaunchHelper;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.util.Vector;
import java.util.List;
public class LaunchpadListener implements Listener {
private static final String CONFIG_PATH = "launchpad";
private final LoHub plugin;
public LaunchpadListener(LoHub plugin) {
@ -21,46 +24,59 @@ public class LaunchpadListener implements Listener {
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
if (!plugin.getConfig().getBoolean("launchpad.enabled", true)) {
if (!ConfigHelper.isEnabled(plugin.getConfig(), CONFIG_PATH)) {
return;
}
if (event.getFrom().getBlockX() == event.getTo().getBlockX()
&& event.getFrom().getBlockY() == event.getTo().getBlockY()
&& event.getFrom().getBlockZ() == event.getTo().getBlockZ()) {
if (!hasMoved(event)) {
return;
}
Player player = event.getPlayer();
Block block = player.getLocation().getBlock();
String topBlockStr = plugin.getConfig().getString("launchpad.top_block", "STONE_PRESSURE_PLATE");
String bottomBlockStr = plugin.getConfig().getString("launchpad.bottom_block", "REDSTONE_BLOCK");
Material topMaterial = Material.getMaterial(topBlockStr);
Material bottomMaterial = Material.getMaterial(bottomBlockStr);
if (topMaterial == null || bottomMaterial == null) {
return;
}
if (block.getType() == topMaterial) {
Block below = block.getRelative(0, -1, 0);
if (below.getType() == bottomMaterial) {
launchPlayer(player);
}
if (isLaunchpad(block)) {
launchPlayer(player);
}
}
private boolean hasMoved(PlayerMoveEvent event) {
return event.getFrom().getBlockX() != event.getTo().getBlockX()
|| event.getFrom().getBlockY() != event.getTo().getBlockY()
|| event.getFrom().getBlockZ() != event.getTo().getBlockZ();
}
private boolean isLaunchpad(Block block) {
Material topMaterial = getMaterial("top_block", "STONE_PRESSURE_PLATE");
Material bottomMaterial = getMaterial("bottom_block", "REDSTONE_BLOCK");
if (topMaterial == null || bottomMaterial == null) {
return false;
}
if (block.getType() != topMaterial) {
return false;
}
Block below = block.getRelative(0, -1, 0);
return below.getType() == bottomMaterial;
}
private Material getMaterial(String key, String defaultValue) {
String materialName = plugin.getConfig().getString(CONFIG_PATH + "." + key, defaultValue);
return Material.getMaterial(materialName);
}
private void launchPlayer(Player player) {
double power = plugin.getConfig().getDouble("launchpad.launch_power", 3.0);
double powerY = plugin.getConfig().getDouble("launchpad.launch_power_y", 1.0);
double power = ConfigHelper.getPower(plugin.getConfig(), CONFIG_PATH, "launch_power", 3.0);
double powerY = ConfigHelper.getPower(plugin.getConfig(), CONFIG_PATH, "launch_power_y", 1.0);
Vector velocity = player.getLocation().getDirection().multiply(power);
velocity.setY(powerY);
player.setVelocity(velocity);
LaunchHelper.launch(player, power, powerY);
executeActions(player);
}
List<String> actions = plugin.getConfig().getStringList("launchpad.actions");
private void executeActions(Player player) {
List<String> actions = plugin.getConfig().getStringList(CONFIG_PATH + ".actions");
if (!actions.isEmpty()) {
plugin.getActionManager().executeActions(player, actions, false);
}

View file

@ -1,13 +1,10 @@
package com.loki.lohub.listeners;
import com.loki.lohub.LoHub;
import com.loki.lohub.utils.TextUtil;
import net.kyori.adventure.text.Component;
import org.bukkit.Color;
import org.bukkit.FireworkEffect;
import com.loki.lohub.features.FireworkHandler;
import com.loki.lohub.features.JoinMessageHandler;
import com.loki.lohub.features.JoinSettingsHandler;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Firework;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@ -16,37 +13,11 @@ import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.inventory.meta.FireworkMeta;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
public class PlayerJoinListener implements Listener {
private static final Map<String, Color> COLOR_MAP = new HashMap<>();
static {
COLOR_MAP.put("AQUA", Color.AQUA);
COLOR_MAP.put("BLACK", Color.BLACK);
COLOR_MAP.put("BLUE", Color.BLUE);
COLOR_MAP.put("FUCHSIA", Color.FUCHSIA);
COLOR_MAP.put("GRAY", Color.GRAY);
COLOR_MAP.put("GREEN", Color.GREEN);
COLOR_MAP.put("LIME", Color.LIME);
COLOR_MAP.put("MAROON", Color.MAROON);
COLOR_MAP.put("NAVY", Color.NAVY);
COLOR_MAP.put("OLIVE", Color.OLIVE);
COLOR_MAP.put("ORANGE", Color.ORANGE);
COLOR_MAP.put("PURPLE", Color.PURPLE);
COLOR_MAP.put("RED", Color.RED);
COLOR_MAP.put("SILVER", Color.SILVER);
COLOR_MAP.put("TEAL", Color.TEAL);
COLOR_MAP.put("WHITE", Color.WHITE);
COLOR_MAP.put("YELLOW", Color.YELLOW);
}
private final LoHub plugin;
public PlayerJoinListener(LoHub plugin) {
@ -61,10 +32,10 @@ public class PlayerJoinListener implements Listener {
return;
}
handleJoinMessage(event, player);
handleJoinSettings(player);
handleJoinEvents(player);
handleFirework(player);
JoinMessageHandler.handleJoin(event, player, plugin.getConfig());
JoinSettingsHandler.apply(player, plugin);
executeJoinActions(player);
FireworkHandler.spawn(player, plugin.getConfig());
plugin.getScoreboardManager().setScoreboard(player, true);
plugin.getTablistManager().setTablist(player);
@ -81,14 +52,7 @@ public class PlayerJoinListener implements Listener {
plugin.getPlayerHiderManager().removePlayer(player);
plugin.getScoreboardManager().removePlayer(player);
if (plugin.getConfig().getBoolean("join_leave_messages.enabled", true)) {
String quitMessage = plugin.getConfig().getString("join_leave_messages.quit_message", "");
if (quitMessage.isEmpty()) {
event.quitMessage(null);
} else {
event.quitMessage(Component.text(TextUtil.colorize(quitMessage.replace("%player%", player.getName()))));
}
}
JoinMessageHandler.handleQuit(event, player, plugin.getConfig());
}
@EventHandler(priority = EventPriority.HIGHEST)
@ -118,100 +82,13 @@ public class PlayerJoinListener implements Listener {
}
}
private void handleJoinMessage(PlayerJoinEvent event, Player player) {
if (plugin.getConfig().getBoolean("join_leave_messages.enabled", true)) {
String joinMessage = plugin.getConfig().getString("join_leave_messages.join_message", "");
if (joinMessage.isEmpty()) {
event.joinMessage(null);
} else {
event.joinMessage(Component.text(TextUtil.colorize(joinMessage.replace("%player%", player.getName()))));
}
}
}
private void handleJoinSettings(Player player) {
if (plugin.getConfig().getBoolean("join_settings.spawn_join", true)) {
Location spawn = plugin.getSpawnManager().getSpawn();
if (spawn != null) {
player.teleport(spawn);
}
}
if (plugin.getConfig().getBoolean("join_settings.heal", true)) {
player.setHealth(player.getMaxHealth());
player.setFoodLevel(20);
player.setSaturation(20);
}
if (plugin.getConfig().getBoolean("join_settings.extinguish", true)) {
player.setFireTicks(0);
}
if (plugin.getConfig().getBoolean("join_settings.clear_inventory", false)) {
player.getInventory().clear();
}
plugin.getHotbarManager().giveItems(player);
plugin.getPlayerHiderManager().giveItem(player);
}
private void handleJoinEvents(Player player) {
private void executeJoinActions(Player player) {
List<String> actions = plugin.getConfig().getStringList("join_events");
if (!actions.isEmpty()) {
plugin.getActionManager().executeActions(player, actions, false);
}
}
private void handleFirework(Player player) {
if (!plugin.getConfig().getBoolean("join_settings.firework.enabled", false)) {
return;
}
boolean firstJoinOnly = plugin.getConfig().getBoolean("join_settings.firework.first_join_only", true);
if (firstJoinOnly && player.hasPlayedBefore()) {
return;
}
Location loc = player.getLocation();
Firework firework = (Firework) loc.getWorld().spawnEntity(loc, EntityType.FIREWORK_ROCKET);
FireworkMeta meta = firework.getFireworkMeta();
String typeStr = plugin.getConfig().getString("join_settings.firework.type", "BALL_LARGE");
FireworkEffect.Type type;
try {
type = FireworkEffect.Type.valueOf(typeStr);
} catch (IllegalArgumentException e) {
type = FireworkEffect.Type.BALL_LARGE;
}
List<Color> colors = new ArrayList<>();
for (String colorStr : plugin.getConfig().getStringList("join_settings.firework.colors")) {
Color color = parseColor(colorStr);
if (color != null) {
colors.add(color);
}
}
if (colors.isEmpty()) {
colors.add(Color.AQUA);
}
FireworkEffect effect = FireworkEffect.builder()
.with(type)
.withColor(colors)
.flicker(plugin.getConfig().getBoolean("join_settings.firework.flicker", true))
.trail(plugin.getConfig().getBoolean("join_settings.firework.trail", true))
.build();
meta.addEffect(effect);
meta.setPower(plugin.getConfig().getInt("join_settings.firework.power", 1));
firework.setFireworkMeta(meta);
}
private Color parseColor(String colorStr) {
return COLOR_MAP.get(colorStr.toUpperCase());
}
private boolean isWorldDisabled(String worldName) {
List<String> disabledWorlds = plugin.getConfig().getStringList("disabled-worlds.worlds");
boolean invert = plugin.getConfig().getBoolean("disabled-worlds.invert", false);

View file

@ -1,8 +1,8 @@
package com.loki.lohub.managers;
import com.loki.lohub.LoHub;
import com.loki.lohub.utils.PlaceholderUtil;
import com.loki.lohub.utils.TextUtil;
import com.loki.lohub.common.ConfigHelper;
import com.loki.lohub.common.TextFormatter;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.Sound;
@ -14,6 +14,8 @@ import java.util.List;
public class AnnouncementManager {
private static final String CONFIG_PATH = "announcements";
private final LoHub plugin;
private final List<Announcement> announcements = new ArrayList<>();
private int currentIndex = 0;
@ -26,21 +28,21 @@ public class AnnouncementManager {
private void loadAnnouncements() {
announcements.clear();
ConfigurationSection section = plugin.getConfig().getConfigurationSection("announcements.announcements");
ConfigurationSection section = plugin.getConfig().getConfigurationSection(CONFIG_PATH + ".announcements");
if (section == null) {
return;
}
for (String key : section.getKeys(false)) {
section.getKeys(false).forEach(key -> {
List<String> messages = section.getStringList(key);
if (!messages.isEmpty()) {
announcements.add(new Announcement(key, messages));
}
}
});
}
public void start() {
if (!plugin.getConfig().getBoolean("announcements.enabled", false)) {
if (!ConfigHelper.isEnabled(plugin.getConfig(), CONFIG_PATH)) {
return;
}
@ -48,7 +50,7 @@ public class AnnouncementManager {
return;
}
int delay = plugin.getConfig().getInt("announcements.delay", 60) * 20;
int delay = plugin.getConfig().getInt(CONFIG_PATH + ".delay", 60) * 20;
taskId = Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, this::broadcast, delay, delay);
}
@ -60,8 +62,7 @@ public class AnnouncementManager {
}
private void broadcast() {
int requiredPlayers = plugin.getConfig().getInt("announcements.required_players", 1);
if (Bukkit.getOnlinePlayers().size() < requiredPlayers) {
if (!hasEnoughPlayers()) {
return;
}
@ -69,25 +70,40 @@ public class AnnouncementManager {
return;
}
Announcement announcement = getNextAnnouncement();
Bukkit.getOnlinePlayers().forEach(player -> sendAnnouncement(player, announcement));
}
private boolean hasEnoughPlayers() {
int required = plugin.getConfig().getInt(CONFIG_PATH + ".required_players", 1);
return Bukkit.getOnlinePlayers().size() >= required;
}
private Announcement getNextAnnouncement() {
Announcement announcement = announcements.get(currentIndex);
currentIndex = (currentIndex + 1) % announcements.size();
return announcement;
}
for (Player player : Bukkit.getOnlinePlayers()) {
for (String message : announcement.messages()) {
String formatted = PlaceholderUtil.parse(message, player);
player.sendMessage(Component.text(TextUtil.colorize(formatted)));
}
private void sendAnnouncement(Player player, Announcement announcement) {
announcement.messages().forEach(message -> {
String formatted = TextFormatter.format(message, player);
player.sendMessage(Component.text(formatted));
});
if (plugin.getConfig().getBoolean("announcements.sound.enabled", true)) {
playSound(player);
}
if (shouldPlaySound()) {
playSound(player);
}
}
private boolean shouldPlaySound() {
return plugin.getConfig().getBoolean(CONFIG_PATH + ".sound.enabled", true);
}
private void playSound(Player player) {
String soundStr = plugin.getConfig().getString("announcements.sound.value", "BLOCK_NOTE_BLOCK_PLING");
float volume = (float) plugin.getConfig().getDouble("announcements.sound.volume", 1.0);
float pitch = (float) plugin.getConfig().getDouble("announcements.sound.pitch", 1.0);
String soundStr = plugin.getConfig().getString(CONFIG_PATH + ".sound.value", "BLOCK_NOTE_BLOCK_PLING");
float volume = (float) plugin.getConfig().getDouble(CONFIG_PATH + ".sound.volume", 1.0);
float pitch = (float) plugin.getConfig().getDouble(CONFIG_PATH + ".sound.pitch", 1.0);
try {
Sound sound = Sound.valueOf(soundStr);

View file

@ -0,0 +1,25 @@
package com.loki.lohub.managers;
import com.loki.lohub.LoHub;
public class ChatLockManager {
private final LoHub plugin;
public ChatLockManager(LoHub plugin) {
this.plugin = plugin;
}
public boolean isChatLocked() {
return plugin.getConfigManager().getData().getBoolean("chat_locked", false);
}
public void setChatLocked(boolean locked) {
plugin.getConfigManager().getData().set("chat_locked", locked);
plugin.getConfigManager().saveData();
}
public void toggle() {
setChatLocked(!isChatLocked());
}
}

View file

@ -0,0 +1,62 @@
package com.loki.lohub.managers;
import com.loki.lohub.LoHub;
import org.bukkit.configuration.ConfigurationSection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class CustomCommandManager {
private final LoHub plugin;
private final Map<String, CustomCommand> commands = new HashMap<>();
public CustomCommandManager(LoHub plugin) {
this.plugin = plugin;
loadCommands();
}
private void loadCommands() {
commands.clear();
ConfigurationSection section = plugin.getConfigManager().getCommands().getConfigurationSection("custom_commands");
if (section == null) {
return;
}
for (String key : section.getKeys(false)) {
ConfigurationSection cmdSection = section.getConfigurationSection(key);
if (cmdSection == null) {
continue;
}
List<String> aliases = cmdSection.getStringList("aliases");
String permission = cmdSection.getString("permission");
List<String> actions = cmdSection.getStringList("actions");
if (aliases.isEmpty() || actions.isEmpty()) {
continue;
}
CustomCommand cmd = new CustomCommand(key, aliases, permission, actions);
for (String alias : aliases) {
commands.put(alias.toLowerCase(), cmd);
}
}
}
public CustomCommand getCommand(String alias) {
return commands.get(alias.toLowerCase());
}
public boolean hasCommand(String alias) {
return commands.containsKey(alias.toLowerCase());
}
public void reload() {
loadCommands();
}
public record CustomCommand(String name, List<String> aliases, String permission, List<String> actions) {
}
}

View file

@ -13,6 +13,8 @@ import java.util.Map;
public class HotbarManager {
private static final String CONFIG_PATH = "custom_join_items";
private final LoHub plugin;
private final Map<Integer, HotbarItem> items = new HashMap<>();
@ -23,46 +25,56 @@ public class HotbarManager {
private void loadItems() {
items.clear();
ConfigurationSection section = plugin.getConfig().getConfigurationSection("custom_join_items.items");
ConfigurationSection section = plugin.getConfig().getConfigurationSection(CONFIG_PATH + ".items");
if (section == null) {
return;
}
for (String key : section.getKeys(false)) {
ConfigurationSection itemSection = section.getConfigurationSection(key);
if (itemSection == null) {
continue;
}
Material material = Material.getMaterial(itemSection.getString("material", "STONE"));
if (material == null) {
continue;
}
int slot = itemSection.getInt("slot", 0);
int amount = itemSection.getInt("amount", 1);
String displayName = itemSection.getString("display_name", "");
List<String> lore = itemSection.getStringList("lore");
List<String> actions = itemSection.getStringList("actions");
ItemStack item = new ItemBuilder(material)
.amount(amount)
.name(displayName)
.lore(lore)
.build();
items.put(slot, new HotbarItem(item, actions));
loadItem(section.getConfigurationSection(key));
}
}
public void giveItems(Player player) {
if (!plugin.getConfig().getBoolean("custom_join_items.enabled", false)) {
private void loadItem(ConfigurationSection itemSection) {
if (itemSection == null) {
return;
}
for (Map.Entry<Integer, HotbarItem> entry : items.entrySet()) {
player.getInventory().setItem(entry.getKey(), entry.getValue().item());
Material material = getMaterial(itemSection);
if (material == null) {
return;
}
int slot = itemSection.getInt("slot", 0);
ItemStack item = buildItem(itemSection, material);
List<String> actions = itemSection.getStringList("actions");
items.put(slot, new HotbarItem(item, actions));
}
private Material getMaterial(ConfigurationSection section) {
String materialName = section.getString("material", "STONE");
return Material.getMaterial(materialName);
}
private ItemStack buildItem(ConfigurationSection section, Material material) {
int amount = section.getInt("amount", 1);
String displayName = section.getString("display_name", "");
List<String> lore = section.getStringList("lore");
return new ItemBuilder(material)
.amount(amount)
.name(displayName)
.lore(lore)
.build();
}
public void giveItems(Player player) {
if (!isEnabled()) {
return;
}
items.forEach((slot, item) -> player.getInventory().setItem(slot, item.item()));
}
public void handleClick(Player player, int slot) {
@ -82,6 +94,10 @@ public class HotbarManager {
loadItems();
}
private boolean isEnabled() {
return plugin.getConfig().getBoolean(CONFIG_PATH + ".enabled", false);
}
private record HotbarItem(ItemStack item, List<String> actions) {
}
}

View file

@ -14,6 +14,10 @@ import java.util.UUID;
public class PlayerHiderManager {
private static final String CONFIG_PATH = "player_hider";
private static final String HIDDEN_PATH = CONFIG_PATH + ".hidden";
private static final String NOT_HIDDEN_PATH = CONFIG_PATH + ".not_hidden";
private final LoHub plugin;
private final Set<UUID> hiddenPlayers = new HashSet<>();
@ -22,23 +26,18 @@ public class PlayerHiderManager {
}
public void giveItem(Player player) {
if (!plugin.getConfig().getBoolean("player_hider.enabled", true)) {
if (!isEnabled()) {
return;
}
int slot = plugin.getConfig().getInt("player_hider.slot", 8);
player.getInventory().setItem(slot, getItem(player));
player.getInventory().setItem(getSlot(), getItem(player));
}
public ItemStack getItem(Player player) {
boolean hidden = hiddenPlayers.contains(player.getUniqueId());
String path = hidden ? "player_hider.hidden" : "player_hider.not_hidden";
Material material = Material.getMaterial(plugin.getConfig().getString(path + ".material", "LIME_DYE"));
if (material == null) {
material = Material.LIME_DYE;
}
boolean hidden = isHidden(player);
String path = hidden ? HIDDEN_PATH : NOT_HIDDEN_PATH;
Material material = getMaterial(path);
int amount = plugin.getConfig().getInt(path + ".amount", 1);
String displayName = plugin.getConfig().getString(path + ".display_name", "");
List<String> lore = plugin.getConfig().getStringList(path + ".lore");
@ -51,7 +50,7 @@ public class PlayerHiderManager {
}
public void toggle(Player player) {
if (hiddenPlayers.contains(player.getUniqueId())) {
if (isHidden(player)) {
showPlayers(player);
} else {
hidePlayers(player);
@ -62,33 +61,53 @@ public class PlayerHiderManager {
private void hidePlayers(Player player) {
hiddenPlayers.add(player.getUniqueId());
for (Player online : Bukkit.getOnlinePlayers()) {
if (!online.equals(player)) {
player.hidePlayer(plugin, online);
}
}
applyVisibility(player, false);
}
private void showPlayers(Player player) {
hiddenPlayers.remove(player.getUniqueId());
applyVisibility(player, true);
}
private void applyVisibility(Player player, boolean visible) {
for (Player online : Bukkit.getOnlinePlayers()) {
if (!online.equals(player)) {
player.showPlayer(plugin, online);
if (visible) {
player.showPlayer(plugin, online);
} else {
player.hidePlayer(plugin, online);
}
}
}
}
private void updateItem(Player player) {
int slot = plugin.getConfig().getInt("player_hider.slot", 8);
player.getInventory().setItem(slot, getItem(player));
player.getInventory().setItem(getSlot(), getItem(player));
}
public boolean isPlayerHiderSlot(int slot) {
return plugin.getConfig().getBoolean("player_hider.enabled", true)
&& slot == plugin.getConfig().getInt("player_hider.slot", 8);
return isEnabled() && slot == getSlot();
}
public void removePlayer(Player player) {
hiddenPlayers.remove(player.getUniqueId());
}
private boolean isEnabled() {
return plugin.getConfig().getBoolean(CONFIG_PATH + ".enabled", true);
}
private int getSlot() {
return plugin.getConfig().getInt(CONFIG_PATH + ".slot", 8);
}
private boolean isHidden(Player player) {
return hiddenPlayers.contains(player.getUniqueId());
}
private Material getMaterial(String path) {
String materialName = plugin.getConfig().getString(path + ".material", "LIME_DYE");
Material material = Material.getMaterial(materialName);
return material != null ? material : Material.LIME_DYE;
}
}

View file

@ -1,8 +1,8 @@
package com.loki.lohub.managers;
import com.loki.lohub.LoHub;
import com.loki.lohub.utils.PlaceholderUtil;
import com.loki.lohub.utils.TextUtil;
import com.loki.lohub.common.ConfigHelper;
import com.loki.lohub.common.TextFormatter;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
@ -18,6 +18,9 @@ import java.util.UUID;
public class ScoreboardManager {
private static final String CONFIG_PATH = "scoreboard";
private static final String OBJECTIVE_NAME = "lohub";
private final LoHub plugin;
private final Map<UUID, Scoreboard> scoreboards = new HashMap<>();
private int taskId = -1;
@ -27,12 +30,12 @@ public class ScoreboardManager {
}
public void start() {
if (!plugin.getConfig().getBoolean("scoreboard.enabled", false)) {
if (!ConfigHelper.isEnabled(plugin.getConfig(), CONFIG_PATH)) {
return;
}
if (plugin.getConfig().getBoolean("scoreboard.refresh.enabled", true)) {
int rate = plugin.getConfig().getInt("scoreboard.refresh.rate", 200);
if (shouldRefresh()) {
int rate = plugin.getConfig().getInt(CONFIG_PATH + ".refresh.rate", 200);
taskId = Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, this::updateAll, 0L, rate);
}
}
@ -46,12 +49,12 @@ public class ScoreboardManager {
}
public void setScoreboard(Player player, boolean delayed) {
if (!plugin.getConfig().getBoolean("scoreboard.enabled", false)) {
if (!ConfigHelper.isEnabled(plugin.getConfig(), CONFIG_PATH)) {
return;
}
if (delayed) {
int delay = plugin.getConfig().getInt("scoreboard.display_delay.server_enter", 60);
int delay = plugin.getConfig().getInt(CONFIG_PATH + ".display_delay.server_enter", 60);
Bukkit.getScheduler().runTaskLater(plugin, () -> createScoreboard(player), delay);
} else {
createScoreboard(player);
@ -60,7 +63,7 @@ public class ScoreboardManager {
private void createScoreboard(Player player) {
Scoreboard scoreboard = Bukkit.getScoreboardManager().getNewScoreboard();
Objective objective = scoreboard.registerNewObjective("lohub", "dummy", Component.text(""));
Objective objective = scoreboard.registerNewObjective(OBJECTIVE_NAME, "dummy", Component.text(""));
objective.setDisplaySlot(DisplaySlot.SIDEBAR);
updateScoreboard(player, scoreboard, objective);
@ -70,43 +73,56 @@ public class ScoreboardManager {
}
private void updateScoreboard(Player player, Scoreboard scoreboard, Objective objective) {
String title = plugin.getConfig().getString("scoreboard.title", "");
title = PlaceholderUtil.parse(title, player);
objective.displayName(Component.text(TextUtil.colorize(title)));
updateTitle(player, objective);
updateLines(player, scoreboard, objective);
}
List<String> lines = plugin.getConfig().getStringList("scoreboard.lines");
private void updateTitle(Player player, Objective objective) {
String title = plugin.getConfig().getString(CONFIG_PATH + ".title", "");
objective.displayName(Component.text(TextFormatter.format(title, player)));
}
private void updateLines(Player player, Scoreboard scoreboard, Objective objective) {
List<String> lines = plugin.getConfig().getStringList(CONFIG_PATH + ".lines");
int score = lines.size();
for (String line : lines) {
line = PlaceholderUtil.parse(line, player);
line = TextUtil.colorize(line);
String formatted = TextFormatter.format(line, player);
String teamName = "line_" + score;
Team team = scoreboard.getTeam(teamName);
if (team == null) {
team = scoreboard.registerNewTeam(teamName);
team.addEntry(teamName);
}
team.prefix(Component.text(line));
Team team = getOrCreateTeam(scoreboard, teamName);
team.prefix(Component.text(formatted));
objective.getScore(teamName).setScore(score);
score--;
}
}
private Team getOrCreateTeam(Scoreboard scoreboard, String teamName) {
Team team = scoreboard.getTeam(teamName);
if (team == null) {
team = scoreboard.registerNewTeam(teamName);
team.addEntry(teamName);
}
return team;
}
private void updateAll() {
for (Player player : Bukkit.getOnlinePlayers()) {
Scoreboard scoreboard = scoreboards.get(player.getUniqueId());
if (scoreboard != null) {
Objective objective = scoreboard.getObjective("lohub");
scoreboards.forEach((uuid, scoreboard) -> {
Player player = Bukkit.getPlayer(uuid);
if (player != null) {
Objective objective = scoreboard.getObjective(OBJECTIVE_NAME);
if (objective != null) {
updateScoreboard(player, scoreboard, objective);
}
}
}
});
}
public void removePlayer(Player player) {
scoreboards.remove(player.getUniqueId());
}
private boolean shouldRefresh() {
return plugin.getConfig().getBoolean(CONFIG_PATH + ".refresh.enabled", true);
}
}

View file

@ -4,83 +4,56 @@ import com.loki.lohub.LoHub;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import java.io.File;
import java.io.IOException;
public class SpawnManager {
private final LoHub plugin;
private final File spawnFile;
private FileConfiguration spawnConfig;
private Location spawnLocation;
public SpawnManager(LoHub plugin) {
this.plugin = plugin;
this.spawnFile = new File(plugin.getDataFolder(), "spawn.yml");
loadSpawn();
}
private void loadSpawn() {
if (!spawnFile.exists()) {
try {
spawnFile.createNewFile();
} catch (IOException e) {
plugin.getLogger().severe("Failed to create spawn.yml: " + e.getMessage());
}
}
spawnConfig = YamlConfiguration.loadConfiguration(spawnFile);
if (spawnConfig.contains("spawn")) {
String worldName = spawnConfig.getString("spawn.world");
double x = spawnConfig.getDouble("spawn.x");
double y = spawnConfig.getDouble("spawn.y");
double z = spawnConfig.getDouble("spawn.z");
float yaw = (float) spawnConfig.getDouble("spawn.yaw");
float pitch = (float) spawnConfig.getDouble("spawn.pitch");
World world = Bukkit.getWorld(worldName);
if (world != null) {
spawnLocation = new Location(world, x, y, z, yaw, pitch);
plugin.getLogger().info("Spawn loaded: " + worldName + " (" + x + ", " + y + ", " + z + ")");
} else {
plugin.getLogger().warning("World '" + worldName + "' not found!");
}
}
}
public Location getSpawn() {
return spawnLocation;
String worldName = plugin.getConfigManager().getData().getString("spawn.world");
if (worldName == null || worldName.equals("null")) {
return null;
}
World world = Bukkit.getWorld(worldName);
if (world == null) {
plugin.getLogger().warning("World '" + worldName + "' not found!");
return null;
}
double x = plugin.getConfigManager().getData().getDouble("spawn.x");
double y = plugin.getConfigManager().getData().getDouble("spawn.y");
double z = plugin.getConfigManager().getData().getDouble("spawn.z");
float yaw = (float) plugin.getConfigManager().getData().getDouble("spawn.yaw");
float pitch = (float) plugin.getConfigManager().getData().getDouble("spawn.pitch");
return new Location(world, x, y, z, yaw, pitch);
}
public void setSpawn(Location location) {
this.spawnLocation = location;
plugin.getConfigManager().getData().set("spawn.world", location.getWorld().getName());
plugin.getConfigManager().getData().set("spawn.x", location.getX());
plugin.getConfigManager().getData().set("spawn.y", location.getY());
plugin.getConfigManager().getData().set("spawn.z", location.getZ());
plugin.getConfigManager().getData().set("spawn.yaw", location.getYaw());
plugin.getConfigManager().getData().set("spawn.pitch", location.getPitch());
plugin.getConfigManager().saveData();
spawnConfig.set("spawn.world", location.getWorld().getName());
spawnConfig.set("spawn.x", location.getX());
spawnConfig.set("spawn.y", location.getY());
spawnConfig.set("spawn.z", location.getZ());
spawnConfig.set("spawn.yaw", location.getYaw());
spawnConfig.set("spawn.pitch", location.getPitch());
try {
spawnConfig.save(spawnFile);
plugin.getLogger().info("Spawn set: " + location.getWorld().getName() +
" (" + location.getBlockX() + ", " + location.getBlockY() + ", " + location.getBlockZ() + ")");
} catch (IOException e) {
plugin.getLogger().severe("Failed to save spawn.yml: " + e.getMessage());
}
plugin.getLogger().info("Spawn set: " + location.getWorld().getName() +
" (" + location.getBlockX() + ", " + location.getBlockY() + ", " + location.getBlockZ() + ")");
}
public void teleportToSpawn(Player player) {
if (spawnLocation == null) {
Location spawn = getSpawn();
if (spawn == null) {
return;
}
player.teleport(spawnLocation);
player.teleport(spawn);
}
}

View file

@ -1,8 +1,8 @@
package com.loki.lohub.managers;
import com.loki.lohub.LoHub;
import com.loki.lohub.utils.PlaceholderUtil;
import com.loki.lohub.utils.TextUtil;
import com.loki.lohub.common.ConfigHelper;
import com.loki.lohub.common.TextFormatter;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
@ -11,6 +11,8 @@ import java.util.List;
public class TablistManager {
private static final String CONFIG_PATH = "tablist";
private final LoHub plugin;
private int taskId = -1;
@ -19,12 +21,12 @@ public class TablistManager {
}
public void start() {
if (!plugin.getConfig().getBoolean("tablist.enabled", false)) {
if (!ConfigHelper.isEnabled(plugin.getConfig(), CONFIG_PATH)) {
return;
}
if (plugin.getConfig().getBoolean("tablist.refresh.enabled", true)) {
int rate = plugin.getConfig().getInt("tablist.refresh.rate", 400);
if (shouldRefresh()) {
int rate = plugin.getConfig().getInt(CONFIG_PATH + ".refresh.rate", 400);
taskId = Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, this::updateAll, 0L, rate);
}
}
@ -37,7 +39,7 @@ public class TablistManager {
}
public void setTablist(Player player) {
if (!plugin.getConfig().getBoolean("tablist.enabled", false)) {
if (!ConfigHelper.isEnabled(plugin.getConfig(), CONFIG_PATH)) {
return;
}
@ -45,36 +47,25 @@ public class TablistManager {
}
private void updateTablist(Player player) {
List<String> headerLines = plugin.getConfig().getStringList("tablist.header");
List<String> footerLines = plugin.getConfig().getStringList("tablist.footer");
StringBuilder header = new StringBuilder();
for (int i = 0; i < headerLines.size(); i++) {
String line = PlaceholderUtil.parse(headerLines.get(i), player);
header.append(TextUtil.colorize(line));
if (i < headerLines.size() - 1) {
header.append("\n");
}
}
StringBuilder footer = new StringBuilder();
for (int i = 0; i < footerLines.size(); i++) {
String line = PlaceholderUtil.parse(footerLines.get(i), player);
footer.append(TextUtil.colorize(line));
if (i < footerLines.size() - 1) {
footer.append("\n");
}
}
String header = formatLines(CONFIG_PATH + ".header", player);
String footer = formatLines(CONFIG_PATH + ".footer", player);
player.sendPlayerListHeaderAndFooter(
Component.text(header.toString()),
Component.text(footer.toString())
Component.text(header),
Component.text(footer)
);
}
private String formatLines(String path, Player player) {
List<String> lines = plugin.getConfig().getStringList(path);
return TextFormatter.joinLines(lines, player, "\n");
}
private void updateAll() {
for (Player player : Bukkit.getOnlinePlayers()) {
updateTablist(player);
}
Bukkit.getOnlinePlayers().forEach(this::updateTablist);
}
private boolean shouldRefresh() {
return plugin.getConfig().getBoolean(CONFIG_PATH + ".refresh.enabled", true);
}
}

View file

@ -0,0 +1,105 @@
# ╔══════════════════════════════════════════════════════════════════════════════╗
# ║ LoHub Custom Commands Configuration ║
# ║ Create custom commands with actions ║
# ╚══════════════════════════════════════════════════════════════════════════════╝
#
# ACTIONS:
# [MESSAGE] text - Send message to player
# [BROADCAST] text - Broadcast to all players
# [TITLE] title;subtitle;fadeIn;stay;fadeOut - Send title
# [ACTIONBAR] text - Send actionbar message
# [SOUND] sound - Play sound
# [COMMAND] command - Execute as player
# [CONSOLE] command - Execute as console
# [GAMEMODE] mode - Change gamemode
# [EFFECT] effect;level - Give potion effect
# [CLOSE] - Close inventory
#
# PLACEHOLDERS:
# %player% - Player name
# %player_displayname% - Player display name
# %online% - Online players count
# %online_max% - Max players
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# | CUSTOM COMMANDS |
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
custom_commands:
# Discord command
discord:
aliases:
- discord
- дискорд
- ds
permission: null # null = no permission required
actions:
- "[MESSAGE] &#9B59B6Discord: &#FFFFFFdiscord.example.net"
- "[SOUND] BLOCK_NOTE_BLOCK_PLING"
# Website command
website:
aliases:
- website
- site
- сайт
- web
permission: null
actions:
- "[MESSAGE] &#00D4FFWebsite: &#FFFFFFwww.example.net"
- "[SOUND] BLOCK_NOTE_BLOCK_PLING"
# Store command
store:
aliases:
- store
- shop
- магазин
- донат
permission: null
actions:
- "[MESSAGE] &#FF6B6BStore: &#FFFFFFstore.example.net"
- "[SOUND] BLOCK_NOTE_BLOCK_PLING"
# Clear inventory command (admin only)
clearinventory:
aliases:
- ci
- clearinv
- очиститьинв
permission: lohub.admin
actions:
- "[CONSOLE] minecraft:clear %player%"
- "[MESSAGE] {prefix}&#6BCB77Your inventory has been cleared!"
- "[SOUND] ENTITY_ITEM_PICKUP"
# Heal command (admin only)
heal:
aliases:
- heal
- хил
permission: lohub.admin
actions:
- "[CONSOLE] minecraft:effect give %player% minecraft:instant_health 1 10 true"
- "[CONSOLE] minecraft:effect give %player% minecraft:saturation 1 10 true"
- "[MESSAGE] {prefix}&#6BCB77You have been healed!"
- "[SOUND] ENTITY_PLAYER_LEVELUP"
# Rules command
rules:
aliases:
- rules
- правила
permission: null
actions:
- "[MESSAGE] &r"
- "[MESSAGE] &#FFD700╔═══ Server Rules ═══╗"
- "[MESSAGE] &r"
- "[MESSAGE] &#6BCB771. Be respectful to all players"
- "[MESSAGE] &#6BCB772. No cheating or hacking"
- "[MESSAGE] &#6BCB773. No spam in chat"
- "[MESSAGE] &#6BCB774. Follow staff instructions"
- "[MESSAGE] &#6BCB775. Have fun!"
- "[MESSAGE] &r"
- "[MESSAGE] &#FFD700╚═══════════════════════╝"
- "[SOUND] BLOCK_NOTE_BLOCK_PLING"

View file

@ -328,16 +328,14 @@ announcements:
- "&#FFD700╚═══════════════════════╝"
- "&r"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# | CUSTOM COMMANDS |
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# All custom commands are now in commands.yml
# You can create custom commands with actions there
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# | MESSAGES |
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
messages:
prefix: "&#00D4FF[&#00D4FFL&#00CAF7o&#00C0EEH&#00B7E6u&#00ADDDb&#0099CC]"
spawn-set: "&#6BCB77Spawn set successfully!"
spawn-teleport: "&#6BCB77Teleporting to spawn..."
no-spawn: "&#FF6B6BSpawn not set! Use /sethub"
no-permission: "&#FF6B6BYou don't have permission!"
players-only: "&#FF6B6BThis command is only for players!"
config-reloaded: "&#6BCB77Configuration reloaded!"
usage: "&#FFD700Usage: /lohub reload"
# All messages are now in messages.yml
# You can customize the plugin prefix and all messages there

View file

@ -0,0 +1,17 @@
# ╔══════════════════════════════════════════════════════════════════════════════╗
# ║ LoHub Data Storage ║
# ║ DO NOT EDIT THIS FILE MANUALLY! ║
# ║ This file is automatically managed by the plugin ║
# ╚══════════════════════════════════════════════════════════════════════════════╝
# Chat lock state
chat_locked: false
# Spawn location (set via /sethub command)
spawn:
world: null
x: 0.0
y: 0.0
z: 0.0
yaw: 0.0
pitch: 0.0

View file

@ -0,0 +1,84 @@
# ╔══════════════════════════════════════════════════════════════════════════════╗
# ║ LoHub Messages Configuration ║
# ║ All plugin messages in one place ║
# ╚══════════════════════════════════════════════════════════════════════════════╝
# Plugin prefix (used in all messages with {prefix} placeholder)
prefix: "&#00D4FF[&#00D4FFL&#00CAF7o&#00C0EEH&#00B7E6u&#00ADDDb&#0099CC] "
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# | GENERAL MESSAGES |
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
general:
no-permission: "{prefix}&#FF6B6BYou don't have permission!"
players-only: "{prefix}&#FF6B6BThis command is only for players!"
config-reloaded: "{prefix}&#6BCB77Configuration reloaded!"
invalid-player: "{prefix}&#FF6B6BPlayer %player% not found!"
cooldown-active: "{prefix}&#FF6B6BPlease wait %time%s before using this again!"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# | SPAWN MESSAGES |
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
spawn:
set: "{prefix}&#6BCB77Spawn set successfully!"
teleport: "{prefix}&#6BCB77Teleporting to spawn..."
no-spawn: "{prefix}&#FF6B6BSpawn not set! Use /sethub"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# | CHAT LOCK MESSAGES |
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
chat:
locked: "{prefix}&#FF6B6BChat is currently locked!"
lock-enabled: "{prefix}&#6BCB77Chat has been locked!"
lock-disabled: "{prefix}&#FF6B6BChat has been unlocked!"
lock-broadcast-enabled: "{prefix}&#FFD700%player% &#AAAAAAhas locked the chat!"
lock-broadcast-disabled: "{prefix}&#FFD700%player% &#AAAAAAhas unlocked the chat!"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# | PLAYER HIDER MESSAGES |
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
player-hider:
hidden: "{prefix}&#FF6B6BPlayers are now hidden!"
shown: "{prefix}&#6BCB77Players are now visible!"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# | WORLD PROTECTION MESSAGES |
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
protection:
item-drop: "{prefix}&#FF6B6BYou cannot drop items here!"
item-pickup: "{prefix}&#FF6B6BYou cannot pickup items here!"
block-place: "{prefix}&#FF6B6BYou cannot place blocks here!"
block-break: "{prefix}&#FF6B6BYou cannot break blocks here!"
block-interact: "{prefix}&#FF6B6BYou cannot interact with this!"
player-pvp: "{prefix}&#FF6B6BPVP is disabled here!"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# | DOUBLE JUMP MESSAGES |
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
double-jump:
cooldown: "{prefix}&#FF6B6BWait %time%s before double jumping again!"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# | COMMAND MESSAGES |
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
commands:
usage-lohub: "{prefix}&#FFD700Usage: /lohub reload"
usage-hub: "{prefix}&#FFD700Usage: /hub"
usage-sethub: "{prefix}&#FFD700Usage: /sethub"
usage-chatlock: "{prefix}&#FFD700Usage: /chatlock"
command-blocked: "{prefix}&#FF6B6BThis command is blocked!"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# | ANTI-SWEAR MESSAGES |
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
anti-swear:
blocked: "{prefix}&#FF6B6BYour message contains inappropriate language!"
admin-notify: "{prefix}&#FFD700%player% &#AAAAAAattempted to say: &#FF6B6B%word%"

View file

@ -27,6 +27,12 @@ commands:
description: LoHub admin command
usage: /lohub [reload]
permission: lohub.admin
chatlock:
description: Lock/unlock chat
usage: /chatlock
permission: lohub.chatlock
aliases: [ lockchat, чатлок ]
permissions:
lohub.hub:
@ -38,3 +44,9 @@ permissions:
lohub.bypass.protection:
description: Bypass world protection
default: op
lohub.chatlock:
description: Can lock/unlock chat
default: op
lohub.chatlock.bypass:
description: Can bypass chat lock
default: op

View file

@ -0,0 +1,139 @@
# ╔══════════════════════════════════════════════════════════════════════════════╗
# ║ LoHub Server Selector Configuration ║
# ║ GUI menu for server selection ║
# ╚══════════════════════════════════════════════════════════════════════════════╝
#
# This file defines the server selector GUI that players can open
# You can customize the items, layout, and actions
#
# ITEM ACTIONS:
# [MESSAGE] text - Send message to player
# [BROADCAST] text - Broadcast to all players
# [TITLE] title;subtitle;fadeIn;stay;fadeOut - Send title
# [ACTIONBAR] text - Send actionbar message
# [SOUND] sound - Play sound
# [COMMAND] command - Execute as player
# [CONSOLE] command - Execute as console
# [PROXY] server - Send player to BungeeCord/Velocity server
# [CLOSE] - Close inventory
#
# ITEM FLAGS:
# - HIDE_ATTRIBUTES
# - HIDE_DESTROYS
# - HIDE_ENCHANTS
# - HIDE_PLACED_ON
# - HIDE_POTION_EFFECTS
# - HIDE_UNBREAKABLE
#
# PLAYER HEADS:
# material: PLAYER_HEAD
# username: <player_name> # OR
# base64: <base64_texture>
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# | SERVER SELECTOR GUI |
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# GUI settings
gui:
# Title of the inventory
title: "&#FFD700S&#FFC700e&#FFB700r&#FFA700v&#FF9700e&#FF8700r &#FF7700S&#FF6700e&#FF5700l&#FF4700e&#FF3700c&#FF2700t&#FF1700o&#FF0700r"
# Number of rows (1-6, each row = 9 slots)
rows: 3
# Auto-update GUI (for placeholders)
refresh:
enabled: false
rate: 40 # In ticks (20 ticks = 1 second)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# | GUI ITEMS |
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
items:
# Filler item (decorative glass)
filler:
material: GRAY_STAINED_GLASS_PANE
slot: -1 # -1 = fill all empty slots
amount: 1
display_name: " "
item_flags:
- HIDE_ATTRIBUTES
# Survival server
survival:
material: GRASS_BLOCK
slot: 11
amount: 1
glow: true
display_name: "&#6BCB77S&#62C973u&#5AC76Fr&#51C56Bv&#49C367i&#40C163v&#38BF5Fa&#2FBD5Bl"
lore:
- ""
- "&#AAAAAAClassic survival experience"
- "&#AAAAAABuild, mine, and survive!"
- ""
- "&#FFD700Click to join!"
actions:
- "[CLOSE]"
- "[MESSAGE] {prefix}&#6BCB77Connecting to Survival..."
- "[SOUND] ENTITY_ENDERMAN_TELEPORT"
# For BungeeCord/Velocity:
# - "[PROXY] survival"
# For Multiverse-Core (multi-world):
# - "[CONSOLE] mvtp %player% survival_world"
# Creative server
creative:
material: COMMAND_BLOCK
slot: 13
amount: 1
glow: true
display_name: "&#FF6B6BC&#FF5757r&#FF4343e&#FF2F2Fa&#FF1B1Bt&#FF0707i&#F30000v&#DF0000e"
lore:
- ""
- "&#AAAAAAUnlimited creativity!"
- "&#AAAAAABuild anything you imagine"
- ""
- "&#FFD700Click to join!"
actions:
- "[CLOSE]"
- "[MESSAGE] {prefix}&#FF6B6BConnecting to Creative..."
- "[SOUND] ENTITY_ENDERMAN_TELEPORT"
# For BungeeCord/Velocity:
# - "[PROXY] creative"
# For Multiverse-Core:
# - "[CONSOLE] mvtp %player% creative_world"
# Minigames server
minigames:
material: DIAMOND_SWORD
slot: 15
amount: 1
glow: true
display_name: "&#9B59B6M&#8E4FB0i&#8145AAn&#7A3BA4i&#6D319Eg&#603798a&#532D92m&#462389e&#39197Fs"
lore:
- ""
- "&#AAAAAAFun minigames and competitions"
- "&#AAAAAAPlay with friends!"
- ""
- "&#FFD700Click to join!"
actions:
- "[CLOSE]"
- "[MESSAGE] {prefix}&#9B59B6Connecting to Minigames..."
- "[SOUND] ENTITY_ENDERMAN_TELEPORT"
# For BungeeCord/Velocity:
# - "[PROXY] minigames"
# Close button
close:
material: BARRIER
slot: 22
amount: 1
display_name: "&#FF6B6BClose"
lore:
- ""
- "&#AAAAAAClick to close this menu"
actions:
- "[CLOSE]"
- "[SOUND] BLOCK_CHEST_CLOSE"

View file

@ -0,0 +1,81 @@
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.data.model.MuteData;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class MuteHistoryManager {
private final Map<UUID, List<MuteData.MuteHistoryEntry>> history = new ConcurrentHashMap<>();
private final File historyFile;
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
public MuteHistoryManager(File historyFile) {
this.historyFile = historyFile;
load();
}
public void addEntry(UUID uuid, String playerName, long duration, String reason, String mutedBy) {
List<MuteData.MuteHistoryEntry> entries = history.computeIfAbsent(uuid, k -> new ArrayList<>());
entries.add(new MuteData.MuteHistoryEntry(
playerName,
duration,
reason,
mutedBy,
System.currentTimeMillis(),
false,
null,
0
));
}
public void updateUnmute(UUID uuid, String unmutedBy) {
List<MuteData.MuteHistoryEntry> entries = history.get(uuid);
if (entries != null && !entries.isEmpty()) {
MuteData.MuteHistoryEntry lastEntry = entries.get(entries.size() - 1);
if (lastEntry.unmutedBy == null) {
lastEntry.unmuted = true;
lastEntry.unmutedBy = unmutedBy;
lastEntry.unmutedAt = System.currentTimeMillis();
}
}
}
public List<MuteData.MuteHistoryEntry> getHistory(UUID uuid) {
return history.getOrDefault(uuid, Collections.emptyList());
}
public void save() {
try (FileWriter writer = new FileWriter(historyFile)) {
gson.toJson(history, writer);
} catch (IOException e) {
e.printStackTrace();
}
}
private void load() {
if (!historyFile.exists()) {
return;
}
try (FileReader reader = new FileReader(historyFile)) {
Type type = new TypeToken<Map<UUID, List<MuteData.MuteHistoryEntry>>>() {
}.getType();
Map<UUID, List<MuteData.MuteHistoryEntry>> loaded = gson.fromJson(reader, type);
if (loaded != null) {
history.putAll(loaded);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

View file

@ -6,6 +6,7 @@ import com.google.gson.reflect.TypeToken;
import com.loki.lochat.api.service.MuteService;
import com.loki.lochat.data.model.MuteData;
import com.loki.lochat.gradient.util.FoliaUtil;
import com.loki.lochat.utils.TimeFormatter;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
@ -14,23 +15,19 @@ import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* Реализация сервиса мутов
*/
public class MuteServiceImpl implements MuteService {
private final JavaPlugin plugin;
private final Map<UUID, MuteData> mutes = new ConcurrentHashMap<>();
private final Map<UUID, List<MuteData.MuteHistoryEntry>> history = new ConcurrentHashMap<>();
private final MuteHistoryManager historyManager;
private final File dataFile;
private final File historyFile;
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
public MuteServiceImpl(JavaPlugin plugin) {
this.plugin = plugin;
this.dataFile = new File(plugin.getDataFolder(), "mutes.json");
this.historyFile = new File(plugin.getDataFolder(), "mute-history.json");
this.historyManager = new MuteHistoryManager(new File(plugin.getDataFolder(), "mute-history.json"));
load();
loadHistory();
}
@Override
@ -39,9 +36,7 @@ public class MuteServiceImpl implements MuteService {
MuteData data = new MuteData(uuid, playerName, endTime, reason, mutedBy);
mutes.put(uuid, data);
// Добавляем в историю
addToHistory(uuid, playerName, duration, reason, mutedBy);
historyManager.addEntry(uuid, playerName, duration, reason, mutedBy);
saveAsync();
}
@ -49,7 +44,7 @@ public class MuteServiceImpl implements MuteService {
public boolean unmute(UUID uuid, String unmutedBy) {
MuteData removed = mutes.remove(uuid);
if (removed != null) {
updateHistoryUnmute(uuid, unmutedBy);
historyManager.updateUnmute(uuid, unmutedBy);
saveAsync();
return true;
}
@ -59,9 +54,11 @@ public class MuteServiceImpl implements MuteService {
@Override
public boolean isMuted(UUID uuid) {
MuteData data = mutes.get(uuid);
if (data == null) return false;
if (data == null) {
return false;
}
if (data.getEndTime() > 0 && System.currentTimeMillis() > data.getEndTime()) {
if (isExpired(data)) {
mutes.remove(uuid);
saveAsync();
return false;
@ -71,66 +68,35 @@ public class MuteServiceImpl implements MuteService {
@Override
public MuteData getMuteData(UUID uuid) {
if (!isMuted(uuid)) return null;
return mutes.get(uuid);
return isMuted(uuid) ? mutes.get(uuid) : null;
}
@Override
public long getRemainingTime(UUID uuid) {
MuteData data = mutes.get(uuid);
if (data == null) return 0;
if (data.getEndTime() == 0) return -1; // Перманентный
if (data == null) {
return 0;
}
if (data.getEndTime() == 0) {
return -1;
}
return Math.max(0, data.getEndTime() - System.currentTimeMillis());
}
@Override
public String formatTime(long millis) {
if (millis <= 0) return "0с";
long seconds = millis / 1000;
long minutes = seconds / 60;
long hours = minutes / 60;
long days = hours / 24;
if (days > 0) {
return days + "д " + (hours % 24) + "ч";
} else if (hours > 0) {
return hours + "ч " + (minutes % 60) + "м";
} else if (minutes > 0) {
return minutes + "м " + (seconds % 60) + "с";
} else {
return seconds + "с";
}
return TimeFormatter.format(millis);
}
@Override
public long parseTime(String timeStr) {
if (timeStr == null || timeStr.isEmpty()) return 0;
timeStr = timeStr.toLowerCase();
long multiplier = 1000; // базово в миллисекундах
try {
if (timeStr.endsWith("d")) {
return Long.parseLong(timeStr.replace("d", "")) * 24 * 60 * 60 * multiplier;
} else if (timeStr.endsWith("h")) {
return Long.parseLong(timeStr.replace("h", "")) * 60 * 60 * multiplier;
} else if (timeStr.endsWith("m")) {
return Long.parseLong(timeStr.replace("m", "")) * 60 * multiplier;
} else if (timeStr.endsWith("s")) {
return Long.parseLong(timeStr.replace("s", "")) * multiplier;
} else {
return Long.parseLong(timeStr) * 60 * multiplier;
}
} catch (NumberFormatException e) {
return -1;
}
return TimeFormatter.parse(timeStr);
}
@Override
public long getMaxDuration(Player player) {
if (player.hasPermission("lochat.mute.dur.perm")) {
return 0; // Перманентный
return 0;
}
String[] durations = {"30d", "14d", "7d", "3d", "1d", "12h", "6h", "1h", "30m", "10m"};
@ -140,7 +106,7 @@ public class MuteServiceImpl implements MuteService {
}
}
return -1; // Нет прав
return -1;
}
@Override
@ -154,37 +120,39 @@ public class MuteServiceImpl implements MuteService {
}
long maxDuration = getMaxDuration(player);
if (maxDuration == -1) return false;
if (maxDuration == 0) return true;
if (maxDuration == -1) {
return false;
}
if (maxDuration == 0) {
return true;
}
return duration <= maxDuration;
}
@Override
public List<MuteData.MuteHistoryEntry> getPlayerHistory(UUID uuid) {
return history.getOrDefault(uuid, new ArrayList<>());
return historyManager.getHistory(uuid);
}
@Override
public List<MuteData.MuteHistoryEntry> getMutesByOperator(String operatorName) {
List<MuteData.MuteHistoryEntry> result = new ArrayList<>();
for (List<MuteData.MuteHistoryEntry> playerHistory : history.values()) {
for (MuteData.MuteHistoryEntry entry : playerHistory) {
if (entry.mutedBy != null && entry.mutedBy.equalsIgnoreCase(operatorName)) {
result.add(entry);
}
}
}
mutes.keySet().forEach(uuid -> {
historyManager.getHistory(uuid).stream()
.filter(entry -> entry.mutedBy != null && entry.mutedBy.equalsIgnoreCase(operatorName))
.forEach(result::add);
});
result.sort((a, b) -> Long.compare(b.mutedAt, a.mutedAt));
return result;
}
@Override
public UUID getUUIDByName(String name) {
for (Map.Entry<UUID, List<MuteData.MuteHistoryEntry>> entry : history.entrySet()) {
for (MuteData.MuteHistoryEntry historyEntry : entry.getValue()) {
if (historyEntry.playerName != null && historyEntry.playerName.equalsIgnoreCase(name)) {
return entry.getKey();
for (UUID uuid : mutes.keySet()) {
for (MuteData.MuteHistoryEntry entry : historyManager.getHistory(uuid)) {
if (entry.playerName != null && entry.playerName.equalsIgnoreCase(name)) {
return uuid;
}
}
}
@ -193,10 +161,7 @@ public class MuteServiceImpl implements MuteService {
@Override
public Map<UUID, MuteData> getActiveMutes() {
mutes.entrySet().removeIf(entry -> {
MuteData data = entry.getValue();
return data.getEndTime() > 0 && System.currentTimeMillis() > data.getEndTime();
});
mutes.entrySet().removeIf(entry -> isExpired(entry.getValue()));
return new HashMap<>(mutes);
}
@ -212,23 +177,37 @@ public class MuteServiceImpl implements MuteService {
@Override
public void save() {
try {
if (!dataFile.getParentFile().exists()) {
dataFile.getParentFile().mkdirs();
}
Map<String, MuteData> toSave = new ConcurrentHashMap<>();
mutes.forEach((uuid, data) -> toSave.put(uuid.toString(), data));
try (Writer writer = new FileWriter(dataFile)) {
gson.toJson(toSave, writer);
}
ensureDataFolderExists();
saveMutes();
historyManager.save();
} catch (IOException e) {
plugin.getLogger().warning("Ошибка сохранения мутов: " + e.getMessage());
plugin.getLogger().warning("Error saving mutes: " + e.getMessage());
}
}
private boolean isExpired(MuteData data) {
return data.getEndTime() > 0 && System.currentTimeMillis() > data.getEndTime();
}
private void ensureDataFolderExists() {
if (!dataFile.getParentFile().exists()) {
dataFile.getParentFile().mkdirs();
}
}
private void saveMutes() throws IOException {
Map<String, MuteData> toSave = new ConcurrentHashMap<>();
mutes.forEach((uuid, data) -> toSave.put(uuid.toString(), data));
try (Writer writer = new FileWriter(dataFile)) {
gson.toJson(toSave, writer);
}
}
private void load() {
if (!dataFile.exists()) return;
if (!dataFile.exists()) {
return;
}
try (Reader reader = new FileReader(dataFile)) {
Type type = new TypeToken<Map<String, MuteData>>() {
@ -243,86 +222,11 @@ public class MuteServiceImpl implements MuteService {
});
}
} catch (IOException e) {
plugin.getLogger().warning("Ошибка загрузки мутов: " + e.getMessage());
plugin.getLogger().warning("Error loading mutes: " + e.getMessage());
}
}
private void loadHistory() {
if (!historyFile.exists()) return;
try (Reader reader = new FileReader(historyFile)) {
Type type = new TypeToken<Map<String, List<MuteData.MuteHistoryEntry>>>() {
}.getType();
Map<String, List<MuteData.MuteHistoryEntry>> loaded = gson.fromJson(reader, type);
if (loaded != null) {
loaded.forEach((key, value) -> {
try {
history.put(UUID.fromString(key), new ArrayList<>(value));
} catch (IllegalArgumentException ignored) {
}
});
}
} catch (IOException e) {
plugin.getLogger().warning("Ошибка загрузки истории мутов: " + e.getMessage());
}
}
private void saveHistory() {
try {
if (!historyFile.getParentFile().exists()) {
historyFile.getParentFile().mkdirs();
}
Map<String, List<MuteData.MuteHistoryEntry>> toSave = new ConcurrentHashMap<>();
history.forEach((uuid, data) -> toSave.put(uuid.toString(), data));
try (Writer writer = new FileWriter(historyFile)) {
gson.toJson(toSave, writer);
}
} catch (IOException e) {
plugin.getLogger().warning("Ошибка сохранения истории мутов: " + e.getMessage());
}
}
private void addToHistory(UUID uuid, String playerName, long duration, String reason, String mutedBy) {
List<MuteData.MuteHistoryEntry> playerHistory = history.computeIfAbsent(uuid, k -> new ArrayList<>());
playerHistory.add(new MuteData.MuteHistoryEntry(
playerName,
duration,
reason,
mutedBy,
System.currentTimeMillis(),
false,
null,
0
));
saveHistoryAsync();
}
private void updateHistoryUnmute(UUID uuid, String unmutedBy) {
List<MuteData.MuteHistoryEntry> playerHistory = history.get(uuid);
if (playerHistory != null && !playerHistory.isEmpty()) {
for (int i = playerHistory.size() - 1; i >= 0; i--) {
MuteData.MuteHistoryEntry entry = playerHistory.get(i);
if (!entry.unmuted) {
entry.unmuted = true;
entry.unmutedBy = unmutedBy;
entry.unmutedAt = System.currentTimeMillis();
break;
}
}
}
saveHistoryAsync();
}
private void saveHistoryAsync() {
FoliaUtil.runAsync(plugin, this::saveHistory);
}
private void saveAsync() {
FoliaUtil.runAsync(plugin, () -> {
save();
saveHistory();
});
FoliaUtil.runAsync(plugin, this::save);
}
}

View file

@ -0,0 +1,59 @@
package com.loki.lochat.utils;
public final class TimeFormatter {
private TimeFormatter() {
}
public static String format(long millis) {
if (millis <= 0) {
return "0с";
}
long seconds = millis / 1000;
long minutes = seconds / 60;
long hours = minutes / 60;
long days = hours / 24;
if (days > 0) {
return days + "д " + (hours % 24) + "ч";
} else if (hours > 0) {
return hours + "ч " + (minutes % 60) + "м";
} else if (minutes > 0) {
return minutes + "м " + (seconds % 60) + "с";
} else {
return seconds + "с";
}
}
public static long parse(String timeStr) {
if (timeStr == null || timeStr.isEmpty()) {
return 0;
}
long totalMillis = 0;
StringBuilder number = new StringBuilder();
for (char c : timeStr.toCharArray()) {
if (Character.isDigit(c)) {
number.append(c);
} else if (number.length() > 0) {
long value = Long.parseLong(number.toString());
totalMillis += getMillisForUnit(c, value);
number.setLength(0);
}
}
return totalMillis;
}
private static long getMillisForUnit(char unit, long value) {
return switch (Character.toLowerCase(unit)) {
case 's', 'с' -> value * 1000;
case 'm', 'м' -> value * 60 * 1000;
case 'h', 'ч' -> value * 60 * 60 * 1000;
case 'd', 'д' -> value * 24 * 60 * 60 * 1000;
default -> 0;
};
}
}