diff --git a/src/main/java/dev/loki/lomines/core/Mines.java b/src/main/java/dev/loki/lomines/core/Mines.java index 0284b24..b6413c2 100644 --- a/src/main/java/dev/loki/lomines/core/Mines.java +++ b/src/main/java/dev/loki/lomines/core/Mines.java @@ -6,6 +6,7 @@ import dev.loki.lomines.core.service.MineFileManager; import dev.loki.lomines.core.service.MineRepository; import dev.loki.lomines.data.config.ConfigLoader; import dev.loki.lomines.data.config.MineConfig; +import dev.loki.lomines.integration.worldguard.WorldGuardRegionService; import org.bukkit.Location; import java.io.IOException; @@ -26,6 +27,7 @@ public final class Mines { private final MineFileManager fileManager; private final MineRepository repository; private final MaskScanService maskScanService; + private final WorldGuardRegionService worldGuardService; public Mines(LoMinesPlugin plugin) { this.plugin = plugin; @@ -33,6 +35,7 @@ public final class Mines { this.fileManager = new MineFileManager(minesFolder); this.repository = new MineRepository(plugin, fileManager); this.maskScanService = new MaskScanService(repository, fileManager); + this.worldGuardService = new WorldGuardRegionService(plugin); } /** @@ -77,6 +80,7 @@ public final class Mines { /** * Creates a new mine; optional corners define {@code selection.1} and {@code selection.2} (same world required). + * If WorldGuard integration is enabled, creates a region for the mine. */ public void create(String name, Location corner1, Location corner2) throws IOException { if (repository.exists(name)) { @@ -96,12 +100,19 @@ public final class Mines { throw new IOException("Failed to parse created mine configuration: " + e.getMessage(), e); } + // Create WorldGuard region if enabled + String regionName = worldGuardService.createRegion(name, config); + if (regionName != null) { + plugin.loLogger().info("Created WorldGuard region '" + regionName + "' for mine: " + name); + } + repository.createAndStart(name, config); plugin.loLogger().info("Created mine: " + name); } /** * Deletes a mine by name. + * Also deletes the associated WorldGuard region if it exists. */ public void delete(String name) throws IOException { Mine mine = repository.remove(name); @@ -111,6 +122,14 @@ public final class Mines { } mine.stop(); + + // Delete WorldGuard region if exists + MineConfig config = mine.getConfig(); + boolean wgDeleted = worldGuardService.deleteRegion(name, config); + if (wgDeleted) { + plugin.loLogger().info("Deleted WorldGuard region for mine: " + name); + } + fileManager.deleteConfig(name); } @@ -156,8 +175,27 @@ public final class Mines { /** * Reloads one mine from disk (stop, replace instance, start tasks). + * Updates WorldGuard region if mine regions changed. */ public void reloadMine(String name) throws IOException, ConfigLoader.ConfigLoadException { + // Get old config before reload + Mine oldMine = repository.find(name).orElse(null); + repository.reload(name); + + // Update WorldGuard region if mine exists + if (oldMine != null) { + Mine newMine = repository.find(name).orElse(null); + if (newMine != null) { + worldGuardService.updateRegion(name, newMine.getConfig()); + } + } + } + + /** + * Returns the WorldGuard region service for direct access. + */ + public WorldGuardRegionService getWorldGuardService() { + return worldGuardService; } } diff --git a/src/main/java/dev/loki/lomines/core/service/MineFileManager.java b/src/main/java/dev/loki/lomines/core/service/MineFileManager.java index 716fcfd..0ca04ce 100644 --- a/src/main/java/dev/loki/lomines/core/service/MineFileManager.java +++ b/src/main/java/dev/loki/lomines/core/service/MineFileManager.java @@ -10,6 +10,7 @@ import dev.loki.lomines.data.config.reset.ResetConfig; import dev.loki.lomines.data.config.reward.RewardConfig; import dev.loki.lomines.data.config.teleport.TeleportConfig; import dev.loki.lomines.data.config.ui.UIConfig; +import dev.loki.lomines.integration.worldguard.WorldGuardConfig; import dev.loki.lomines.util.location.Cuboid; import dev.loki.lomines.util.location.LocationParser; import org.bukkit.Location; @@ -44,6 +45,7 @@ public record MineFileManager(Path minesFolder, ConfigLoader configLoader) { /** * Creates a default mine configuration with new format. + * WorldGuard region settings are loaded from defaults.yml. */ public void createDefaultConfig(String name, Location corner1, Location corner2) throws IOException { ensureFolderExists(); @@ -62,6 +64,9 @@ public record MineFileManager(Path minesFolder, ConfigLoader configLoader) { weights.put(new BlockKey.Vanilla(Material.STONE), 1.0); BlockConfig blocks = new BlockConfig(weights, FillMode.CUBOID, null); + // Load WorldGuard defaults from _defaults.yml + WorldGuardConfig wgConfig = loadWorldGuardDefaults(); + // Build mine config with defaults MineConfig config = MineConfig.builder(name) .region(region) @@ -70,12 +75,51 @@ public record MineFileManager(Path minesFolder, ConfigLoader configLoader) { .rewards(RewardConfig.empty()) .teleport(TeleportConfig.disabled()) .ui(UIConfig.defaults()) + .worldGuard(wgConfig) .build(); // Save using new loader configLoader.save(config); } + /** + * Loads WorldGuard configuration from defaults.yml. + */ + private WorldGuardConfig loadWorldGuardDefaults() { + Path defaultsPath = minesFolder.resolve("_defaults.yml"); + if (!Files.exists(defaultsPath)) { + return WorldGuardConfig.disabled(); + } + + try { + YamlConfiguration yaml = YamlConfiguration.loadConfiguration(defaultsPath.toFile()); + ConfigurationSection wgSection = yaml.getConfigurationSection("worldguard"); + + if (wgSection == null) { + return WorldGuardConfig.disabled(); + } + + boolean enabled = wgSection.getBoolean("enabled", false); + if (!enabled) { + return WorldGuardConfig.disabled(); + } + + String template = wgSection.getString("region-template", "{mine_name}_{random_4}"); + + return WorldGuardConfig.builder() + .enabled(true) + .template(template) + .owners(wgSection.getStringList("owners")) + .members(wgSection.getStringList("members")) + .flags(wgSection.getStringList("flags")) + .protectOnCreate(wgSection.getBoolean("protect-on-create", true)) + .build(); + + } catch (Exception e) { + return WorldGuardConfig.disabled(); + } + } + public MineConfig loadConfig(String name) throws IOException, ConfigLoader.ConfigLoadException { return configLoader.load(name); } @@ -115,6 +159,7 @@ public record MineFileManager(Path minesFolder, ConfigLoader configLoader) { .rewards(config.rewards()) .teleport(config.teleport()) .ui(config.ui()) + .worldGuard(config.worldGuard()) .build(); configLoader.save(updated); diff --git a/src/main/java/dev/loki/lomines/data/config/ConfigLoader.java b/src/main/java/dev/loki/lomines/data/config/ConfigLoader.java index 7abaeb7..21d3685 100644 --- a/src/main/java/dev/loki/lomines/data/config/ConfigLoader.java +++ b/src/main/java/dev/loki/lomines/data/config/ConfigLoader.java @@ -1,47 +1,38 @@ package dev.loki.lomines.data.config; -import dev.loki.lomines.data.config.block.BlockConfig; -import dev.loki.lomines.data.config.block.BlockKey; -import dev.loki.lomines.data.config.block.FillMode; -import dev.loki.lomines.data.config.region.RegionConfig; -import dev.loki.lomines.data.config.reset.ResetConfig; -import dev.loki.lomines.data.config.reward.RewardConfig; -import dev.loki.lomines.data.config.reward.RewardConfig.RewardEntry; -import dev.loki.lomines.data.config.reward.RewardConfig.RewardEntry.ItemReward; -import dev.loki.lomines.data.config.teleport.TeleportConfig; -import dev.loki.lomines.data.config.ui.UIConfig; -import dev.loki.lomines.util.location.Cuboid; -import dev.loki.lomines.util.location.LocationParser; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.configuration.ConfigurationSection; +import dev.loki.lomines.data.config.loader.*; import org.bukkit.configuration.file.YamlConfiguration; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.time.Duration; -import java.util.*; +import java.util.List; /** * Clean config loader for section-based configuration. * Supports YAML inheritance from defaults. + * Delegates to specialized loaders for each section. */ public final class ConfigLoader { private final Path dataFolder; private YamlConfiguration defaults; + // Section loaders + private RegionConfigLoader regionLoader; + private BlockConfigLoader blockLoader; + private ResetConfigLoader resetLoader; + private RewardConfigLoader rewardLoader; + private TeleportConfigLoader teleportLoader; + private UIConfigLoader uiLoader; + private WorldGuardConfigLoader worldGuardLoader; + public ConfigLoader(Path dataFolder) { this.dataFolder = dataFolder; loadDefaults(); + initLoaders(); } - /** - * Load or create defaults.yml. - */ private void loadDefaults() { Path defaultsPath = dataFolder.resolve("mines").resolve("_defaults.yml"); defaults = new YamlConfiguration(); @@ -49,7 +40,6 @@ public final class ConfigLoader { if (Files.exists(defaultsPath)) { defaults = YamlConfiguration.loadConfiguration(defaultsPath.toFile()); } else { - // Create defaults with sensible values setDefaults(defaults); try { Files.createDirectories(defaultsPath.getParent()); @@ -73,8 +63,19 @@ public final class ConfigLoader { yaml.set("ui.timer-format", "mm:ss"); yaml.set("teleport.enabled", false); - yaml.set("rewards", List.of()); + + worldGuardLoader.setDefaults(yaml); + } + + private void initLoaders() { + regionLoader = new RegionConfigLoader(); + blockLoader = new BlockConfigLoader(); + resetLoader = new ResetConfigLoader(defaults); + rewardLoader = new RewardConfigLoader(); + teleportLoader = new TeleportConfigLoader(); + uiLoader = new UIConfigLoader(defaults); + worldGuardLoader = new WorldGuardConfigLoader(defaults); } /** @@ -88,18 +89,17 @@ public final class ConfigLoader { } YamlConfiguration yaml = YamlConfiguration.loadConfiguration(configPath.toFile()); - - // Merge with defaults (YAML inheritance) mergeDefaults(yaml); try { return MineConfig.builder(mineName) - .region(parseRegion(yaml)) - .blocks(parseBlocks(yaml)) - .reset(parseReset(yaml)) - .rewards(parseRewards(yaml)) - .teleport(parseTeleport(yaml)) - .ui(parseUI(yaml)) + .region(regionLoader.parse(yaml)) + .blocks(blockLoader.parse(yaml)) + .reset(resetLoader.parse(yaml)) + .rewards(rewardLoader.parse(yaml)) + .teleport(teleportLoader.parse(yaml)) + .ui(uiLoader.parse(yaml)) + .worldGuard(worldGuardLoader.parse(yaml)) .build(); } catch (Exception e) { throw new ConfigLoadException("Failed to load mine '" + mineName + "': " + e.getMessage(), e); @@ -111,26 +111,15 @@ public final class ConfigLoader { */ public void save(MineConfig config) throws ConfigLoadException { Path configPath = dataFolder.resolve("mines").resolve(config.name() + ".yml"); - YamlConfiguration yaml = new YamlConfiguration(); - // Save regions - saveRegion(yaml, config.region()); - - // Save blocks - saveBlocks(yaml, config.blocks()); - - // Save reset - saveReset(yaml, config.reset()); - - // Save rewards - saveRewards(yaml, config.rewards()); - - // Save teleport - saveTeleport(yaml, config.teleport()); - - // Save UI - saveUI(yaml, config.ui()); + regionLoader.save(yaml, config.region()); + blockLoader.save(yaml, config.blocks()); + resetLoader.save(yaml, config.reset()); + rewardLoader.save(yaml, config.rewards()); + teleportLoader.save(yaml, config.teleport()); + uiLoader.save(yaml, config.ui()); + worldGuardLoader.save(yaml, config.worldGuard()); try { Files.createDirectories(configPath.getParent()); @@ -140,272 +129,7 @@ public final class ConfigLoader { } } - // --- Parsers --- - - private RegionConfig parseRegion(YamlConfiguration yaml) { - List selections = new ArrayList<>(); - - for (int i = 1; i <= 10; i++) { - String key = "region.selection." + i; - if (yaml.contains(key)) { - String locStr = yaml.getString(key); - if (locStr != null && !locStr.isBlank()) { - selections.add(parseLocation(locStr)); - } - } - } - - if (selections.isEmpty()) { - // Legacy fallback - for (int i = 1; i <= 10; i++) { - String key = "selection." + i; - if (yaml.contains(key)) { - String locStr = yaml.getString(key); - if (locStr != null && !locStr.isBlank()) { - selections.add(parseLocation(locStr)); - } - } - } - } - - if (selections.size() < 2) { - throw new IllegalArgumentException("Mine must have at least 2 selection points (1 region)"); - } - - return RegionConfig.fromSelections(selections); - } - - private BlockConfig parseBlocks(YamlConfiguration yaml) { - ConfigurationSection contents = yaml.getConfigurationSection("blocks.contents"); - if (contents == null) { - contents = yaml.getConfigurationSection("contents"); // Legacy fallback - } - - if (contents == null) { - throw new IllegalArgumentException("Missing 'blocks.contents' section"); - } - - Map weights = new HashMap<>(); - for (String key : contents.getKeys(false)) { - double weight = contents.getDouble(key); - if (weight <= 0) continue; - weights.put(BlockKey.deserialize(key), weight); - } - - FillMode fillMode = FillMode.valueOf( - yaml.getString("blocks.fill-mode", "CUBOID").toUpperCase() - ); - - // Parse mask config if applicable - BlockConfig.MaskConfig mask = null; - if (fillMode == FillMode.MASK) { - String markerStr = yaml.getString("blocks.mask.marker", "pink_concrete"); - BlockKey marker = BlockKey.deserialize(markerStr); - - Map positions = new HashMap<>(); - List posList = yaml.getStringList("blocks.mask.positions"); - for (String pos : posList) { - positions.put(pos, true); - } - - mask = new BlockConfig.MaskConfig(marker, positions); - } - - return new BlockConfig(weights, fillMode, mask); - } - - private ResetConfig parseReset(YamlConfiguration yaml) { - String intervalStr = yaml.getString("reset.interval", defaults.getString("reset.interval", "5m")); - - return ResetConfig.builder() - .interval(intervalStr) - .percentTrigger(yaml.getDouble("reset.percent-trigger", defaults.getDouble("reset.percent-trigger", 10.0))) - .percentEnabled(yaml.getBoolean("reset.percent-enabled", defaults.getBoolean("reset.percent-enabled", false))) - .commands(yaml.getStringList("reset.commands")) - .broadcastMessage(yaml.getString("reset.broadcast", "")) - .build(); - } - - private RewardConfig parseRewards(YamlConfiguration yaml) { - List entries = new ArrayList<>(); - List> rewardList = yaml.getMapList("rewards"); - - if (rewardList.isEmpty()) { - rewardList = yaml.getMapList("random-rewards"); // Legacy fallback - } - - for (Map map : rewardList) { - entries.add(parseRewardEntry(map)); - } - - return new RewardConfig(entries); - } - - private RewardEntry parseRewardEntry(Map map) { - double chance = ((Number) map.getOrDefault("chance", 0)).doubleValue(); - - List blocks = new ArrayList<>(); - List blockList = (List) map.get("blocks"); - if (blockList != null) { - for (Object b : blockList) { - blocks.add(BlockKey.deserialize(b.toString())); - } - } - - List items = new ArrayList<>(); - List itemList = (List) map.get("items"); - if (itemList != null) { - for (Object i : itemList) { - @SuppressWarnings("unchecked") - Map itemMap = (Map) i; - items.add(parseItemReward(itemMap)); - } - } - - List commands = (List) map.getOrDefault("commands", List.of()); - boolean preventDrops = (Boolean) map.getOrDefault("prevent-drops", false); - - return new RewardEntry(blocks, chance, items, commands, preventDrops); - } - - private ItemReward parseItemReward(Map map) { - String typeStr = (String) map.get("type"); - Material material = Material.matchMaterial(typeStr); - if (material == null) { - material = Material.STONE; - } - - int amount = ((Number) map.getOrDefault("amount", 1)).intValue(); - String name = (String) map.get("name"); - @SuppressWarnings("unchecked") - List lore = (List) map.getOrDefault("lore", List.of()); - - return new ItemReward(material, amount, name, lore); - } - - private TeleportConfig parseTeleport(YamlConfiguration yaml) { - boolean enabled = yaml.getBoolean("teleport.enabled", false); - if (!enabled) { - return TeleportConfig.disabled(); - } - - String locStr = yaml.getString("teleport.location"); - if (locStr == null) { - return TeleportConfig.disabled(); - } - - return TeleportConfig.at(parseLocation(locStr)); - } - - private UIConfig parseUI(YamlConfiguration yaml) { - boolean actionBarEnabled = yaml.getBoolean("ui.actionbar.enabled", - defaults.getBoolean("ui.actionbar.enabled", true)); - - return new UIConfig( - actionBarEnabled, - yaml.getString("ui.actionbar.format", UIConfig.DEFAULT_ACTIONBAR_FORMAT), - yaml.getDouble("ui.actionbar.range", 50.0), - yaml.getString("ui.timer-format", UIConfig.DEFAULT_TIMER_FORMAT) - ); - } - - // --- Savers --- - - private void saveRegion(YamlConfiguration yaml, RegionConfig region) { - int i = 1; - for (Cuboid cuboid : region.regions()) { - yaml.set("region.selection." + i, LocationParser.format(cuboid.getMin())); - i++; - yaml.set("region.selection." + i, LocationParser.format(cuboid.getMax())); - i++; - } - } - - private void saveBlocks(YamlConfiguration yaml, BlockConfig blocks) { - for (var entry : blocks.weights().entrySet()) { - yaml.set("blocks.contents." + entry.getKey().serialize(), entry.getValue()); - } - yaml.set("blocks.fill-mode", blocks.fillMode().name().toLowerCase()); - - if (blocks.fillMode() == FillMode.MASK && blocks.mask() != null) { - yaml.set("blocks.mask.marker", blocks.mask().marker().serialize()); - yaml.set("blocks.mask.positions", new ArrayList<>(blocks.mask().positions().keySet())); - } - } - - private void saveReset(YamlConfiguration yaml, ResetConfig reset) { - yaml.set("reset.interval", reset.intervalDisplay()); - yaml.set("reset.percent-trigger", reset.percentTrigger()); - yaml.set("reset.percent-enabled", reset.percentEnabled()); - yaml.set("reset.commands", reset.commands()); - yaml.set("reset.broadcast", reset.broadcastMessage()); - } - - private void saveRewards(YamlConfiguration yaml, RewardConfig rewards) { - List> list = new ArrayList<>(); - for (RewardEntry entry : rewards.entries()) { - Map map = new LinkedHashMap<>(); - map.put("chance", entry.chance()); - map.put("blocks", entry.blocks().stream().map(BlockKey::serialize).toList()); - - List> items = new ArrayList<>(); - for (ItemReward item : entry.items()) { - Map itemMap = new LinkedHashMap<>(); - itemMap.put("type", item.material().name().toLowerCase()); - itemMap.put("amount", item.amount()); - if (item.displayName() != null) { - itemMap.put("name", item.displayName()); - } - if (!item.lore().isEmpty()) { - itemMap.put("lore", item.lore()); - } - items.add(itemMap); - } - map.put("items", items); - map.put("commands", entry.commands()); - map.put("prevent-drops", entry.preventVanillaDrops()); - list.add(map); - } - yaml.set("rewards", list); - } - - private void saveTeleport(YamlConfiguration yaml, TeleportConfig teleport) { - yaml.set("teleport.enabled", teleport.enabled()); - teleport.getLocation().ifPresent(loc -> - yaml.set("teleport.location", LocationParser.format(loc))); - } - - private void saveUI(YamlConfiguration yaml, UIConfig ui) { - yaml.set("ui.actionbar.enabled", ui.actionBarEnabled()); - yaml.set("ui.actionbar.format", ui.actionBarFormat()); - yaml.set("ui.actionbar.range", ui.actionBarRange()); - yaml.set("ui.timer-format", ui.timerFormat()); - } - - // --- Helpers --- - - private Location parseLocation(String str) { - String[] parts = str.split(";"); - if (parts.length < 4) { - throw new IllegalArgumentException("Invalid location format: " + str); - } - - World world = Bukkit.getWorld(parts[0]); - if (world == null) { - throw new IllegalArgumentException("Unknown world: " + parts[0]); - } - - double x = Double.parseDouble(parts[1]); - double y = Double.parseDouble(parts[2]); - double z = Double.parseDouble(parts[3]); - float yaw = parts.length > 4 ? Float.parseFloat(parts[4]) : 0; - float pitch = parts.length > 5 ? Float.parseFloat(parts[5]) : 0; - - return new Location(world, x, y, z, yaw, pitch); - } - private void mergeDefaults(YamlConfiguration yaml) { - // Simple shallow merge - could be enhanced for deep merging for (String key : defaults.getKeys(true)) { if (!yaml.contains(key)) { yaml.set(key, defaults.get(key)); diff --git a/src/main/java/dev/loki/lomines/data/config/MineConfig.java b/src/main/java/dev/loki/lomines/data/config/MineConfig.java index fbfa0fa..753a422 100644 --- a/src/main/java/dev/loki/lomines/data/config/MineConfig.java +++ b/src/main/java/dev/loki/lomines/data/config/MineConfig.java @@ -6,6 +6,7 @@ import dev.loki.lomines.data.config.reset.ResetConfig; import dev.loki.lomines.data.config.reward.RewardConfig; import dev.loki.lomines.data.config.teleport.TeleportConfig; import dev.loki.lomines.data.config.ui.UIConfig; +import dev.loki.lomines.integration.worldguard.WorldGuardConfig; import java.util.Objects; @@ -20,7 +21,8 @@ public record MineConfig( ResetConfig reset, RewardConfig rewards, TeleportConfig teleport, - UIConfig ui + UIConfig ui, + WorldGuardConfig worldGuard ) { public MineConfig { @@ -36,6 +38,7 @@ public record MineConfig( Objects.requireNonNull(rewards, "Reward config cannot be null"); Objects.requireNonNull(teleport, "Teleport config cannot be null"); Objects.requireNonNull(ui, "UI config cannot be null"); + // worldGuard can be null (disabled by default) } /** @@ -70,7 +73,8 @@ public record MineConfig( ResetConfig.defaults(), RewardConfig.empty(), TeleportConfig.disabled(), - UIConfig.defaults() + UIConfig.defaults(), + WorldGuardConfig.disabled() ); } @@ -84,6 +88,7 @@ public record MineConfig( private RewardConfig rewards = RewardConfig.empty(); private TeleportConfig teleport = TeleportConfig.disabled(); private UIConfig ui = UIConfig.defaults(); + private WorldGuardConfig worldGuard = WorldGuardConfig.disabled(); private Builder(String name) { this.name = name; @@ -119,8 +124,13 @@ public record MineConfig( return this; } + public Builder worldGuard(WorldGuardConfig worldGuard) { + this.worldGuard = worldGuard; + return this; + } + public MineConfig build() { - return new MineConfig(name, region, blocks, reset, rewards, teleport, ui); + return new MineConfig(name, region, blocks, reset, rewards, teleport, ui, worldGuard); } } } diff --git a/src/main/java/dev/loki/lomines/data/config/loader/BlockConfigLoader.java b/src/main/java/dev/loki/lomines/data/config/loader/BlockConfigLoader.java new file mode 100644 index 0000000..16a5023 --- /dev/null +++ b/src/main/java/dev/loki/lomines/data/config/loader/BlockConfigLoader.java @@ -0,0 +1,75 @@ +package dev.loki.lomines.data.config.loader; + +import dev.loki.lomines.data.config.block.BlockConfig; +import dev.loki.lomines.data.config.block.BlockKey; +import dev.loki.lomines.data.config.block.FillMode; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Loader for blocks configuration section. + */ +public final class BlockConfigLoader { + + /** + * Parses block configuration from YAML. + */ + public BlockConfig parse(YamlConfiguration yaml) { + ConfigurationSection contents = yaml.getConfigurationSection("blocks.contents"); + if (contents == null) { + contents = yaml.getConfigurationSection("contents"); // Legacy fallback + } + + if (contents == null) { + throw new IllegalArgumentException("Missing 'blocks.contents' section"); + } + + Map weights = new HashMap<>(); + for (String key : contents.getKeys(false)) { + double weight = contents.getDouble(key); + if (weight <= 0) continue; + weights.put(BlockKey.deserialize(key), weight); + } + + FillMode fillMode = FillMode.valueOf( + yaml.getString("blocks.fill-mode", "CUBOID").toUpperCase() + ); + + // Parse mask config if applicable + BlockConfig.MaskConfig mask = null; + if (fillMode == FillMode.MASK) { + String markerStr = yaml.getString("blocks.mask.marker", "pink_concrete"); + BlockKey marker = BlockKey.deserialize(markerStr); + + Map positions = new HashMap<>(); + List posList = yaml.getStringList("blocks.mask.positions"); + for (String pos : posList) { + positions.put(pos, true); + } + + mask = new BlockConfig.MaskConfig(marker, positions); + } + + return new BlockConfig(weights, fillMode, mask); + } + + /** + * Saves block configuration to YAML. + */ + public void save(YamlConfiguration yaml, BlockConfig blocks) { + for (var entry : blocks.weights().entrySet()) { + yaml.set("blocks.contents." + entry.getKey().serialize(), entry.getValue()); + } + yaml.set("blocks.fill-mode", blocks.fillMode().name().toLowerCase()); + + if (blocks.fillMode() == FillMode.MASK && blocks.mask() != null) { + yaml.set("blocks.mask.marker", blocks.mask().marker().serialize()); + yaml.set("blocks.mask.positions", new ArrayList<>(blocks.mask().positions().keySet())); + } + } +} diff --git a/src/main/java/dev/loki/lomines/data/config/loader/RegionConfigLoader.java b/src/main/java/dev/loki/lomines/data/config/loader/RegionConfigLoader.java new file mode 100644 index 0000000..e4d71c8 --- /dev/null +++ b/src/main/java/dev/loki/lomines/data/config/loader/RegionConfigLoader.java @@ -0,0 +1,87 @@ +package dev.loki.lomines.data.config.loader; + +import dev.loki.lomines.data.config.region.RegionConfig; +import dev.loki.lomines.util.location.LocationParser; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.util.ArrayList; +import java.util.List; + +/** + * Loader for region configuration section. + */ +public final class RegionConfigLoader { + + /** + * Parses region configuration from YAML. + */ + public RegionConfig parse(YamlConfiguration yaml) { + List selections = new ArrayList<>(); + + // Try new format first + for (int i = 1; i <= 10; i++) { + String key = "region.selection." + i; + if (yaml.contains(key)) { + String locStr = yaml.getString(key); + if (locStr != null && !locStr.isBlank()) { + selections.add(parseLocation(locStr)); + } + } + } + + // Legacy fallback + if (selections.isEmpty()) { + for (int i = 1; i <= 10; i++) { + String key = "selection." + i; + if (yaml.contains(key)) { + String locStr = yaml.getString(key); + if (locStr != null && !locStr.isBlank()) { + selections.add(parseLocation(locStr)); + } + } + } + } + + if (selections.size() < 2) { + throw new IllegalArgumentException("Mine must have at least 2 selection points (1 region)"); + } + + return RegionConfig.fromSelections(selections); + } + + /** + * Saves region configuration to YAML. + */ + public void save(YamlConfiguration yaml, RegionConfig region) { + int i = 1; + for (var cuboid : region.regions()) { + yaml.set("region.selection." + i, LocationParser.format(cuboid.getMin())); + i++; + yaml.set("region.selection." + i, LocationParser.format(cuboid.getMax())); + i++; + } + } + + private Location parseLocation(String str) { + String[] parts = str.split(";"); + if (parts.length < 4) { + throw new IllegalArgumentException("Invalid location format: " + str); + } + + World world = Bukkit.getWorld(parts[0]); + if (world == null) { + throw new IllegalArgumentException("Unknown world: " + parts[0]); + } + + double x = Double.parseDouble(parts[1]); + double y = Double.parseDouble(parts[2]); + double z = Double.parseDouble(parts[3]); + float yaw = parts.length > 4 ? Float.parseFloat(parts[4]) : 0; + float pitch = parts.length > 5 ? Float.parseFloat(parts[5]) : 0; + + return new Location(world, x, y, z, yaw, pitch); + } +} diff --git a/src/main/java/dev/loki/lomines/data/config/loader/ResetConfigLoader.java b/src/main/java/dev/loki/lomines/data/config/loader/ResetConfigLoader.java new file mode 100644 index 0000000..5ef7729 --- /dev/null +++ b/src/main/java/dev/loki/lomines/data/config/loader/ResetConfigLoader.java @@ -0,0 +1,47 @@ +package dev.loki.lomines.data.config.loader; + +import dev.loki.lomines.data.config.reset.ResetConfig; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.util.List; + +/** + * Loader for reset configuration section. + */ +public final class ResetConfigLoader { + + private final YamlConfiguration defaults; + + public ResetConfigLoader(YamlConfiguration defaults) { + this.defaults = defaults; + } + + /** + * Parses reset configuration from YAML. + */ + public ResetConfig parse(YamlConfiguration yaml) { + String intervalStr = yaml.getString("reset.interval", + defaults.getString("reset.interval", "5m")); + + return ResetConfig.builder() + .interval(intervalStr) + .percentTrigger(yaml.getDouble("reset.percent-trigger", + defaults.getDouble("reset.percent-trigger", 10.0))) + .percentEnabled(yaml.getBoolean("reset.percent-enabled", + defaults.getBoolean("reset.percent-enabled", false))) + .commands(yaml.getStringList("reset.commands")) + .broadcastMessage(yaml.getString("reset.broadcast", "")) + .build(); + } + + /** + * Saves reset configuration to YAML. + */ + public void save(YamlConfiguration yaml, ResetConfig reset) { + yaml.set("reset.interval", reset.intervalDisplay()); + yaml.set("reset.percent-trigger", reset.percentTrigger()); + yaml.set("reset.percent-enabled", reset.percentEnabled()); + yaml.set("reset.commands", reset.commands()); + yaml.set("reset.broadcast", reset.broadcastMessage()); + } +} diff --git a/src/main/java/dev/loki/lomines/data/config/loader/RewardConfigLoader.java b/src/main/java/dev/loki/lomines/data/config/loader/RewardConfigLoader.java new file mode 100644 index 0000000..ec9b7e7 --- /dev/null +++ b/src/main/java/dev/loki/lomines/data/config/loader/RewardConfigLoader.java @@ -0,0 +1,111 @@ +package dev.loki.lomines.data.config.loader; + +import dev.loki.lomines.data.config.block.BlockKey; +import dev.loki.lomines.data.config.reward.RewardConfig; +import dev.loki.lomines.data.config.reward.RewardConfig.RewardEntry; +import dev.loki.lomines.data.config.reward.RewardConfig.RewardEntry.ItemReward; +import org.bukkit.Material; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Loader for rewards configuration section. + */ +public final class RewardConfigLoader { + + /** + * Parses rewards from YAML configuration. + */ + public RewardConfig parse(YamlConfiguration yaml) { + List entries = new ArrayList<>(); + List> rewardList = yaml.getMapList("rewards"); + + if (rewardList.isEmpty()) { + rewardList = yaml.getMapList("random-rewards"); // Legacy fallback + } + + for (Map map : rewardList) { + entries.add(parseRewardEntry(map)); + } + + return new RewardConfig(entries); + } + + private RewardEntry parseRewardEntry(Map map) { + double chance = ((Number) map.getOrDefault("chance", 0)).doubleValue(); + + List blocks = new ArrayList<>(); + List blockList = (List) map.get("blocks"); + if (blockList != null) { + for (Object b : blockList) { + blocks.add(BlockKey.deserialize(b.toString())); + } + } + + List items = new ArrayList<>(); + List itemList = (List) map.get("items"); + if (itemList != null) { + for (Object i : itemList) { + @SuppressWarnings("unchecked") + Map itemMap = (Map) i; + items.add(parseItemReward(itemMap)); + } + } + + @SuppressWarnings("unchecked") + List commands = (List) map.getOrDefault("commands", List.of()); + boolean preventDrops = (Boolean) map.getOrDefault("prevent-drops", false); + + return new RewardEntry(blocks, chance, items, commands, preventDrops); + } + + private ItemReward parseItemReward(Map map) { + String typeStr = (String) map.get("type"); + Material material = Material.matchMaterial(typeStr); + if (material == null) { + material = Material.STONE; + } + + int amount = ((Number) map.getOrDefault("amount", 1)).intValue(); + String name = (String) map.get("name"); + @SuppressWarnings("unchecked") + List lore = (List) map.getOrDefault("lore", List.of()); + + return new ItemReward(material, amount, name, lore); + } + + /** + * Saves rewards to YAML configuration. + */ + public void save(YamlConfiguration yaml, RewardConfig rewards) { + List> list = new ArrayList<>(); + for (RewardEntry entry : rewards.entries()) { + Map map = new LinkedHashMap<>(); + map.put("chance", entry.chance()); + map.put("blocks", entry.blocks().stream().map(BlockKey::serialize).toList()); + + List> items = new ArrayList<>(); + for (ItemReward item : entry.items()) { + Map itemMap = new LinkedHashMap<>(); + itemMap.put("type", item.material().name().toLowerCase()); + itemMap.put("amount", item.amount()); + if (item.displayName() != null) { + itemMap.put("name", item.displayName()); + } + if (!item.lore().isEmpty()) { + itemMap.put("lore", item.lore()); + } + items.add(itemMap); + } + map.put("items", items); + map.put("commands", entry.commands()); + map.put("prevent-drops", entry.preventVanillaDrops()); + list.add(map); + } + yaml.set("rewards", list); + } +} diff --git a/src/main/java/dev/loki/lomines/data/config/loader/TeleportConfigLoader.java b/src/main/java/dev/loki/lomines/data/config/loader/TeleportConfigLoader.java new file mode 100644 index 0000000..76a430b --- /dev/null +++ b/src/main/java/dev/loki/lomines/data/config/loader/TeleportConfigLoader.java @@ -0,0 +1,60 @@ +package dev.loki.lomines.data.config.loader; + +import dev.loki.lomines.data.config.teleport.TeleportConfig; +import dev.loki.lomines.util.location.LocationParser; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.file.YamlConfiguration; + +/** + * Loader for teleport configuration section. + */ +public final class TeleportConfigLoader { + + /** + * Parses teleport configuration from YAML. + */ + public TeleportConfig parse(YamlConfiguration yaml) { + boolean enabled = yaml.getBoolean("teleport.enabled", false); + if (!enabled) { + return TeleportConfig.disabled(); + } + + String locStr = yaml.getString("teleport.location"); + if (locStr == null || locStr.isBlank()) { + return TeleportConfig.disabled(); + } + + return TeleportConfig.at(parseLocation(locStr)); + } + + /** + * Saves teleport configuration to YAML. + */ + public void save(YamlConfiguration yaml, TeleportConfig teleport) { + yaml.set("teleport.enabled", teleport.enabled()); + teleport.getLocation().ifPresent(loc -> + yaml.set("teleport.location", LocationParser.format(loc))); + } + + private Location parseLocation(String str) { + String[] parts = str.split(";"); + if (parts.length < 4) { + throw new IllegalArgumentException("Invalid location format: " + str); + } + + World world = Bukkit.getWorld(parts[0]); + if (world == null) { + throw new IllegalArgumentException("Unknown world: " + parts[0]); + } + + double x = Double.parseDouble(parts[1]); + double y = Double.parseDouble(parts[2]); + double z = Double.parseDouble(parts[3]); + float yaw = parts.length > 4 ? Float.parseFloat(parts[4]) : 0; + float pitch = parts.length > 5 ? Float.parseFloat(parts[5]) : 0; + + return new Location(world, x, y, z, yaw, pitch); + } +} diff --git a/src/main/java/dev/loki/lomines/data/config/loader/UIConfigLoader.java b/src/main/java/dev/loki/lomines/data/config/loader/UIConfigLoader.java new file mode 100644 index 0000000..4ceafd3 --- /dev/null +++ b/src/main/java/dev/loki/lomines/data/config/loader/UIConfigLoader.java @@ -0,0 +1,41 @@ +package dev.loki.lomines.data.config.loader; + +import dev.loki.lomines.data.config.ui.UIConfig; +import org.bukkit.configuration.file.YamlConfiguration; + +/** + * Loader for UI configuration section. + */ +public final class UIConfigLoader { + + private final YamlConfiguration defaults; + + public UIConfigLoader(YamlConfiguration defaults) { + this.defaults = defaults; + } + + /** + * Parses UI configuration from YAML. + */ + public UIConfig parse(YamlConfiguration yaml) { + boolean actionBarEnabled = yaml.getBoolean("ui.actionbar.enabled", + defaults.getBoolean("ui.actionbar.enabled", true)); + + return new UIConfig( + actionBarEnabled, + yaml.getString("ui.actionbar.format", UIConfig.DEFAULT_ACTIONBAR_FORMAT), + yaml.getDouble("ui.actionbar.range", 50.0), + yaml.getString("ui.timer-format", UIConfig.DEFAULT_TIMER_FORMAT) + ); + } + + /** + * Saves UI configuration to YAML. + */ + public void save(YamlConfiguration yaml, UIConfig ui) { + yaml.set("ui.actionbar.enabled", ui.actionBarEnabled()); + yaml.set("ui.actionbar.format", ui.actionBarFormat()); + yaml.set("ui.actionbar.range", ui.actionBarRange()); + yaml.set("ui.timer-format", ui.timerFormat()); + } +} diff --git a/src/main/java/dev/loki/lomines/data/config/loader/WorldGuardConfigLoader.java b/src/main/java/dev/loki/lomines/data/config/loader/WorldGuardConfigLoader.java new file mode 100644 index 0000000..5add2f1 --- /dev/null +++ b/src/main/java/dev/loki/lomines/data/config/loader/WorldGuardConfigLoader.java @@ -0,0 +1,104 @@ +package dev.loki.lomines.data.config.loader; + +import dev.loki.lomines.data.config.ConfigLoader; +import dev.loki.lomines.integration.worldguard.WorldGuardConfig; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Loader for WorldGuard configuration section. + */ +public final class WorldGuardConfigLoader { + + private final YamlConfiguration defaults; + + public WorldGuardConfigLoader(YamlConfiguration defaults) { + this.defaults = defaults; + } + + /** + * Parses WorldGuard configuration from YAML. + */ + public WorldGuardConfig parse(YamlConfiguration yaml) { + ConfigurationSection wgSection = yaml.getConfigurationSection("worldguard"); + if (wgSection == null) { + // Check defaults + boolean defaultEnabled = defaults.getBoolean("worldguard.enabled", false); + if (!defaultEnabled) { + return WorldGuardConfig.disabled(); + } + } + + boolean enabled = yaml.getBoolean("worldguard.enabled", + defaults.getBoolean("worldguard.enabled", false)); + + if (!enabled) { + return WorldGuardConfig.disabled(); + } + + String template = yaml.getString("worldguard.region-template", + defaults.getString("worldguard.region-template", "{mine_name}_{random_4}")); + + List owners = yaml.getStringList("worldguard.owners"); + if (owners.isEmpty()) { + owners = defaults.getStringList("worldguard.owners"); + } + + List members = yaml.getStringList("worldguard.members"); + if (members.isEmpty()) { + members = defaults.getStringList("worldguard.members"); + } + + List flags = yaml.getStringList("worldguard.flags"); + if (flags.isEmpty()) { + flags = defaults.getStringList("worldguard.flags"); + } + if (flags.isEmpty()) { + flags = List.of("passthrough=deny", "build=allow"); + } + + boolean protect = yaml.getBoolean("worldguard.protect-on-create", + defaults.getBoolean("worldguard.protect-on-create", true)); + + return WorldGuardConfig.builder() + .enabled(true) + .template(template) + .owners(owners) + .members(members) + .flags(flags) + .protectOnCreate(protect) + .build(); + } + + /** + * Saves WorldGuard configuration to YAML. + */ + public void save(YamlConfiguration yaml, WorldGuardConfig config) { + yaml.set("worldguard.enabled", config.enabled()); + + if (config.enabled()) { + yaml.set("worldguard.region-template", config.regionNameTemplate()); + yaml.set("worldguard.owners", config.owners()); + yaml.set("worldguard.members", config.members()); + yaml.set("worldguard.flags", config.flags()); + yaml.set("worldguard.protect-on-create", config.protectOnCreate()); + } + } + + /** + * Sets default values for WorldGuard configuration. + */ + public void setDefaults(YamlConfiguration yaml) { + yaml.set("worldguard.enabled", false); + yaml.set("worldguard.region-template", "{mine_name}_{random_4}"); + yaml.set("worldguard.owners", List.of()); + yaml.set("worldguard.members", List.of()); + yaml.set("worldguard.flags", List.of("passthrough=deny", "build=allow")); + yaml.set("worldguard.protect-on-create", true); + } +} diff --git a/src/main/java/dev/loki/lomines/integration/worldguard/WorldGuardConfig.java b/src/main/java/dev/loki/lomines/integration/worldguard/WorldGuardConfig.java new file mode 100644 index 0000000..6a96631 --- /dev/null +++ b/src/main/java/dev/loki/lomines/integration/worldguard/WorldGuardConfig.java @@ -0,0 +1,155 @@ +package dev.loki.lomines.integration.worldguard; + +import java.util.List; +import java.util.Objects; +import java.util.Random; + +/** + * Configuration for WorldGuard auto-region creation. + */ +public record WorldGuardConfig( + boolean enabled, + String regionNameTemplate, + List owners, + List members, + List flags, + boolean protectOnCreate +) { + + private static final Random RANDOM = new Random(); + private static final String DEFAULT_TEMPLATE = "{mine_name}_{random_4}"; + + public WorldGuardConfig { + enabled = enabled; + regionNameTemplate = regionNameTemplate != null && !regionNameTemplate.isBlank() + ? regionNameTemplate + : DEFAULT_TEMPLATE; + owners = owners != null ? List.copyOf(owners) : List.of(); + members = members != null ? List.copyOf(members) : List.of(); + flags = flags != null ? List.copyOf(flags) : List.of( + "passthrough=deny", + "build=allow" + ); + protectOnCreate = protectOnCreate; + } + + /** + * Generates a region name from the template. + * + * @param mineName the name of the mine + * @return the generated region name + */ + public String generateRegionName(String mineName) { + String result = regionNameTemplate; + + // Replace {mine_name} + result = result.replace("{mine_name}", mineName); + + // Replace {random_4} with 4 random digits + if (result.contains("{random_4}")) { + int randomNum = RANDOM.nextInt(10000); + result = result.replace("{random_4}", String.format("%04d", randomNum)); + } + + // Replace {random_3} with 3 random digits + if (result.contains("{random_3}")) { + int randomNum = RANDOM.nextInt(1000); + result = result.replace("{random_3}", String.format("%03d", randomNum)); + } + + // Replace {random_6} with 6 random alphanumeric + if (result.contains("{random_6}")) { + result = result.replace("{random_6}", generateRandomString(6)); + } + + // Sanitize for WorldGuard (only alphanumeric, underscore, hyphen) + return result.replaceAll("[^a-zA-Z0-9_-]", "_").toLowerCase(); + } + + private String generateRandomString(int length) { + String chars = "abcdefghijklmnopqrstuvwxyz0123456789"; + StringBuilder sb = new StringBuilder(length); + for (int i = 0; i < length; i++) { + sb.append(chars.charAt(RANDOM.nextInt(chars.length()))); + } + return sb.toString(); + } + + /** + * Default configuration with auto-region enabled. + */ + public static WorldGuardConfig defaults() { + return new WorldGuardConfig( + true, + DEFAULT_TEMPLATE, + List.of(), + List.of(), + List.of("passthrough=deny", "build=allow"), + true + ); + } + + /** + * Disabled configuration. + */ + public static WorldGuardConfig disabled() { + return new WorldGuardConfig( + false, + DEFAULT_TEMPLATE, + List.of(), + List.of(), + List.of(), + false + ); + } + + /** + * Builder for fluent construction. + */ + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private boolean enabled = true; + private String template = DEFAULT_TEMPLATE; + private List owners = List.of(); + private List members = List.of(); + private List flags = List.of("passthrough=deny", "build=allow"); + private boolean protectOnCreate = true; + + public Builder enabled(boolean enabled) { + this.enabled = enabled; + return this; + } + + public Builder template(String template) { + this.template = template; + return this; + } + + public Builder owners(List owners) { + this.owners = owners; + return this; + } + + public Builder members(List members) { + this.members = members; + return this; + } + + public Builder flags(List flags) { + this.flags = flags; + return this; + } + + public Builder protectOnCreate(boolean protect) { + this.protectOnCreate = protect; + return this; + } + + public WorldGuardConfig build() { + return new WorldGuardConfig(enabled, template, owners, members, flags, protectOnCreate); + } + } +} diff --git a/src/main/java/dev/loki/lomines/integration/worldguard/WorldGuardFlagParser.java b/src/main/java/dev/loki/lomines/integration/worldguard/WorldGuardFlagParser.java new file mode 100644 index 0000000..bb722d4 --- /dev/null +++ b/src/main/java/dev/loki/lomines/integration/worldguard/WorldGuardFlagParser.java @@ -0,0 +1,55 @@ +package dev.loki.lomines.integration.worldguard; + +import com.sk89q.worldguard.protection.flags.Flags; +import com.sk89q.worldguard.protection.flags.StateFlag; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import dev.loki.lomines.LoMinesPlugin; + +/** + * Parses and applies WorldGuard flags from string entries. + */ +public final class WorldGuardFlagParser { + + private final LoMinesPlugin plugin; + + public WorldGuardFlagParser(LoMinesPlugin plugin) { + this.plugin = plugin; + } + + /** + * Parses and sets a flag from a "key=value" string entry. + * + * @param region the region to set the flag on + * @param flagEntry the flag entry in format "flag=value" + */ + public void parseAndSetFlag(ProtectedRegion region, String flagEntry) { + String[] parts = flagEntry.split("=", 2); + if (parts.length != 2) { + return; + } + + String flagName = parts[0].trim().toLowerCase(); + String value = parts[1].trim(); + + try { + switch (flagName) { + case "passthrough" -> setStateFlag(region, Flags.PASSTHROUGH, value); + case "build" -> setStateFlag(region, Flags.BUILD, value); + case "pvp" -> setStateFlag(region, Flags.PVP, value); + case "tnt" -> setStateFlag(region, Flags.TNT, value); + case "creeper-explosion" -> setStateFlag(region, Flags.CREEPER_EXPLOSION, value); + case "greeting" -> region.setFlag(Flags.GREET_MESSAGE, value); + case "farewell" -> region.setFlag(Flags.FAREWELL_MESSAGE, value); + default -> plugin.loLogger().warn("Unknown WorldGuard flag: " + flagName); + } + } catch (Exception e) { + plugin.loLogger().warn("Failed to set flag " + flagName + ": " + e.getMessage()); + } + } + + private void setStateFlag(ProtectedRegion region, StateFlag flag, String value) { + region.setFlag(flag, "allow".equalsIgnoreCase(value) + ? StateFlag.State.ALLOW + : StateFlag.State.DENY); + } +} diff --git a/src/main/java/dev/loki/lomines/integration/worldguard/WorldGuardRegionService.java b/src/main/java/dev/loki/lomines/integration/worldguard/WorldGuardRegionService.java new file mode 100644 index 0000000..1b6bd6e --- /dev/null +++ b/src/main/java/dev/loki/lomines/integration/worldguard/WorldGuardRegionService.java @@ -0,0 +1,225 @@ +package dev.loki.lomines.integration.worldguard; + +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; +import dev.loki.lomines.LoMinesPlugin; +import dev.loki.lomines.data.config.MineConfig; +import dev.loki.lomines.util.location.Cuboid; +import org.bukkit.Bukkit; +import org.bukkit.World; + +import java.util.List; +import java.util.UUID; + +/** + * Service for managing WorldGuard regions for mines. + * Creates, updates, and deletes WorldGuard regions when mines are managed. + */ +public final class WorldGuardRegionService { + + private final LoMinesPlugin plugin; + private final WorldGuardFlagParser flagParser; + private boolean worldGuardEnabled; + + public WorldGuardRegionService(LoMinesPlugin plugin) { + this.plugin = plugin; + this.flagParser = new WorldGuardFlagParser(plugin); + checkWorldGuard(); + } + + private void checkWorldGuard() { + this.worldGuardEnabled = Bukkit.getPluginManager().getPlugin("WorldGuard") != null + && Bukkit.getPluginManager().isPluginEnabled("WorldGuard"); + + if (worldGuardEnabled) { + plugin.loLogger().info("WorldGuard integration enabled - auto-region creation active"); + } + } + + /** + * Creates a WorldGuard region for a mine if enabled in config. + */ + public String createRegion(String mineName, MineConfig config) { + if (!worldGuardEnabled) { + return null; + } + + WorldGuardConfig wgConfig = config.worldGuard(); + if (wgConfig == null || !wgConfig.enabled()) { + return null; + } + + String regionName = wgConfig.generateRegionName(mineName); + + try { + RegionManager regionManager = getRegionManager(config.worldName()); + if (regionManager == null) { + return null; + } + + if (regionManager.hasRegion(regionName)) { + plugin.loLogger().info("WorldGuard region already exists: " + regionName); + return regionName; + } + + ProtectedCuboidRegion region = createRegionFromCuboids(regionName, config); + if (region == null) { + plugin.loLogger().warn("Failed to create region for mine " + mineName + ": no valid regions"); + return null; + } + + applyOwnersAndMembers(region, wgConfig); + applyFlags(region, wgConfig.flags()); + + regionManager.addRegion(region); + plugin.loLogger().info("Created WorldGuard region '" + regionName + "' for mine " + mineName); + return regionName; + + } catch (Exception e) { + plugin.loLogger().error("Failed to create WorldGuard region for mine " + mineName + ": " + e.getMessage()); + e.printStackTrace(); + return null; + } + } + + /** + * Updates an existing WorldGuard region when mine regions change. + */ + public boolean updateRegion(String mineName, MineConfig config) { + if (!worldGuardEnabled) { + return false; + } + + WorldGuardConfig wgConfig = config.worldGuard(); + if (wgConfig == null || !wgConfig.enabled()) { + return false; + } + + String regionName = wgConfig.generateRegionName(mineName); + + try { + RegionManager regionManager = getRegionManager(config.worldName()); + if (regionManager == null) { + return false; + } + + if (regionManager.hasRegion(regionName)) { + regionManager.removeRegion(regionName); + } + + return createRegion(mineName, config) != null; + + } catch (Exception e) { + plugin.loLogger().error("Failed to update WorldGuard region: " + e.getMessage()); + return false; + } + } + + /** + * Deletes a WorldGuard region for a mine. + */ + public boolean deleteRegion(String mineName, MineConfig config) { + if (!worldGuardEnabled) { + return false; + } + + WorldGuardConfig wgConfig = config.worldGuard(); + if (wgConfig == null) { + return false; + } + + String regionName = wgConfig.generateRegionName(mineName); + + try { + RegionManager regionManager = getRegionManager(config.worldName()); + if (regionManager == null) { + return false; + } + + if (regionManager.hasRegion(regionName)) { + regionManager.removeRegion(regionName); + plugin.loLogger().info("Deleted WorldGuard region: " + regionName); + return true; + } + + return false; + } catch (Exception e) { + plugin.loLogger().error("Failed to delete WorldGuard region: " + e.getMessage()); + return false; + } + } + + private RegionManager getRegionManager(String worldName) { + World world = Bukkit.getWorld(worldName); + if (world == null) { + return null; + } + + return WorldGuard.getInstance() + .getPlatform() + .getRegionContainer() + .get(com.sk89q.worldedit.bukkit.BukkitAdapter.adapt(world)); + } + + private ProtectedCuboidRegion createRegionFromCuboids(String regionName, MineConfig config) { + List regions = config.region().regions(); + if (regions.isEmpty()) { + return null; + } + + Cuboid first = regions.get(0); + BlockVector3 min = BlockVector3.at(first.getMinX(), first.getMinY(), first.getMinZ()); + BlockVector3 max = BlockVector3.at(first.getMaxX(), first.getMaxY(), first.getMaxZ()); + + ProtectedCuboidRegion mainRegion = new ProtectedCuboidRegion(regionName, min, max); + + if (regions.size() > 1) { + plugin.loLogger().warn("Mine " + regionName + " has multiple regions - " + + "only the first region is fully protected by WorldGuard"); + } + + return mainRegion; + } + + private void applyOwnersAndMembers(ProtectedCuboidRegion region, WorldGuardConfig wgConfig) { + // Set owners + for (String owner : wgConfig.owners()) { + if (owner.startsWith("uuid:")) { + try { + UUID uuid = UUID.fromString(owner.substring(5)); + region.getOwners().addPlayer(uuid); + } catch (IllegalArgumentException e) { + plugin.loLogger().warn("Invalid owner UUID: " + owner); + } + } else { + region.getOwners().addPlayer(owner); + } + } + + // Set members + for (String member : wgConfig.members()) { + if (member.startsWith("uuid:")) { + try { + UUID uuid = UUID.fromString(member.substring(5)); + region.getMembers().addPlayer(uuid); + } catch (IllegalArgumentException e) { + plugin.loLogger().warn("Invalid member UUID: " + member); + } + } else { + region.getMembers().addPlayer(member); + } + } + } + + private void applyFlags(ProtectedCuboidRegion region, List flags) { + for (String flagEntry : flags) { + flagParser.parseAndSetFlag(region, flagEntry); + } + } + + public boolean isWorldGuardEnabled() { + return worldGuardEnabled; + } +} diff --git a/src/main/resources/defaults.yml b/src/main/resources/defaults.yml index 8ab0651..233442e 100644 --- a/src/main/resources/defaults.yml +++ b/src/main/resources/defaults.yml @@ -62,3 +62,34 @@ rewards: [] teleport: enabled: false location: "" + +# WorldGuard integration +worldguard: + # Enable automatic region creation when mines are created + enabled: false + + # Region name template. Available placeholders: + # {mine_name} - name of the mine + # {random_4} - 4 random digits (0000-9999) + # {random_3} - 3 random digits (000-999) + # {random_6} - 6 random alphanumeric characters + region-template: "{mine_name}_{random_4}" + + # Region owners (player names or "uuid:") + owners: [] + # - "player_name" + # - "uuid:12345678-1234-1234-1234-123456789012" + + # Region members (player names or "uuid:") + members: [] + + # WorldGuard flags to set + flags: + - "passthrough=deny" + - "build=allow" + - "pvp=deny" + - "tnt=deny" + - "creeper-explosion=deny" + + # Automatically protect the region on creation + protect-on-create: true