diff --git a/pom.xml b/pom.xml
index a8ddf14..2bfa0f5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -82,5 +82,10 @@
v2.0.0-BETA-9
compile
+
+ com.github.ben-manes.caffeine
+ caffeine
+ 3.1.8
+
diff --git a/src/main/java/me/xginko/villageroptimizer/VillagerCache.java b/src/main/java/me/xginko/villageroptimizer/VillagerCache.java
new file mode 100644
index 0000000..a7246e9
--- /dev/null
+++ b/src/main/java/me/xginko/villageroptimizer/VillagerCache.java
@@ -0,0 +1,40 @@
+package me.xginko.villageroptimizer;
+
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import me.xginko.villageroptimizer.models.WrappedVillager;
+import org.bukkit.World;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Villager;
+import org.bukkit.plugin.java.JavaPlugin;
+
+import java.time.Duration;
+import java.util.Map;
+import java.util.UUID;
+
+public class VillagerCache {
+ private final Cache villagerCache = Caffeine.newBuilder().expireAfterWrite(Duration.ofSeconds(30)).build();
+
+ public VillagerCache(JavaPlugin plugin) {
+ plugin.getServer().getGlobalRegionScheduler().run(plugin, populateCache -> {
+ for (World world : plugin.getServer().getWorlds()) {
+ for (Entity entity : world.getEntities()) {
+ if (entity instanceof Villager villager) {
+ this.villagerCache.put(villager.getUniqueId(), new WrappedVillager(villager));
+ }
+ }
+ }
+ });
+ }
+
+ public WrappedVillager getVillager(Villager villager) {
+ WrappedVillager wrappedVillager = villagerCache.getIfPresent(villager.getUniqueId());
+ if (wrappedVillager == null) wrappedVillager = new WrappedVillager(villager);
+ this.villagerCache.put(villager.getUniqueId(), wrappedVillager);
+ return wrappedVillager;
+ }
+
+ public Map get() {
+ return this.villagerCache.asMap();
+ }
+}
diff --git a/src/main/java/me/xginko/villageroptimizer/enums/NamespacedKeys.java b/src/main/java/me/xginko/villageroptimizer/enums/NamespacedKeys.java
new file mode 100644
index 0000000..fa428b5
--- /dev/null
+++ b/src/main/java/me/xginko/villageroptimizer/enums/NamespacedKeys.java
@@ -0,0 +1,22 @@
+package me.xginko.villageroptimizer.enums;
+
+import me.xginko.villageroptimizer.VillagerOptimizer;
+import org.bukkit.NamespacedKey;
+
+public enum NamespacedKeys {
+
+ OPTIMIZED(VillagerOptimizer.getKey("optimized")),
+ COOLDOWN_RESTOCK(VillagerOptimizer.getKey("restock-cooldown")),
+ COOLDOWN_EXPERIENCE(VillagerOptimizer.getKey("experience-cooldown")),
+ GAME_TIME(VillagerOptimizer.getKey("game-time"));
+
+ private final NamespacedKey key;
+
+ NamespacedKeys(NamespacedKey key) {
+ this.key = key;
+ }
+
+ public NamespacedKey get() {
+ return key;
+ }
+}
diff --git a/src/main/java/me/xginko/villageroptimizer/enums/OptimizationType.java b/src/main/java/me/xginko/villageroptimizer/enums/OptimizationType.java
new file mode 100644
index 0000000..bbf7232
--- /dev/null
+++ b/src/main/java/me/xginko/villageroptimizer/enums/OptimizationType.java
@@ -0,0 +1,11 @@
+package me.xginko.villageroptimizer.enums;
+
+public enum OptimizationType {
+
+ COMMAND,
+ NAMETAG,
+ WORKSTATION,
+ BLOCK,
+ OFF
+
+}
diff --git a/src/main/java/me/xginko/villageroptimizer/models/OptimizedVillager.java b/src/main/java/me/xginko/villageroptimizer/models/OptimizedVillager.java
deleted file mode 100644
index 232fae8..0000000
--- a/src/main/java/me/xginko/villageroptimizer/models/OptimizedVillager.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package me.xginko.villageroptimizer.models;
-
-import org.bukkit.entity.Villager;
-
-public class OptimizedVillager {
-
- private final Villager villager;
-
-
- public OptimizedVillager(Villager villager) {
- this.villager = villager;
- }
-
- public Villager villager() {
- return villager;
- }
-}
diff --git a/src/main/java/me/xginko/villageroptimizer/models/WrappedVillager.java b/src/main/java/me/xginko/villageroptimizer/models/WrappedVillager.java
new file mode 100644
index 0000000..5b7cab9
--- /dev/null
+++ b/src/main/java/me/xginko/villageroptimizer/models/WrappedVillager.java
@@ -0,0 +1,67 @@
+package me.xginko.villageroptimizer.models;
+
+import me.xginko.villageroptimizer.enums.NamespacedKeys;
+import me.xginko.villageroptimizer.enums.OptimizationType;
+import org.bukkit.entity.Villager;
+import org.bukkit.persistence.PersistentDataContainer;
+import org.bukkit.persistence.PersistentDataType;
+
+public record WrappedVillager(Villager villager) {
+
+ public long getLevel() {
+ // Villager Level depending on their XP (source: https://minecraft.fandom.com/wiki/Trading#Mechanics)
+ final int experience = villager.getVillagerExperience();
+ if (experience >= 250) return 5;
+ if (experience >= 150) return 4;
+ if (experience >= 70) return 3;
+ if (experience >= 10) return 2;
+ return 1;
+ }
+
+ public boolean isOptimized() {
+ return villager.getPersistentDataContainer().has(NamespacedKeys.OPTIMIZED.get());
+ }
+
+ public void setOptimization(OptimizationType type) {
+ if (type.equals(OptimizationType.OFF) && isOptimized()) {
+ villager.getPersistentDataContainer().remove(NamespacedKeys.OPTIMIZED.get());
+ } else {
+ villager.getPersistentDataContainer().set(NamespacedKeys.OPTIMIZED.get(), PersistentDataType.STRING, type.name());
+ }
+ }
+
+ public OptimizationType getOptimizationType() {
+ return isOptimized() ? OptimizationType.valueOf(villager().getPersistentDataContainer().get(NamespacedKeys.OPTIMIZED.get(), PersistentDataType.STRING)) : OptimizationType.OFF;
+ }
+
+ public void setRestockCooldown(long milliseconds) {
+ villager.getPersistentDataContainer().set(NamespacedKeys.COOLDOWN_RESTOCK.get(), PersistentDataType.LONG, System.currentTimeMillis() + milliseconds);
+ }
+
+ public boolean shouldRestock() {
+ PersistentDataContainer villagerData = villager.getPersistentDataContainer();
+ return villagerData.has(NamespacedKeys.COOLDOWN_RESTOCK.get(), PersistentDataType.LONG) && villagerData.get(NamespacedKeys.COOLDOWN_RESTOCK.get(), PersistentDataType.LONG) <= System.currentTimeMillis();
+ }
+
+ public void restock() {
+ villager.getRecipes().forEach(recipe -> recipe.setUses(0));
+ }
+
+ public void setExpCooldown(long milliseconds) {
+ villager.getPersistentDataContainer().set(NamespacedKeys.COOLDOWN_EXPERIENCE.get(), PersistentDataType.LONG, System.currentTimeMillis() + milliseconds);
+ }
+
+ public boolean isOnExpCooldown() {
+ PersistentDataContainer villagerData = villager.getPersistentDataContainer();
+ return villagerData.has(NamespacedKeys.COOLDOWN_EXPERIENCE.get(), PersistentDataType.LONG) && villagerData.get(NamespacedKeys.COOLDOWN_EXPERIENCE.get(), PersistentDataType.LONG) <= System.currentTimeMillis();
+ }
+
+ public void saveWorldTime() {
+ villager.getPersistentDataContainer().set(NamespacedKeys.GAME_TIME.get(), PersistentDataType.LONG, villager.getWorld().getFullTime());
+ }
+
+ public long getSavedWorldTime() {
+ PersistentDataContainer villagerData = villager.getPersistentDataContainer();
+ return villagerData.has(NamespacedKeys.GAME_TIME.get(), PersistentDataType.LONG) ? villagerData.get(NamespacedKeys.GAME_TIME.get(), PersistentDataType.LONG) : villager.getWorld().getFullTime();
+ }
+}
diff --git a/src/main/java/me/xginko/villageroptimizer/utils/CalculateLevel.java b/src/main/java/me/xginko/villageroptimizer/utils/CalculateLevel.java
deleted file mode 100644
index 941b21c..0000000
--- a/src/main/java/me/xginko/villageroptimizer/utils/CalculateLevel.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package me.xginko.villageroptimizer.utils;
-
-import org.bukkit.entity.Villager;
-
-public class CalculateLevel {
- public static long villagerEXP (Villager vil) {
-
- int vilEXP = vil.getVillagerExperience();
-
- // Villager Level depending on their XP
- // source: https://minecraft.fandom.com/wiki/Trading#Mechanics
-
- if (vilEXP >= 250) return 5;
- if (vilEXP >= 150) return 4;
- if (vilEXP >= 70) return 3;
- if (vilEXP >= 10) return 2;
-
- // default level is 1
- return 1;
- }
-}
diff --git a/src/main/java/me/xginko/villageroptimizer/utils/NamespacedKeys.java b/src/main/java/me/xginko/villageroptimizer/utils/NamespacedKeys.java
deleted file mode 100644
index 8d939e4..0000000
--- a/src/main/java/me/xginko/villageroptimizer/utils/NamespacedKeys.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package me.xginko.villageroptimizer.utils;
-
-import me.xginko.villageroptimizer.VillagerOptimizer;
-import org.bukkit.NamespacedKey;
-
-public enum NamespacedKeys {
-
- COOLDOWN(VillagerOptimizer.getKey("cooldown")),
- TIME(VillagerOptimizer.getKey("time")),
- LEVEL_COOLDOWN(VillagerOptimizer.getKey("level-cooldown")),
- NAMETAG_DISABLED(VillagerOptimizer.getKey("nametag-disabled")),
- BLOCK_DISABLED(VillagerOptimizer.getKey("block-disabled")),
- WORKSTATION_DISABLED(VillagerOptimizer.getKey("workstation-disabled"));
-
- private final NamespacedKey key;
-
- NamespacedKeys(NamespacedKey key) {
- this.key = key;
- }
-
- public NamespacedKey get() {
- return key;
- }
-}
diff --git a/src/main/java/me/xginko/villageroptimizer/utils/VillagerUtils.java b/src/main/java/me/xginko/villageroptimizer/utils/VillagerUtils.java
index 26e2dd4..2a65b25 100644
--- a/src/main/java/me/xginko/villageroptimizer/utils/VillagerUtils.java
+++ b/src/main/java/me/xginko/villageroptimizer/utils/VillagerUtils.java
@@ -1,28 +1,14 @@
package me.xginko.villageroptimizer.utils;
import me.xginko.villageroptimizer.VillagerOptimizer;
+import me.xginko.villageroptimizer.models.WrappedVillager;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Villager;
-import org.bukkit.inventory.MerchantRecipe;
-import org.bukkit.persistence.PersistentDataType;
-import org.bukkit.plugin.java.JavaPlugin;
-
-import java.util.Optional;
public class VillagerUtils {
- public static boolean isDisabled(Villager villager) {
- return hasDisabledByBlock(villager) || hasDisabledByWorkstation(villager) || hasMarker(villager);
- }
-
- public static void restockTrades(Villager villager) {
- for (MerchantRecipe recipe : villager.getRecipes()) {
- recipe.setUses(0);
- }
- }
-
public static boolean shouldDisable(Villager villager) {
// Check nametag
Component nameTag = villager.customName();
@@ -38,80 +24,6 @@ public class VillagerUtils {
}
// Check Workstation
- return getDisabledByWorkstation(villager).orElse(false);
- }
-
- /*
- *
- * Helper Methods for storing and reading data inside villagers using PersistentDataContainer
- *
- * */
-
- // Disabled by Block
- public static void setDisabledByBlock(Villager villager, boolean state) {
- villager.getPersistentDataContainer().set(NamespacedKeys.BLOCK_DISABLED.get(), PersistentDataType.BOOLEAN, state);
- }
- public static boolean isDisabledByBlock(Villager villager) {
- if (villager.getPersistentDataContainer().has(NamespacedKeys.BLOCK_DISABLED.get(), PersistentDataType.BOOLEAN)) {
- return villager.getPersistentDataContainer().get(NamespacedKeys.BLOCK_DISABLED.get(), PersistentDataType.BOOLEAN);
- } else {
- setDisabledByBlock(villager, false);
- return false;
- }
- }
-
- // Disabled by Workstation
- public static void setDisabledByWorkstation(Villager villager, Boolean state) {
- villager.getPersistentDataContainer().set(NamespacedKeys.WORKSTATION_DISABLED.get(), PersistentDataType.BOOLEAN, state);
- }
- public static boolean hasDisabledByWorkstation(Villager villager) {
- return villager.getPersistentDataContainer().has(NamespacedKeys.WORKSTATION_DISABLED.get(), PersistentDataType.BOOLEAN);
- }
- public static Optional getDisabledByWorkstation(Villager villager) {
- return Optional.ofNullable(villager.getPersistentDataContainer().get(NamespacedKeys.WORKSTATION_DISABLED.get(), PersistentDataType.BOOLEAN));
- }
-
- // Cooldown
- public static void setCooldown(Villager villager, long cooldown_millis) {
- villager.getPersistentDataContainer().set(NamespacedKeys.COOLDOWN.get(), PersistentDataType.LONG, System.currentTimeMillis() + cooldown_millis);
- }
- public static boolean hasCooldown(Villager villager) {
- return villager.getPersistentDataContainer().has(NamespacedKeys.COOLDOWN.get(), PersistentDataType.LONG);
- }
- public static Optional getCooldown(Villager villager) {
- return Optional.ofNullable(villager.getPersistentDataContainer().get(NamespacedKeys.COOLDOWN.get(), PersistentDataType.LONG));
- }
-
- // Time
- public static void setTime(Villager villager) {
- villager.getPersistentDataContainer().set(NamespacedKeys.TIME.get(), PersistentDataType.LONG, villager.getWorld().getFullTime());
- }
- public static boolean hasTime(Villager villager) {
- return villager.getPersistentDataContainer().has(NamespacedKeys.TIME.get(), PersistentDataType.LONG);
- }
- public static Optional getTime(Villager villager) {
- return Optional.ofNullable(villager.getPersistentDataContainer().get(NamespacedKeys.TIME.get(), PersistentDataType.LONG));
- }
-
- // Level Cooldown
- public static void setLevelCooldown(Villager villager, long cooldown_millis) {
- villager.getPersistentDataContainer().set(NamespacedKeys.LEVEL_COOLDOWN.get(), PersistentDataType.LONG, System.currentTimeMillis() + cooldown_millis);
- }
- public static boolean hasLevelCooldown(Villager villager, JavaPlugin plugin) {
- return villager.getPersistentDataContainer().has(NamespacedKeys.LEVEL_COOLDOWN.get(), PersistentDataType.LONG);
- }
- public static Optional getLevelCooldown(Villager villager) {
- return Optional.ofNullable(villager.getPersistentDataContainer().get(NamespacedKeys.LEVEL_COOLDOWN.get(), PersistentDataType.LONG));
- }
-
- // Marker
- public static void setMarker(Villager villager) {
- villager.getPersistentDataContainer().set(NamespacedKeys.NAMETAG_DISABLED.get(), PersistentDataType.BYTE, (byte)1);
- }
- public static boolean hasMarker(Villager villager) {
- return villager.getPersistentDataContainer().has(NamespacedKeys.NAMETAG_DISABLED.get(), PersistentDataType.BYTE);
- }
- public static void removeMarker(Villager villager) {
- villager.getPersistentDataContainer().remove(NamespacedKeys.NAMETAG_DISABLED.get());
+ return new WrappedVillager(villager).isOptimized();
}
}