upload progress

This commit is contained in:
xGinko 2024-02-28 14:50:37 +01:00
parent c80f2c2eff
commit 93c7cb4a31
20 changed files with 173 additions and 200 deletions

View File

@ -154,7 +154,7 @@ public final class VillagerOptimizer extends JavaPlugin {
File langDirectory = new File(getDataFolder() + File.separator + "lang"); File langDirectory = new File(getDataFolder() + File.separator + "lang");
Files.createDirectories(langDirectory.toPath()); Files.createDirectories(langDirectory.toPath());
for (String fileName : getDefaultLanguageFiles()) { 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( if (startup) logger.info(
Component.text("").style(STYLE) Component.text("").style(STYLE)
.append(Component.text(" "+localeString).color(NamedTextColor.WHITE).decorate(TextDecoration.BOLD)) .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())) { try (final JarFile pluginJarFile = new JarFile(this.getFile())) {
return pluginJarFile.stream() return pluginJarFile.stream()
.map(ZipEntry::getName) .map(ZipEntry::getName)
.filter(name -> name.startsWith("lang" + File.separator) && name.endsWith(".yml")) .filter(name -> name.startsWith("lang/") && name.endsWith(".yml"))
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} catch (IOException ioException) { } catch (IOException ioException) {
logger.error("Failed getting default lang files!", ioException); logger.error("Failed getting default lang files!", ioException);

View File

@ -2,9 +2,8 @@ package me.xginko.villageroptimizer;
import me.xginko.villageroptimizer.enums.Keyring; import me.xginko.villageroptimizer.enums.Keyring;
import me.xginko.villageroptimizer.enums.OptimizationType; 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.Location;
import org.bukkit.Sound;
import org.bukkit.entity.Villager; import org.bukkit.entity.Villager;
import org.bukkit.entity.memory.MemoryKey; import org.bukkit.entity.memory.MemoryKey;
import org.bukkit.inventory.MerchantRecipe; import org.bukkit.inventory.MerchantRecipe;
@ -262,6 +261,14 @@ public final class WrappedVillager {
return 1; 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. * @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 * @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; return cooldown_millis;
} }
public void memorizeName(final Component customName) { public void sayNo() {
dataContainer.set(Keyring.VillagerOptimizer.LAST_OPTIMIZE_NAME.getKey(), PersistentDataType.STRING, MiniMessage.miniMessage().serialize(customName)); try {
} villager.shakeHead();
} catch (NoSuchMethodError e) {
public @Nullable Component getMemorizedName() { villager.getWorld().playSound(villager.getLocation(), Sound.ENTITY_VILLAGER_NO, 1.0F, 1.0F);
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());
} }
private static class CachedJobSite { private static class CachedJobSite {
private final @NotNull Villager villager;
private @Nullable Location jobSite; private @Nullable Location jobSite;
private long lastRefresh; private long lastRefresh;
private CachedJobSite(Villager villager) { private CachedJobSite(@NotNull Villager villager) {
this.jobSite = villager.getMemory(MemoryKey.JOB_SITE); this.villager = villager;
this.lastRefresh = System.currentTimeMillis(); this.jobSite = getJobSite();
} }
private @Nullable Location getJobSite(Villager villager) { private @Nullable Location getJobSite() {
final long now = System.currentTimeMillis(); final long now = System.currentTimeMillis();
if (now - lastRefresh > 1000L) { if (now - lastRefresh > 1000L) {
this.jobSite = villager.getMemory(MemoryKey.JOB_SITE); this.jobSite = villager.getMemory(MemoryKey.JOB_SITE);
@ -338,11 +340,6 @@ public final class WrappedVillager {
public @Nullable Location getJobSite() { public @Nullable Location getJobSite() {
if (cachedJobSite == null) if (cachedJobSite == null)
cachedJobSite = new CachedJobSite(villager); cachedJobSite = new CachedJobSite(villager);
return cachedJobSite.getJobSite(villager); return cachedJobSite.getJobSite();
}
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;
} }
} }

View File

@ -32,7 +32,7 @@ public class LanguageCache {
VillagerOptimizer.getLog().error("Failed to create lang directory."); 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 // Check if the file already exists and save the one from the plugin's resources folder if it does not
if (!langYML.exists()) if (!langYML.exists())
plugin.saveResource("lang" + File.separator + locale + ".yml", false); plugin.saveResource("lang/" + locale + ".yml", false);
// Finally, load the lang file with configmaster // Finally, load the lang file with configmaster
this.lang = ConfigFile.loadConfig(langYML); this.lang = ConfigFile.loadConfig(langYML);

View File

@ -27,7 +27,7 @@ public interface VillagerOptimizerModule {
modules.add(new FixOptimisationAfterCure()); modules.add(new FixOptimisationAfterCure());
modules.add(new RestockOptimizedTrades()); modules.add(new RestockOptimizedTrades());
modules.add(new LevelOptimizedProfession()); modules.add(new LevelOptimizedProfession());
modules.add(new RenameOptimizedVillagers()); modules.add(new VisuallyHighlightOptimized());
modules.add(new MakeVillagersSpawnAdult()); modules.add(new MakeVillagersSpawnAdult());
modules.add(new PreventUnoptimizedTrading()); modules.add(new PreventUnoptimizedTrading());
modules.add(new PreventOptimizedTargeting()); modules.add(new PreventOptimizedTargeting());

View File

@ -62,11 +62,12 @@ public class EnableLeashingVillagers implements VillagerOptimizerModule, Listene
final ItemStack handItem = player.getInventory().getItem(event.getHand()); final ItemStack handItem = player.getInventory().getItem(event.getHand());
if (handItem == null || !handItem.getType().equals(Material.LEAD)) return; if (handItem == null || !handItem.getType().equals(Material.LEAD)) return;
Villager villager = (Villager) event.getRightClicked(); final Villager villager = (Villager) event.getRightClicked();
if (villager.isLeashed()) return; 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; 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 // Call event for compatibility with other plugins, constructing non deprecated if available
PlayerLeashEntityEvent leashEvent; PlayerLeashEntityEvent leashEvent;
try { try {
@ -86,7 +87,7 @@ public class EnableLeashingVillagers implements VillagerOptimizerModule, Listene
if (log_enabled) { if (log_enabled) {
VillagerOptimizer.getLog().info(Component.text(player.getName() + " leashed a villager at " + 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));
} }
}); });
} }

View File

@ -68,22 +68,21 @@ public class LevelOptimizedProfession implements VillagerOptimizerModule, Listen
event.getInventory().getType().equals(InventoryType.MERCHANT) event.getInventory().getType().equals(InventoryType.MERCHANT)
&& event.getInventory().getHolder() instanceof Villager && event.getInventory().getHolder() instanceof Villager
) { ) {
Villager villager = (Villager) event.getInventory().getHolder(); final Villager villager = (Villager) event.getInventory().getHolder();
WrappedVillager wVillager = villagerCache.getOrAdd(villager); final WrappedVillager wVillager = villagerCache.getOrAdd(villager);
if (!wVillager.isOptimized()) return; if (!wVillager.isOptimized()) return;
if (wVillager.canLevelUp(cooldown_millis)) { if (wVillager.canLevelUp(cooldown_millis)) {
if (wVillager.calculateLevel() > villager.getVillagerLevel()) { if (wVillager.calculateLevel() <= villager.getVillagerLevel()) return;
scheduler.runAtEntity(villager, enableAI -> {
villager.addPotionEffect(new PotionEffect(PotionEffectType.SLOW, 120, 120, false, false));
villager.setAware(true);
scheduler.runAtEntityLater(villager, disableAI -> { scheduler.runAtEntity(villager, enableAI -> {
villager.setAware(false); villager.addPotionEffect(new PotionEffect(PotionEffectType.SLOW, 120, 120, false, false));
wVillager.saveLastLevelUp(); villager.setAware(true);
}, 5, TimeUnit.SECONDS); scheduler.runAtEntityLater(villager, disableAI -> {
}); villager.setAware(false);
} wVillager.saveLastLevelUp();
}, 5, TimeUnit.SECONDS);
});
} else { } else {
if (notify_player) { if (notify_player) {
Player player = (Player) event.getPlayer(); Player player = (Player) event.getPlayer();

View File

@ -37,7 +37,7 @@ public class MakeVillagersSpawnAdult implements VillagerOptimizerModule, Listene
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onVillagerSpawn(CreatureSpawnEvent event) { private void onVillagerSpawn(CreatureSpawnEvent event) {
if (event.getEntityType().equals(EntityType.VILLAGER)) { if (event.getEntityType().equals(EntityType.VILLAGER)) {
Villager villager = (Villager) event.getEntity(); final Villager villager = (Villager) event.getEntity();
if (!villager.isAdult()) villager.setAdult(); if (!villager.isAdult()) villager.setAdult();
} }
} }

View File

@ -42,7 +42,7 @@ public class PreventOptimizedTargeting implements VillagerOptimizerModule, Liste
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onTarget(EntityTargetEvent event) { private void onTarget(EntityTargetEvent event) {
Entity target = event.getTarget(); final Entity target = event.getTarget();
if ( if (
target != null target != null
&& target.getType().equals(EntityType.VILLAGER) && target.getType().equals(EntityType.VILLAGER)
@ -55,7 +55,7 @@ public class PreventOptimizedTargeting implements VillagerOptimizerModule, Liste
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onEntityTargetVillager(EntityPathfindEvent event) { private void onEntityTargetVillager(EntityPathfindEvent event) {
Entity target = event.getTargetEntity(); final Entity target = event.getTargetEntity();
if ( if (
target != null target != null
&& target.getType().equals(EntityType.VILLAGER) && target.getType().equals(EntityType.VILLAGER)

View File

@ -54,16 +54,14 @@ public class PreventUnoptimizedTrading implements VillagerOptimizerModule, Liste
if (!event.getInventory().getType().equals(InventoryType.MERCHANT)) return; if (!event.getInventory().getType().equals(InventoryType.MERCHANT)) return;
if (event.getWhoClicked().hasPermission(Bypass.TRADE_PREVENTION.get())) return; if (event.getWhoClicked().hasPermission(Bypass.TRADE_PREVENTION.get())) return;
if (!(event.getInventory().getHolder() instanceof Villager)) 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()) { if (notify_player) {
event.setCancelled(true); Player player = (Player) event.getWhoClicked();
if (notify_player) { VillagerOptimizer.getLang(player.locale()).optimize_for_trading
Player player = (Player) event.getWhoClicked(); .forEach(line -> KyoriUtil.sendMessage(player, line));
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.getInventory().getType().equals(InventoryType.MERCHANT)) return;
if (event.getWhoClicked().hasPermission(Bypass.TRADE_PREVENTION.get())) return; if (event.getWhoClicked().hasPermission(Bypass.TRADE_PREVENTION.get())) return;
if (!(event.getInventory().getHolder() instanceof Villager)) 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()) { if (notify_player) {
event.setCancelled(true); Player player = (Player) event.getWhoClicked();
if (notify_player) { VillagerOptimizer.getLang(player.locale()).optimize_for_trading
Player player = (Player) event.getWhoClicked(); .forEach(line -> KyoriUtil.sendMessage(player, line));
VillagerOptimizer.getLang(player.locale()).optimize_for_trading
.forEach(line -> KyoriUtil.sendMessage(player, line));
}
} }
} }
} }

View File

@ -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", "<green>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);
}
}

View File

@ -61,10 +61,10 @@ public class RestockOptimizedTrades implements VillagerOptimizerModule, Listener
private void onInteract(PlayerInteractEntityEvent event) { private void onInteract(PlayerInteractEntityEvent event) {
if (!event.getRightClicked().getType().equals(EntityType.VILLAGER)) return; 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; if (!wVillager.isOptimized()) return;
Player player = event.getPlayer();
final Player player = event.getPlayer();
final boolean player_bypassing = player.hasPermission(Bypass.RESTOCK_COOLDOWN.get()); final boolean player_bypassing = player.hasPermission(Bypass.RESTOCK_COOLDOWN.get());
if (wVillager.canRestock(restock_delay_millis) || player_bypassing) { if (wVillager.canRestock(restock_delay_millis) || player_bypassing) {

View File

@ -39,9 +39,7 @@ public class UnoptimizeOnJobLoose implements VillagerOptimizerModule, Listener {
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onJobReset(VillagerCareerChangeEvent event) { private void onJobReset(VillagerCareerChangeEvent event) {
if (!event.getReason().equals(VillagerCareerChangeEvent.ChangeReason.LOSING_JOB)) return; if (!event.getReason().equals(VillagerCareerChangeEvent.ChangeReason.LOSING_JOB)) return;
final WrappedVillager wrappedVillager = villagerCache.getOrAdd(event.getEntity());
WrappedVillager wrappedVillager = villagerCache.getOrAdd(event.getEntity());
if (wrappedVillager.isOptimized()) { if (wrappedVillager.isOptimized()) {
wrappedVillager.setOptimizationType(OptimizationType.NONE); wrappedVillager.setOptimizationType(OptimizationType.NONE);
} }

View File

@ -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);
});
}
}

View File

@ -17,8 +17,6 @@ import net.kyori.adventure.text.TextReplacementConfig;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.entity.Villager; import org.bukkit.entity.Villager;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@ -58,7 +56,7 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener {
try { try {
return Material.valueOf(configuredMaterial); return Material.valueOf(configuredMaterial);
} catch (IllegalArgumentException e) { } 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: " + "' not recognized. Please use correct Material enums from: " +
"https://jd.papermc.io/paper/1.20/org/bukkit/Material.html"); "https://jd.papermc.io/paper/1.20/org/bukkit/Material.html");
return null; return null;
@ -96,9 +94,9 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener {
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onBlockPlace(BlockPlaceEvent event) { private void onBlockPlace(BlockPlaceEvent event) {
Block placed = event.getBlock(); final Block placed = event.getBlock();
if (!blocks_that_disable.contains(placed.getType())) return; 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 (!player.hasPermission(Optimize.BLOCK.get())) return;
if (only_while_sneaking && !player.isSneaking()) return; if (only_while_sneaking && !player.isSneaking()) return;
@ -106,16 +104,14 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener {
WrappedVillager closestOptimizableVillager = null; WrappedVillager closestOptimizableVillager = null;
double closestDistance = Double.MAX_VALUE; double closestDistance = Double.MAX_VALUE;
for (Entity entity : blockLoc.getNearbyEntities(search_radius, search_radius, search_radius)) { for (Villager villager : blockLoc.getNearbyEntitiesByType(Villager.class, search_radius)) {
if (!entity.getType().equals(EntityType.VILLAGER)) continue;
Villager villager = (Villager) entity;
final Villager.Profession profession = villager.getProfession(); final Villager.Profession profession = villager.getProfession();
if (profession.equals(Villager.Profession.NONE) || profession.equals(Villager.Profession.NITWIT)) continue; 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 WrappedVillager wVillager = villagerCache.getOrAdd(villager);
final double distance = entity.getLocation().distanceSquared(blockLoc); if (wVillager.canOptimize(cooldown_millis)) {
if (distance < closestDistance && wVillager.canOptimize(cooldown_millis)) {
closestOptimizableVillager = wVillager; closestOptimizableVillager = wVillager;
closestDistance = distance; closestDistance = distance;
} }
@ -132,7 +128,6 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener {
); );
if (!optimizeEvent.callEvent()) return; if (!optimizeEvent.callEvent()) return;
closestOptimizableVillager.setOptimizationType(optimizeEvent.getOptimizationType()); closestOptimizableVillager.setOptimizationType(optimizeEvent.getOptimizationType());
closestOptimizableVillager.saveOptimizeTime(); closestOptimizableVillager.saveOptimizeTime();
@ -154,7 +149,7 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener {
CommonUtil.formatLocation(closestOptimizableVillager.villager().getLocation())).color(VillagerOptimizer.COLOR)); CommonUtil.formatLocation(closestOptimizableVillager.villager().getLocation())).color(VillagerOptimizer.COLOR));
} }
} else { } else {
CommonUtil.shakeHead(closestOptimizableVillager.villager()); closestOptimizableVillager.sayNo();
if (notify_player) { if (notify_player) {
final TextReplacementConfig timeLeft = TextReplacementConfig.builder() final TextReplacementConfig timeLeft = TextReplacementConfig.builder()
.matchLiteral("%time%") .matchLiteral("%time%")
@ -168,9 +163,9 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener {
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onBlockBreak(BlockBreakEvent event) { private void onBlockBreak(BlockBreakEvent event) {
Block broken = event.getBlock(); final Block broken = event.getBlock();
if (!blocks_that_disable.contains(broken.getType())) return; 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 (!player.hasPermission(Optimize.BLOCK.get())) return;
if (only_while_sneaking && !player.isSneaking()) return; if (only_while_sneaking && !player.isSneaking()) return;
@ -178,14 +173,12 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener {
WrappedVillager closestOptimizedVillager = null; WrappedVillager closestOptimizedVillager = null;
double closestDistance = Double.MAX_VALUE; double closestDistance = Double.MAX_VALUE;
for (Entity entity : blockLoc.getNearbyEntities(search_radius, search_radius, search_radius)) { for (Villager villager : blockLoc.getNearbyEntitiesByType(Villager.class, search_radius)) {
if (!entity.getType().equals(EntityType.VILLAGER)) continue; final double distance = villager.getLocation().distanceSquared(blockLoc);
Villager villager = (Villager) entity; if (distance >= closestDistance) continue;
WrappedVillager wVillager = villagerCache.getOrAdd(villager); final WrappedVillager wVillager = villagerCache.getOrAdd(villager);
final double distance = entity.getLocation().distanceSquared(blockLoc); if (wVillager.isOptimized()) {
if (distance < closestDistance && wVillager.isOptimized()) {
closestOptimizedVillager = wVillager; closestOptimizedVillager = wVillager;
closestDistance = distance; closestDistance = distance;
} }
@ -201,7 +194,6 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener {
); );
if (!unOptimizeEvent.callEvent()) return; if (!unOptimizeEvent.callEvent()) return;
closestOptimizedVillager.setOptimizationType(OptimizationType.NONE); closestOptimizedVillager.setOptimizationType(OptimizationType.NONE);
if (notify_player) { if (notify_player) {

View File

@ -14,7 +14,6 @@ import me.xginko.villageroptimizer.utils.CommonUtil;
import me.xginko.villageroptimizer.utils.KyoriUtil; import me.xginko.villageroptimizer.utils.KyoriUtil;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextReplacementConfig; import net.kyori.adventure.text.TextReplacementConfig;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -36,7 +35,6 @@ import java.util.stream.Collectors;
public class OptimizeByNametag implements VillagerOptimizerModule, Listener { public class OptimizeByNametag implements VillagerOptimizerModule, Listener {
private final PlainTextComponentSerializer plainTextSerializer;
private final VillagerCache villagerCache; private final VillagerCache villagerCache;
private final Set<String> nametags; private final Set<String> nametags;
private final long cooldown; private final long cooldown;
@ -44,7 +42,6 @@ public class OptimizeByNametag implements VillagerOptimizerModule, Listener {
public OptimizeByNametag() { public OptimizeByNametag() {
shouldEnable(); shouldEnable();
this.plainTextSerializer = PlainTextComponentSerializer.plainText();
this.villagerCache = VillagerOptimizer.getCache(); this.villagerCache = VillagerOptimizer.getCache();
Config config = VillagerOptimizer.getConfiguration(); Config config = VillagerOptimizer.getConfiguration();
config.master().addComment("optimization-methods.nametag-optimization.enable", config.master().addComment("optimization-methods.nametag-optimization.enable",
@ -83,23 +80,23 @@ public class OptimizeByNametag implements VillagerOptimizerModule, Listener {
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onPlayerInteractEntity(PlayerInteractEntityEvent event) { private void onPlayerInteractEntity(PlayerInteractEntityEvent event) {
if (!event.getRightClicked().getType().equals(EntityType.VILLAGER)) return; if (!event.getRightClicked().getType().equals(EntityType.VILLAGER)) return;
Player player = event.getPlayer(); final Player player = event.getPlayer();
if (!player.hasPermission(Optimize.NAMETAG.get())) return; 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 != null && !usedItem.getType().equals(Material.NAME_TAG)) return;
if (!usedItem.hasItemMeta()) return; if (!usedItem.hasItemMeta()) return;
ItemMeta meta = usedItem.getItemMeta(); final ItemMeta meta = usedItem.getItemMeta();
if (!meta.hasDisplayName()) return; if (!meta.hasDisplayName()) return;
// Get component name first, so we can manually name the villager when canceling the event to avoid item consumption. // 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() assert newVillagerName != null; // Legitimate since we checked for hasDisplayName()
final String name = plainTextSerializer.serialize(newVillagerName); final String nameTagPlainText = CommonUtil.plainTextSerializer.serialize(newVillagerName);
Villager villager = (Villager) event.getRightClicked(); final Villager villager = (Villager) event.getRightClicked();
WrappedVillager wVillager = villagerCache.getOrAdd(villager); 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())) { if (wVillager.canOptimize(cooldown) || player.hasPermission(Bypass.NAMETAG_COOLDOWN.get())) {
VillagerOptimizeEvent optimizeEvent = new VillagerOptimizeEvent( VillagerOptimizeEvent optimizeEvent = new VillagerOptimizeEvent(
wVillager, wVillager,
@ -125,12 +122,12 @@ public class OptimizeByNametag implements VillagerOptimizerModule, Listener {
if (log_enabled) { if (log_enabled) {
VillagerOptimizer.getLog().info(Component.text(player.getName() + 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)); CommonUtil.formatLocation(wVillager.villager().getLocation())).color(VillagerOptimizer.COLOR));
} }
} else { } else {
event.setCancelled(true); event.setCancelled(true);
CommonUtil.shakeHead(villager); wVillager.sayNo();
if (notify_player) { if (notify_player) {
final TextReplacementConfig timeLeft = TextReplacementConfig.builder() final TextReplacementConfig timeLeft = TextReplacementConfig.builder()
.matchLiteral("%time%") .matchLiteral("%time%")
@ -142,9 +139,14 @@ public class OptimizeByNametag implements VillagerOptimizerModule, Listener {
} }
} else { } else {
if (wVillager.isOptimized()) { if (wVillager.isOptimized()) {
VillagerUnoptimizeEvent unOptimizeEvent = new VillagerUnoptimizeEvent(wVillager, player, OptimizationType.NAMETAG, event.isAsynchronous()); VillagerUnoptimizeEvent unOptimizeEvent = new VillagerUnoptimizeEvent(
if (!unOptimizeEvent.callEvent()) return; wVillager,
player,
OptimizationType.NAMETAG,
event.isAsynchronous()
);
if (!unOptimizeEvent.callEvent()) return;
wVillager.setOptimizationType(OptimizationType.NONE); wVillager.setOptimizationType(OptimizationType.NONE);
if (notify_player) { if (notify_player) {
@ -154,7 +156,7 @@ public class OptimizeByNametag implements VillagerOptimizerModule, Listener {
if (log_enabled) { if (log_enabled) {
VillagerOptimizer.getLog().info(Component.text(player.getName() + 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)); CommonUtil.formatLocation(wVillager.villager().getLocation())).color(VillagerOptimizer.COLOR));
} }
} }

View File

@ -30,6 +30,7 @@ import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.VillagerCareerChangeEvent;
import org.bukkit.util.NumberConversions; import org.bukkit.util.NumberConversions;
import java.time.Duration; import java.time.Duration;
@ -93,6 +94,20 @@ public class OptimizeByWorkstation implements VillagerOptimizerModule, Listener
return VillagerOptimizer.getConfiguration().getBoolean("optimization-methods.workstation-optimization.enable", false); 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) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onBlockPlace(BlockPlaceEvent event) { private void onBlockPlace(BlockPlaceEvent event) {
final Block placed = event.getBlock(); final Block placed = event.getBlock();
@ -127,7 +142,7 @@ public class OptimizeByWorkstation implements VillagerOptimizerModule, Listener
pending_optimizations.put(placed.getLocation(), scheduler.runAtLocationLater(workstationLoc, () -> { pending_optimizations.put(placed.getLocation(), scheduler.runAtLocationLater(workstationLoc, () -> {
if (!finalToOptimize.canOptimize(cooldown_millis) && !player.hasPermission(Bypass.WORKSTATION_COOLDOWN.get())) { if (!finalToOptimize.canOptimize(cooldown_millis) && !player.hasPermission(Bypass.WORKSTATION_COOLDOWN.get())) {
CommonUtil.shakeHead(finalToOptimize.villager()); finalToOptimize.sayNo();
if (notify_player) { if (notify_player) {
final TextReplacementConfig timeLeft = TextReplacementConfig.builder() final TextReplacementConfig timeLeft = TextReplacementConfig.builder()
.matchLiteral("%time%") .matchLiteral("%time%")

View File

@ -1,5 +1,6 @@
package me.xginko.villageroptimizer.utils; package me.xginko.villageroptimizer.utils;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.bukkit.Chunk; import org.bukkit.Chunk;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
@ -9,6 +10,8 @@ import org.jetbrains.annotations.NotNull;
import java.time.Duration; import java.time.Duration;
public class CommonUtil { public class CommonUtil {
public static final PlainTextComponentSerializer plainTextSerializer = PlainTextComponentSerializer.plainText();
public static @NotNull String formatDuration(Duration duration) { public static @NotNull String formatDuration(Duration duration) {
if (duration.isNegative()) duration = duration.negated(); 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) { public static Villager.Profession getWorkstationProfession(@NotNull Material workstation) {
switch (workstation) { switch (workstation) {
case BARREL: case BARREL:

View File

@ -1,4 +1,4 @@
package me.xginko.villageroptimizer.logging; package me.xginko.villageroptimizer.utils;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import net.kyori.adventure.text.logger.slf4j.ComponentLogger; 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); return helper.delegating(LoggerFactory.getLogger(name), SERIALIZER::serialize);
} }
} }

View File

@ -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.Component;
import net.kyori.adventure.text.TranslatableComponent; import net.kyori.adventure.text.TranslatableComponent;

View File

@ -1 +1 @@
me.xginko.villageroptimizer.logging.ComponentLoggerProviderImpl me.xginko.villageroptimizer.utils.ComponentLoggerProviderImpl