diff --git a/src/main/java/me/xginko/villageroptimizer/modules/BlockOptimization.java b/src/main/java/me/xginko/villageroptimizer/modules/BlockOptimization.java index e871830..3461d0a 100644 --- a/src/main/java/me/xginko/villageroptimizer/modules/BlockOptimization.java +++ b/src/main/java/me/xginko/villageroptimizer/modules/BlockOptimization.java @@ -30,6 +30,10 @@ import java.util.List; public class BlockOptimization implements VillagerOptimizerModule, Listener { + /* + * TODO: Think of better logic than just checking under the villagers feet for block + * */ + private final VillagerManager villagerManager; private final HashSet blocks_that_disable = new HashSet<>(4); private final boolean shouldLog, shouldNotifyPlayer; @@ -43,8 +47,7 @@ public class BlockOptimization implements VillagerOptimizerModule, Listener { config.addComment("optimization-methods.block-optimization.enable", """ When enabled, villagers standing on the configured specific blocks will become optimized once a\s player interacts with them. If the block is broken or moved, the villager will become unoptimized\s - again once a player interacts with the villager afterwards. - """); + again once a player interacts with the villager afterwards."""); config.getList("optimization-methods.block-optimization.materials", List.of( "LAPIS_BLOCK", "GLOWSTONE", "IRON_BLOCK" ), "Values here need to be valid bukkit Material enums for your server version." @@ -53,13 +56,12 @@ public class BlockOptimization implements VillagerOptimizerModule, Listener { Material disableBlock = Material.valueOf(configuredMaterial); this.blocks_that_disable.add(disableBlock); } catch (IllegalArgumentException e) { - LogUtils.materialNotRecognized("optimization-methods.block-optimization", configuredMaterial); + LogUtils.materialNotRecognized("block-optimization", configuredMaterial); } }); this.cooldown = config.getInt("optimization-methods.block-optimization.optimize-cooldown-seconds", 600, """ Cooldown in seconds until a villager can be optimized again by using specific blocks. \s - Here for configuration freedom. Recommended to leave as is to not enable any exploitable behavior. - """) * 1000L; + Here for configuration freedom. Recommended to leave as is to not enable any exploitable behavior.""") * 1000L; this.maxVillagers = config.getInt("optimization-methods.block-optimization.max-villagers-per-block", 3, "How many villagers can be optimized at once by placing a block under them."); this.shouldNotifyPlayer = config.getBoolean("optimization-methods.block-optimization.notify-player", true, @@ -92,11 +94,12 @@ public class BlockOptimization implements VillagerOptimizerModule, Listener { int counter = 0; for (Entity entity : placed.getRelative(BlockFace.UP).getLocation().getNearbyEntities(0.5,1,0.5)) { + if (counter >= maxVillagers) return; if (!entity.getType().equals(EntityType.VILLAGER)) continue; WrappedVillager wVillager = villagerManager.getOrAdd((Villager) entity); - if (wVillager.isOptimized()) continue; - if (counter >= maxVillagers) return; + final OptimizationType type = wVillager.getOptimizationType(); + if (!type.equals(OptimizationType.OFF) && !type.equals(OptimizationType.COMMAND)) continue; if (wVillager.canOptimize(cooldown) || player.hasPermission(Permissions.Bypass.BLOCK_COOLDOWN.get())) { wVillager.setOptimization(OptimizationType.BLOCK); diff --git a/src/main/java/me/xginko/villageroptimizer/modules/RestockTrades.java b/src/main/java/me/xginko/villageroptimizer/modules/RestockTrades.java index 5a5c97d..ab07e86 100644 --- a/src/main/java/me/xginko/villageroptimizer/modules/RestockTrades.java +++ b/src/main/java/me/xginko/villageroptimizer/modules/RestockTrades.java @@ -18,6 +18,10 @@ import org.bukkit.event.player.PlayerInteractEntityEvent; public class RestockTrades implements VillagerOptimizerModule, Listener { + /* + * TODO: Disable notify message for cooldown bypassers + * */ + private final VillagerManager villagerManager; private final long restock_delay_millis; private final boolean shouldLog, notifyPlayer; diff --git a/src/main/java/me/xginko/villageroptimizer/modules/VillagerChunkLimit.java b/src/main/java/me/xginko/villageroptimizer/modules/VillagerChunkLimit.java index ffa9267..6718355 100644 --- a/src/main/java/me/xginko/villageroptimizer/modules/VillagerChunkLimit.java +++ b/src/main/java/me/xginko/villageroptimizer/modules/VillagerChunkLimit.java @@ -23,13 +23,17 @@ import java.util.logging.Level; public class VillagerChunkLimit implements VillagerOptimizerModule, Listener { + /* + * TODO: expand villager chunk limit with settings for optimized and optimzed. + * */ + private final VillagerOptimizer plugin; private final VillagerManager villagerManager; private ScheduledTask scheduledTask; private final List removalPriority = new ArrayList<>(16); - private final int maxVillagersPerChunk; + private final int global_max_villagers_per_chunk, max_unoptimized_per_chunk, max_optimized_per_chunk; private final boolean logIsEnabled; - private final long checkPeriod; + private final long check_period; protected VillagerChunkLimit() { shouldEnable(); @@ -40,10 +44,17 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener { Checks chunks for too many villagers and removes excess villagers based on priority.\s Naturally, optimized villagers will be picked last since they don't affect performance\s as much as unoptimized villagers."""); - this.maxVillagersPerChunk = config.getInt("villager-chunk-limit.max-villagers-per-chunk", 25); - this.logIsEnabled = config.getBoolean("villager-chunk-limit.log-removals", false); - this.checkPeriod = config.getInt("villager-chunk-limit.check-period-in-ticks", 600, + this.global_max_villagers_per_chunk = config.getInt("villager-chunk-limit.global-max-villagers-per-chunk", 50, """ + The total amount of villagers, no matter if optimized or unoptimized per chunk.\s + You want this number to be minimum as high as the sum of optimized and unoptimized\s + per chunk if you don't want to risk deleting the wrong villagers."""); + this.max_unoptimized_per_chunk = config.getInt("villager-chunk-limit.max-unoptimized-per-chunk", 30, + "The maximum amount of unoptimized villagers per chunk."); + this.max_optimized_per_chunk = config.getInt("villager-chunk-limit.max-optimized-per-chunk", 20, + "The maximum amount of optimized villagers per chunk."); + this.check_period = config.getInt("villager-chunk-limit.check-period-in-ticks", 600, "Check all loaded chunks every X ticks. 1 second = 20 ticks"); + this.logIsEnabled = config.getBoolean("villager-chunk-limit.log-removals", false); config.getList("villager-chunk-limit.removal-priority", List.of( "NONE", "NITWIT", "SHEPHERD", "FISHERMAN", "BUTCHER", "CARTOGRAPHER", "LEATHERWORKER", "FLETCHER", "MASON", "FARMER", "ARMORER", "TOOLSMITH", "WEAPONSMITH", "CLERIC", "LIBRARIAN" @@ -64,7 +75,13 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener { @Override public void enable() { plugin.getServer().getPluginManager().registerEvents(this, plugin); - this.scheduledTask = plugin.getServer().getGlobalRegionScheduler().runAtFixedRate(plugin, task -> run(), checkPeriod, checkPeriod); + this.scheduledTask = plugin.getServer().getGlobalRegionScheduler().runAtFixedRate(plugin, periodic_chunk_check -> { + plugin.getServer().getWorlds().forEach(world -> { + for (Chunk chunk : world.getLoadedChunks()) { + plugin.getServer().getRegionScheduler().run(plugin, world, chunk.getX(), chunk.getZ(), check_chunk -> checkVillagersInChunk(chunk)); + } + }); + }, check_period, check_period); } @Override @@ -94,14 +111,6 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener { } } - private void run() { - plugin.getServer().getWorlds().forEach(world -> { - for (Chunk chunk : world.getLoadedChunks()) { - plugin.getServer().getRegionScheduler().run(plugin, world, chunk.getX(), chunk.getZ(), task -> checkVillagersInChunk(chunk)); - } - }); - } - private void checkVillagersInChunk(Chunk chunk) { // Create a list with all villagers in that chunk List villagers_in_chunk = new ArrayList<>(); @@ -112,7 +121,7 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener { } // Check if there are more villagers in that chunk than allowed - int amount_over_the_limit = villagers_in_chunk.size() - maxVillagersPerChunk; + int amount_over_the_limit = villagers_in_chunk.size() - global_max_villagers_per_chunk; if (amount_over_the_limit <= 0) return; // Sort villager list by profession priority diff --git a/src/main/java/me/xginko/villageroptimizer/modules/VillagerOptimizerModule.java b/src/main/java/me/xginko/villageroptimizer/modules/VillagerOptimizerModule.java index 4233943..cd5f9b8 100644 --- a/src/main/java/me/xginko/villageroptimizer/modules/VillagerOptimizerModule.java +++ b/src/main/java/me/xginko/villageroptimizer/modules/VillagerOptimizerModule.java @@ -17,12 +17,12 @@ public interface VillagerOptimizerModule { modules.add(new BlockOptimization()); modules.add(new LevelVillagers()); modules.add(new NametagOptimization()); - modules.add(new NoBabyVillagers()); modules.add(new PreventUnoptimizedTrading()); modules.add(new PreventVillagerDamage()); modules.add(new PreventVillagerTargetting()); modules.add(new RestockTrades()); modules.add(new VillagerChunkLimit()); + modules.add(new VillagersSpawnAdult()); modules.add(new WorkstationOptimization()); modules.forEach(module -> { diff --git a/src/main/java/me/xginko/villageroptimizer/modules/NoBabyVillagers.java b/src/main/java/me/xginko/villageroptimizer/modules/VillagersSpawnAdult.java similarity index 53% rename from src/main/java/me/xginko/villageroptimizer/modules/NoBabyVillagers.java rename to src/main/java/me/xginko/villageroptimizer/modules/VillagersSpawnAdult.java index 9bb8c7b..8539c06 100644 --- a/src/main/java/me/xginko/villageroptimizer/modules/NoBabyVillagers.java +++ b/src/main/java/me/xginko/villageroptimizer/modules/VillagersSpawnAdult.java @@ -9,9 +9,9 @@ import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.entity.CreatureSpawnEvent; -public class NoBabyVillagers implements VillagerOptimizerModule, Listener { +public class VillagersSpawnAdult implements VillagerOptimizerModule, Listener { - protected NoBabyVillagers() {} + protected VillagersSpawnAdult() {} @Override public void enable() { @@ -26,14 +26,18 @@ public class NoBabyVillagers implements VillagerOptimizerModule, Listener { @Override public boolean shouldEnable() { - return VillagerOptimizer.getConfiguration().getBoolean("gameplay.villagers-spawn-as-adults", false, - "Automatically turns baby villagers into adults when spawning."); + return VillagerOptimizer.getConfiguration().getBoolean("gameplay.villagers-spawn-as-adults.enable", false, """ + Spawned villagers will immediately be adults.\s + This is to save some more performance as players don't have to keep unoptimized\s + villagers loaded because they have to wait for them to turn into adults before they can\s + optimize them."""); } - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onVillagerSpawn(CreatureSpawnEvent event) { - if (!event.getEntityType().equals(EntityType.VILLAGER)) return; - Villager villager = (Villager) event.getEntity(); - if (!villager.isAdult()) villager.setAdult(); + if (event.getEntityType().equals(EntityType.VILLAGER)) { + Villager villager = (Villager) event.getEntity(); + if (!villager.isAdult()) villager.setAdult(); + } } } diff --git a/src/main/java/me/xginko/villageroptimizer/modules/WorkstationOptimization.java b/src/main/java/me/xginko/villageroptimizer/modules/WorkstationOptimization.java index ffe30f4..0d820a1 100644 --- a/src/main/java/me/xginko/villageroptimizer/modules/WorkstationOptimization.java +++ b/src/main/java/me/xginko/villageroptimizer/modules/WorkstationOptimization.java @@ -27,7 +27,10 @@ import java.util.HashSet; import java.util.List; public class WorkstationOptimization implements VillagerOptimizerModule, Listener { - + /* + * TODO: Make placed workstation villager profession related. + * */ + private final VillagerManager villagerManager; private final HashSet workstations_that_disable = new HashSet<>(14); private final boolean shouldLog, shouldNotifyPlayer; @@ -50,7 +53,7 @@ public class WorkstationOptimization implements VillagerOptimizerModule, Listene Material disableBlock = Material.valueOf(configuredMaterial); this.workstations_that_disable.add(disableBlock); } catch (IllegalArgumentException e) { - LogUtils.materialNotRecognized("optimization-methods.workstation-optimization", configuredMaterial); + LogUtils.materialNotRecognized("workstation-optimization", configuredMaterial); } }); this.search_radius = config.getDouble("optimization-methods.workstation-optimization.search-radius-in-blocks", 2.0, """ @@ -100,9 +103,12 @@ public class WorkstationOptimization implements VillagerOptimizerModule, Listene WrappedVillager wVillager = villagerManager.getOrAdd(villager); final double distance = entity.getLocation().distance(workstationLoc); - if (!wVillager.isOptimized() && distance < closestDistance) { - closestOptimizableVillager = wVillager; - closestDistance = distance; + if (distance < closestDistance) { + final OptimizationType type = wVillager.getOptimizationType(); + if (type.equals(OptimizationType.OFF) || type.equals(OptimizationType.COMMAND)) { + closestOptimizableVillager = wVillager; + closestDistance = distance; + } } } @@ -150,15 +156,17 @@ public class WorkstationOptimization implements VillagerOptimizerModule, Listene WrappedVillager wVillager = villagerManager.getOrAdd(villager); final double distance = entity.getLocation().distance(workstationLoc); - if (wVillager.isOptimized() && distance < closestDistance) { - closestOptimizedVillager = wVillager; - closestDistance = distance; + if (distance < closestDistance) { + final OptimizationType type = wVillager.getOptimizationType(); + if (type.equals(OptimizationType.WORKSTATION) || type.equals(OptimizationType.COMMAND)) { + closestOptimizedVillager = wVillager; + closestDistance = distance; + } } } - if (closestOptimizedVillager == null) return; - - if (closestOptimizedVillager.getOptimizationType().equals(OptimizationType.WORKSTATION)) { + if (closestOptimizedVillager != null) { + closestOptimizedVillager.setOptimization(OptimizationType.OFF); if (shouldNotifyPlayer) { final String villagerType = closestOptimizedVillager.villager().getProfession().toString().toLowerCase(); final String workstation = placed.getType().toString().toLowerCase();