This commit is contained in:
xGinko 2023-09-04 23:24:57 +02:00
commit d542755a49
14 changed files with 819 additions and 0 deletions

11
.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
# Java
*.class
*.jar
hs_err_pid*
# Maven
target/
# IntelliJ
.idea
*.iml

86
pom.xml Normal file
View File

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>me.xginko</groupId>
<artifactId>VillagerOptimizer</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>VillagerOptimizer</name>
<description>Combat heavy lag caused by tons of loaded villagers by letting players optimize their trading halls.</description>
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<minimizeJar>true</minimizeJar>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<repositories>
<repository>
<id>papermc-repo</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
<repository>
<id>sonatype</id>
<url>https://oss.sonatype.org/content/groups/public/</url>
</repository>
<repository>
<id>configmaster-repo</id>
<url>https://ci.pluginwiki.us/plugin/repository/everything/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.20.1-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.thatsmusic99</groupId>
<artifactId>ConfigurationMaster-API</artifactId>
<version>v2.0.0-BETA-9</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,146 @@
package me.xginko.villageroptimizer;
import me.xginko.villageroptimizer.config.Config;
import me.xginko.villageroptimizer.config.LanguageCache;
import me.xginko.villageroptimizer.modules.VillagerOptimizerModule;
import org.bukkit.NamespacedKey;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class VillagerOptimizer extends JavaPlugin {
private static VillagerOptimizer instance;
private static Logger logger;
private static Config config;
private static HashMap<String, LanguageCache> languageCacheMap;
@Override
public void onEnable() {
instance = this;
logger = getLogger();
logger.info("Loading Translations");
reloadLang();
logger.info("Loading Config");
reloadConfiguration();
logger.info("Done.");
}
@Override
public void onDisable() {
// Plugin shutdown logic
}
public static LanguageCache getLang(String lang) {
lang = lang.replace("-", "_");
if (config.auto_lang) {
return languageCacheMap.getOrDefault(lang, languageCacheMap.get(config.default_lang.toString().toLowerCase()));
} else {
return languageCacheMap.get(config.default_lang.toString().toLowerCase());
}
}
public static LanguageCache getLang(Locale locale) {
return getLang(locale.toString().toLowerCase());
}
public static LanguageCache getLang(CommandSender commandSender) {
if (commandSender instanceof Player player) {
return getLang(player.locale());
} else {
return getLang(config.default_lang);
}
}
public void reloadPlugin() {
reloadLang();
reloadConfiguration();
}
private void reloadConfiguration() {
try {
config = new Config();
VillagerOptimizerModule.reloadModules();
config.saveConfig();
} catch (Exception e) {
logger.severe("Failed to load config file! - " + e.getLocalizedMessage());
e.printStackTrace();
}
}
private void reloadLang() {
languageCacheMap = new HashMap<>();
try {
File langDirectory = new File(getDataFolder() + "/lang");
Files.createDirectories(langDirectory.toPath());
for (String fileName : getDefaultLanguageFiles()) {
String localeString = fileName.substring(fileName.lastIndexOf('/') + 1, fileName.lastIndexOf('.'));
logger.info(String.format("Found language file for %s", localeString));
LanguageCache langCache = new LanguageCache(localeString);
languageCacheMap.put(localeString, langCache);
}
Pattern langPattern = Pattern.compile("([a-z]{1,3}_[a-z]{1,3})(\\.yml)", Pattern.CASE_INSENSITIVE);
for (File langFile : langDirectory.listFiles()) {
Matcher langMatcher = langPattern.matcher(langFile.getName());
if (langMatcher.find()) {
String localeString = langMatcher.group(1).toLowerCase();
if(!languageCacheMap.containsKey(localeString)) { // make sure it wasn't a default file that we already loaded
logger.info(String.format("Found language file for %s", localeString));
LanguageCache langCache = new LanguageCache(localeString);
languageCacheMap.put(localeString, langCache);
}
}
}
} catch (Exception e) {
e.printStackTrace();
logger.severe("Error loading language files! Language files will not reload to avoid errors, make sure to correct this before restarting the server!");
}
}
private Set<String> getDefaultLanguageFiles() {
Set<String> languageFiles = new HashSet<>();
try (JarFile jarFile = new JarFile(this.getFile())) {
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
String path = entries.nextElement().getName();
if (path.startsWith("lang/") && path.endsWith(".yml")) {
languageFiles.add(path);
}
}
} catch (IOException e) {
logger.severe("Error while getting default language file names! - " + e.getLocalizedMessage());
e.printStackTrace();
}
return languageFiles;
}
public static VillagerOptimizer getInstance() {
return instance;
}
public static NamespacedKey getKey(String key) {
return new NamespacedKey(instance, key);
}
public static Config getConfiguration() {
return config;
}
public static Logger getLog() {
return logger;
}
}

