diff --git a/src/main/java/me/xginko/villageroptimizer/VillagerOptimizer.java b/src/main/java/me/xginko/villageroptimizer/VillagerOptimizer.java index 58c6905..27f5b14 100644 --- a/src/main/java/me/xginko/villageroptimizer/VillagerOptimizer.java +++ b/src/main/java/me/xginko/villageroptimizer/VillagerOptimizer.java @@ -154,7 +154,7 @@ public final class VillagerOptimizer extends JavaPlugin { File langDirectory = new File(getDataFolder() + File.separator + "lang"); Files.createDirectories(langDirectory.toPath()); for (String fileName : getDefaultLanguageFiles()) { - final String localeString = fileName.substring(fileName.lastIndexOf(File.separator) + 1, fileName.lastIndexOf('.')); + final String localeString = fileName.substring(fileName.lastIndexOf('/') + 1, fileName.lastIndexOf('.')); if (startup) logger.info( Component.text("│ ").style(STYLE) .append(Component.text(" "+localeString).color(NamedTextColor.WHITE).decorate(TextDecoration.BOLD)) @@ -190,7 +190,7 @@ public final class VillagerOptimizer extends JavaPlugin { try (final JarFile pluginJarFile = new JarFile(this.getFile())) { return pluginJarFile.stream() .map(ZipEntry::getName) - .filter(name -> name.startsWith("lang" + File.separator) && name.endsWith(".yml")) + .filter(name -> name.startsWith("lang/") && name.endsWith(".yml")) .collect(Collectors.toSet()); } catch (IOException ioException) { logger.error("Failed getting default lang files!", ioException); diff --git a/src/main/java/me/xginko/villageroptimizer/WrappedVillager.java b/src/main/java/me/xginko/villageroptimizer/WrappedVillager.java index 720bc8e..6ab72da 100644 --- a/src/main/java/me/xginko/villageroptimizer/WrappedVillager.java +++ b/src/main/java/me/xginko/villageroptimizer/WrappedVillager.java @@ -2,9 +2,8 @@ package me.xginko.villageroptimizer; import me.xginko.villageroptimizer.enums.Keyring; import me.xginko.villageroptimizer.enums.OptimizationType; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Location; +import org.bukkit.Sound; import org.bukkit.entity.Villager; import org.bukkit.entity.memory.MemoryKey; import org.bukkit.inventory.MerchantRecipe; @@ -262,6 +261,14 @@ public final class WrappedVillager { return 1; } + /** + * @return true if the villager can loose his acquired profession by having their workstation destroyed. + */ + public boolean canLooseProfession() { + // A villager with a level of 1 and no trading experience is liable to lose its profession. + return villager.getVillagerLevel() <= 1 && villager.getVillagerExperience() <= 0; + } + /** * @param cooldown_millis The configured cooldown in milliseconds you want to check against. * @return Whether the villager can be leveled up or not with the checked milliseconds @@ -304,28 +311,23 @@ public final class WrappedVillager { return cooldown_millis; } - public void memorizeName(final Component customName) { - dataContainer.set(Keyring.VillagerOptimizer.LAST_OPTIMIZE_NAME.getKey(), PersistentDataType.STRING, MiniMessage.miniMessage().serialize(customName)); - } - - public @Nullable Component getMemorizedName() { - if (dataContainer.has(Keyring.VillagerOptimizer.LAST_OPTIMIZE_NAME.getKey(), PersistentDataType.STRING)) - return MiniMessage.miniMessage().deserialize(dataContainer.get(Keyring.VillagerOptimizer.LAST_OPTIMIZE_NAME.getKey(), PersistentDataType.STRING)); - return null; - } - - public void forgetName() { - dataContainer.remove(Keyring.VillagerOptimizer.LAST_OPTIMIZE_NAME.getKey()); + public void sayNo() { + try { + villager.shakeHead(); + } catch (NoSuchMethodError e) { + villager.getWorld().playSound(villager.getLocation(), Sound.ENTITY_VILLAGER_NO, 1.0F, 1.0F); + } } private static class CachedJobSite { + private final @NotNull Villager villager; private @Nullable Location jobSite; private long lastRefresh; - private CachedJobSite(Villager villager) { - this.jobSite = villager.getMemory(MemoryKey.JOB_SITE); - this.lastRefresh = System.currentTimeMillis(); + private CachedJobSite(@NotNull Villager villager) { + this.villager = villager; + this.jobSite = getJobSite(); } - private @Nullable Location getJobSite(Villager villager) { + private @Nullable Location getJobSite() { final long now = System.currentTimeMillis(); if (now - lastRefresh > 1000L) { this.jobSite = villager.getMemory(MemoryKey.JOB_SITE); @@ -338,11 +340,6 @@ public final class WrappedVillager { public @Nullable Location getJobSite() { if (cachedJobSite == null) cachedJobSite = new CachedJobSite(villager); - return cachedJobSite.getJobSite(villager); - } - - public boolean canLooseProfession() { - // A villager with a level of 1 and no trading experience is liable to lose its profession. - return villager.getVillagerLevel() <= 1 && villager.getVillagerExperience() <= 0; + return cachedJobSite.getJobSite(); } } \ No newline at end of file diff --git a/src/main/java/me/xginko/villageroptimizer/config/LanguageCache.java b/src/main/java/me/xginko/villageroptimizer/config/LanguageCache.java index d46583d..d7eaf93 100644 --- a/src/main/java/me/xginko/villageroptimizer/config/LanguageCache.java +++ b/src/main/java/me/xginko/villageroptimizer/config/LanguageCache.java @@ -32,7 +32,7 @@ public class LanguageCache { VillagerOptimizer.getLog().error("Failed to create lang directory."); // Check if the file already exists and save the one from the plugin's resources folder if it does not if (!langYML.exists()) - plugin.saveResource("lang" + File.separator + locale + ".yml", false); + plugin.saveResource("lang/" + locale + ".yml", false); // Finally, load the lang file with configmaster this.lang = ConfigFile.loadConfig(langYML); diff --git a/src/main/java/me/xginko/villageroptimizer/modules/VillagerOptimizerModule.java b/src/main/java/me/xginko/villageroptimizer/modules/VillagerOptimizerModule.java index 9b42841..3f0bfe6 100644 --- a/src/main/java/me/xginko/villageroptimizer/modules/VillagerOptimizerModule.java +++ b/src/main/java/me/xginko/villageroptimizer/modules/VillagerOptimizerModule.java @@ -27,7 +27,7 @@ public interface VillagerOptimizerModule { modules.add(new FixOptimisationAfterCure()); modules.add(new RestockOptimizedTrades()); modules.add(new LevelOptimizedProfession()); - modules.add(new RenameOptimizedVillagers()); + modules.add(new VisuallyHighlightOptimized()); modules.add(new MakeVillagersSpawnAdult()); modules.add(new PreventUnoptimizedTrading()); modules.add(new PreventOptimizedTargeting()); diff --git a/src/main/java/me/xginko/villageroptimizer/modules/gameplay/EnableLeashingVillagers.java b/src/main/java/me/xginko/villageroptimizer/modules/gameplay/EnableLeashingVillagers.java index 365b015..0fd9581 100644 --- a/src/main/java/me/xginko/villageroptimizer/modules/gameplay/EnableLeashingVillagers.java +++ b/src/main/java/me/xginko/villageroptimizer/modules/gameplay/EnableLeashingVillagers.java @@ -62,11 +62,12 @@ public class EnableLeashingVillagers implements VillagerOptimizerModule, Listene final ItemStack handItem = player.getInventory().getItem(event.getHand()); if (handItem == null || !handItem.getType().equals(Material.LEAD)) return; - Villager villager = (Villager) event.getRightClicked(); + final Villager villager = (Villager) event.getRightClicked(); if (villager.isLeashed()) return; - event.setCancelled(true); // Cancel the event, so we don't interact with the villager if (only_optimized && !villagerCache.getOrAdd(villager).isOptimized()) return; + event.setCancelled(true); // Cancel the event, so we don't interact with the villager + // Call event for compatibility with other plugins, constructing non deprecated if available PlayerLeashEntityEvent leashEvent; try { @@ -86,7 +87,7 @@ public class EnableLeashingVillagers implements VillagerOptimizerModule, Listene if (log_enabled) { VillagerOptimizer.getLog().info(Component.text(player.getName() + " leashed a villager at " + - CommonUtil.formatLocation(villager.getLocation())).color(VillagerOptimizer.STYLE.color())); + CommonUtil.formatLocation(villager.getLocation())).color(VillagerOptimizer.COLOR)); } }); } diff --git a/src/main/java/me/xginko/villageroptimizer/modules/gameplay/LevelOptimizedProfession.java b/src/main/java/me/xginko/villageroptimizer/modules/gameplay/LevelOptimizedProfession.java index 679c544..8d6d77d 100644 --- a/src/main/java/me/xginko/villageroptimizer/modules/gameplay/LevelOptimizedProfession.java +++ b/src/main/java/me/xginko/villageroptimizer/modules/gameplay/LevelOptimizedProfession.java @@ -68,22 +68,21 @@ public class LevelOptimizedProfession implements VillagerOptimizerModule, Listen event.getInventory().getType().equals(InventoryType.MERCHANT) && event.getInventory().getHolder() instanceof Villager ) { - Villager villager = (Villager) event.getInventory().getHolder(); - WrappedVillager wVillager = villagerCache.getOrAdd(villager); + final Villager villager = (Villager) event.getInventory().getHolder(); + final WrappedVillager wVillager = villagerCache.getOrAdd(villager); if (!wVillager.isOptimized()) return; if (wVillager.canLevelUp(cooldown_millis)) { - if (wVillager.calculateLevel() > villager.getVillagerLevel()) { - scheduler.runAtEntity(villager, enableAI -> { - villager.addPotionEffect(new PotionEffect(PotionEffectType.SLOW, 120, 120, false, false)); - villager.setAware(true); + if (wVillager.calculateLevel() <= villager.getVillagerLevel()) return; - scheduler.runAtEntityLater(villager, disableAI -> { - villager.setAware(false); - wVillager.saveLastLevelUp(); - }, 5, TimeUnit.SECONDS); - }); - } + scheduler.runAtEntity(villager, enableAI -> { + villager.addPotionEffect(new PotionEffect(PotionEffectType.SLOW, 120, 120, false, false)); + villager.setAware(true); + scheduler.runAtEntityLater(villager, disableAI -> { + villager.setAware(false); + wVillager.saveLastLevelUp(); + }, 5, TimeUnit.SECONDS); + }); } else { if (notify_player) { Player player = (Player) event.getPlayer(); diff --git a/src/main/java/me/xginko/villageroptimizer/modules/gameplay/MakeVillagersSpawnAdult.java b/src/main/java/me/xginko/villageroptimizer/modules/gameplay/MakeVillagersSpawnAdult.java index 57d599e..ccc345d 100644 --- a/src/main/java/me/xginko/villageroptimizer/modules/gameplay/MakeVillagersSpawnAdult.java +++ b/src/main/java/me/xginko/villageroptimizer/modules/gameplay/MakeVillagersSpawnAdult.java @@ -37,7 +37,7 @@ public class MakeVillagersSpawnAdult implements VillagerOptimizerModule, Listene @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onVillagerSpawn(CreatureSpawnEvent event) { if (event.getEntityType().equals(EntityType.VILLAGER)) { - Villager villager = (Villager) event.getEntity(); + final Villager villager = (Villager) event.getEntity(); if (!villager.isAdult()) villager.setAdult(); } } diff --git a/src/main/java/me/xginko/villageroptimizer/modules/gameplay/PreventOptimizedTargeting.java b/src/main/java/me/xginko/villageroptimizer/modules/gameplay/PreventOptimizedTargeting.java index 65d375c..d66b70b 100644 --- a/src/main/java/me/xginko/villageroptimizer/modules/gameplay/PreventOptimizedTargeting.java +++ b/src/main/java/me/xginko/villageroptimizer/modules/gameplay/PreventOptimizedTargeting.java @@ -42,7 +42,7 @@ public class PreventOptimizedTargeting implements VillagerOptimizerModule, Liste @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onTarget(EntityTargetEvent event) { - Entity target = event.getTarget(); + final Entity target = event.getTarget(); if ( target != null && target.getType().equals(EntityType.VILLAGER) @@ -55,7 +55,7 @@ public class PreventOptimizedTargeting implements VillagerOptimizerModule, Liste @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onEntityTargetVillager(EntityPathfindEvent event) { - Entity target = event.getTargetEntity(); + final Entity target = event.getTargetEntity(); if ( target != null && target.getType().equals(EntityType.VILLAGER) diff --git a/src/main/java/me/xginko/villageroptimizer/modules/gameplay/PreventUnoptimizedTrading.java b/src/main/java/me/xginko/villageroptimizer/modules/gameplay/PreventUnoptimizedTrading.java index 9672d76..c0e8b57 100644 --- a/src/main/java/me/xginko/villageroptimizer/modules/gameplay/PreventUnoptimizedTrading.java +++ b/src/main/java/me/xginko/villageroptimizer/modules/gameplay/PreventUnoptimizedTrading.java @@ -54,16 +54,14 @@ public class PreventUnoptimizedTrading implements VillagerOptimizerModule, Liste if (!event.getInventory().getType().equals(InventoryType.MERCHANT)) return; if (event.getWhoClicked().hasPermission(Bypass.TRADE_PREVENTION.get())) return; if (!(event.getInventory().getHolder() instanceof Villager)) return; + if (villagerCache.getOrAdd((Villager) event.getInventory().getHolder()).isOptimized()) return; - Villager villager = (Villager) event.getInventory().getHolder(); + event.setCancelled(true); - if (!villagerCache.getOrAdd(villager).isOptimized()) { - event.setCancelled(true); - if (notify_player) { - Player player = (Player) event.getWhoClicked(); - VillagerOptimizer.getLang(player.locale()).optimize_for_trading - .forEach(line -> KyoriUtil.sendMessage(player, line)); - } + if (notify_player) { + Player player = (Player) event.getWhoClicked(); + VillagerOptimizer.getLang(player.locale()).optimize_for_trading + .forEach(line -> KyoriUtil.sendMessage(player, line)); } } @@ -72,16 +70,14 @@ public class PreventUnoptimizedTrading implements VillagerOptimizerModule, Liste if (!event.getInventory().getType().equals(InventoryType.MERCHANT)) return; if (event.getWhoClicked().hasPermission(Bypass.TRADE_PREVENTION.get())) return; if (!(event.getInventory().getHolder() instanceof Villager)) return; + if (villagerCache.getOrAdd((Villager) event.getInventory().getHolder()).isOptimized()) return; - Villager villager = (Villager) event.getInventory().getHolder(); + event.setCancelled(true); - if (!villagerCache.getOrAdd(villager).isOptimized()) { - event.setCancelled(true); - if (notify_player) { - Player player = (Player) event.getWhoClicked(); - VillagerOptimizer.getLang(player.locale()).optimize_for_trading - .forEach(line -> KyoriUtil.sendMessage(player, line)); - } + if (notify_player) { + Player player = (Player) event.getWhoClicked(); + VillagerOptimizer.getLang(player.locale()).optimize_for_trading + .forEach(line -> KyoriUtil.sendMessage(player, line)); } } } diff --git a/src/main/java/me/xginko/villageroptimizer/modules/gameplay/RenameOptimizedVillagers.java b/src/main/java/me/xginko/villageroptimizer/modules/gameplay/RenameOptimizedVillagers.java deleted file mode 100644 index d764cab..0000000 --- a/src/main/java/me/xginko/villageroptimizer/modules/gameplay/RenameOptimizedVillagers.java +++ /dev/null @@ -1,81 +0,0 @@ -package me.xginko.villageroptimizer.modules.gameplay; - -import com.tcoded.folialib.impl.ServerImplementation; -import me.xginko.villageroptimizer.VillagerOptimizer; -import me.xginko.villageroptimizer.WrappedVillager; -import me.xginko.villageroptimizer.config.Config; -import me.xginko.villageroptimizer.events.VillagerOptimizeEvent; -import me.xginko.villageroptimizer.events.VillagerUnoptimizeEvent; -import me.xginko.villageroptimizer.modules.VillagerOptimizerModule; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.MiniMessage; -import org.bukkit.entity.Villager; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; - -public class RenameOptimizedVillagers implements VillagerOptimizerModule, Listener { - - private final ServerImplementation scheduler; - private final Component optimized_name; - private final boolean overwrite_previous_name; - - public RenameOptimizedVillagers() { - shouldEnable(); - this.scheduler = VillagerOptimizer.getFoliaLib().getImpl(); - Config config = VillagerOptimizer.getConfiguration(); - config.master().addComment("gameplay.rename-optimized-villagers.enable", - "Will change a villager's name to the name configured below when they are optimized.\n" + - "These names will be removed when unoptimized again if they were not changed in the meantime."); - this.optimized_name = MiniMessage.miniMessage().deserialize( - config.getString("gameplay.rename-optimized-villagers.optimized-name", "Optimized", - "The name that will be used to mark optimized villagers. Uses MiniMessage format.")); - this.overwrite_previous_name = config.getBoolean("gameplay.rename-optimized-villagers.overwrite-existing-name", false, - "If set to true, will rename even if the villager has already been named."); - } - - @Override - public void enable() { - VillagerOptimizer plugin = VillagerOptimizer.getInstance(); - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public void disable() { - HandlerList.unregisterAll(this); - } - - @Override - public boolean shouldEnable() { - return VillagerOptimizer.getConfiguration().getBoolean("gameplay.rename-optimized-villagers.enable", false); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onOptimize(VillagerOptimizeEvent event) { - WrappedVillager wVillager = event.getWrappedVillager(); - Villager villager = wVillager.villager(); - - if (overwrite_previous_name || villager.customName() == null) { - scheduler.runAtEntityLater(villager, () -> { - villager.customName(optimized_name); - wVillager.memorizeName(optimized_name); - }, 10L); - } - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void onUnOptimize(VillagerUnoptimizeEvent event) { - WrappedVillager wVillager = event.getWrappedVillager(); - Villager villager = wVillager.villager(); - - scheduler.runAtEntityLater(villager, () -> { - final Component currentName = villager.customName(); - final Component memorizedName = wVillager.getMemorizedName(); - if (currentName != null && currentName.equals(memorizedName)) - villager.customName(null); - if (memorizedName != null) - wVillager.forgetName(); - }, 10L); - } -} \ No newline at end of file diff --git a/src/main/java/me/xginko/villageroptimizer/modules/gameplay/RestockOptimizedTrades.java b/src/main/java/me/xginko/villageroptimizer/modules/gameplay/RestockOptimizedTrades.java index 0dd7e92..5f70b79 100644 --- a/src/main/java/me/xginko/villageroptimizer/modules/gameplay/RestockOptimizedTrades.java +++ b/src/main/java/me/xginko/villageroptimizer/modules/gameplay/RestockOptimizedTrades.java @@ -61,10 +61,10 @@ public class RestockOptimizedTrades implements VillagerOptimizerModule, Listener private void onInteract(PlayerInteractEntityEvent event) { if (!event.getRightClicked().getType().equals(EntityType.VILLAGER)) return; - WrappedVillager wVillager = villagerCache.getOrAdd((Villager) event.getRightClicked()); + final WrappedVillager wVillager = villagerCache.getOrAdd((Villager) event.getRightClicked()); if (!wVillager.isOptimized()) return; - Player player = event.getPlayer(); + final Player player = event.getPlayer(); final boolean player_bypassing = player.hasPermission(Bypass.RESTOCK_COOLDOWN.get()); if (wVillager.canRestock(restock_delay_millis) || player_bypassing) { diff --git a/src/main/java/me/xginko/villageroptimizer/modules/gameplay/UnoptimizeOnJobLoose.java b/src/main/java/me/xginko/villageroptimizer/modules/gameplay/UnoptimizeOnJobLoose.java index 364018c..bbab7f0 100644 --- a/src/main/java/me/xginko/villageroptimizer/modules/gameplay/UnoptimizeOnJobLoose.java +++ b/src/main/java/me/xginko/villageroptimizer/modules/gameplay/UnoptimizeOnJobLoose.java @@ -39,9 +39,7 @@ public class UnoptimizeOnJobLoose implements VillagerOptimizerModule, Listener { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onJobReset(VillagerCareerChangeEvent event) { if (!event.getReason().equals(VillagerCareerChangeEvent.ChangeReason.LOSING_JOB)) return; - - WrappedVillager wrappedVillager = villagerCache.getOrAdd(event.getEntity()); - + final WrappedVillager wrappedVillager = villagerCache.getOrAdd(event.getEntity()); if (wrappedVillager.isOptimized()) { wrappedVillager.setOptimizationType(OptimizationType.NONE); } diff --git a/src/main/java/me/xginko/villageroptimizer/modules/gameplay/VisuallyHighlightOptimized.java b/src/main/java/me/xginko/villageroptimizer/modules/gameplay/VisuallyHighlightOptimized.java new file mode 100644 index 0000000..58f8ba8 --- /dev/null +++ b/src/main/java/me/xginko/villageroptimizer/modules/gameplay/VisuallyHighlightOptimized.java @@ -0,0 +1,58 @@ +package me.xginko.villageroptimizer.modules.gameplay; + +import com.tcoded.folialib.impl.ServerImplementation; +import me.xginko.villageroptimizer.VillagerOptimizer; +import me.xginko.villageroptimizer.config.Config; +import me.xginko.villageroptimizer.events.VillagerOptimizeEvent; +import me.xginko.villageroptimizer.events.VillagerUnoptimizeEvent; +import me.xginko.villageroptimizer.modules.VillagerOptimizerModule; +import org.bukkit.entity.Villager; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; + +public class VisuallyHighlightOptimized implements VillagerOptimizerModule, Listener { + + private final ServerImplementation scheduler; + + public VisuallyHighlightOptimized() { + shouldEnable(); + this.scheduler = VillagerOptimizer.getFoliaLib().getImpl(); + Config config = VillagerOptimizer.getConfiguration(); + config.master().addComment("gameplay.outline-optimized-villagers.enable", + "Will make optimized villagers glow."); + } + + @Override + public void enable() { + VillagerOptimizer plugin = VillagerOptimizer.getInstance(); + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @Override + public void disable() { + HandlerList.unregisterAll(this); + } + + @Override + public boolean shouldEnable() { + return VillagerOptimizer.getConfiguration().getBoolean("gameplay.outline-optimized-villagers.enable", false); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onOptimize(VillagerOptimizeEvent event) { + Villager villager = event.getWrappedVillager().villager(); + scheduler.runAtEntity(villager, glow -> { + if (!villager.isGlowing()) villager.setGlowing(true); + }); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onUnOptimize(VillagerUnoptimizeEvent event) { + Villager villager = event.getWrappedVillager().villager(); + scheduler.runAtEntity(villager, unGlow -> { + if (villager.isGlowing()) villager.setGlowing(false); + }); + } +} \ No newline at end of file diff --git a/src/main/java/me/xginko/villageroptimizer/modules/optimization/OptimizeByBlock.java b/src/main/java/me/xginko/villageroptimizer/modules/optimization/OptimizeByBlock.java index c042841..7a60066 100644 --- a/src/main/java/me/xginko/villageroptimizer/modules/optimization/OptimizeByBlock.java +++ b/src/main/java/me/xginko/villageroptimizer/modules/optimization/OptimizeByBlock.java @@ -17,8 +17,6 @@ import net.kyori.adventure.text.TextReplacementConfig; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; -import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.entity.Villager; import org.bukkit.event.EventHandler; @@ -58,7 +56,7 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener { try { return Material.valueOf(configuredMaterial); } catch (IllegalArgumentException e) { - VillagerOptimizer.getLog().warn("(block-optimization) Material '"+configuredMaterial + + VillagerOptimizer.getLog().warn("(block-optimization) Material '" + configuredMaterial + "' not recognized. Please use correct Material enums from: " + "https://jd.papermc.io/paper/1.20/org/bukkit/Material.html"); return null; @@ -96,9 +94,9 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onBlockPlace(BlockPlaceEvent event) { - Block placed = event.getBlock(); + final Block placed = event.getBlock(); if (!blocks_that_disable.contains(placed.getType())) return; - Player player = event.getPlayer(); + final Player player = event.getPlayer(); if (!player.hasPermission(Optimize.BLOCK.get())) return; if (only_while_sneaking && !player.isSneaking()) return; @@ -106,16 +104,14 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener { WrappedVillager closestOptimizableVillager = null; double closestDistance = Double.MAX_VALUE; - for (Entity entity : blockLoc.getNearbyEntities(search_radius, search_radius, search_radius)) { - if (!entity.getType().equals(EntityType.VILLAGER)) continue; - Villager villager = (Villager) entity; + for (Villager villager : blockLoc.getNearbyEntitiesByType(Villager.class, search_radius)) { final Villager.Profession profession = villager.getProfession(); if (profession.equals(Villager.Profession.NONE) || profession.equals(Villager.Profession.NITWIT)) continue; + final double distance = villager.getLocation().distanceSquared(blockLoc); + if (distance >= closestDistance) continue; - WrappedVillager wVillager = villagerCache.getOrAdd(villager); - final double distance = entity.getLocation().distanceSquared(blockLoc); - - if (distance < closestDistance && wVillager.canOptimize(cooldown_millis)) { + final WrappedVillager wVillager = villagerCache.getOrAdd(villager); + if (wVillager.canOptimize(cooldown_millis)) { closestOptimizableVillager = wVillager; closestDistance = distance; } @@ -132,7 +128,6 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener { ); if (!optimizeEvent.callEvent()) return; - closestOptimizableVillager.setOptimizationType(optimizeEvent.getOptimizationType()); closestOptimizableVillager.saveOptimizeTime(); @@ -154,7 +149,7 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener { CommonUtil.formatLocation(closestOptimizableVillager.villager().getLocation())).color(VillagerOptimizer.COLOR)); } } else { - CommonUtil.shakeHead(closestOptimizableVillager.villager()); + closestOptimizableVillager.sayNo(); if (notify_player) { final TextReplacementConfig timeLeft = TextReplacementConfig.builder() .matchLiteral("%time%") @@ -168,9 +163,9 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onBlockBreak(BlockBreakEvent event) { - Block broken = event.getBlock(); + final Block broken = event.getBlock(); if (!blocks_that_disable.contains(broken.getType())) return; - Player player = event.getPlayer(); + final Player player = event.getPlayer(); if (!player.hasPermission(Optimize.BLOCK.get())) return; if (only_while_sneaking && !player.isSneaking()) return; @@ -178,14 +173,12 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener { WrappedVillager closestOptimizedVillager = null; double closestDistance = Double.MAX_VALUE; - for (Entity entity : blockLoc.getNearbyEntities(search_radius, search_radius, search_radius)) { - if (!entity.getType().equals(EntityType.VILLAGER)) continue; - Villager villager = (Villager) entity; + for (Villager villager : blockLoc.getNearbyEntitiesByType(Villager.class, search_radius)) { + final double distance = villager.getLocation().distanceSquared(blockLoc); + if (distance >= closestDistance) continue; - WrappedVillager wVillager = villagerCache.getOrAdd(villager); - final double distance = entity.getLocation().distanceSquared(blockLoc); - - if (distance < closestDistance && wVillager.isOptimized()) { + final WrappedVillager wVillager = villagerCache.getOrAdd(villager); + if (wVillager.isOptimized()) { closestOptimizedVillager = wVillager; closestDistance = distance; } @@ -201,7 +194,6 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener { ); if (!unOptimizeEvent.callEvent()) return; - closestOptimizedVillager.setOptimizationType(OptimizationType.NONE); if (notify_player) { diff --git a/src/main/java/me/xginko/villageroptimizer/modules/optimization/OptimizeByNametag.java b/src/main/java/me/xginko/villageroptimizer/modules/optimization/OptimizeByNametag.java index c7d8d04..cd29920 100644 --- a/src/main/java/me/xginko/villageroptimizer/modules/optimization/OptimizeByNametag.java +++ b/src/main/java/me/xginko/villageroptimizer/modules/optimization/OptimizeByNametag.java @@ -14,7 +14,6 @@ import me.xginko.villageroptimizer.utils.CommonUtil; import me.xginko.villageroptimizer.utils.KyoriUtil; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextReplacementConfig; -import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import org.bukkit.Material; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; @@ -36,7 +35,6 @@ import java.util.stream.Collectors; public class OptimizeByNametag implements VillagerOptimizerModule, Listener { - private final PlainTextComponentSerializer plainTextSerializer; private final VillagerCache villagerCache; private final Set nametags; private final long cooldown; @@ -44,7 +42,6 @@ public class OptimizeByNametag implements VillagerOptimizerModule, Listener { public OptimizeByNametag() { shouldEnable(); - this.plainTextSerializer = PlainTextComponentSerializer.plainText(); this.villagerCache = VillagerOptimizer.getCache(); Config config = VillagerOptimizer.getConfiguration(); config.master().addComment("optimization-methods.nametag-optimization.enable", @@ -83,23 +80,23 @@ public class OptimizeByNametag implements VillagerOptimizerModule, Listener { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onPlayerInteractEntity(PlayerInteractEntityEvent event) { if (!event.getRightClicked().getType().equals(EntityType.VILLAGER)) return; - Player player = event.getPlayer(); + final Player player = event.getPlayer(); if (!player.hasPermission(Optimize.NAMETAG.get())) return; - ItemStack usedItem = player.getInventory().getItem(event.getHand()); + final ItemStack usedItem = player.getInventory().getItem(event.getHand()); if (usedItem != null && !usedItem.getType().equals(Material.NAME_TAG)) return; if (!usedItem.hasItemMeta()) return; - ItemMeta meta = usedItem.getItemMeta(); + final ItemMeta meta = usedItem.getItemMeta(); if (!meta.hasDisplayName()) return; // Get component name first, so we can manually name the villager when canceling the event to avoid item consumption. - Component newVillagerName = meta.displayName(); + final Component newVillagerName = meta.displayName(); assert newVillagerName != null; // Legitimate since we checked for hasDisplayName() - final String name = plainTextSerializer.serialize(newVillagerName); - Villager villager = (Villager) event.getRightClicked(); - WrappedVillager wVillager = villagerCache.getOrAdd(villager); + final String nameTagPlainText = CommonUtil.plainTextSerializer.serialize(newVillagerName); + final Villager villager = (Villager) event.getRightClicked(); + final WrappedVillager wVillager = villagerCache.getOrAdd(villager); - if (nametags.contains(name.toLowerCase())) { + if (nametags.contains(nameTagPlainText.toLowerCase())) { if (wVillager.canOptimize(cooldown) || player.hasPermission(Bypass.NAMETAG_COOLDOWN.get())) { VillagerOptimizeEvent optimizeEvent = new VillagerOptimizeEvent( wVillager, @@ -125,12 +122,12 @@ public class OptimizeByNametag implements VillagerOptimizerModule, Listener { if (log_enabled) { VillagerOptimizer.getLog().info(Component.text(player.getName() + - " optimized villager by nametag '" + name + "' at " + + " optimized villager by nametag '" + nameTagPlainText + "' at " + CommonUtil.formatLocation(wVillager.villager().getLocation())).color(VillagerOptimizer.COLOR)); } } else { event.setCancelled(true); - CommonUtil.shakeHead(villager); + wVillager.sayNo(); if (notify_player) { final TextReplacementConfig timeLeft = TextReplacementConfig.builder() .matchLiteral("%time%") @@ -142,9 +139,14 @@ public class OptimizeByNametag implements VillagerOptimizerModule, Listener { } } else { if (wVillager.isOptimized()) { - VillagerUnoptimizeEvent unOptimizeEvent = new VillagerUnoptimizeEvent(wVillager, player, OptimizationType.NAMETAG, event.isAsynchronous()); - if (!unOptimizeEvent.callEvent()) return; + VillagerUnoptimizeEvent unOptimizeEvent = new VillagerUnoptimizeEvent( + wVillager, + player, + OptimizationType.NAMETAG, + event.isAsynchronous() + ); + if (!unOptimizeEvent.callEvent()) return; wVillager.setOptimizationType(OptimizationType.NONE); if (notify_player) { @@ -154,7 +156,7 @@ public class OptimizeByNametag implements VillagerOptimizerModule, Listener { if (log_enabled) { VillagerOptimizer.getLog().info(Component.text(player.getName() + - " unoptimized villager by nametag '" + name + "' at " + + " unoptimized villager by nametag '" + nameTagPlainText + "' at " + CommonUtil.formatLocation(wVillager.villager().getLocation())).color(VillagerOptimizer.COLOR)); } } diff --git a/src/main/java/me/xginko/villageroptimizer/modules/optimization/OptimizeByWorkstation.java b/src/main/java/me/xginko/villageroptimizer/modules/optimization/OptimizeByWorkstation.java index cd7f3e1..f0556d5 100644 --- a/src/main/java/me/xginko/villageroptimizer/modules/optimization/OptimizeByWorkstation.java +++ b/src/main/java/me/xginko/villageroptimizer/modules/optimization/OptimizeByWorkstation.java @@ -30,6 +30,7 @@ import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.VillagerCareerChangeEvent; import org.bukkit.util.NumberConversions; import java.time.Duration; @@ -93,6 +94,20 @@ public class OptimizeByWorkstation implements VillagerOptimizerModule, Listener return VillagerOptimizer.getConfiguration().getBoolean("optimization-methods.workstation-optimization.enable", false); } + // Place block -> Remember what and where + // Wait for villager to claim jobsite + // Do optimization on that villager + + // Destroy workstation -> look for nearby villager that claimed it + // Do unoptimization immediately + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onCareerChange(VillagerCareerChangeEvent event) { + if (!event.getReason().equals(VillagerCareerChangeEvent.ChangeReason.EMPLOYED)) return; + + + } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) private void onBlockPlace(BlockPlaceEvent event) { final Block placed = event.getBlock(); @@ -127,7 +142,7 @@ public class OptimizeByWorkstation implements VillagerOptimizerModule, Listener pending_optimizations.put(placed.getLocation(), scheduler.runAtLocationLater(workstationLoc, () -> { if (!finalToOptimize.canOptimize(cooldown_millis) && !player.hasPermission(Bypass.WORKSTATION_COOLDOWN.get())) { - CommonUtil.shakeHead(finalToOptimize.villager()); + finalToOptimize.sayNo(); if (notify_player) { final TextReplacementConfig timeLeft = TextReplacementConfig.builder() .matchLiteral("%time%") diff --git a/src/main/java/me/xginko/villageroptimizer/utils/CommonUtil.java b/src/main/java/me/xginko/villageroptimizer/utils/CommonUtil.java index d6c25c2..74acffe 100644 --- a/src/main/java/me/xginko/villageroptimizer/utils/CommonUtil.java +++ b/src/main/java/me/xginko/villageroptimizer/utils/CommonUtil.java @@ -1,5 +1,6 @@ package me.xginko.villageroptimizer.utils; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Material; @@ -9,6 +10,8 @@ import org.jetbrains.annotations.NotNull; import java.time.Duration; public class CommonUtil { + public static final PlainTextComponentSerializer plainTextSerializer = PlainTextComponentSerializer.plainText(); + public static @NotNull String formatDuration(Duration duration) { if (duration.isNegative()) duration = duration.negated(); @@ -42,12 +45,6 @@ public class CommonUtil { } } - public static void shakeHead(@NotNull Villager villager) { - try { - villager.shakeHead(); - } catch (NoSuchMethodError ignored) {} - } - public static Villager.Profession getWorkstationProfession(@NotNull Material workstation) { switch (workstation) { case BARREL: diff --git a/src/main/java/me/xginko/villageroptimizer/logging/ComponentLoggerProviderImpl.java b/src/main/java/me/xginko/villageroptimizer/utils/ComponentLoggerProviderImpl.java similarity index 94% rename from src/main/java/me/xginko/villageroptimizer/logging/ComponentLoggerProviderImpl.java rename to src/main/java/me/xginko/villageroptimizer/utils/ComponentLoggerProviderImpl.java index 1ef26ed..2494265 100644 --- a/src/main/java/me/xginko/villageroptimizer/logging/ComponentLoggerProviderImpl.java +++ b/src/main/java/me/xginko/villageroptimizer/utils/ComponentLoggerProviderImpl.java @@ -1,4 +1,4 @@ -package me.xginko.villageroptimizer.logging; +package me.xginko.villageroptimizer.utils; import com.google.auto.service.AutoService; import net.kyori.adventure.text.logger.slf4j.ComponentLogger; @@ -21,5 +21,4 @@ public final class ComponentLoggerProviderImpl implements ComponentLoggerProvide ) { return helper.delegating(LoggerFactory.getLogger(name), SERIALIZER::serialize); } -} - +} \ No newline at end of file diff --git a/src/main/java/me/xginko/villageroptimizer/logging/TranslatableMapper.java b/src/main/java/me/xginko/villageroptimizer/utils/TranslatableMapper.java similarity index 97% rename from src/main/java/me/xginko/villageroptimizer/logging/TranslatableMapper.java rename to src/main/java/me/xginko/villageroptimizer/utils/TranslatableMapper.java index 2517e7b..7213e17 100644 --- a/src/main/java/me/xginko/villageroptimizer/logging/TranslatableMapper.java +++ b/src/main/java/me/xginko/villageroptimizer/utils/TranslatableMapper.java @@ -1,4 +1,4 @@ -package me.xginko.villageroptimizer.logging; +package me.xginko.villageroptimizer.utils; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TranslatableComponent; diff --git a/src/main/resources/META-INF/services/me.xginko.villageroptimizer.libs.kyori.adventure.text.logger.slf4j.ComponentLoggerProvider b/src/main/resources/META-INF/services/me.xginko.villageroptimizer.libs.kyori.adventure.text.logger.slf4j.ComponentLoggerProvider index 7673648..535b896 100644 --- a/src/main/resources/META-INF/services/me.xginko.villageroptimizer.libs.kyori.adventure.text.logger.slf4j.ComponentLoggerProvider +++ b/src/main/resources/META-INF/services/me.xginko.villageroptimizer.libs.kyori.adventure.text.logger.slf4j.ComponentLoggerProvider @@ -1 +1 @@ -me.xginko.villageroptimizer.logging.ComponentLoggerProviderImpl \ No newline at end of file +me.xginko.villageroptimizer.utils.ComponentLoggerProviderImpl \ No newline at end of file