package me.xginko.villageroptimizer; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import me.xginko.villageroptimizer.commands.VillagerOptimizerCommand; import me.xginko.villageroptimizer.config.Config; import me.xginko.villageroptimizer.config.LanguageCache; import me.xginko.villageroptimizer.struct.enums.Permissions; import me.xginko.villageroptimizer.modules.VillagerOptimizerModule; import me.xginko.villageroptimizer.utils.Util; import me.xginko.villageroptimizer.wrapper.WrappedVillager; import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextDecoration; import net.kyori.adventure.text.logger.slf4j.ComponentLogger; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.config.Configurator; import org.bstats.bukkit.Metrics; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.entity.Villager; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; import space.arim.morepaperlib.MorePaperLib; import space.arim.morepaperlib.commands.CommandRegistration; import space.arim.morepaperlib.scheduling.GracefulScheduling; import java.io.File; import java.io.FileNotFoundException; import java.nio.file.Files; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.SortedSet; import java.util.TreeSet; import java.util.jar.JarFile; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.zip.ZipEntry; public final class VillagerOptimizer extends JavaPlugin { private static VillagerOptimizer instance; private static CommandRegistration commandRegistration; private static GracefulScheduling scheduling; private static Cache wrapperCache; private static Map languageCacheMap; private static Config config; private static BukkitAudiences audiences; private static ComponentLogger logger; private static Metrics bStats; @Override public void onLoad() { // Disable reflection logging String shadedLibs = getClass().getPackage().getName() + ".libs"; Configurator.setLevel(shadedLibs + ".reflections.Reflections", Level.OFF); } @Override public void onEnable() { instance = this; MorePaperLib morePaperLib = new MorePaperLib(this); commandRegistration = morePaperLib.commandRegistration(); scheduling = morePaperLib.scheduling(); audiences = BukkitAudiences.create(this); logger = ComponentLogger.logger(getLogger().getName()); bStats = new Metrics(this, 19954); if (getServer().getPluginManager().getPlugin("AntiVillagerLag") != null) { logger.warn("While VillagerOptimizer can read data previously created by AVL, running"); logger.warn("both plugins at the same time is unsafe and definitely will cause issues."); logger.warn("To protect your game from corruption, VillagerOptimizer will now disable!"); logger.warn("Please decide for one of the plugins!"); getServer().getPluginManager().disablePlugin(this); return; } try { getDataFolder().mkdirs(); } catch (Exception e) { logger.error("Failed to create plugin directory! Cannot enable!", e); getServer().getPluginManager().disablePlugin(this); return; } logger.info(Component.text("╭────────────────────────────────────────────────────────────╮").style(Util.PL_STYLE)); logger.info(Component.text("│ │").style(Util.PL_STYLE)); logger.info(Component.text("│ │").style(Util.PL_STYLE)); logger.info(Component.text("│ _ __ _ __ __ │").style(Util.PL_STYLE)); logger.info(Component.text("│ | | / /(_)/ // /___ _ ___ _ ___ ____ │").style(Util.PL_STYLE)); logger.info(Component.text("│ | |/ // // // // _ `// _ `// -_)/ __/ │").style(Util.PL_STYLE)); logger.info(Component.text("│ |___//_//_//_/ \\_,_/ \\_, / \\__//_/ │").style(Util.PL_STYLE)); logger.info(Component.text("│ ____ __ _ /___/_ │").style(Util.PL_STYLE)); logger.info(Component.text("│ / __ \\ ___ / /_ (_)__ _ (_)___ ___ ____ │").style(Util.PL_STYLE)); logger.info(Component.text("│ / /_/ // _ \\/ __// // ' \\ / //_ // -_)/ __/ │").style(Util.PL_STYLE)); logger.info(Component.text("│ \\____// .__/\\__//_//_/_/_//_/ /__/\\__//_/ │").style(Util.PL_STYLE)); logger.info(Component.text("│ /_/ by xGinko │").style(Util.PL_STYLE)); logger.info(Component.text("│ │").style(Util.PL_STYLE)); logger.info(Component.text("│ │").style(Util.PL_STYLE)); logger.info(Component.text("│ ") .style(Util.PL_STYLE).append(Component.text("https://github.com/xGinko/VillagerOptimizer") .color(NamedTextColor.GRAY)).append(Component.text(" │").style(Util.PL_STYLE))); logger.info(Component.text("│ │").style(Util.PL_STYLE)); logger.info(Component.text("│ │").style(Util.PL_STYLE)); Permissions.registerAll(); logger.info(Component.text("│ ") .style(Util.PL_STYLE).append(Component.text(" ➤ Loading Config...").style(Util.PL_STYLE)) .append(Component.text(" │").style(Util.PL_STYLE))); reloadConfiguration(); logger.info(Component.text("│ ") .style(Util.PL_STYLE).append(Component.text(" ➤ Loading Translations...").style(Util.PL_STYLE)) .append(Component.text(" │").style(Util.PL_STYLE))); reloadLang(true); logger.info(Component.text("│ ") .style(Util.PL_STYLE).append(Component.text(" ✓ Done.").color(NamedTextColor.WHITE).decorate(TextDecoration.BOLD)) .append(Component.text(" │").style(Util.PL_STYLE))); logger.info(Component.text("│ │").style(Util.PL_STYLE)); logger.info(Component.text("│ │").style(Util.PL_STYLE)); logger.info(Component.text("╰────────────────────────────────────────────────────────────╯").style(Util.PL_STYLE)); } @Override public void onDisable() { VillagerOptimizerModule.ENABLED_MODULES.forEach(VillagerOptimizerModule::disable); VillagerOptimizerModule.ENABLED_MODULES.clear(); VillagerOptimizerCommand.COMMANDS.forEach(VillagerOptimizerCommand::disable); VillagerOptimizerCommand.COMMANDS.clear(); if (wrapperCache != null) { wrapperCache.cleanUp(); wrapperCache = null; } if (scheduling != null) { scheduling.cancelGlobalTasks(); scheduling = null; } if (audiences != null) { audiences.close(); audiences = null; } if (bStats != null) { bStats.shutdown(); bStats = null; } commandRegistration = null; languageCacheMap = null; instance = null; config = null; logger = null; } public static @NotNull VillagerOptimizer getInstance() { return instance; } public static @NotNull GracefulScheduling scheduling() { return scheduling; } public static @NotNull CommandRegistration commandRegistration() { return commandRegistration; } public static @NotNull Cache wrappers() { return wrapperCache; } public static @NotNull Config config() { return config; } public static @NotNull ComponentLogger logger() { return logger; } public static @NotNull BukkitAudiences audiences() { return audiences; } public static @NotNull LanguageCache getLang(Locale locale) { return getLang(locale.toString().toLowerCase()); } public static @NotNull LanguageCache getLang(CommandSender commandSender) { return commandSender instanceof Player ? getLang(((Player) commandSender).locale()) : getLang(config.default_lang); } public static @NotNull LanguageCache getLang(String lang) { if (!config.auto_lang) return languageCacheMap.get(config.default_lang.toString().toLowerCase()); return languageCacheMap.getOrDefault(lang.replace("-", "_"), languageCacheMap.get(config.default_lang.toString().toLowerCase())); } public void reloadPlugin() { reloadLang(false); reloadConfiguration(); } private void reloadConfiguration() { try { config = new Config(); if (wrapperCache != null) wrapperCache.cleanUp(); wrapperCache = Caffeine.newBuilder().expireAfterWrite(config.cache_keep_time).build(); VillagerOptimizerCommand.reloadCommands(); VillagerOptimizerModule.reloadModules(); config.saveConfig(); } catch (Exception exception) { logger.error("Error during config reload!", exception); } } private void reloadLang(boolean logFancy) { try { final SortedSet availableLocales = getAvailableTranslations(); if (!config.auto_lang) { final String defaultLang = config.default_lang.toString().replace("-", "_").toLowerCase(); if (!availableLocales.contains(defaultLang)) throw new FileNotFoundException("Could not find any translation file for language '" + config.default_lang + "'"); availableLocales.removeIf(localeString -> !localeString.equalsIgnoreCase(defaultLang)); } languageCacheMap = new HashMap<>(availableLocales.size()); for (String localeString : availableLocales) { if (logFancy) logger.info(Component.text("│ ").style(Util.PL_STYLE) .append(Component.text(" "+localeString).color(NamedTextColor.WHITE).decorate(TextDecoration.BOLD)) .append(Component.text(" │").style(Util.PL_STYLE))); else logger.info(String.format("Found language file for %s", localeString)); languageCacheMap.put(localeString, new LanguageCache(localeString)); } } catch (Throwable t) { if (logFancy) logger.error(Component.text("│ ").style(Util.PL_STYLE) .append(Component.text("LANG ERROR").color(NamedTextColor.RED).decorate(TextDecoration.BOLD)) .append(Component.text(" │").style(Util.PL_STYLE)), t); else logger.error("Error while loading translation files!", t); } } private @NotNull SortedSet getAvailableTranslations() { try (final JarFile pluginJar = new JarFile(getFile())) { final File langDirectory = new File(getDataFolder() + "/lang"); Files.createDirectories(langDirectory.toPath()); final Pattern langPattern = Pattern.compile("([a-z]{1,3}_[a-z]{1,3})(\\.yml)", Pattern.CASE_INSENSITIVE); return Stream.concat(pluginJar.stream().map(ZipEntry::getName), Arrays.stream(langDirectory.listFiles()).map(File::getName)) .map(langPattern::matcher) .filter(Matcher::find) .map(matcher -> matcher.group(1)) .collect(Collectors.toCollection(TreeSet::new)); } catch (Throwable t) { logger.error("Failed while searching for available translations!", t); return Collections.emptySortedSet(); } } }