View File

@ -0,0 +1,157 @@
package me.xginko.villageroptimizer.config;
import io.github.thatsmusic99.configurationmaster.api.ConfigFile;
import io.github.thatsmusic99.configurationmaster.api.ConfigSection;
import me.xginko.villageroptimizer.VillagerOptimizer;
import me.xginko.villageroptimizer.utils.LogUtils;
import org.bukkit.Material;
import java.io.File;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public class Config {
private final ConfigFile config;
public final Locale default_lang;
public final boolean auto_lang;
public final HashSet<String> names_that_disable = new HashSet<>(2);
public final HashSet<Material> blocks_that_disable = new HashSet<>(2);
public final HashSet<Material> workstations_that_disable = new HashSet<>(13);
public Config() throws Exception {
this.config = loadConfig(new File(VillagerOptimizer.getInstance().getDataFolder(), "config.yml"));
structureConfig();
// Language Settings
this.default_lang = Locale.forLanguageTag(getString("language.default-language", "en_us", "The default language that will be used if auto-language is false or no matching language file was found.").replace("_", "-"));
this.auto_lang = getBoolean("language.auto-language", true, "If set to true, will display messages based on client language");
// AI-Disabling
this.names_that_disable.addAll(getList("ai-disabling.names-that-disable", List.of("Optimize", "DisableAI")));
getList("ai-disabling.blocks-that-disable", List.of("EMERALD_BLOCK", "COBBLESTONE")).forEach(configuredMaterial -> {
try {
Material disableBlock = Material.valueOf(configuredMaterial);
this.blocks_that_disable.add(disableBlock);
} catch (IllegalArgumentException e) {
LogUtils.materialNotRecognized("blocks-that-disable", configuredMaterial);
}
});
getList("ai-disabling.workstations-that-disable", List.of(
"COMPOSTER", "SMOKER", "BARREL", "LOOM", "BLAST_FURNACE", "BREWING_STAND", "CAULDRON",
"FLETCHING_TABLE", "CARTOGRAPHY_TABLE", "LECTERN", "SMITHING_TABLE", "STONECUTTER", "GRINDSTONE"
)).forEach(configuredMaterial -> {
try {
Material disableBlock = Material.valueOf(configuredMaterial);
this.blocks_that_disable.add(disableBlock);
} catch (IllegalArgumentException e) {
LogUtils.materialNotRecognized("workstations-that-disable", configuredMaterial);
}
});
}
private ConfigFile loadConfig(File ymlFile) throws Exception {
File parent = new File(ymlFile.getParent());
if (!parent.exists())
if (!parent.mkdir())
VillagerOptimizer.getLog().severe("Unable to create plugin config directory.");
if (!ymlFile.exists())
ymlFile.createNewFile(); // Result can be ignored because this method only returns false if the file already exists
return ConfigFile.loadConfig(ymlFile);
}
public void saveConfig() {
try {
config.save();
} catch (Exception e) {
VillagerOptimizer.getLog().severe("Failed to save config file! - " + e.getLocalizedMessage());
}
}
private void structureConfig() {
config.addDefault("config-version", 1.00);
createTitledSection("Language", "language");
createTitledSection("AI Disabling", "ai-disabling");
}
public void createTitledSection(String title, String path) {
config.addSection(title);
config.addDefault(path, null);
}
public boolean getBoolean(String path, boolean def, String comment) {
config.addDefault(path, def, comment);
return config.getBoolean(path, def);
}
public boolean getBoolean(String path, boolean def) {
config.addDefault(path, def);
return config.getBoolean(path, def);
}
public String getString(String path, String def, String comment) {
config.addDefault(path, def, comment);
return config.getString(path, def);
}
public String getString(String path, String def) {
config.addDefault(path, def);
return config.getString(path, def);
}
public double getDouble(String path, Double def, String comment) {
config.addDefault(path, def, comment);
return config.getDouble(path, def);
}
public double getDouble(String path, Double def) {
config.addDefault(path, def);
return config.getDouble(path, def);
}
public int getInt(String path, int def, String comment) {
config.addDefault(path, def, comment);
return config.getInteger(path, def);
}
public int getInt(String path, int def) {
config.addDefault(path, def);
return config.getInteger(path, def);
}
public List<String> getList(String path, List<String> def, String comment) {
config.addDefault(path, def, comment);
return config.getStringList(path);
}
public List<String> getList(String path, List<String> def) {
config.addDefault(path, def);
return config.getStringList(path);
}
public ConfigSection getConfigSection(String path, Map<String, Object> defaultKeyValue) {
config.makeSectionLenient(path);
config.addDefault(path, defaultKeyValue);
return config.getConfigSection(path);
}
public ConfigSection getConfigSection(String path, Map<String, Object> defaultKeyValue, String comment) {
config.makeSectionLenient(path);
config.addDefault(path, defaultKeyValue, comment);
return config.getConfigSection(path);
}
public void addComment(String path, String comment) {
config.addComment(path, comment);
}
public void addComments(String path, String... comments) {
config.addComments(path, comments);
}
}

