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");
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);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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) {
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) {

View File

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

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.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) {

View File

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

View File

@ -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%")

View File

@ -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:

View File

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

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.TranslatableComponent;

View File

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