feat: transition to VexlioHub, clean up modules, add effects & localize configs

- Rename and relocate package from DeluxeHubReloaded to VexlioHub
- Remove deprecated modules: antiwdl, scoreboard, tablist, pvp_mode, teleportation_bow, anti_swear, lockchat, vanish, holograms
- Refactor all commands to standard Bukkit CommandExecutor/TabCompleter registered dynamically via Bukkit CommandMap
- Add particle, sound, and trail configurations for launchpad and double jump modules
- Implement actionbar announcements and join title welcome animation modules
- Fully customize, translate to Czech, and restyle all configuration files in the 'uprava/' directory with the purple-cyan gradient theme
This commit is contained in:
admin
2026-06-24 23:53:11 +02:00
commit fa547dce64
99 changed files with 8999 additions and 0 deletions
@@ -0,0 +1,112 @@
package eu.milujukockoholky.vexliolobby.config;
import org.bukkit.configuration.InvalidConfigurationException;
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;
public class ConfigHandler {
private final JavaPlugin plugin;
private final String name;
private final File file;
private FileConfiguration configuration;
private boolean needsMigration;
public ConfigHandler(JavaPlugin plugin, String name) {
this.plugin = plugin;
this.name = name + ".yml";
this.file = new File(plugin.getDataFolder(), this.name);
this.configuration = new YamlConfiguration();
this.needsMigration = false;
}
/**
* Saves the default config if it doesn't exist, otherwise loads the existing config
* and marks it for migration.
*/
public void saveDefaultConfig() {
boolean fileExists = file.exists();
if (!fileExists) {
plugin.saveResource(name, false);
} else {
// If file exists, it might need migration when plugin updates
needsMigration = true;
}
try {
configuration.load(file);
} catch (InvalidConfigurationException | IOException e) {
handleConfigError(e);
}
}
/**
* Saves the current configuration to file
*/
public void save() {
if (configuration == null || file == null) return;
try {
getConfig().save(file);
} catch (IOException e) {
plugin.getLogger().severe("Failed to save configuration file: " + name + "" + e.getMessage());
}
}
/**
* Reloads the configuration from file
*/
public void reload() {
try {
configuration = YamlConfiguration.loadConfiguration(file);
} catch (Exception e) {
handleConfigError(e);
}
}
/**
* Gets the file configuration
*/
public FileConfiguration getConfig() {
return configuration;
}
/**
* Gets the file associated with this config handler
*/
public File getFile() {
return file;
}
/**
* Checks if this configuration needs migration
*/
public boolean needsMigration() {
return needsMigration;
}
/**
* Marks this configuration as migrated
*/
public void setMigrated() {
this.needsMigration = false;
}
/**
* Handle configuration errors
*/
private void handleConfigError(Exception e) {
e.printStackTrace();
plugin.getLogger().severe("============= CONFIGURATION ERROR =============");
plugin.getLogger().severe("There was an error loading " + name);
plugin.getLogger().severe("Please check for any obvious configuration mistakes");
plugin.getLogger().severe("such as using tabs for spaces or forgetting to end quotes");
plugin.getLogger().severe("before reporting to the developer. The plugin will now disable..");
plugin.getLogger().severe("============= CONFIGURATION ERROR =============");
plugin.getServer().getPluginManager().disablePlugin(plugin);
}
}
@@ -0,0 +1,76 @@
package eu.milujukockoholky.vexliolobby.config;
import eu.milujukockoholky.vexliolobby.VexlioHubPlugin;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
public class ConfigManager {
private final Map<ConfigType, ConfigHandler> configurations;
private ConfigMigrator migrator;
public ConfigManager() {
configurations = new HashMap<>();
}
public void loadFiles(VexlioHubPlugin plugin) {
// Initialize the config migrator
migrator = new ConfigMigrator(plugin);
// Register all config files
registerFile(ConfigType.SETTINGS, new ConfigHandler(plugin, "config"));
registerFile(ConfigType.MESSAGES, new ConfigHandler(plugin, "messages"));
registerFile(ConfigType.DATA, new ConfigHandler(plugin, "data"));
registerFile(ConfigType.COMMANDS, new ConfigHandler(plugin, "commands"));
// Load all configs
configurations.values().forEach(ConfigHandler::saveDefaultConfig);
// Perform migrations if needed
migrateConfigs();
// Set the Messages configuration
Messages.setConfiguration(getFile(ConfigType.MESSAGES).getConfig());
}
/**
* Migrates all configurations that need updating
*/
private void migrateConfigs() {
for (Map.Entry<ConfigType, ConfigHandler> entry : configurations.entrySet()) {
ConfigHandler handler = entry.getValue();
if (handler.needsMigration()) {
if (migrator.migrateConfig(entry.getKey(), handler)) {
handler.setMigrated();
}
}
}
}
public ConfigHandler getFile(ConfigType type) {
return configurations.get(type);
}
public void reloadFiles() {
configurations.values().forEach(ConfigHandler::reload);
// Perform migrations after reload in case plugin has been updated
migrateConfigs();
Messages.setConfiguration(getFile(ConfigType.MESSAGES).getConfig());
}
public void saveFiles() {
configurations.values().forEach(ConfigHandler::save);
}
public void registerFile(ConfigType type, ConfigHandler config) {
configurations.put(type, config);
}
public FileConfiguration getFileConfiguration(File file) {
return YamlConfiguration.loadConfiguration(file);
}
}
@@ -0,0 +1,254 @@
package eu.milujukockoholky.vexliolobby.config;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
public class ConfigMigrator {
private final JavaPlugin plugin;
private final Version currentVersion;
private final List<VersionMigration> versionMigrations;
private final Set<String> excludedPaths;
/**
* Creates a new ConfigMigrator with the current plugin version
*/
public ConfigMigrator(JavaPlugin plugin) {
this.plugin = plugin;
this.currentVersion = Version.parse(plugin.getPluginMeta().getVersion());
this.versionMigrations = new ArrayList<>();
this.excludedPaths = new HashSet<>();
// Register paths to exclude from migration
registerExcludedPaths();
// Register any version-specific migrations here
registerMigrations();
}
/**
* Register paths that should be excluded from migration
*/
private void registerExcludedPaths() {
// Add the announcements section to excluded paths
excludedPaths.add("announcements");
excludedPaths.add("custom_join_items");
excludedPaths.add("join_events");
excludedPaths.add("custom_commands");
excludedPaths.add("commands");
excludedPaths.add("command_block");
excludedPaths.add("anti_swear");
excludedPaths.add("join_settings");
excludedPaths.add("pvp_mode");
// Add other paths to exclude if needed
// excludedPaths.add("another.path.to.exclude");
}
private void registerMigrations() {
// Example: Add migrations for specific version jumps
// versionMigrations.add(new VersionMigration(
// Version.parse("1.0.0"),
// Version.parse("1.1.0"),
// this::migrateFrom1_0_0To1_1_0
// ));
}
/**
* Migrates a configuration file, adding missing options from the default config
* while preserving existing values.
*
* @param configType The type of config to migrate
* @param configHandler The config handler containing the file
* @return True if changes were made, false otherwise
*/
public boolean migrateConfig(ConfigType configType, ConfigHandler configHandler) {
String fileName = getFileNameForType(configType);
if (fileName == null) {
plugin.getLogger().warning("No file name found for config type: " + configType);
return false;
}
// Get the resource from jar
InputStream defaultConfigStream = plugin.getResource(fileName);
if (defaultConfigStream == null) {
plugin.getLogger().warning("Could not find default configuration for: " + fileName);
return false;
}
// Load default config from jar
FileConfiguration defaultConfig = YamlConfiguration.loadConfiguration(
new InputStreamReader(defaultConfigStream));
// Get current config
FileConfiguration currentConfig = configHandler.getConfig();
// Track if we made changes
boolean changes = false;
// Check if we need to perform version-specific migrations
String storedVersion = currentConfig.getString("version", "0.0.0");
Version configVersion = Version.parse(storedVersion);
// Run version-specific migrations if config version is older than current version
if (configVersion.isOlderThan(currentVersion)) {
for (VersionMigration migration : versionMigrations) {
if (configVersion.isOlderThan(migration.getTargetVersion()) &&
(migration.getSourceVersion().isSameAs(configVersion) ||
migration.getSourceVersion().isOlderThan(configVersion))) {
plugin.getLogger().info("Applying migration from " +
migration.getSourceVersion() + " to " + migration.getTargetVersion() +
" for " + fileName);
// Apply the migration
if (migration.apply(configType, currentConfig)) {
changes = true;
}
}
}
// Update the version in the config
currentConfig.set("version", currentVersion.toString());
changes = true;
}
// Add missing sections and values
if (addMissingEntries(defaultConfig, currentConfig, "")) {
changes = true;
}
// Save if changes were made
if (changes) {
try {
configHandler.save();
plugin.getLogger().info("Updated configuration file: " + fileName);
} catch (Exception e) {
plugin.getLogger().log(Level.SEVERE, "Could not save updated configuration for: " + fileName, e);
}
}
return changes;
}
/**
* Recursively adds missing entries from default config to current config,
* but skips excluded paths
*
* @param defaultConfig The default configuration from resources
* @param currentConfig The current configuration file
* @param path The current path being examined
* @return True if changes were made, false otherwise
*/
private boolean addMissingEntries(FileConfiguration defaultConfig, FileConfiguration currentConfig, String path) {
boolean changes = false;
// Skip this path and its children if it's in the excluded paths
for (String excludedPath : excludedPaths) {
if (path.equals(excludedPath) || path.startsWith(excludedPath + ".")) {
return false;
}
}
Set<String> defaultKeys = path.isEmpty()
? defaultConfig.getKeys(false)
: defaultConfig.getConfigurationSection(path).getKeys(false);
for (String key : defaultKeys) {
String fullPath = path.isEmpty() ? key : path + "." + key;
// Skip excluded paths
boolean shouldSkip = false;
for (String excludedPath : excludedPaths) {
if (fullPath.equals(excludedPath) || fullPath.startsWith(excludedPath + ".")) {
shouldSkip = true;
break;
}
}
if (shouldSkip) {
continue;
}
if (!currentConfig.contains(fullPath)) {
// This key doesn't exist in the current config, so add it
currentConfig.set(fullPath, defaultConfig.get(fullPath));
changes = true;
plugin.getLogger().info("Added missing config option: " + fullPath);
} else if (defaultConfig.isConfigurationSection(fullPath)) {
// This is a section, so recursively check its children
changes |= addMissingEntries(defaultConfig, currentConfig, fullPath);
}
// If the path exists and isn't a section, keep the user's current value
}
return changes;
}
/**
* Gets the file name for the given config type
*/
private String getFileNameForType(ConfigType type) {
switch (type) {
case SETTINGS:
return "config.yml";
case MESSAGES:
return "messages.yml";
case COMMANDS:
return "commands.yml";
case DATA:
return "data.yml";
default:
return null;
}
}
/**
* Class representing a migration between two specific versions
*/
private class VersionMigration {
private final Version sourceVersion;
private final Version targetVersion;
private final MigrationFunction migrationFunction;
@SuppressWarnings("unused")
public VersionMigration(Version sourceVersion, Version targetVersion,
MigrationFunction migrationFunction) {
this.sourceVersion = sourceVersion;
this.targetVersion = targetVersion;
this.migrationFunction = migrationFunction;
}
public Version getSourceVersion() {
return sourceVersion;
}
public Version getTargetVersion() {
return targetVersion;
}
public boolean apply(ConfigType configType, FileConfiguration config) {
return migrationFunction.migrate(configType, config);
}
}
/**
* Functional interface for migration functions
*/
@FunctionalInterface
private interface MigrationFunction {
boolean migrate(ConfigType configType, FileConfiguration config);
}
}
@@ -0,0 +1,10 @@
package eu.milujukockoholky.vexliolobby.config;
public enum ConfigType {
SETTINGS,
MESSAGES,
COMMANDS,
DATA
}
@@ -0,0 +1,92 @@
package eu.milujukockoholky.vexliolobby.config;
import eu.milujukockoholky.vexliolobby.utility.TextUtil;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration;
import java.util.List;
public enum Messages {
PREFIX("GENERAL.PREFIX"),
NO_PERMISSION("GENERAL.NO_PERMISSION"),
CUSTOM_COMMAND_NO_PERMISSION("GENERAL.CUSTOM_COMMAND_NO_PERMISSION"),
INVALID_PLAYER("GENERAL.INVALID_PLAYER"),
CONFIG_RELOAD("GENERAL.CONFIG_RELOAD"),
COOLDOWN_ACTIVE("GENERAL.COOLDOWN_ACTIVE"),
GAMEMODE_CHANGE("GAMEMODE.GAMEMODE_CHANGE"),
GAMEMODE_CHANGE_OTHER("GAMEMODE.GAMEMODE_CHANGE_OTHER"),
GAMEMODE_INVALID("GAMEMODE.GAMEMODE_INVALID"),
FLIGHT_ENABLE("FLIGHT.ENABLE"),
FLIGHT_ENABLE_OTHER("FLIGHT.ENABLE_OTHER"),
FLIGHT_DISABLE("FLIGHT.DISABLE"),
FLIGHT_DISABLE_OTHER("FLIGHT.DISABLE_OTHER"),
PLAYER_HIDER_HIDDEN("PLAYER_HIDER.HIDDEN"),
PLAYER_HIDER_SHOWN("PLAYER_HIDER.SHOWN"),
SET_LOBBY("LOBBY.SET_LOBBY"),
CLEARCHAT("CHAT.CLEARCHAT"),
CLEARCHAT_PLAYER("CHAT.CLEARCHAT_PLAYER"),
DOUBLE_JUMP_COOLDOWN("DOUBLE_JUMP.COOLDOWN_ACTIVE"),
EVENT_ITEM_DROP("WORLD_EVENT_MODIFICATIONS.ITEM_DROP"),
EVENT_ITEM_PICKUP("WORLD_EVENT_MODIFICATIONS.ITEM_PICKUP"),
EVENT_BLOCK_PLACE("WORLD_EVENT_MODIFICATIONS.BLOCK_PLACE"),
EVENT_BLOCK_BREAK("WORLD_EVENT_MODIFICATIONS.BLOCK_BREAK"),
EVENT_BLOCK_INTERACT("WORLD_EVENT_MODIFICATIONS.BLOCK_INTERACT"),
BUILD_MODE_ENABLED_ACTIONBAR("BUILD_MODE.ENABLED_ACTION_BAR"),
BUILD_MODE_ENABLED("BUILD_MODE.ENABLED"),
BUILD_MODE_DISABLED("BUILD_MODE.DISABLED"),
BUILD_MODE_COMMAND_TARGET_NOT_FOUND("BUILD_MODE.TARGET_NOT_FOUND"),
BUILD_MODE_COMMAND_ENABLED_FOR_TARGET("BUILD_MODE.ENABLED_FOR_TARGET");
private static FileConfiguration config;
private final String path;
Messages(String path) {
this.path = path;
}
public static void setConfiguration(FileConfiguration c) {
config = c;
}
public String toString() {
Object value = config.get("Messages." + this.path);
String message;
if (value == null) {
message = "VexlioHub: message not found (" + this.path + ") - Check your messages.yml!";
} else {
message = value instanceof List ? TextUtil.fromList((List<?>) value) : value.toString();
}
return message;
}
public void send(CommandSender receiver, Object... replacements) {
String message = toString();
if (!message.isEmpty()) {
receiver.sendMessage(TextUtil.color(replace(message, replacements)));
}
}
private String replace(String message, Object... replacements) {
for (int i = 0; i < replacements.length; i += 2) {
if (i + 1 >= replacements.length) break;
message = message.replace(String.valueOf(replacements[i]), String.valueOf(replacements[i + 1]));
}
String prefix = config.getString("Messages." + PREFIX.getPath());
return message.replace("%prefix%", prefix != null && !prefix.isEmpty() ? prefix : "");
}
public String getPath() {
return this.path;
}
}
@@ -0,0 +1,97 @@
package eu.milujukockoholky.vexliolobby.config;
/**
* Represents a version in the format major.minor.patch
* Used for tracking plugin versions and migrations
*/
public class Version implements Comparable<Version> {
private final int major;
private final int minor;
private final int patch;
/**
* Constructs a version from major, minor, and patch values
*/
public Version(int major, int minor, int patch) {
this.major = major;
this.minor = minor;
this.patch = patch;
}
/**
* Parses a version string in the format "major.minor.patch"
*/
public static Version parse(String versionStr) {
String[] parts = versionStr.split("\\.");
int major = 0, minor = 0, patch = 0;
if (parts.length >= 1) {
try {
major = Integer.parseInt(parts[0]);
} catch (NumberFormatException e) {
// Ignore and use default
}
}
if (parts.length >= 2) {
try {
minor = Integer.parseInt(parts[1]);
} catch (NumberFormatException e) {
// Ignore and use default
}
}
if (parts.length >= 3) {
try {
patch = Integer.parseInt(parts[2]);
} catch (NumberFormatException e) {
// Ignore and use default
}
}
return new Version(major, minor, patch);
}
/**
* Compares this version with another version
*/
@Override
public int compareTo(Version other) {
if (this.major != other.major) {
return Integer.compare(this.major, other.major);
}
if (this.minor != other.minor) {
return Integer.compare(this.minor, other.minor);
}
return Integer.compare(this.patch, other.patch);
}
/**
* Checks if this version is newer than another version
*/
public boolean isNewerThan(Version other) {
return compareTo(other) > 0;
}
/**
* Checks if this version is the same as another version
*/
public boolean isSameAs(Version other) {
return compareTo(other) == 0;
}
/**
* Checks if this version is older than another version
*/
public boolean isOlderThan(Version other) {
return compareTo(other) < 0;
}
@Override
public String toString() {
return major + "." + minor + "." + patch;
}
}