View File

@ -0,0 +1,65 @@
package me.xginko.villageroptimizer.config;
import io.github.thatsmusic99.configurationmaster.api.ConfigFile;
import me.xginko.villageroptimizer.VillagerOptimizer;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import java.io.File;
import java.util.List;
public class LanguageCache {
private final ConfigFile lang;
private final MiniMessage miniMessage;
public final Component no_permission;
public LanguageCache(String lang) throws Exception {
this.lang = loadLang(new File(VillagerOptimizer.getInstance().getDataFolder() + File.separator + "lang", lang + ".yml"));
this.miniMessage = MiniMessage.miniMessage();
// No Permission
this.no_permission = getTranslation("no-permission", "<red>You don't have permission to use this command.", false);
saveLang();
}
private ConfigFile loadLang(File ymlFile) throws Exception {
File parent = new File(ymlFile.getParent());
if (!parent.exists())
if (!parent.mkdir())
VillagerOptimizer.getLog().severe("Unable to create lang directory.");
if (!ymlFile.exists())
ymlFile.createNewFile(); // Result can be ignored because this method only returns false if the file already exists
return ConfigFile.loadConfig(ymlFile);
}
private void saveLang() {
try {
lang.save();
} catch (Exception e) {
VillagerOptimizer.getLog().severe("Failed to save language file: "+ lang.getFile().getName() +" - " + e.getLocalizedMessage());
}
}
public Component getTranslation(String path, String defaultTranslation, boolean upperCase) {
lang.addDefault(path, defaultTranslation);
return miniMessage.deserialize(upperCase ? lang.getString(path, defaultTranslation).toUpperCase() : lang.getString(path, defaultTranslation));
}
public Component getTranslation(String path, String defaultTranslation, boolean upperCase, String comment) {
lang.addDefault(path, defaultTranslation, comment);
return miniMessage.deserialize(upperCase ? lang.getString(path, defaultTranslation).toUpperCase() : lang.getString(path, defaultTranslation));
}
public List<Component> getListTranslation(String path, List<String> defaultTranslation, boolean upperCase) {
lang.addDefault(path, defaultTranslation);
return lang.getStringList(path).stream().map(line -> miniMessage.deserialize(upperCase ? line.toUpperCase() : line)).toList();
}
public List<Component> getListTranslation(String path, List<String> defaultTranslation, boolean upperCase, String comment) {
lang.addDefault(path, defaultTranslation, comment);
return lang.getStringList(path).stream().map(line -> miniMessage.deserialize(upperCase ? line.toUpperCase() : line)).toList();
}
}

