diff --git a/README.md b/README.md index e6ca00b..c0c0c0a 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,13 @@ There are 4 methods to do so: It aims to be highly customizable and performant. Offering a multilang system, which displays messages based on the player client's language setting as well as an optimize- and unoptimize event, so you may extend the plugin with your own custom solutions. +### Commands: +| Command | Aliases | Description | +|:---------------------------------------------:|:-------------------:|:------------------------------------------:| +| /villageroptimizer [reload, version, disable] | voptimizer, vo | VillagerOptimizer admin commands | +| /optimizevillagers | noai, optvils | Optmize villagers in a radius around you | +| /unoptimizevillagers | noaiundo, unoptvils | Unoptmize villagers in a radius around you | + Other features: - Prevent trading with unoptimized villagers to encourage players to optimize them - Smart villager chunk limit with configurable max numbers for optimized and unoptimized villagers and a villager profession based priorisation system (you can configure what kind of villagers should be deleted first, like for example nitwits or jobless villagers.) diff --git a/src/main/java/me/xginko/villageroptimizer/VillagerOptimizer.java b/src/main/java/me/xginko/villageroptimizer/VillagerOptimizer.java index 0be8190..ab8a764 100644 --- a/src/main/java/me/xginko/villageroptimizer/VillagerOptimizer.java +++ b/src/main/java/me/xginko/villageroptimizer/VillagerOptimizer.java @@ -11,10 +11,9 @@ import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.Style; import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.format.TextDecoration; +import net.kyori.adventure.text.logger.slf4j.ComponentLogger; import org.bstats.bukkit.Metrics; -import org.bukkit.NamespacedKey; import org.bukkit.command.CommandSender; -import org.bukkit.command.ConsoleCommandSender; import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; @@ -23,7 +22,6 @@ import java.io.IOException; import java.nio.file.Files; import java.util.*; import java.util.jar.JarFile; -import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -37,49 +35,47 @@ public final class VillagerOptimizer extends JavaPlugin { private static FoliaLib foliaLib; private static HashMap languageCacheMap; private static Config config; - private static ConsoleCommandSender console; - private static Logger logger; + private static ComponentLogger logger; @Override public void onEnable() { instance = this; - logger = getLogger(); - console = getServer().getConsoleSender(); + logger = getComponentLogger(); foliaLib = new FoliaLib(this); - console.sendMessage(Component.text("╭────────────────────────────────────────────────────────────╮").style(plugin_style)); - console.sendMessage(Component.text("│ │").style(plugin_style)); - console.sendMessage(Component.text("│ │").style(plugin_style)); - console.sendMessage(Component.text("│ _ __ _ __ __ │").style(plugin_style)); - console.sendMessage(Component.text("│ | | / /(_)/ // /___ _ ___ _ ___ ____ │").style(plugin_style)); - console.sendMessage(Component.text("│ | |/ // // // // _ `// _ `// -_)/ __/ │").style(plugin_style)); - console.sendMessage(Component.text("│ |___//_//_//_/ \\_,_/ \\_, / \\__//_/ │").style(plugin_style)); - console.sendMessage(Component.text("│ ____ __ _ /___/_ │").style(plugin_style)); - console.sendMessage(Component.text("│ / __ \\ ___ / /_ (_)__ _ (_)___ ___ ____ │").style(plugin_style)); - console.sendMessage(Component.text("│ / /_/ // _ \\/ __// // ' \\ / //_ // -_)/ __/ │").style(plugin_style)); - console.sendMessage(Component.text("│ \\____// .__/\\__//_//_/_/_//_/ /__/\\__//_/ │").style(plugin_style)); - console.sendMessage(Component.text("│ /_/ by xGinko │").style(plugin_style)); - console.sendMessage(Component.text("│ │").style(plugin_style)); - console.sendMessage(Component.text("│ │").style(plugin_style)); - console.sendMessage(Component.text("│ ") + logger.info(Component.text("╭────────────────────────────────────────────────────────────╮").style(plugin_style)); + logger.info(Component.text("│ │").style(plugin_style)); + logger.info(Component.text("│ │").style(plugin_style)); + logger.info(Component.text("│ _ __ _ __ __ │").style(plugin_style)); + logger.info(Component.text("│ | | / /(_)/ // /___ _ ___ _ ___ ____ │").style(plugin_style)); + logger.info(Component.text("│ | |/ // // // // _ `// _ `// -_)/ __/ │").style(plugin_style)); + logger.info(Component.text("│ |___//_//_//_/ \\_,_/ \\_, / \\__//_/ │").style(plugin_style)); + logger.info(Component.text("│ ____ __ _ /___/_ │").style(plugin_style)); + logger.info(Component.text("│ / __ \\ ___ / /_ (_)__ _ (_)___ ___ ____ │").style(plugin_style)); + logger.info(Component.text("│ / /_/ // _ \\/ __// // ' \\ / //_ // -_)/ __/ │").style(plugin_style)); + logger.info(Component.text("│ \\____// .__/\\__//_//_/_/_//_/ /__/\\__//_/ │").style(plugin_style)); + logger.info(Component.text("│ /_/ by xGinko │").style(plugin_style)); + logger.info(Component.text("│ │").style(plugin_style)); + logger.info(Component.text("│ │").style(plugin_style)); + logger.info(Component.text("│ ") .style(plugin_style).append(Component.text("https://github.com/xGinko/VillagerOptimizer") .color(NamedTextColor.GRAY)).append(Component.text(" │").style(plugin_style))); - console.sendMessage(Component.text("│ │").style(plugin_style)); - console.sendMessage(Component.text("│ │").style(plugin_style)); - console.sendMessage(Component.text("│ ") + logger.info(Component.text("│ │").style(plugin_style)); + logger.info(Component.text("│ │").style(plugin_style)); + logger.info(Component.text("│ ") .style(plugin_style).append(Component.text(" ➤ Loading Translations...").style(plugin_style)) .append(Component.text(" │").style(plugin_style))); reloadLang(true); - console.sendMessage(Component.text("│ ") + logger.info(Component.text("│ ") .style(plugin_style).append(Component.text(" ➤ Loading Config...").style(plugin_style)) .append(Component.text(" │").style(plugin_style))); reloadConfiguration(); - console.sendMessage(Component.text("│ ") + logger.info(Component.text("│ ") .style(plugin_style).append(Component.text(" ✓ Done.").color(NamedTextColor.WHITE).decorate(TextDecoration.BOLD)) .append(Component.text(" │").style(plugin_style))); - console.sendMessage(Component.text("│ │").style(plugin_style)); - console.sendMessage(Component.text("│ │").style(plugin_style)); - console.sendMessage(Component.text("╰────────────────────────────────────────────────────────────╯").style(plugin_style)); + logger.info(Component.text("│ │").style(plugin_style)); + logger.info(Component.text("│ │").style(plugin_style)); + logger.info(Component.text("╰────────────────────────────────────────────────────────────╯").style(plugin_style)); new Metrics(this, 19954); } @@ -96,13 +92,7 @@ public final class VillagerOptimizer extends JavaPlugin { public static FoliaLib getFoliaLib() { return foliaLib; } - public static ServerImplementation getScheduler() { - return foliaLib.getImpl(); - } - public static ConsoleCommandSender getConsole() { - return console; - } - public static Logger getLog() { + public static ComponentLogger getLog() { return logger; } public static LanguageCache getLang(Locale locale) { @@ -129,7 +119,7 @@ public final class VillagerOptimizer extends JavaPlugin { VillagerOptimizerModule.reloadModules(); config.saveConfig(); } catch (Exception e) { - logger.severe("Error loading config! - " + e.getLocalizedMessage()); + logger.error("Error loading config! - " + e.getLocalizedMessage()); e.printStackTrace(); } } @@ -141,7 +131,7 @@ public final class VillagerOptimizer extends JavaPlugin { Files.createDirectories(langDirectory.toPath()); for (String fileName : getDefaultLanguageFiles()) { final String localeString = fileName.substring(fileName.lastIndexOf(File.separator) + 1, fileName.lastIndexOf('.')); - if (startup) console.sendMessage( + if (startup) logger.info( Component.text("│ ").style(plugin_style) .append(Component.text(" "+localeString).color(NamedTextColor.WHITE).decorate(TextDecoration.BOLD)) .append(Component.text(" │").style(plugin_style))); @@ -154,7 +144,7 @@ public final class VillagerOptimizer extends JavaPlugin { 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 - if (startup) console.sendMessage( + if (startup) logger.info( Component.text("│ ").style(plugin_style) .append(Component.text(" "+localeString).color(NamedTextColor.WHITE).decorate(TextDecoration.BOLD)) .append(Component.text(" │").style(plugin_style))); @@ -164,11 +154,11 @@ public final class VillagerOptimizer extends JavaPlugin { } } } catch (Exception e) { - if (startup) console.sendMessage( + if (startup) logger.error( Component.text("│ ").style(plugin_style) .append(Component.text("LANG ERROR").color(NamedTextColor.RED).decorate(TextDecoration.BOLD)) .append(Component.text(" │").style(plugin_style))); - else logger.severe("Error loading language files! Language files will not reload to avoid errors, make sure to correct this before restarting the server!"); + else logger.error("Error loading language files! Language files will not reload to avoid errors, make sure to correct this before restarting the server!"); e.printStackTrace(); } } @@ -180,7 +170,7 @@ public final class VillagerOptimizer extends JavaPlugin { .filter(name -> name.startsWith("lang" + File.separator) && name.endsWith(".yml")) .collect(Collectors.toSet()); } catch (IOException e) { - logger.severe("Failed getting default lang files! - "+e.getLocalizedMessage()); + logger.error("Failed getting default lang files! - "+e.getLocalizedMessage()); return Collections.emptySet(); } } diff --git a/src/main/java/me/xginko/villageroptimizer/WrappedVillager.java b/src/main/java/me/xginko/villageroptimizer/WrappedVillager.java index 5a123ef..e759615 100644 --- a/src/main/java/me/xginko/villageroptimizer/WrappedVillager.java +++ b/src/main/java/me/xginko/villageroptimizer/WrappedVillager.java @@ -5,11 +5,14 @@ import me.xginko.villageroptimizer.enums.OptimizationType; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.entity.Villager; +import org.bukkit.inventory.MerchantRecipe; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.concurrent.TimeUnit; + public final class WrappedVillager { private final @NotNull Villager villager; @@ -81,18 +84,23 @@ public final class WrappedVillager { * @param type OptimizationType the villager should be set to. */ public void setOptimization(OptimizationType type) { - if (type.equals(OptimizationType.NONE) && isOptimized()) { - if (!parseOther || isOptimized(Keys.Namespaces.VillagerOptimizer)) { - dataContainer.remove(Keys.Own.OPTIMIZATION_TYPE.key()); - } - VillagerOptimizer.getScheduler().runAtEntity(villager, enableAI -> { + VillagerOptimizer.getFoliaLib().getImpl().runAtEntityTimer(villager, setOptimization -> { + // Keep repeating task until villager is no longer trading with a player + if (villager.isTrading()) return; + + if (type.equals(OptimizationType.NONE) && isOptimized()) { + if (!parseOther || isOptimized(Keys.Namespaces.VillagerOptimizer)) + dataContainer.remove(Keys.Own.OPTIMIZATION_TYPE.key()); villager.setAware(true); - villager.setAI(true); - }); - } else { - dataContainer.set(Keys.Own.OPTIMIZATION_TYPE.key(), PersistentDataType.STRING, type.name()); - VillagerOptimizer.getScheduler().runAtEntity(villager, disableAI -> villager.setAware(false)); - } + villager.setAI(true); // Done for stability so villager is guaranteed to wake up + } else { + dataContainer.set(Keys.Own.OPTIMIZATION_TYPE.key(), PersistentDataType.STRING, type.name()); + villager.setAware(false); + } + + // End repeating task once logic is finished + setOptimization.cancel(); + }, 0L, 1L, TimeUnit.SECONDS); } /** @@ -190,7 +198,9 @@ public final class WrappedVillager { * Restock all trading recipes. */ public void restock() { - villager.getRecipes().forEach(recipe -> recipe.setUses(0)); + for (MerchantRecipe recipe : villager.getRecipes()) { + recipe.setUses(0); + } } /** diff --git a/src/main/java/me/xginko/villageroptimizer/commands/villageroptimizer/subcommands/ReloadSubCmd.java b/src/main/java/me/xginko/villageroptimizer/commands/villageroptimizer/subcommands/ReloadSubCmd.java index 92cf44a..afa36cd 100644 --- a/src/main/java/me/xginko/villageroptimizer/commands/villageroptimizer/subcommands/ReloadSubCmd.java +++ b/src/main/java/me/xginko/villageroptimizer/commands/villageroptimizer/subcommands/ReloadSubCmd.java @@ -29,7 +29,7 @@ public class ReloadSubCmd extends SubCommand { public void perform(CommandSender sender, String[] args) { if (sender.hasPermission(Commands.RELOAD.get())) { sender.sendMessage(Component.text("Reloading VillagerOptimizer...").color(NamedTextColor.WHITE)); - VillagerOptimizer.getScheduler().runNextTick(reload -> { // Reload in sync with the server + VillagerOptimizer.getFoliaLib().getImpl().runNextTick(reload -> { // Reload in sync with the server VillagerOptimizer.getInstance().reloadPlugin(); sender.sendMessage(Component.text("Reload complete.").color(NamedTextColor.GREEN)); }); diff --git a/src/main/java/me/xginko/villageroptimizer/config/Config.java b/src/main/java/me/xginko/villageroptimizer/config/Config.java index 8341e20..a0f9cd1 100644 --- a/src/main/java/me/xginko/villageroptimizer/config/Config.java +++ b/src/main/java/me/xginko/villageroptimizer/config/Config.java @@ -19,7 +19,7 @@ public class Config { // Create plugin folder first if it does not exist yet File pluginFolder = VillagerOptimizer.getInstance().getDataFolder(); if (!pluginFolder.exists() && !pluginFolder.mkdir()) - VillagerOptimizer.getLog().severe("Failed to create plugin directory."); + VillagerOptimizer.getLog().error("Failed to create plugin directory."); // Load config.yml with ConfigMaster this.config = ConfigFile.loadConfig(new File(pluginFolder, "config.yml")); @@ -42,7 +42,7 @@ public class Config { try { this.config.save(); } catch (Exception e) { - VillagerOptimizer.getLog().severe("Failed to save config file! - " + e.getLocalizedMessage()); + VillagerOptimizer.getLog().error("Failed to save config file! - " + e.getLocalizedMessage()); } } diff --git a/src/main/java/me/xginko/villageroptimizer/config/LanguageCache.java b/src/main/java/me/xginko/villageroptimizer/config/LanguageCache.java index 20086f4..e759ec6 100644 --- a/src/main/java/me/xginko/villageroptimizer/config/LanguageCache.java +++ b/src/main/java/me/xginko/villageroptimizer/config/LanguageCache.java @@ -27,7 +27,7 @@ public class LanguageCache { // Check if the lang folder has already been created File parent = langYML.getParentFile(); if (!parent.exists() && !parent.mkdir()) - VillagerOptimizer.getLog().severe("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 plugins resources folder if it does not if (!langYML.exists()) plugin.saveResource("lang" + File.separator + locale + ".yml", false); @@ -83,7 +83,7 @@ public class LanguageCache { try { this.lang.save(); } catch (Exception e) { - VillagerOptimizer.getLog().severe("Failed to save language file: "+ langYML.getName() +" - " + e.getLocalizedMessage()); + VillagerOptimizer.getLog().error("Failed to save language file: "+ langYML.getName() +" - " + e.getLocalizedMessage()); } } diff --git a/src/main/java/me/xginko/villageroptimizer/enums/Keys.java b/src/main/java/me/xginko/villageroptimizer/enums/Keys.java index b1c7fa6..40a0cae 100644 --- a/src/main/java/me/xginko/villageroptimizer/enums/Keys.java +++ b/src/main/java/me/xginko/villageroptimizer/enums/Keys.java @@ -55,9 +55,9 @@ public class Keys { } public enum AntiVillagerLag { - NEXT_OPTIMIZATION_SYSTIME_SECONDS("cooldown"), // Returns LONG -> System.currentTimeMillis() / 1000 + cooldown seconds + NEXT_OPTIMIZATION_SYSTIME_SECONDS("cooldown"), // Returns LONG -> (System.currentTimeMillis() / 1000) + cooldown seconds LAST_RESTOCK_WORLDFULLTIME("time"), // Returns LONG -> villager.getWorld().getFullTime() - NEXT_LEVELUP_SYSTIME_SECONDS("levelCooldown"), // Returns LONG -> System.currentTimeMillis() / 1000 + cooldown seconds + NEXT_LEVELUP_SYSTIME_SECONDS("levelCooldown"), // Returns LONG -> (System.currentTimeMillis() / 1000) + cooldown seconds OPTIMIZED_ANY("Marker"), // Returns STRING -> "AVL" OPTIMIZED_BLOCK("disabledByBlock"), // Returns STRING -> key().toString() OPTIMIZED_WORKSTATION("disabledByWorkstation"); // Returns STRING -> key().toString() diff --git a/src/main/java/me/xginko/villageroptimizer/modules/VillagerChunkLimit.java b/src/main/java/me/xginko/villageroptimizer/modules/VillagerChunkLimit.java index dda7791..ecd96f9 100644 --- a/src/main/java/me/xginko/villageroptimizer/modules/VillagerChunkLimit.java +++ b/src/main/java/me/xginko/villageroptimizer/modules/VillagerChunkLimit.java @@ -20,10 +20,9 @@ import org.bukkit.event.Listener; import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.player.PlayerInteractEntityEvent; import org.jetbrains.annotations.NotNull; +import org.slf4j.event.Level; import java.util.*; -import java.util.logging.Level; -import java.util.stream.Stream; public class VillagerChunkLimit implements VillagerOptimizerModule, Listener { @@ -38,7 +37,7 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener { protected VillagerChunkLimit() { shouldEnable(); - this.scheduler = VillagerOptimizer.getScheduler(); + this.scheduler = VillagerOptimizer.getFoliaLib().getImpl(); this.villagerCache = VillagerOptimizer.getCache(); Config config = VillagerOptimizer.getConfiguration(); config.master().addComment("villager-chunk-limit.enable", """ @@ -62,7 +61,7 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener { try { return Villager.Profession.valueOf(configuredProfession); } catch (IllegalArgumentException e) { - LogUtil.moduleLog(Level.WARNING, "villager-chunk-limit.unoptimized", + LogUtil.moduleLog(Level.WARN, "villager-chunk-limit.unoptimized", "Villager profession '"+configuredProfession+"' not recognized. " + "Make sure you're using the correct profession enums from https://jd.papermc.io/paper/1.20/org/bukkit/entity/Villager.Profession.html."); return null; @@ -77,7 +76,7 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener { try { return Villager.Profession.valueOf(configuredProfession); } catch (IllegalArgumentException e) { - LogUtil.moduleLog(Level.WARNING, "villager-chunk-limit.optimized", + LogUtil.moduleLog(Level.WARN, "villager-chunk-limit.optimized", "Villager profession '"+configuredProfession+"' not recognized. " + "Make sure you're using the correct profession enums from https://jd.papermc.io/paper/1.20/org/bukkit/entity/Villager.Profession.html."); return null; diff --git a/src/main/java/me/xginko/villageroptimizer/modules/gameplay/LevelOptimizedProfession.java b/src/main/java/me/xginko/villageroptimizer/modules/gameplay/LevelOptimizedProfession.java index 7c1e607..a6ecb38 100644 --- a/src/main/java/me/xginko/villageroptimizer/modules/gameplay/LevelOptimizedProfession.java +++ b/src/main/java/me/xginko/villageroptimizer/modules/gameplay/LevelOptimizedProfession.java @@ -68,11 +68,11 @@ public class LevelOptimizedProfession implements VillagerOptimizerModule, Listen if (wVillager.canLevelUp(cooldown_millis)) { if (wVillager.calculateLevel() > villager.getVillagerLevel()) { - VillagerOptimizer.getScheduler().runAtEntity(villager, enableAI -> { + VillagerOptimizer.getFoliaLib().getImpl().runAtEntity(villager, enableAI -> { villager.addPotionEffect(new PotionEffect(PotionEffectType.SLOW, 120, 120, false, false)); villager.setAware(true); }); - VillagerOptimizer.getScheduler().runAtEntityLater(villager, disableAI -> { + VillagerOptimizer.getFoliaLib().getImpl().runAtEntityLater(villager, disableAI -> { villager.setAware(false); wVillager.saveLastLevelUp(); }, 5, TimeUnit.SECONDS); diff --git a/src/main/java/me/xginko/villageroptimizer/modules/gameplay/RenameOptimizedVillagers.java b/src/main/java/me/xginko/villageroptimizer/modules/gameplay/RenameOptimizedVillagers.java index 38ee278..5542f1f 100644 --- a/src/main/java/me/xginko/villageroptimizer/modules/gameplay/RenameOptimizedVillagers.java +++ b/src/main/java/me/xginko/villageroptimizer/modules/gameplay/RenameOptimizedVillagers.java @@ -23,7 +23,7 @@ public class RenameOptimizedVillagers implements VillagerOptimizerModule, Listen public RenameOptimizedVillagers() { shouldEnable(); - this.scheduler = VillagerOptimizer.getScheduler(); + 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.\s diff --git a/src/main/java/me/xginko/villageroptimizer/utils/LogUtil.java b/src/main/java/me/xginko/villageroptimizer/utils/LogUtil.java index 38929e1..a98f375 100644 --- a/src/main/java/me/xginko/villageroptimizer/utils/LogUtil.java +++ b/src/main/java/me/xginko/villageroptimizer/utils/LogUtil.java @@ -1,26 +1,26 @@ package me.xginko.villageroptimizer.utils; import me.xginko.villageroptimizer.VillagerOptimizer; +import org.slf4j.event.Level; -import java.util.logging.Level; public class LogUtil { public static void moduleLog(Level logLevel, String path, String logMessage) { - VillagerOptimizer.getLog().log(logLevel, "(" + path + ") " + logMessage); + VillagerOptimizer.getLog().atLevel(logLevel).log("(" + path + ") " + logMessage); } public static void materialNotRecognized(String path, String material) { - moduleLog(Level.WARNING, path, "Material '" + material + "' not recognized. Please use correct Material enums from: " + + moduleLog(Level.WARN, path, "Material '" + material + "' not recognized. Please use correct Material enums from: " + "https://jd.papermc.io/paper/1.20/org/bukkit/Material.html"); } public static void damageCauseNotRecognized(String path, String cause) { - moduleLog(Level.WARNING, path, "DamageCause '" + cause + "' not recognized. Please use correct DamageCause enums from: " + + moduleLog(Level.WARN, path, "DamageCause '" + cause + "' not recognized. Please use correct DamageCause enums from: " + "https://jd.papermc.io/paper/1.20/org/bukkit/event/entity/EntityDamageEvent.DamageCause.html"); } 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!"); + moduleLog(Level.WARN, path, "EntityType '" + entityType + "' not recognized. Please use correct Spigot EntityType enums for your Minecraft version!"); } } \ No newline at end of file