View File

@ -0,0 +1,17 @@
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;
}
}

View File

@ -0,0 +1,119 @@
package me.xginko.villageroptimizer.modules;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import me.xginko.villageroptimizer.VillagerOptimizer;
import me.xginko.villageroptimizer.config.Config;
import me.xginko.villageroptimizer.utils.LogUtils;
import org.bukkit.Chunk;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
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;
import org.bukkit.event.entity.CreatureSpawnEvent;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.logging.Level;
public class ChunkLimit implements VillagerOptimizerModule, Listener {
private final VillagerOptimizer plugin;
private ScheduledTask scheduledTask;
private final List<Villager.Profession> removalPriority = new ArrayList<>();
private final int maxVillagersPerChunk;
private final boolean logIsEnabled;
private final long checkPeriod;
public ChunkLimit() {
shouldEnable();
this.plugin = VillagerOptimizer.getInstance();
Config config = VillagerOptimizer.getConfiguration();
this.maxVillagersPerChunk = config.getInt("villager-chunk-limit.max-villagers-per-chunk", 25);
this.logIsEnabled = config.getBoolean("villager-chunk-limit.log-removals", false);
this.checkPeriod = config.getInt("villager-chunk-limit.check-period-in-ticks", 600, "check all chunks every x ticks.");
config.getList("villager-chunk-limit.removal-priority", List.of(
"NONE", "NITWIT", "SHEPHERD", "FISHERMAN", "BUTCHER", "CARTOGRAPHER", "LEATHERWORKER",
"FLETCHER", "MASON", "FARMER", "ARMORER", "TOOLSMITH", "WEAPONSMITH", "CLERIC", "LIBRARIAN"
),
"Professions that are in the top of the list are going to be scheduled for removal first."
).forEach(configuredProfession -> {
try {
Villager.Profession profession = Villager.Profession.valueOf(configuredProfession);
this.removalPriority.add(profession);
} catch (IllegalArgumentException e) {
LogUtils.moduleLog(Level.WARNING, "villager-chunk-limit", "Villager profession '"+configuredProfession+"' not recognized. Make sure you're using the correct profession enums.");
}
});
}
@Override
public void enable() {
plugin.getServer().getPluginManager().registerEvents(this, plugin);
this.scheduledTask = plugin.getServer().getGlobalRegionScheduler().runAtFixedRate(plugin, task -> run(), checkPeriod, checkPeriod);
}
@Override
public boolean shouldEnable() {
return VillagerOptimizer.getConfiguration().getBoolean("villager-chunk-limit.enable", false);
}
@Override
public void disable() {
HandlerList.unregisterAll(this);
if (scheduledTask != null) scheduledTask.cancel();
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
private void onCreateSpawn(CreatureSpawnEvent event) {
if (event.getEntityType().equals(EntityType.VILLAGER)) {
checkVillagersInChunk(event.getEntity().getChunk());
}
}
private void run() {
for (World world : plugin.getServer().getWorlds()) {
for (Chunk chunk : world.getLoadedChunks()) {
plugin.getServer().getRegionScheduler().run(plugin, world, chunk.getX(), chunk.getZ(), task -> checkVillagersInChunk(chunk));
}
}
}
private void checkVillagersInChunk(Chunk chunk) {
// Create a list with all villagers in that chunk
List<Villager> villagers_in_chunk = new ArrayList<>();
for (Entity entity : chunk.getEntities()) {
if (entity.getType().equals(EntityType.VILLAGER)) {
villagers_in_chunk.add((Villager) entity);
}
}
// Check if there are more villagers in that chunk than allowed
int amount_over_the_limit = villagers_in_chunk.size() - maxVillagersPerChunk;
if (amount_over_the_limit <= 0) return;
// Sort villager list by profession priority
villagers_in_chunk.sort(Comparator.comparingInt(this::getProfessionPriority));
// Remove prioritized villagers that are too many
for (int i = 0; i < amount_over_the_limit; i++) {
Villager villager = villagers_in_chunk.get(i);
if (logIsEnabled) LogUtils.moduleLog(
Level.INFO,
"villager-chunk-limit",
"Removing villager of profession type '"+villager.getProfession()+"' at "+villager.getLocation()
);
villager.remove();
}
}
private int getProfessionPriority(Villager villager) {
Villager.Profession profession = villager.getProfession();
return removalPriority.contains(profession) ? removalPriority.indexOf(profession) : Integer.MAX_VALUE;
}
}

View File

@ -0,0 +1,24 @@
package me.xginko.villageroptimizer.modules;
import java.util.HashSet;
public interface VillagerOptimizerModule {
void enable();
void disable();
boolean shouldEnable();
HashSet<VillagerOptimizerModule> modules = new HashSet<>();
static void reloadModules() {
modules.forEach(VillagerOptimizerModule::disable);
modules.clear();
// Modules here
for (VillagerOptimizerModule module : modules) {
if (module.shouldEnable()) module.enable();
}
}
}

View File

@ -0,0 +1,21 @@
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;
}
}

View File

@ -0,0 +1,28 @@
package me.xginko.villageroptimizer.utils;
import me.xginko.villageroptimizer.VillagerOptimizer;
import java.util.logging.Level;
public class LogUtils {
public static void moduleLog(Level logLevel, String path, String logMessage) {
VillagerOptimizer.getLog().log(logLevel, "<" + path + "> " + logMessage);
}
public static void materialNotRecognized(String path, String material) {
moduleLog(Level.WARNING, path, "Material '" + material + "' not recognized. Please use correct Spigot Material enums for your Minecraft version!");
}
public static void entityTypeNotRecognized(String path, String entityType) {
moduleLog(Level.WARNING, path, "EntityType '" + entityType + "' not recognized. Please use correct Spigot EntityType enums for your Minecraft version!");
}
public static void enchantmentNotRecognized(String path, String enchantment) {
moduleLog(Level.WARNING, path, "Enchantment '" + enchantment + "' not recognized. Please use correct Spigot Enchantment enums for your Minecraft version!");
}
public static void integerNotRecognized(String path, String element) {
moduleLog(Level.WARNING, path, "The configured amount for "+element+" is not an integer.");
}
}

View File

@ -0,0 +1,24 @@
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;
}
}

View File

@ -0,0 +1,117 @@
package me.xginko.villageroptimizer.utils;
import me.xginko.villageroptimizer.VillagerOptimizer;
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();
if (nameTag != null) {
if (VillagerOptimizer.getConfiguration().names_that_disable.contains(PlainTextComponentSerializer.plainText().serialize(nameTag))) {
return true;
}
}
// Check block below
if (VillagerOptimizer.getConfiguration().blocks_that_disable.contains(villager.getLocation().getBlock().getRelative(BlockFace.DOWN).getType())) {
return true;
}
// 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<Boolean> 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<Long> 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<Long> 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<Long> 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());
}
}

View File

View File

@ -0,0 +1,4 @@
name: VillagerOptimizer
version: '${project.version}'
main: me.xginko.villageroptimizer.VillagerOptimizer
api-version: '1.20'