fix backwards compatibility issues with MiniMessage and Components

downgrade to java 8 so 1.16 modded servers arent left out
This commit is contained in:
xGinko 2024-02-21 14:30:54 +01:00
parent f4a5d2b9fd
commit 6a099ddfe3
31 changed files with 510 additions and 427 deletions

30
pom.xml
View File

@ -6,7 +6,7 @@
<groupId>me.xginko.VillagerOptimizer</groupId>
<artifactId>VillagerOptimizer</artifactId>
<version>1.4.1</version>
<version>1.5.0</version>
<packaging>jar</packaging>
<name>VillagerOptimizer</name>
@ -14,7 +14,7 @@
<url>https://github.com/xGinko/VillagerOptimizer</url>
<properties>
<java.version>16</java.version>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
@ -107,51 +107,53 @@
<version>1.20.4-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!-- Adventure API for easier cross-version compatibility -->
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-platform-bukkit</artifactId>
<version>4.3.2</version>
</dependency>
<!-- Adventure MiniMessage for parsing fancy tags in lang files -->
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-minimessage</artifactId>
<version>4.15.0</version>
<scope>compile</scope>
<version>4.16.0</version>
</dependency>
<!-- Adventure ComponentLogger for colorful logging -->
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-logger-slf4j</artifactId>
<version>4.15.0</version>
<scope>compile</scope>
<version>4.16.0</version>
</dependency>
<!-- Adventure plaintext serializer for reading unstyled content of components -->
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-serializer-plain</artifactId>
<version>4.15.0</version>
<scope>compile</scope>
<version>4.16.0</version>
</dependency>
<!-- Bukkit bStats -->
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>3.0.2</version>
<scope>compile</scope>
</dependency>
<!-- Enhanced config.yml manager -->
<dependency>
<groupId>com.github.thatsmusic99</groupId>
<artifactId>ConfigurationMaster-API</artifactId>
<version>v2.0.0-rc.1</version>
<scope>compile</scope>
</dependency>
<!-- Fast Caching -->
<!-- Fast Caching (Needs to be 2.9.3 for java 8 support) -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.1.8</version>
<scope>compile</scope>
<version>2.9.3</version>
</dependency>
<!-- Folia Support -->
<dependency>
<groupId>com.tcoded</groupId>
<artifactId>FoliaLib</artifactId>
<version>0.3.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -2,10 +2,8 @@ package me.xginko.villageroptimizer;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.bukkit.Bukkit;
import org.bukkit.entity.Villager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.time.Duration;
import java.util.UUID;
@ -23,11 +21,6 @@ public final class VillagerCache {
return this.villagerCache.asMap();
}
public @Nullable WrappedVillager get(@NotNull UUID uuid) {
WrappedVillager wrappedVillager = this.villagerCache.getIfPresent(uuid);
return wrappedVillager == null && Bukkit.getEntity(uuid) instanceof Villager villager ? this.add(villager) : wrappedVillager;
}
public @NotNull WrappedVillager getOrAdd(@NotNull Villager villager) {
WrappedVillager wrappedVillager = this.villagerCache.getIfPresent(villager.getUniqueId());
return wrappedVillager == null ? this.add(new WrappedVillager(villager)) : this.add(wrappedVillager);

View File

@ -5,6 +5,7 @@ import me.xginko.villageroptimizer.commands.VillagerOptimizerCommand;
import me.xginko.villageroptimizer.config.Config;
import me.xginko.villageroptimizer.config.LanguageCache;
import me.xginko.villageroptimizer.modules.VillagerOptimizerModule;
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.Style;
@ -15,14 +16,12 @@ import org.bstats.bukkit.Metrics;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Set;
import java.util.*;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -30,80 +29,99 @@ import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
public final class VillagerOptimizer extends JavaPlugin {
public static final Style plugin_style = Style.style(TextColor.color(102,255,230), TextDecoration.BOLD);
public static final Style STYLE = Style.style(TextColor.color(102,255,230), TextDecoration.BOLD);
private static VillagerOptimizer instance;
private static VillagerCache villagerCache;
private static FoliaLib foliaLib;
private static HashMap<String, LanguageCache> languageCacheMap;
private static Map<String, LanguageCache> languageCacheMap;
private static Config config;
private static BukkitAudiences audiences;
private static ComponentLogger logger;
@Override
public void onEnable() {
instance = this;
logger = ComponentLogger.logger(this.getName());
foliaLib = new FoliaLib(this);
audiences = BukkitAudiences.create(this);
logger = ComponentLogger.logger(this.getName());
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(STYLE));
logger.info(Component.text("│ │").style(STYLE));
logger.info(Component.text("│ │").style(STYLE));
logger.info(Component.text("│ _ __ _ __ __ │").style(STYLE));
logger.info(Component.text("│ | | / /(_)/ // /___ _ ___ _ ___ ____ │").style(STYLE));
logger.info(Component.text("│ | |/ // // // // _ `// _ `// -_)/ __/ │").style(STYLE));
logger.info(Component.text("│ |___//_//_//_/ \\_,_/ \\_, / \\__//_/ │").style(STYLE));
logger.info(Component.text("│ ____ __ _ /___/_ │").style(STYLE));
logger.info(Component.text("│ / __ \\ ___ / /_ (_)__ _ (_)___ ___ ____ │").style(STYLE));
logger.info(Component.text("│ / /_/ // _ \\/ __// // ' \\ / //_ // -_)/ __/ │").style(STYLE));
logger.info(Component.text("\\____// .__/\\__//_//_/_/_//_/ /__/\\__//_/ │").style(STYLE));
logger.info(Component.text("│ /_/ by xGinko │").style(STYLE));
logger.info(Component.text("│ │").style(STYLE));
logger.info(Component.text("│ │").style(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)));
logger.info(Component.text("│ │").style(plugin_style));
logger.info(Component.text("│ │").style(plugin_style));
.style(STYLE).append(Component.text("https://github.com/xGinko/VillagerOptimizer")
.color(NamedTextColor.GRAY)).append(Component.text("").style(STYLE)));
logger.info(Component.text("│ │").style(STYLE));
logger.info(Component.text("│ │").style(STYLE));
logger.info(Component.text("")
.style(plugin_style).append(Component.text(" ➤ Loading Translations...").style(plugin_style))
.append(Component.text("").style(plugin_style)));
.style(STYLE).append(Component.text(" ➤ Loading Translations...").style(STYLE))
.append(Component.text("").style(STYLE)));
reloadLang(true);
logger.info(Component.text("")
.style(plugin_style).append(Component.text(" ➤ Loading Config...").style(plugin_style))
.append(Component.text("").style(plugin_style)));
.style(STYLE).append(Component.text(" ➤ Loading Config...").style(STYLE))
.append(Component.text("").style(STYLE)));
reloadConfiguration();
logger.info(Component.text("")
.style(plugin_style).append(Component.text(" ✓ Done.").color(NamedTextColor.WHITE).decorate(TextDecoration.BOLD))
.append(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));
.style(STYLE).append(Component.text(" ✓ Done.").color(NamedTextColor.WHITE).decorate(TextDecoration.BOLD))
.append(Component.text("").style(STYLE)));
logger.info(Component.text("│ │").style(STYLE));
logger.info(Component.text("│ │").style(STYLE));
logger.info(Component.text("╰────────────────────────────────────────────────────────────╯").style(STYLE));
new Metrics(this, 19954);
}
public static VillagerOptimizer getInstance() {
@Override
public void onDisable() {
VillagerOptimizerModule.modules.forEach(VillagerOptimizerModule::disable);
VillagerOptimizerModule.modules.clear();
if (villagerCache != null) {
villagerCache.cacheMap().clear();
villagerCache = null;
}
if (audiences != null) {
audiences.close();
audiences = null;
}
}
public static @NotNull VillagerOptimizer getInstance() {
return instance;
}
public static Config getConfiguration() {
public static @NotNull Config getConfiguration() {
return config;
}
public static VillagerCache getCache() {
public static @NotNull VillagerCache getCache() {
return villagerCache;
}
public static FoliaLib getFoliaLib() {
public static @NotNull FoliaLib getFoliaLib() {
return foliaLib;
}
public static ComponentLogger getLog() {
public static @NotNull ComponentLogger getLog() {
return logger;
}
public static LanguageCache getLang(Locale locale) {
public static @NotNull BukkitAudiences getAudiences() {
return audiences;
}
public static @NotNull LanguageCache getLang(Locale locale) {
return getLang(locale.toString().toLowerCase());
}
public static LanguageCache getLang(CommandSender commandSender) {
return commandSender instanceof Player player ? getLang(player.locale()) : getLang(config.default_lang);
public static @NotNull LanguageCache getLang(CommandSender commandSender) {
return commandSender instanceof Player ? getLang(((Player) commandSender).locale()) : getLang(config.default_lang);
}
public static LanguageCache getLang(String 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()));
}
@ -120,9 +138,8 @@ public final class VillagerOptimizer extends JavaPlugin {
VillagerOptimizerCommand.reloadCommands();
VillagerOptimizerModule.reloadModules();
config.saveConfig();
} catch (Exception e) {
logger.error("Error loading config! - " + e.getLocalizedMessage());
e.printStackTrace();
} catch (Exception exception) {
logger.error("Error loading config!", exception);
}
}
@ -134,9 +151,9 @@ public final class VillagerOptimizer extends JavaPlugin {
for (String fileName : getDefaultLanguageFiles()) {
final String localeString = fileName.substring(fileName.lastIndexOf(File.separator) + 1, fileName.lastIndexOf('.'));
if (startup) logger.info(
Component.text("").style(plugin_style)
Component.text("").style(STYLE)
.append(Component.text(" "+localeString).color(NamedTextColor.WHITE).decorate(TextDecoration.BOLD))
.append(Component.text("").style(plugin_style)));
.append(Component.text("").style(STYLE)));
else logger.info(String.format("Found language file for %s", localeString));
languageCacheMap.put(localeString, new LanguageCache(localeString));
}
@ -147,9 +164,9 @@ public final class VillagerOptimizer extends JavaPlugin {
String localeString = langMatcher.group(1).toLowerCase();
if (!languageCacheMap.containsKey(localeString)) { // make sure it wasn't a default file that we already loaded
if (startup) logger.info(
Component.text("").style(plugin_style)
Component.text("").style(STYLE)
.append(Component.text(" "+localeString).color(NamedTextColor.WHITE).decorate(TextDecoration.BOLD))
.append(Component.text("").style(plugin_style)));
.append(Component.text("").style(STYLE)));
else logger.info(String.format("Found language file for %s", localeString));
languageCacheMap.put(localeString, new LanguageCache(localeString));
}
@ -157,22 +174,21 @@ public final class VillagerOptimizer extends JavaPlugin {
}
} catch (Exception e) {
if (startup) logger.error(
Component.text("").style(plugin_style)
Component.text("").style(STYLE)
.append(Component.text("LANG ERROR").color(NamedTextColor.RED).decorate(TextDecoration.BOLD))
.append(Component.text("").style(plugin_style)));
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();
.append(Component.text("").style(STYLE)), e);
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);
}
}
private Set<String> getDefaultLanguageFiles() {
private @NotNull Set<String> getDefaultLanguageFiles() {
try (final JarFile pluginJarFile = new JarFile(this.getFile())) {
return pluginJarFile.stream()
.map(ZipEntry::getName)
.filter(name -> name.startsWith("lang" + File.separator) && name.endsWith(".yml"))
.collect(Collectors.toSet());
} catch (IOException e) {
logger.error("Failed getting default lang files! - "+e.getLocalizedMessage());
} catch (IOException ioException) {
logger.error("Failed getting default lang files!", ioException);
return Collections.emptySet();
}
}

View File

@ -59,13 +59,14 @@ public final class WrappedVillager {
/**
* @return True if the villager is optimized by the supported plugin, otherwise false.
*/
public boolean isOptimized(Keyring.Spaces namespaces) {
return switch (namespaces) {
case VillagerOptimizer -> dataContainer.has(Keyring.VillagerOptimizer.OPTIMIZATION_TYPE.getKey(), PersistentDataType.STRING);
case AntiVillagerLag -> dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_ANY.getKey(), PersistentDataType.STRING)
|| dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_WORKSTATION.getKey(), PersistentDataType.STRING)
|| dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_BLOCK.getKey(), PersistentDataType.STRING);
};
public boolean isOptimized(Keyring.Spaces namespace) {
if (namespace == Keyring.Spaces.VillagerOptimizer) {
return dataContainer.has(Keyring.VillagerOptimizer.OPTIMIZATION_TYPE.getKey(), PersistentDataType.STRING);
} else {
return dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_ANY.getKey(), PersistentDataType.STRING)
|| dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_WORKSTATION.getKey(), PersistentDataType.STRING)
|| dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_BLOCK.getKey(), PersistentDataType.STRING);
}
}
/**
@ -92,26 +93,23 @@ public final class WrappedVillager {
// Keep repeating task until villager is no longer trading with a player
if (villager.isTrading()) return;
switch (type) {
case NAMETAG, COMMAND, BLOCK, WORKSTATION -> {
dataContainer.set(Keyring.VillagerOptimizer.OPTIMIZATION_TYPE.getKey(), PersistentDataType.STRING, type.name());
villager.setAware(false);
if (type == OptimizationType.NONE) {
if (isOptimized(Keyring.Spaces.VillagerOptimizer)) {
dataContainer.remove(Keyring.VillagerOptimizer.OPTIMIZATION_TYPE.getKey());
}
case NONE -> {
if (isOptimized(Keyring.Spaces.VillagerOptimizer)) {
dataContainer.remove(Keyring.VillagerOptimizer.OPTIMIZATION_TYPE.getKey());
}
if (parseOther) {
if (dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_ANY.getKey(), PersistentDataType.STRING))
dataContainer.remove(Keyring.AntiVillagerLag.OPTIMIZED_ANY.getKey());
if (dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_WORKSTATION.getKey(), PersistentDataType.STRING))
dataContainer.remove(Keyring.AntiVillagerLag.OPTIMIZED_WORKSTATION.getKey());
if (dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_BLOCK.getKey(), PersistentDataType.STRING))
dataContainer.remove(Keyring.AntiVillagerLag.OPTIMIZED_BLOCK.getKey());
}
villager.setAware(true);
villager.setAI(true);
if (parseOther) {
if (dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_ANY.getKey(), PersistentDataType.STRING))
dataContainer.remove(Keyring.AntiVillagerLag.OPTIMIZED_ANY.getKey());
if (dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_WORKSTATION.getKey(), PersistentDataType.STRING))
dataContainer.remove(Keyring.AntiVillagerLag.OPTIMIZED_WORKSTATION.getKey());
if (dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_BLOCK.getKey(), PersistentDataType.STRING))
dataContainer.remove(Keyring.AntiVillagerLag.OPTIMIZED_BLOCK.getKey());
}
villager.setAware(true);
villager.setAI(true);
} else {
dataContainer.set(Keyring.VillagerOptimizer.OPTIMIZATION_TYPE.getKey(), PersistentDataType.STRING, type.name());
villager.setAware(false);
}
// End repeating task once logic is finished
@ -134,26 +132,26 @@ public final class WrappedVillager {
}
public @NotNull OptimizationType getOptimizationType(Keyring.Spaces namespaces) {
return switch (namespaces) {
case VillagerOptimizer -> {
if (isOptimized(Keyring.Spaces.VillagerOptimizer)) {
yield OptimizationType.valueOf(dataContainer.get(Keyring.VillagerOptimizer.OPTIMIZATION_TYPE.getKey(), PersistentDataType.STRING));
}
yield OptimizationType.NONE;
if (namespaces == Keyring.Spaces.VillagerOptimizer) {
if (!isOptimized(Keyring.Spaces.VillagerOptimizer)) {
return OptimizationType.valueOf(dataContainer.get(Keyring.VillagerOptimizer.OPTIMIZATION_TYPE.getKey(), PersistentDataType.STRING));
} else {
return OptimizationType.NONE;
}
case AntiVillagerLag -> {
if (dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_BLOCK.getKey(), PersistentDataType.STRING)) {
yield OptimizationType.BLOCK;
}
if (dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_WORKSTATION.getKey(), PersistentDataType.STRING)) {
yield OptimizationType.WORKSTATION;
}
if (dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_ANY.getKey(), PersistentDataType.STRING)) {
yield OptimizationType.COMMAND; // Best we can do
}
yield OptimizationType.NONE;
}
if (namespaces == Keyring.Spaces.AntiVillagerLag) {
if (dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_BLOCK.getKey(), PersistentDataType.STRING)) {
return OptimizationType.BLOCK;
}
};
if (dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_WORKSTATION.getKey(), PersistentDataType.STRING)) {
return OptimizationType.WORKSTATION;
}
if (dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_ANY.getKey(), PersistentDataType.STRING)) {
return OptimizationType.COMMAND; // Best we can do
}
return OptimizationType.NONE;
}
return OptimizationType.NONE;
}
/**

View File

@ -8,6 +8,7 @@ import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandMap;
import org.bukkit.command.TabCompleter;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@ -17,7 +18,7 @@ public interface VillagerOptimizerCommand extends CommandExecutor, TabCompleter
String label();
List<String> NO_TABCOMPLETES = Collections.emptyList();
List<String> RADIUS_TABCOMPLETES = List.of("5", "10", "25", "50");
List<String> RADIUS_TABCOMPLETES = Arrays.asList("5", "10", "25", "50");
HashSet<VillagerOptimizerCommand> commands = new HashSet<>();

View File

@ -9,6 +9,7 @@ import me.xginko.villageroptimizer.enums.OptimizationType;
import me.xginko.villageroptimizer.enums.permissions.Bypass;
import me.xginko.villageroptimizer.enums.permissions.Commands;
import me.xginko.villageroptimizer.events.VillagerOptimizeEvent;
import me.xginko.villageroptimizer.utils.KyoriUtil;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextReplacementConfig;
import net.kyori.adventure.text.format.NamedTextColor;
@ -32,9 +33,9 @@ public class OptVillagersRadius implements VillagerOptimizerCommand {
public OptVillagersRadius() {
Config config = VillagerOptimizer.getConfiguration();
this.max_radius = config.getInt("optimization-methods.commands.optimizevillagers.max-block-radius", 100);
this.cooldown = config.getInt("optimization-methods.commands.optimizevillagers.cooldown-seconds", 600, """
Cooldown in seconds until a villager can be optimized again using the command.\s
Here for configuration freedom. Recommended to leave as is to not enable any exploitable behavior.""") * 1000L;
this.cooldown = config.getInt("optimization-methods.commands.optimizevillagers.cooldown-seconds", 600,
"Cooldown in seconds until a villager can be optimized again using the command.\n" +
"Here for configuration freedom. Recommended to leave as is to not enable any exploitable behavior.") * 1000L;
}
@Override
@ -50,18 +51,21 @@ public class OptVillagersRadius implements VillagerOptimizerCommand {
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
if (!sender.hasPermission(Commands.OPTIMIZE_RADIUS.get())) {
sender.sendMessage(VillagerOptimizer.getLang(sender).no_permission);
KyoriUtil.sendMessage(sender, VillagerOptimizer.getLang(sender).no_permission);
return true;
}
if (!(sender instanceof Player player)) {
sender.sendMessage(Component.text("This command can only be executed by a player.")
if (!(sender instanceof Player)) {
KyoriUtil.sendMessage(sender, Component.text("This command can only be executed by a player.")
.color(NamedTextColor.RED).decorate(TextDecoration.BOLD));
return true;
}
Player player = (Player) sender;
if (args.length != 1) {
VillagerOptimizer.getLang(player.locale()).command_specify_radius.forEach(player::sendMessage);
VillagerOptimizer.getLang(player.locale()).command_specify_radius
.forEach(line -> KyoriUtil.sendMessage(sender, line));
return true;
}
@ -71,7 +75,8 @@ public class OptVillagersRadius implements VillagerOptimizerCommand {
final int safeRadius = (int) Math.sqrt(specifiedRadius * specifiedRadius);
if (safeRadius == 0) {
VillagerOptimizer.getLang(player.locale()).command_radius_invalid.forEach(player::sendMessage);
VillagerOptimizer.getLang(player.locale()).command_radius_invalid
.forEach(line -> KyoriUtil.sendMessage(sender, line));
return true;
}
@ -80,7 +85,8 @@ public class OptVillagersRadius implements VillagerOptimizerCommand {
.matchLiteral("%distance%")
.replacement(Integer.toString(max_radius))
.build();
VillagerOptimizer.getLang(player.locale()).command_radius_limit_exceed.forEach(line -> player.sendMessage(line.replaceText(limit)));
VillagerOptimizer.getLang(player.locale()).command_radius_limit_exceed
.forEach(line -> KyoriUtil.sendMessage(player, line.replaceText(limit)));
return true;
}
@ -114,7 +120,8 @@ public class OptVillagersRadius implements VillagerOptimizerCommand {
.matchLiteral("%radius%")
.replacement(Integer.toString(safeRadius))
.build();
VillagerOptimizer.getLang(player.locale()).command_no_villagers_nearby.forEach(line -> player.sendMessage(line.replaceText(radius)));
VillagerOptimizer.getLang(player.locale()).command_no_villagers_nearby
.forEach(line -> KyoriUtil.sendMessage(player, line.replaceText(radius)));
return true;
}
@ -127,20 +134,20 @@ public class OptVillagersRadius implements VillagerOptimizerCommand {
.matchLiteral("%radius%")
.replacement(Integer.toString(safeRadius))
.build();
VillagerOptimizer.getLang(player.locale()).command_optimize_success.forEach(line -> player.sendMessage(line
.replaceText(success_amount)
.replaceText(radius)
));
VillagerOptimizer.getLang(player.locale()).command_optimize_success
.forEach(line -> KyoriUtil.sendMessage(player, line.replaceText(success_amount).replaceText(radius)));
}
if (failCount > 0) {
final TextReplacementConfig alreadyOptimized = TextReplacementConfig.builder()
.matchLiteral("%amount%")
.replacement(Integer.toString(failCount))
.build();
VillagerOptimizer.getLang(player.locale()).command_optimize_fail.forEach(line -> player.sendMessage(line.replaceText(alreadyOptimized)));
VillagerOptimizer.getLang(player.locale()).command_optimize_fail
.forEach(line -> KyoriUtil.sendMessage(player, line.replaceText(alreadyOptimized)));
}
} catch (NumberFormatException e) {
VillagerOptimizer.getLang(player.locale()).command_radius_invalid.forEach(player::sendMessage);
VillagerOptimizer.getLang(player.locale()).command_radius_invalid
.forEach(line -> KyoriUtil.sendMessage(player, line));
}
return true;

View File

@ -7,6 +7,7 @@ import me.xginko.villageroptimizer.commands.VillagerOptimizerCommand;
import me.xginko.villageroptimizer.enums.OptimizationType;
import me.xginko.villageroptimizer.enums.permissions.Commands;
import me.xginko.villageroptimizer.events.VillagerUnoptimizeEvent;
import me.xginko.villageroptimizer.utils.KyoriUtil;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextReplacementConfig;
import net.kyori.adventure.text.format.NamedTextColor;
@ -27,7 +28,8 @@ public class UnOptVillagersRadius implements VillagerOptimizerCommand {
private final int max_radius;
public UnOptVillagersRadius() {
this.max_radius = VillagerOptimizer.getConfiguration().getInt("optimization-methods.commands.unoptimizevillagers.max-block-radius", 100);
this.max_radius = VillagerOptimizer.getConfiguration()
.getInt("optimization-methods.commands.unoptimizevillagers.max-block-radius", 100);
}
@Override
@ -43,18 +45,21 @@ public class UnOptVillagersRadius implements VillagerOptimizerCommand {
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
if (!sender.hasPermission(Commands.UNOPTIMIZE_RADIUS.get())) {
sender.sendMessage(VillagerOptimizer.getLang(sender).no_permission);
KyoriUtil.sendMessage(sender, VillagerOptimizer.getLang(sender).no_permission);
return true;
}
if (!(sender instanceof Player player)) {
sender.sendMessage(Component.text("This command can only be executed by a player.")
if (!(sender instanceof Player)) {
KyoriUtil.sendMessage(sender, Component.text("This command can only be executed by a player.")
.color(NamedTextColor.RED).decorate(TextDecoration.BOLD));
return true;
}
Player player = (Player) sender;
if (args.length != 1) {
VillagerOptimizer.getLang(player.locale()).command_specify_radius.forEach(player::sendMessage);
VillagerOptimizer.getLang(player.locale()).command_specify_radius
.forEach(line -> KyoriUtil.sendMessage(sender, line));
return true;
}
@ -64,7 +69,8 @@ public class UnOptVillagersRadius implements VillagerOptimizerCommand {
final int safeRadius = (int) Math.sqrt(specifiedRadius * specifiedRadius);
if (safeRadius == 0) {
VillagerOptimizer.getLang(player.locale()).command_radius_invalid.forEach(player::sendMessage);
VillagerOptimizer.getLang(player.locale()).command_radius_invalid
.forEach(line -> KyoriUtil.sendMessage(sender, line));
return true;
}
@ -73,7 +79,8 @@ public class UnOptVillagersRadius implements VillagerOptimizerCommand {
.matchLiteral("%distance%")
.replacement(Integer.toString(max_radius))
.build();
VillagerOptimizer.getLang(player.locale()).command_radius_limit_exceed.forEach(line -> player.sendMessage(line.replaceText(limit)));
VillagerOptimizer.getLang(player.locale()).command_radius_limit_exceed
.forEach(line -> KyoriUtil.sendMessage(player, line.replaceText(limit)));
return true;
}
@ -102,7 +109,8 @@ public class UnOptVillagersRadius implements VillagerOptimizerCommand {
.matchLiteral("%radius%")
.replacement(Integer.toString(safeRadius))
.build();
VillagerOptimizer.getLang(player.locale()).command_no_villagers_nearby.forEach(line -> player.sendMessage(line.replaceText(radius)));
VillagerOptimizer.getLang(player.locale()).command_no_villagers_nearby
.forEach(line -> KyoriUtil.sendMessage(player, line.replaceText(radius)));
} else {
final TextReplacementConfig success_amount = TextReplacementConfig.builder()
.matchLiteral("%amount%")
@ -112,13 +120,12 @@ public class UnOptVillagersRadius implements VillagerOptimizerCommand {
.matchLiteral("%radius%")
.replacement(Integer.toString(safeRadius))
.build();
VillagerOptimizer.getLang(player.locale()).command_unoptimize_success.forEach(line -> player.sendMessage(line
.replaceText(success_amount)
.replaceText(radius)
));
VillagerOptimizer.getLang(player.locale()).command_unoptimize_success
.forEach(line -> KyoriUtil.sendMessage(player, line.replaceText(success_amount).replaceText(radius)));
}
} catch (NumberFormatException e) {
VillagerOptimizer.getLang(player.locale()).command_radius_invalid.forEach(player::sendMessage);
VillagerOptimizer.getLang(player.locale()).command_radius_invalid
.forEach(line -> KyoriUtil.sendMessage(player, line));
}
return true;

View File

@ -7,13 +7,16 @@ import me.xginko.villageroptimizer.commands.villageroptimizer.subcommands.Disabl
import me.xginko.villageroptimizer.commands.villageroptimizer.subcommands.ReloadSubCmd;
import me.xginko.villageroptimizer.commands.villageroptimizer.subcommands.VersionSubCmd;
import me.xginko.villageroptimizer.enums.permissions.Commands;
import me.xginko.villageroptimizer.utils.KyoriUtil;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class VillagerOptimizerCmd implements VillagerOptimizerCommand {
@ -21,8 +24,8 @@ public class VillagerOptimizerCmd implements VillagerOptimizerCommand {
private final List<String> tabCompleter;
public VillagerOptimizerCmd() {
subCommands = List.of(new ReloadSubCmd(), new VersionSubCmd(), new DisableSubCmd());
tabCompleter = subCommands.stream().map(SubCommand::getLabel).toList();
subCommands = Arrays.asList(new ReloadSubCmd(), new VersionSubCmd(), new DisableSubCmd());
tabCompleter = subCommands.stream().map(SubCommand::getLabel).collect(Collectors.toList());
}
@Override
@ -55,21 +58,21 @@ public class VillagerOptimizerCmd implements VillagerOptimizerCommand {
private void sendCommandOverview(CommandSender sender) {
if (!sender.hasPermission(Commands.RELOAD.get()) && !sender.hasPermission(Commands.VERSION.get())) return;
sender.sendMessage(Component.text("-----------------------------------------------------").color(NamedTextColor.GRAY));
sender.sendMessage(Component.text("VillagerOptimizer Commands").color(VillagerOptimizer.plugin_style.color()));
sender.sendMessage(Component.text("-----------------------------------------------------").color(NamedTextColor.GRAY));
subCommands.forEach(subCommand -> sender.sendMessage(
KyoriUtil.sendMessage(sender, Component.text("-----------------------------------------------------").color(NamedTextColor.GRAY));
KyoriUtil.sendMessage(sender, Component.text("VillagerOptimizer Commands").color(VillagerOptimizer.STYLE.color()));
KyoriUtil.sendMessage(sender, Component.text("-----------------------------------------------------").color(NamedTextColor.GRAY));
subCommands.forEach(subCommand -> KyoriUtil.sendMessage(sender,
subCommand.getSyntax().append(Component.text(" - ").color(NamedTextColor.DARK_GRAY)).append(subCommand.getDescription())));
sender.sendMessage(
Component.text("/optimizevillagers <blockradius>").color(VillagerOptimizer.plugin_style.color())
KyoriUtil.sendMessage(sender,
Component.text("/optimizevillagers <blockradius>").color(VillagerOptimizer.STYLE.color())
.append(Component.text(" - ").color(NamedTextColor.DARK_GRAY))
.append(Component.text("Optimize villagers in a radius").color(NamedTextColor.GRAY))
);
sender.sendMessage(
Component.text("/unoptmizevillagers <blockradius>").color(VillagerOptimizer.plugin_style.color())
KyoriUtil.sendMessage(sender,
Component.text("/unoptmizevillagers <blockradius>").color(VillagerOptimizer.STYLE.color())
.append(Component.text(" - ").color(NamedTextColor.DARK_GRAY))
.append(Component.text("Unoptimize villagers in a radius").color(NamedTextColor.GRAY))
);
sender.sendMessage(Component.text("-----------------------------------------------------").color(NamedTextColor.GRAY));
KyoriUtil.sendMessage(sender, Component.text("-----------------------------------------------------").color(NamedTextColor.GRAY));
}
}

View File

@ -4,6 +4,7 @@ import me.xginko.villageroptimizer.VillagerOptimizer;
import me.xginko.villageroptimizer.commands.SubCommand;
import me.xginko.villageroptimizer.enums.permissions.Commands;
import me.xginko.villageroptimizer.modules.VillagerOptimizerModule;
import me.xginko.villageroptimizer.utils.KyoriUtil;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
@ -23,21 +24,21 @@ public class DisableSubCmd extends SubCommand {
@Override
public TextComponent getSyntax() {
return Component.text("/villageroptimizer disable").color(VillagerOptimizer.plugin_style.color());
return Component.text("/villageroptimizer disable").color(VillagerOptimizer.STYLE.color());
}
@Override
public void perform(CommandSender sender, String[] args) {
if (!sender.hasPermission(Commands.DISABLE.get())) {
sender.sendMessage(VillagerOptimizer.getLang(sender).no_permission);
KyoriUtil.sendMessage(sender, VillagerOptimizer.getLang(sender).no_permission);
return;
}
sender.sendMessage(Component.text("Disabling VillagerOptimizer...").color(NamedTextColor.RED));
KyoriUtil.sendMessage(sender, Component.text("Disabling VillagerOptimizer...").color(NamedTextColor.RED));
VillagerOptimizerModule.modules.forEach(VillagerOptimizerModule::disable);
VillagerOptimizerModule.modules.clear();
VillagerOptimizer.getCache().cacheMap().clear();
sender.sendMessage(Component.text("Disabled all plugin listeners and tasks.").color(NamedTextColor.GREEN));
sender.sendMessage(Component.text("You can enable the plugin again using the reload command.").color(NamedTextColor.YELLOW));
KyoriUtil.sendMessage(sender, Component.text("Disabled all plugin listeners and tasks.").color(NamedTextColor.GREEN));
KyoriUtil.sendMessage(sender, Component.text("You can enable the plugin again using the reload command.").color(NamedTextColor.YELLOW));
}
}

View File

@ -3,6 +3,7 @@ package me.xginko.villageroptimizer.commands.villageroptimizer.subcommands;
import me.xginko.villageroptimizer.VillagerOptimizer;
import me.xginko.villageroptimizer.commands.SubCommand;
import me.xginko.villageroptimizer.enums.permissions.Commands;
import me.xginko.villageroptimizer.utils.KyoriUtil;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
@ -22,20 +23,20 @@ public class ReloadSubCmd extends SubCommand {
@Override
public TextComponent getSyntax() {
return Component.text("/villageroptimizer reload").color(VillagerOptimizer.plugin_style.color());
return Component.text("/villageroptimizer reload").color(VillagerOptimizer.STYLE.color());
}
@Override
public void perform(CommandSender sender, String[] args) {
if (!sender.hasPermission(Commands.RELOAD.get())) {
sender.sendMessage(VillagerOptimizer.getLang(sender).no_permission);
KyoriUtil.sendMessage(sender, VillagerOptimizer.getLang(sender).no_permission);
return;
}
sender.sendMessage(Component.text("Reloading VillagerOptimizer...").color(NamedTextColor.WHITE));
KyoriUtil.sendMessage(sender, Component.text("Reloading VillagerOptimizer...").color(NamedTextColor.WHITE));
VillagerOptimizer.getFoliaLib().getImpl().runNextTick(reload -> { // Reload in sync with the server
VillagerOptimizer.getInstance().reloadPlugin();
sender.sendMessage(Component.text("Reload complete.").color(NamedTextColor.GREEN));
KyoriUtil.sendMessage(sender, Component.text("Reload complete.").color(NamedTextColor.GREEN));
});
}
}

View File

@ -4,6 +4,7 @@ import io.papermc.paper.plugin.configuration.PluginMeta;
import me.xginko.villageroptimizer.VillagerOptimizer;
import me.xginko.villageroptimizer.commands.SubCommand;
import me.xginko.villageroptimizer.enums.permissions.Commands;
import me.xginko.villageroptimizer.utils.KyoriUtil;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.event.ClickEvent;
@ -25,14 +26,14 @@ public class VersionSubCmd extends SubCommand {
@Override
public TextComponent getSyntax() {
return Component.text("/villageroptimizer version").color(VillagerOptimizer.plugin_style.color());
return Component.text("/villageroptimizer version").color(VillagerOptimizer.STYLE.color());
}
@Override
@SuppressWarnings({"deprecation", "UnstableApiUsage"})
public void perform(CommandSender sender, String[] args) {
if (!sender.hasPermission(Commands.VERSION.get())) {
sender.sendMessage(VillagerOptimizer.getLang(sender).no_permission);
KyoriUtil.sendMessage(sender, VillagerOptimizer.getLang(sender).no_permission);
return;
}
@ -52,10 +53,10 @@ public class VersionSubCmd extends SubCommand {
author = pluginYML.getAuthors().get(0);
}
sender.sendMessage(Component.newline()
KyoriUtil.sendMessage(sender, Component.newline()
.append(
Component.text(name + " " + version)
.style(VillagerOptimizer.plugin_style)
.style(VillagerOptimizer.STYLE)
.clickEvent(ClickEvent.openUrl(website))
)
.append(Component.text(" by ").color(NamedTextColor.GRAY))

View File

@ -33,16 +33,16 @@ public class Config {
"If set to true, will display messages based on client language");
this.cache_keep_time_seconds = getInt("general.cache-keep-time-seconds", 30,
"The amount of time in seconds a villager will be kept in the plugin's cache.");
this.support_other_plugins = getBoolean("general.support-avl-villagers", false, """
Enable if you have previously used AntiVillagerLag (https://www.spigotmc.org/resources/antivillagerlag.102949/).\s
Tries to read pre-existing info like optimization state so players don't need to reoptimize their villagers.""");
this.support_other_plugins = getBoolean("general.support-avl-villagers", false,
"Enable if you have previously used AntiVillagerLag (https://www.spigotmc.org/resources/antivillagerlag.102949/).\n" +
"Tries to read pre-existing info like optimization state so players don't need to reoptimize their villagers.");
}
public void saveConfig() {
try {
this.config.save();
} catch (Exception e) {
VillagerOptimizer.getLog().error("Failed to save config file! - " + e.getLocalizedMessage());
} catch (Throwable throwable) {
VillagerOptimizer.getLog().error("Failed to save config file!", throwable);
}
}
@ -51,11 +51,10 @@ public class Config {
this.createTitledSection("General", "general");
this.createTitledSection("Optimization", "optimization-methods");
this.config.addDefault("optimization-methods.commands.unoptimizevillagers", null);
this.config.addComment("optimization-methods.commands", """
If you want to disable commands, negate the following permissions:\s
villageroptimizer.cmd.optimize\s
villageroptimizer.cmd.unoptimize
""");
this.config.addComment("optimization-methods.commands",
"If you want to disable commands, negate the following permissions:\n" +
"villageroptimizer.cmd.optimize\n" +
"villageroptimizer.cmd.unoptimize");
this.config.addDefault("optimization-methods.nametag-optimization.enable", true);
this.createTitledSection("Villager Chunk Limit", "villager-chunk-limit");
this.createTitledSection("Gameplay", "gameplay");

View File

@ -7,7 +7,9 @@ import net.kyori.adventure.text.minimessage.MiniMessage;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class LanguageCache {
@ -28,62 +30,62 @@ public class LanguageCache {
File parent = langYML.getParentFile();
if (!parent.exists() && !parent.mkdir())
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
// 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);
// Finally load the lang file with configmaster
// Finally, load the lang file with configmaster
this.lang = ConfigFile.loadConfig(langYML);
// General
this.no_permission = getTranslation("messages.no-permission",
"<red>You don't have permission to use this command.");
this.trades_restocked = getListTranslation("messages.trades-restocked",
List.of("<green>All trades have been restocked! Next restock in %time%"));
"<green>All trades have been restocked! Next restock in %time%");
this.optimize_for_trading = getListTranslation("messages.optimize-to-trade",
List.of("<red>You need to optimize this villager before you can trade with it."));
"<red>You need to optimize this villager before you can trade with it.");
this.villager_leveling_up = getListTranslation("messages.villager-leveling-up",
List.of("<yellow>Villager is currently leveling up! You can use the villager again in %time%."));
"<yellow>Villager is currently leveling up! You can use the villager again in %time%.");
// Nametag
this.nametag_optimize_success = getListTranslation("messages.nametag.optimize-success",
List.of("<green>Successfully optimized villager by using a nametag."));
"<green>Successfully optimized villager by using a nametag.");
this.nametag_on_optimize_cooldown = getListTranslation("messages.nametag.optimize-on-cooldown",
List.of("<gray>You need to wait %time% until you can optimize this villager again."));
"<gray>You need to wait %time% until you can optimize this villager again.");
this.nametag_unoptimize_success = getListTranslation("messages.nametag.unoptimize-success",
List.of("<green>Successfully unoptimized villager by using a nametag."));
"<green>Successfully unoptimized villager by using a nametag.");
// Block
this.block_optimize_success = getListTranslation("messages.block.optimize-success",
List.of("<green>%villagertype% villager successfully optimized using block %blocktype%."));
"<green>%villagertype% villager successfully optimized using block %blocktype%.");
this.block_on_optimize_cooldown = getListTranslation("messages.block.optimize-on-cooldown",
List.of("<gray>You need to wait %time% until you can optimize this villager again."));
"<gray>You need to wait %time% until you can optimize this villager again.");
this.block_unoptimize_success = getListTranslation("messages.block.unoptimize-success",
List.of("<green>Successfully unoptimized %villagertype% villager by removing %blocktype%."));
"<green>Successfully unoptimized %villagertype% villager by removing %blocktype%.");
// Workstation
this.workstation_optimize_success = getListTranslation("messages.workstation.optimize-success",
List.of("<green>%villagertype% villager successfully optimized using workstation %workstation%."));
"<green>%villagertype% villager successfully optimized using workstation %workstation%.");
this.workstation_on_optimize_cooldown = getListTranslation("messages.workstation.optimize-on-cooldown",
List.of("<gray>You need to wait %time% until you can optimize this villager again."));
"<gray>You need to wait %time% until you can optimize this villager again.");
this.workstation_unoptimize_success = getListTranslation("messages.workstation.unoptimize-success",
List.of("<green>Successfully unoptimized %villagertype% villager by removing workstation block %workstation%."));
"<green>Successfully unoptimized %villagertype% villager by removing workstation block %workstation%.");
// Command
this.command_optimize_success = getListTranslation("messages.command.optimize-success",
List.of("<green>Successfully optimized %amount% villager(s) in a radius of %radius% blocks."));
"<green>Successfully optimized %amount% villager(s) in a radius of %radius% blocks.");
this.command_radius_limit_exceed = getListTranslation("messages.command.radius-limit-exceed",
List.of("<red>The radius you entered exceeds the limit of %distance% blocks."));
"<red>The radius you entered exceeds the limit of %distance% blocks.");
this.command_optimize_fail = getListTranslation("messages.command.optimize-fail",
List.of("<gray>%amount% villagers couldn't be optimized because they have recently been optimized."));
"<gray>%amount% villagers couldn't be optimized because they have recently been optimized.");
this.command_unoptimize_success = getListTranslation("messages.command.unoptimize-success",
List.of("<green>Successfully unoptimized %amount% villager(s) in a radius of %radius% blocks."));
"<green>Successfully unoptimized %amount% villager(s) in a radius of %radius% blocks.");
this.command_specify_radius = getListTranslation("messages.command.specify-radius",
List.of("<red>Please specify a radius."));
"<red>Please specify a radius.");
this.command_radius_invalid = getListTranslation("messages.command.radius-invalid",
List.of("<red>The radius you entered is not a valid number. Try again."));
"<red>The radius you entered is not a valid number. Try again.");
this.command_no_villagers_nearby = getListTranslation("messages.command.no-villagers-nearby",
List.of("<gray>Couldn't find any employed villagers within a radius of %radius%."));
"<gray>Couldn't find any employed villagers within a radius of %radius%.");
try {
this.lang.save();
} catch (Exception e) {
VillagerOptimizer.getLog().error("Failed to save language file: "+ langYML.getName() +" - " + e.getLocalizedMessage());
} catch (Throwable throwable) {
VillagerOptimizer.getLog().error("Failed to save language file: "+ langYML.getName(), throwable);
}
}
@ -92,18 +94,8 @@ public class LanguageCache {
return MiniMessage.miniMessage().deserialize(this.lang.getString(path, defaultTranslation));
}
public @NotNull Component getTranslation(@NotNull String path, @NotNull String defaultTranslation, @NotNull String comment) {
this.lang.addDefault(path, defaultTranslation, comment);
return MiniMessage.miniMessage().deserialize(this.lang.getString(path, defaultTranslation));
}
public @NotNull List<Component> getListTranslation(@NotNull String path, @NotNull List<String> defaultTranslation) {
this.lang.addDefault(path, defaultTranslation);
return this.lang.getStringList(path).stream().map(MiniMessage.miniMessage()::deserialize).toList();
}
public @NotNull List<Component> getListTranslation(@NotNull String path, @NotNull List<String> defaultTranslation, @NotNull String comment) {
this.lang.addDefault(path, defaultTranslation, comment);
return this.lang.getStringList(path).stream().map(MiniMessage.miniMessage()::deserialize).toList();
public @NotNull List<Component> getListTranslation(@NotNull String path, @NotNull String... defaultTranslation) {
this.lang.addDefault(path, Arrays.asList(defaultTranslation));
return this.lang.getStringList(path).stream().map(MiniMessage.miniMessage()::deserialize).collect(Collectors.toList());
}
}

View File

@ -43,7 +43,6 @@ public class Keyring {
* @return a {@link NamespacedKey} that can be used to test for and read data stored by plugins
* from a {@link PersistentDataContainer}
*/
@SuppressWarnings("deprecation")
public static NamespacedKey getKey(@NotNull String pluginName, @NotNull String key) {
return new NamespacedKey(pluginName.toLowerCase(Locale.ROOT), key);
}
@ -58,7 +57,6 @@ public class Keyring {
private final @NotNull NamespacedKey key;
@SuppressWarnings("deprecation")
VillagerOptimizer(@NotNull String key) {
this.key = new NamespacedKey(Spaces.VillagerOptimizer.namespace(), key);
}
@ -80,7 +78,6 @@ public class Keyring {
private final @NotNull NamespacedKey key;
@SuppressWarnings("deprecation")
AntiVillagerLag(@NotNull String avlKey) {
this.key = new NamespacedKey(Spaces.AntiVillagerLag.namespace(), avlKey);
}

View File

@ -17,7 +17,12 @@ public class VillagerOptimizeEvent extends Event implements Cancellable {
private final @Nullable Player whoOptimised;
private boolean isCancelled = false;
public VillagerOptimizeEvent(@NotNull WrappedVillager wrappedVillager, @NotNull OptimizationType optimizationType, @Nullable Player whoOptimised, boolean isAsync) throws IllegalArgumentException {
public VillagerOptimizeEvent(
@NotNull WrappedVillager wrappedVillager,
@NotNull OptimizationType optimizationType,
@Nullable Player whoOptimised,
boolean isAsync
) throws IllegalArgumentException {
super(isAsync);
this.wrappedVillager = wrappedVillager;
this.whoOptimised = whoOptimised;
@ -28,7 +33,11 @@ public class VillagerOptimizeEvent extends Event implements Cancellable {
}
}
public VillagerOptimizeEvent(@NotNull WrappedVillager wrappedVillager, @NotNull OptimizationType optimizationType, @Nullable Player whoOptimised) throws IllegalArgumentException {
public VillagerOptimizeEvent(
@NotNull WrappedVillager wrappedVillager,
@NotNull OptimizationType optimizationType,
@Nullable Player whoOptimised
) throws IllegalArgumentException {
this.wrappedVillager = wrappedVillager;
this.whoOptimised = whoOptimised;
if (optimizationType.equals(OptimizationType.NONE)) {

View File

@ -17,14 +17,23 @@ public class VillagerUnoptimizeEvent extends Event implements Cancellable {
private final @Nullable Player whoUnoptimized;
private boolean isCancelled = false;
public VillagerUnoptimizeEvent(@NotNull WrappedVillager wrappedVillager, @Nullable Player whoUnoptimized, @NotNull OptimizationType unOptimizeType, boolean isAsync) {
public VillagerUnoptimizeEvent(
@NotNull WrappedVillager wrappedVillager,
@Nullable Player whoUnoptimized,
@NotNull OptimizationType unOptimizeType,
boolean isAsync
) {
super(isAsync);
this.wrappedVillager = wrappedVillager;
this.whoUnoptimized = whoUnoptimized;
this.unOptimizeType = unOptimizeType;
}
public VillagerUnoptimizeEvent(@NotNull WrappedVillager wrappedVillager, @Nullable Player whoUnoptimized, @NotNull OptimizationType unOptimizeType) {
public VillagerUnoptimizeEvent(
@NotNull WrappedVillager wrappedVillager,
@Nullable Player whoUnoptimized,
@NotNull OptimizationType unOptimizeType
) {
this.wrappedVillager = wrappedVillager;
this.whoUnoptimized = whoUnoptimized;
this.unOptimizeType = unOptimizeType;

View File

@ -1,29 +0,0 @@
package me.xginko.villageroptimizer.models;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
public final class ExpiringSet<E> {
private final Cache<E, Object> cache;
private static final Object PRESENT = new Object(); // Dummy value to associate with an Object in the backing Cache
public ExpiringSet(long duration, TimeUnit unit) {
this.cache = Caffeine.newBuilder().expireAfterWrite(duration, unit).build();
}
public ExpiringSet(Duration duration) {
this.cache = Caffeine.newBuilder().expireAfterWrite(duration).build();
}
public void add(E item) {
this.cache.put(item, PRESENT);
}
public boolean contains(E item) {
return this.cache.getIfPresent(item) != null;
}
}

View File

@ -21,10 +21,8 @@ import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.*;
import java.util.stream.Collectors;
public class VillagerChunkLimit implements VillagerOptimizerModule, Listener {
@ -41,23 +39,23 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener {
this.scheduler = VillagerOptimizer.getFoliaLib().getImpl();
this.villagerCache = VillagerOptimizer.getCache();
Config config = VillagerOptimizer.getConfiguration();
config.master().addComment("villager-chunk-limit.enable", """
Checks chunks for too many villagers and removes excess villagers based on priority.""");
this.check_period = config.getInt("villager-chunk-limit.check-period-in-ticks", 600, """
Check all loaded chunks every X ticks. 1 second = 20 ticks\s
A shorter delay in between checks is more efficient but is also more resource intense.\s
A larger delay is less resource intense but could become inefficient.""");
config.master().addComment("villager-chunk-limit.enable",
"Checks chunks for too many villagers and removes excess villagers based on priority.");
this.check_period = config.getInt("villager-chunk-limit.check-period-in-ticks", 600,
"Check all loaded chunks every X ticks. 1 second = 20 ticks\n" +
"A shorter delay in between checks is more efficient but is also more resource intense.\n" +
"A larger delay is less resource intense but could become inefficient.");
this.skip_unloaded_entity_chunks = config.getBoolean("villager-chunk-limit.skip-if-chunk-has-not-loaded-entities", true,
"Does not check chunks that don't have their entities loaded.");
this.log_enabled = config.getBoolean("villager-chunk-limit.log-removals", true);
this.non_optimized_max_per_chunk = config.getInt("villager-chunk-limit.unoptimized.max-per-chunk", 20,
"The maximum amount of unoptimized villagers per chunk.");
this.non_optimized_removal_priority = config.getList("villager-chunk-limit.unoptimized.removal-priority", List.of(
this.non_optimized_removal_priority = config.getList("villager-chunk-limit.unoptimized.removal-priority", Arrays.asList(
"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.\s
Use enums from https://jd.papermc.io/paper/1.20/org/bukkit/entity/Villager.Profession.html"""
),
"Professions that are in the top of the list are going to be scheduled for removal first.\n" +
"Use enums from https://jd.papermc.io/paper/1.20/org/bukkit/entity/Villager.Profession.html"
).stream().map(configuredProfession -> {
try {
return Villager.Profession.valueOf(configuredProfession);
@ -67,10 +65,10 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener {
"https://jd.papermc.io/paper/1.20/org/bukkit/entity/Villager.Profession.html.");
return null;
}
}).filter(Objects::nonNull).toList();
}).filter(Objects::nonNull).collect(Collectors.toList());
this.optimized_max_per_chunk = config.getInt("villager-chunk-limit.optimized.max-per-chunk", 60,
"The maximum amount of optimized villagers per chunk.");
this.optimized_removal_priority = config.getList("villager-chunk-limit.optimized.removal-priority", List.of(
this.optimized_removal_priority = config.getList("villager-chunk-limit.optimized.removal-priority", Arrays.asList(
"NONE", "NITWIT", "SHEPHERD", "FISHERMAN", "BUTCHER", "CARTOGRAPHER", "LEATHERWORKER",
"FLETCHER", "MASON", "FARMER", "ARMORER", "TOOLSMITH", "WEAPONSMITH", "CLERIC", "LIBRARIAN"
)).stream().map(configuredProfession -> {
@ -82,7 +80,7 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener {
"https://jd.papermc.io/paper/1.20/org/bukkit/entity/Villager.Profession.html.");
return null;
}
}).filter(Objects::nonNull).toList();
}).filter(Objects::nonNull).collect(Collectors.toList());
}
@Override
@ -159,7 +157,7 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener {
if (log_enabled) {
VillagerOptimizer.getLog().info(Component.text(
"Removed unoptimized villager with profession '" + villager.getProfession().name() + "' at " +
CommonUtil.formatLocation(villager.getLocation())).color(VillagerOptimizer.plugin_style.color()));
CommonUtil.formatLocation(villager.getLocation())).color(VillagerOptimizer.STYLE.color()));
}
});
}
@ -182,7 +180,7 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener {
if (log_enabled) {
VillagerOptimizer.getLog().info(Component.text("Removed optimized villager with profession '" +
villager.getProfession().name() + "' at " +
CommonUtil.formatLocation(villager.getLocation())).color(VillagerOptimizer.plugin_style.color()));
CommonUtil.formatLocation(villager.getLocation())).color(VillagerOptimizer.STYLE.color()));
}
});
}

View File

@ -31,8 +31,8 @@ public class EnableLeashingVillagers implements VillagerOptimizerModule, Listene
this.scheduler = VillagerOptimizer.getFoliaLib().getImpl();
this.villagerCache = VillagerOptimizer.getCache();
Config config = VillagerOptimizer.getConfiguration();
config.master().addComment("gameplay.villagers-can-be-leashed.enable", """
Enable leashing of villagers, enabling players to easily move villagers to where they want them to be.""");
config.master().addComment("gameplay.villagers-can-be-leashed.enable",
"Enable leashing of villagers, enabling players to easily move villagers to where they want them to be.");
this.only_optimized = config.getBoolean("gameplay.villagers-can-be-leashed.only-optimized", false,
"If set to true, only optimized villagers can be leashed.");
this.log_enabled = config.getBoolean("gameplay.villagers-can-be-leashed.log", false);
@ -86,7 +86,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.plugin_style.color()));
CommonUtil.formatLocation(villager.getLocation())).color(VillagerOptimizer.STYLE.color()));
}
});
}

View File

@ -7,6 +7,7 @@ import me.xginko.villageroptimizer.config.Config;
import me.xginko.villageroptimizer.WrappedVillager;
import me.xginko.villageroptimizer.modules.VillagerOptimizerModule;
import me.xginko.villageroptimizer.utils.CommonUtil;
import me.xginko.villageroptimizer.utils.KyoriUtil;
import net.kyori.adventure.text.TextReplacementConfig;
import org.bukkit.entity.Player;
import org.bukkit.entity.Villager;
@ -34,13 +35,13 @@ public class LevelOptimizedProfession implements VillagerOptimizerModule, Listen
this.scheduler = VillagerOptimizer.getFoliaLib().getImpl();
this.villagerCache = VillagerOptimizer.getCache();
Config config = VillagerOptimizer.getConfiguration();
config.master().addComment("gameplay.level-optimized-profession", """
This is needed to allow optimized villagers to level up.\s
Temporarily enables the villagers AI to allow it to level up and then disables it again.""");
config.master().addComment("gameplay.level-optimized-profession",
"This is needed to allow optimized villagers to level up.\n" +
"Temporarily enables the villagers AI to allow it to level up and then disables it again.");
this.cooldown_millis = TimeUnit.SECONDS.toMillis(
config.getInt("gameplay.level-optimized-profession.level-check-cooldown-seconds", 5, """
Cooldown in seconds until the level of a villager will be checked and updated again.\s
Recommended to leave as is."""));
config.getInt("gameplay.level-optimized-profession.level-check-cooldown-seconds", 5,
"Cooldown in seconds until the level of a villager will be checked and updated again.\n" +
"Recommended to leave as is."));
this.notify_player = config.getBoolean("gameplay.level-optimized-profession.notify-player", true,
"Tell players to wait when a villager is leveling up.");
}
@ -61,12 +62,13 @@ public class LevelOptimizedProfession implements VillagerOptimizerModule, Listen
return true;
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onTradeScreenClose(InventoryCloseEvent event) {
if (
event.getInventory().getType().equals(InventoryType.MERCHANT)
&& event.getInventory().getHolder() instanceof Villager villager
&& event.getInventory().getHolder() instanceof Villager
) {
Villager villager = (Villager) event.getInventory().getHolder();
WrappedVillager wVillager = villagerCache.getOrAdd(villager);
if (!wVillager.isOptimized()) return;
@ -89,7 +91,8 @@ public class LevelOptimizedProfession implements VillagerOptimizerModule, Listen
.matchLiteral("%time%")
.replacement(CommonUtil.formatDuration(Duration.ofMillis(wVillager.getLevelCooldownMillis(cooldown_millis))))
.build();
VillagerOptimizer.getLang(player.locale()).villager_leveling_up.forEach(line -> player.sendMessage(line.replaceText(timeLeft)));
VillagerOptimizer.getLang(player.locale()).villager_leveling_up
.forEach(line -> KyoriUtil.sendMessage(player, line.replaceText(timeLeft)));
}
}
}

View File

@ -27,16 +27,16 @@ public class MakeVillagersSpawnAdult implements VillagerOptimizerModule, Listene
@Override
public boolean shouldEnable() {
return VillagerOptimizer.getConfiguration().getBoolean("gameplay.villagers-spawn-as-adults.enable", false, """
Spawned villagers will immediately be adults.\s
This is to save some more resources as players don't have to keep unoptimized\s
villagers loaded because they have to wait for them to turn into adults before they can\s
optimize them.""");
return VillagerOptimizer.getConfiguration().getBoolean("gameplay.villagers-spawn-as-adults.enable", false,
"Spawned villagers will immediately be adults.\n" +
"This is to save some more resources as players don't have to keep unoptimized\n" +
"villagers loaded because they have to wait for them to turn into adults before they can\n" +
"optimize them.");
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onVillagerSpawn(CreatureSpawnEvent event) {
if (event.getEntityType() == EntityType.VILLAGER) {
if (event.getEntityType().equals(EntityType.VILLAGER)) {
Villager villager = (Villager) event.getEntity();
if (!villager.isAdult()) villager.setAdult();
}

View File

@ -23,7 +23,7 @@ public class PreventOptimizedDamage implements VillagerOptimizerModule, Listener
private final VillagerCache villagerCache;
private final Set<EntityDamageEvent.DamageCause> damage_causes_to_cancel;
private final boolean cancelKnockback;
private final boolean cancel_knockback;
public PreventOptimizedDamage() {
shouldEnable();
@ -31,13 +31,13 @@ public class PreventOptimizedDamage implements VillagerOptimizerModule, Listener
Config config = VillagerOptimizer.getConfiguration();
config.master().addComment("gameplay.prevent-damage-to-optimized.enable",
"Configure what kind of damage you want to cancel for optimized villagers here.");
this.cancelKnockback = config.getBoolean("gameplay.prevent-damage-to-optimized.prevent-knockback-from-entity", true,
this.cancel_knockback = config.getBoolean("gameplay.prevent-damage-to-optimized.prevent-knockback-from-entity", true,
"Prevents optimized villagers from getting knocked back by an attacking entity");
this.damage_causes_to_cancel = config.getList("gameplay.prevent-damage-to-optimized.damage-causes-to-cancel",
Arrays.stream(EntityDamageEvent.DamageCause.values()).map(Enum::name).sorted().toList(), """
These are all current entries in the game. Remove what you do not need blocked.\s
If you want a description or need to add a previously removed type, refer to:\s
https://jd.papermc.io/paper/1.20/org/bukkit/event/entity/EntityDamageEvent.DamageCause.html"""
Arrays.stream(EntityDamageEvent.DamageCause.values()).map(Enum::name).sorted().collect(Collectors.toList()),
"These are all current entries in the game. Remove what you do not need blocked.\n" +
"If you want a description or need to add a previously removed type, refer to:\n" +
"https://jd.papermc.io/paper/1.20/org/bukkit/event/entity/EntityDamageEvent.DamageCause.html"
).stream().map(configuredDamageCause -> {
try {
return EntityDamageEvent.DamageCause.valueOf(configuredDamageCause);
@ -80,7 +80,7 @@ public class PreventOptimizedDamage implements VillagerOptimizerModule, Listener
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onKnockbackByEntity(EntityKnockbackByEntityEvent event) {
if (
cancelKnockback
cancel_knockback
&& event.getEntityType().equals(EntityType.VILLAGER)
&& villagerCache.getOrAdd((Villager) event.getEntity()).isOptimized()
) {

View File

@ -42,11 +42,10 @@ public class PreventOptimizedTargeting implements VillagerOptimizerModule, Liste
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onTarget(EntityTargetEvent event) {
// Yes, instanceof checks would look way more beautiful here but checking type is much faster
Entity target = event.getTarget();
if (
target != null
&& target.getType() == EntityType.VILLAGER
&& target.getType().equals(EntityType.VILLAGER)
&& villagerCache.getOrAdd((Villager) target).isOptimized()
) {
event.setTarget(null);
@ -59,7 +58,7 @@ public class PreventOptimizedTargeting implements VillagerOptimizerModule, Liste
Entity target = event.getTargetEntity();
if (
target != null
&& target.getType() == EntityType.VILLAGER
&& target.getType().equals(EntityType.VILLAGER)
&& villagerCache.getOrAdd((Villager) target).isOptimized()
) {
event.setCancelled(true);
@ -69,11 +68,11 @@ public class PreventOptimizedTargeting implements VillagerOptimizerModule, Liste
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onEntityAttackVillager(EntityDamageByEntityEvent event) {
if (
event.getEntityType() == EntityType.VILLAGER
&& event.getDamager() instanceof Mob attacker
event.getEntityType().equals(EntityType.VILLAGER)
&& event.getDamager() instanceof Mob
&& villagerCache.getOrAdd((Villager) event.getEntity()).isOptimized()
) {
attacker.setTarget(null);
((Mob) event.getDamager()).setTarget(null);
}
}
}

View File

@ -5,6 +5,7 @@ import me.xginko.villageroptimizer.VillagerCache;
import me.xginko.villageroptimizer.config.Config;
import me.xginko.villageroptimizer.enums.permissions.Bypass;
import me.xginko.villageroptimizer.modules.VillagerOptimizerModule;
import me.xginko.villageroptimizer.utils.KyoriUtil;
import org.bukkit.entity.Player;
import org.bukkit.entity.Villager;
import org.bukkit.event.EventHandler;
@ -24,10 +25,10 @@ public class PreventUnoptimizedTrading implements VillagerOptimizerModule, Liste
shouldEnable();
this.villagerCache = VillagerOptimizer.getCache();
Config config = VillagerOptimizer.getConfiguration();
config.master().addComment("gameplay.prevent-trading-with-unoptimized.enable", """
Will prevent players from selecting and using trades of unoptimized villagers.\s
Use this if you have a lot of villagers and therefore want to force your players to optimize them.\s
Inventories can still be opened so players can move villagers around.""");
config.master().addComment("gameplay.prevent-trading-with-unoptimized.enable",
"Will prevent players from selecting and using trades of unoptimized villagers.\n" +
"Use this if you have a lot of villagers and therefore want to force your players to optimize them.\n" +
"Inventories can still be opened so players can move villagers around.");
this.notify_player = config.getBoolean("gameplay.prevent-trading-with-unoptimized.notify-player", true,
"Sends players a message when they try to trade with an unoptimized villager.");
}
@ -48,36 +49,38 @@ public class PreventUnoptimizedTrading implements VillagerOptimizerModule, Liste
return VillagerOptimizer.getConfiguration().getBoolean("gameplay.prevent-trading-with-unoptimized.enable", false);
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onTradeOpen(TradeSelectEvent event) {
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 (
event.getInventory().getType().equals(InventoryType.MERCHANT)
&& event.getInventory().getHolder() instanceof Villager villager
&& !villagerCache.getOrAdd(villager).isOptimized()
) {
Villager villager = (Villager) event.getInventory().getHolder();
if (!villagerCache.getOrAdd(villager).isOptimized()) {
event.setCancelled(true);
if (notify_player) {
Player player = (Player) event.getWhoClicked();
VillagerOptimizer.getLang(player.locale()).optimize_for_trading.forEach(player::sendMessage);
VillagerOptimizer.getLang(player.locale()).optimize_for_trading
.forEach(line -> KyoriUtil.sendMessage(player, line));
}
}
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onInventoryClick(InventoryClickEvent event) {
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 (
event.getInventory().getType().equals(InventoryType.MERCHANT)
&& event.getInventory().getHolder() instanceof Villager villager
&& !villagerCache.getOrAdd(villager).isOptimized()
) {
Villager villager = (Villager) event.getInventory().getHolder();
if (!villagerCache.getOrAdd(villager).isOptimized()) {
event.setCancelled(true);
if (notify_player) {
Player player = (Player) event.getWhoClicked();
VillagerOptimizer.getLang(player.locale()).optimize_for_trading.forEach(player::sendMessage);
VillagerOptimizer.getLang(player.locale()).optimize_for_trading
.forEach(line -> KyoriUtil.sendMessage(player, line));
}
}
}

View File

@ -25,10 +25,11 @@ public class RenameOptimizedVillagers implements VillagerOptimizerModule, Listen
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.\s
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",
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.");

View File

@ -7,9 +7,9 @@ import me.xginko.villageroptimizer.config.Config;
import me.xginko.villageroptimizer.enums.permissions.Bypass;
import me.xginko.villageroptimizer.modules.VillagerOptimizerModule;
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 org.bukkit.Location;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.entity.Villager;
@ -31,9 +31,9 @@ public class RestockOptimizedTrades implements VillagerOptimizerModule, Listener
shouldEnable();
this.villagerCache = VillagerOptimizer.getCache();
Config config = VillagerOptimizer.getConfiguration();
config.master().addComment("gameplay.restock-optimized-trades", """
This is for automatic restocking of trades for optimized villagers. Optimized Villagers\s
don't have enough AI to restock their trades naturally, so this is here as a workaround.""");
config.master().addComment("gameplay.restock-optimized-trades",
"This is for automatic restocking of trades for optimized villagers. Optimized Villagers\n" +
"don't have enough AI to restock their trades naturally, so this is here as a workaround.");
this.restock_delay_millis = config.getInt("gameplay.restock-optimized-trades.delay-in-ticks", 1000,
"1 second = 20 ticks. There are 24.000 ticks in a single minecraft day.") * 50L;
this.notify_player = config.getBoolean("gameplay.restock-optimized-trades.notify-player", true,
@ -70,18 +70,19 @@ public class RestockOptimizedTrades implements VillagerOptimizerModule, Listener
if (wVillager.canRestock(restock_delay_millis) || player_bypassing) {
wVillager.restock();
wVillager.saveRestockTime();
if (notify_player && !player_bypassing) {
final TextReplacementConfig timeLeft = TextReplacementConfig.builder()
.matchLiteral("%time%")
.replacement(CommonUtil.formatDuration(Duration.ofMillis(wVillager.getRestockCooldownMillis(restock_delay_millis))))
.build();
VillagerOptimizer.getLang(player.locale()).trades_restocked.forEach(line -> player.sendMessage(line.replaceText(timeLeft)));
VillagerOptimizer.getLang(player.locale()).trades_restocked
.forEach(line -> KyoriUtil.sendMessage(player, line.replaceText(timeLeft)));
}
if (log_enabled) {
final Location location = wVillager.villager().getLocation();
VillagerOptimizer.getLog().info(Component.text("Restocked optimized villager at " +
"x=" + location.getBlockX() + ", y=" + location.getBlockY() + ", z=" + location.getBlockZ() +
", world=" + location.getWorld().getName()).style(VillagerOptimizer.plugin_style));
CommonUtil.formatLocation(wVillager.villager().getLocation())).style(VillagerOptimizer.STYLE));
}
}
}

View File

@ -11,6 +11,7 @@ import me.xginko.villageroptimizer.events.VillagerOptimizeEvent;
import me.xginko.villageroptimizer.events.VillagerUnoptimizeEvent;
import me.xginko.villageroptimizer.modules.VillagerOptimizerModule;
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 org.bukkit.Location;
@ -28,8 +29,8 @@ import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import java.time.Duration;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@ -47,10 +48,10 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener {
shouldEnable();
this.villagerCache = VillagerOptimizer.getCache();
Config config = VillagerOptimizer.getConfiguration();
config.master().addComment("optimization-methods.block-optimization.enable", """
When enabled, the closest villager standing near a configured block being placed will be optimized.\s
If a configured block is broken nearby, the closest villager will become unoptimized again.""");
this.blocks_that_disable = config.getList("optimization-methods.block-optimization.materials", List.of(
config.master().addComment("optimization-methods.block-optimization.enable",
"When enabled, the closest villager standing near a configured block being placed will be optimized.\n" +
"If a configured block is broken nearby, the closest villager will become unoptimized again.");
this.blocks_that_disable = config.getList("optimization-methods.block-optimization.materials", Arrays.asList(
"LAPIS_BLOCK", "GLOWSTONE", "IRON_BLOCK"
), "Values here need to be valid bukkit Material enums for your server version."
).stream().map(configuredMaterial -> {
@ -64,12 +65,12 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener {
}
}).filter(Objects::nonNull).collect(Collectors.toCollection(HashSet::new));
this.cooldown_millis = TimeUnit.SECONDS.toMillis(
config.getInt("optimization-methods.block-optimization.optimize-cooldown-seconds", 600, """
Cooldown in seconds until a villager can be optimized again by using specific blocks.\s
Here for configuration freedom. Recommended to leave as is to not enable any exploitable behavior."""));
this.search_radius = config.getDouble("optimization-methods.block-optimization.search-radius-in-blocks", 2.0, """
The radius in blocks a villager can be away from the player when he places an optimize block.\s
The closest unoptimized villager to the player will be optimized.""") / 2;
config.getInt("optimization-methods.block-optimization.optimize-cooldown-seconds", 600,
"Cooldown in seconds until a villager can be optimized again by using specific blocks.\n" +
"Here for configuration freedom. Recommended to leave as is to not enable any exploitable behavior."));
this.search_radius = config.getDouble("optimization-methods.block-optimization.search-radius-in-blocks", 2.0,
"The radius in blocks a villager can be away from the player when he places an optimize block.\n" +
"The closest unoptimized villager to the player will be optimized.") / 2;
this.only_while_sneaking = config.getBoolean("optimization-methods.block-optimization.only-when-sneaking", true,
"Only optimize/unoptimize by workstation when player is sneaking during place or break.");
this.notify_player = config.getBoolean("optimization-methods.block-optimization.notify-player", true,
@ -123,7 +124,13 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener {
if (closestOptimizableVillager == null) return;
if (closestOptimizableVillager.canOptimize(cooldown_millis) || player.hasPermission(Bypass.BLOCK_COOLDOWN.get())) {
VillagerOptimizeEvent optimizeEvent = new VillagerOptimizeEvent(closestOptimizableVillager, OptimizationType.BLOCK, player, event.isAsynchronous());
VillagerOptimizeEvent optimizeEvent = new VillagerOptimizeEvent(
closestOptimizableVillager,
OptimizationType.BLOCK,
player,
event.isAsynchronous()
);
if (!optimizeEvent.callEvent()) return;
closestOptimizableVillager.setOptimizationType(optimizeEvent.getOptimizationType());
@ -138,15 +145,13 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener {
.matchLiteral("%blocktype%")
.replacement(placed.getType().toString().toLowerCase())
.build();
VillagerOptimizer.getLang(player.locale()).block_optimize_success.forEach(line -> player.sendMessage(line
.replaceText(vilProfession)
.replaceText(placedMaterial)
));
VillagerOptimizer.getLang(player.locale()).block_optimize_success
.forEach(line -> KyoriUtil.sendMessage(player, line.replaceText(vilProfession).replaceText(placedMaterial)));
}
if (log_enabled) {
VillagerOptimizer.getLog().info(Component.text(player.getName() + " optimized villager by block at " +
CommonUtil.formatLocation(closestOptimizableVillager.villager().getLocation())).color(VillagerOptimizer.plugin_style.color()));
CommonUtil.formatLocation(closestOptimizableVillager.villager().getLocation())).color(VillagerOptimizer.STYLE.color()));
}
} else {
CommonUtil.shakeHead(closestOptimizableVillager.villager());
@ -155,7 +160,8 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener {
.matchLiteral("%time%")
.replacement(CommonUtil.formatDuration(Duration.ofMillis(closestOptimizableVillager.getOptimizeCooldownMillis(cooldown_millis))))
.build();
VillagerOptimizer.getLang(player.locale()).block_on_optimize_cooldown.forEach(line -> player.sendMessage(line.replaceText(timeLeft)));
VillagerOptimizer.getLang(player.locale()).block_on_optimize_cooldown
.forEach(line -> KyoriUtil.sendMessage(player, line.replaceText(timeLeft)));
}
}
}
@ -187,7 +193,13 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener {
if (closestOptimizedVillager == null) return;
VillagerUnoptimizeEvent unOptimizeEvent = new VillagerUnoptimizeEvent(closestOptimizedVillager, player, OptimizationType.BLOCK, event.isAsynchronous());
VillagerUnoptimizeEvent unOptimizeEvent = new VillagerUnoptimizeEvent(
closestOptimizedVillager,
player,
OptimizationType.BLOCK,
event.isAsynchronous()
);
if (!unOptimizeEvent.callEvent()) return;
closestOptimizedVillager.setOptimizationType(OptimizationType.NONE);
@ -201,15 +213,13 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener {
.matchLiteral("%blocktype%")
.replacement(broken.getType().toString().toLowerCase())
.build();
VillagerOptimizer.getLang(player.locale()).block_unoptimize_success.forEach(line -> player.sendMessage(line
.replaceText(vilProfession)
.replaceText(brokenMaterial)
));
VillagerOptimizer.getLang(player.locale()).block_unoptimize_success
.forEach(line -> KyoriUtil.sendMessage(player, line.replaceText(vilProfession).replaceText(brokenMaterial)));
}
if (log_enabled) {
VillagerOptimizer.getLog().info(Component.text(player.getName() + " unoptimized villager by block at " +
CommonUtil.formatLocation(closestOptimizedVillager.villager().getLocation())).color(VillagerOptimizer.plugin_style.color()));
CommonUtil.formatLocation(closestOptimizedVillager.villager().getLocation())).color(VillagerOptimizer.STYLE.color()));
}
}
}

View File

@ -11,6 +11,7 @@ import me.xginko.villageroptimizer.events.VillagerOptimizeEvent;
import me.xginko.villageroptimizer.events.VillagerUnoptimizeEvent;
import me.xginko.villageroptimizer.modules.VillagerOptimizerModule;
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;
@ -27,13 +28,15 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.time.Duration;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
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;
@ -41,19 +44,21 @@ 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", """
Enable optimization by naming villagers to one of the names configured below.\s
Nametag optimized villagers will be unoptimized again when they are renamed to something else.""");
this.nametags = config.getList("optimization-methods.nametag-optimization.names", List.of("Optimize", "DisableAI"),
config.master().addComment("optimization-methods.nametag-optimization.enable",
"Enable optimization by naming villagers to one of the names configured below.\n" +
"Nametag optimized villagers will be unoptimized again when they are renamed to something else.");
this.nametags = config.getList("optimization-methods.nametag-optimization.names", Arrays.asList("Optimize", "DisableAI"),
"Names are case insensitive, capital letters won't matter.")
.stream().map(String::toLowerCase).collect(Collectors.toCollection(HashSet::new));
this.consume_nametag = config.getBoolean("optimization-methods.nametag-optimization.nametags-get-consumed", true,
"Enable or disable consumption of the used nametag item.");
this.cooldown = config.getInt("optimization-methods.nametag-optimization.optimize-cooldown-seconds", 600, """
Cooldown in seconds until a villager can be optimized again using a nametag.\s
Here for configuration freedom. Recommended to leave as is to not enable any exploitable behavior.""") * 1000L;
this.cooldown = TimeUnit.SECONDS.toMillis(
config.getInt("optimization-methods.nametag-optimization.optimize-cooldown-seconds", 600,
"Cooldown in seconds until a villager can be optimized again using a nametag.\n" +
"Here for configuration freedom. Recommended to leave as is to not enable any exploitable behavior."));
this.notify_player = config.getBoolean("optimization-methods.nametag-optimization.notify-player", true,
"Sends players a message when they successfully optimized a villager.");
this.log_enabled = config.getBoolean("optimization-methods.nametag-optimization.log", false);
@ -82,20 +87,27 @@ public class OptimizeByNametag implements VillagerOptimizerModule, Listener {
if (!player.hasPermission(Optimize.NAMETAG.get())) return;
ItemStack usedItem = player.getInventory().getItem(event.getHand());
if (!usedItem.getType().equals(Material.NAME_TAG)) return;
if (usedItem != null && !usedItem.getType().equals(Material.NAME_TAG)) return;
if (!usedItem.hasItemMeta()) return;
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();
assert newVillagerName != null; // Legitimate since we checked for hasDisplayName()
final String name = PlainTextComponentSerializer.plainText().serialize(newVillagerName);
final String name = plainTextSerializer.serialize(newVillagerName);
Villager villager = (Villager) event.getRightClicked();
WrappedVillager wVillager = villagerCache.getOrAdd(villager);
if (nametags.contains(name.toLowerCase())) {
if (wVillager.canOptimize(cooldown) || player.hasPermission(Bypass.NAMETAG_COOLDOWN.get())) {
VillagerOptimizeEvent optimizeEvent = new VillagerOptimizeEvent(wVillager, OptimizationType.NAMETAG, player, event.isAsynchronous());
VillagerOptimizeEvent optimizeEvent = new VillagerOptimizeEvent(
wVillager,
OptimizationType.NAMETAG,
player,
event.isAsynchronous()
);
if (!optimizeEvent.callEvent()) return;
if (!consume_nametag) {
@ -107,13 +119,14 @@ public class OptimizeByNametag implements VillagerOptimizerModule, Listener {
wVillager.saveOptimizeTime();
if (notify_player) {
VillagerOptimizer.getLang(player.locale()).nametag_optimize_success.forEach(player::sendMessage);
VillagerOptimizer.getLang(player.locale()).nametag_optimize_success
.forEach(line -> KyoriUtil.sendMessage(player, line));
}
if (log_enabled) {
VillagerOptimizer.getLog().info(Component.text(player.getName() +
" optimized villager by nametag '" + name + "' at " +
CommonUtil.formatLocation(wVillager.villager().getLocation())).color(VillagerOptimizer.plugin_style.color()));
CommonUtil.formatLocation(wVillager.villager().getLocation())).color(VillagerOptimizer.STYLE.color()));
}
} else {
event.setCancelled(true);
@ -123,7 +136,8 @@ public class OptimizeByNametag implements VillagerOptimizerModule, Listener {
.matchLiteral("%time%")
.replacement(CommonUtil.formatDuration(Duration.ofMillis(wVillager.getOptimizeCooldownMillis(cooldown))))
.build();
VillagerOptimizer.getLang(player.locale()).nametag_on_optimize_cooldown.forEach(line -> player.sendMessage(line.replaceText(timeLeft)));
VillagerOptimizer.getLang(player.locale()).nametag_on_optimize_cooldown
.forEach(line -> KyoriUtil.sendMessage(player, line.replaceText(timeLeft)));
}
}
} else {
@ -134,13 +148,14 @@ public class OptimizeByNametag implements VillagerOptimizerModule, Listener {
wVillager.setOptimizationType(OptimizationType.NONE);
if (notify_player) {
VillagerOptimizer.getLang(player.locale()).nametag_unoptimize_success.forEach(player::sendMessage);
VillagerOptimizer.getLang(player.locale()).nametag_unoptimize_success
.forEach(line -> KyoriUtil.sendMessage(player, line));
}
if (log_enabled) {
VillagerOptimizer.getLog().info(Component.text(player.getName() +
" unoptimized villager by nametag '" + name + "' at " +
CommonUtil.formatLocation(wVillager.villager().getLocation())).color(VillagerOptimizer.plugin_style.color()));
CommonUtil.formatLocation(wVillager.villager().getLocation())).color(VillagerOptimizer.STYLE.color()));
}
}
}

View File

@ -15,6 +15,7 @@ import me.xginko.villageroptimizer.events.VillagerOptimizeEvent;
import me.xginko.villageroptimizer.events.VillagerUnoptimizeEvent;
import me.xginko.villageroptimizer.modules.VillagerOptimizerModule;
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 org.bukkit.Location;
@ -48,27 +49,27 @@ public class OptimizeByWorkstation implements VillagerOptimizerModule, Listener
this.scheduler = VillagerOptimizer.getFoliaLib().getImpl();
this.villagerCache = VillagerOptimizer.getCache();
Config config = VillagerOptimizer.getConfiguration();
config.master().addComment("optimization-methods.workstation-optimization.enable", """
When enabled, villagers that have a job and have been traded with at least once will become optimized,\s
if near their workstation. If the workstation is broken, the villager will become unoptimized again.""");
this.delay_millis = Math.max(config.getInt("optimization-methods.workstation-optimization.delay.default-delay-in-ticks", 10, """
The delay in ticks the plugin should wait before trying to optimize the closest villager on workstation place.\s
Gives the villager time to claim the placed workstation. Minimum delay is 1 Tick (Not recommended)"""), 1) * 50L;
this.resettable_delay_millis = Math.max(config.getInt("optimization-methods.workstation-optimization.delay.resettable-delay-in-ticks", 60, """
The delay in ticks the plugin should wait before trying to optimize a villager that can loose its profession\s
by having their workstation destroyed.\s
Intended to fix issues while trade rolling."""), 1) * 50L;
config.master().addComment("optimization-methods.workstation-optimization.enable",
"When enabled, villagers that have a job and have been traded with at least once will become optimized,\n" +
"if near their workstation. If the workstation is broken, the villager will become unoptimized again.");
this.delay_millis = Math.max(config.getInt("optimization-methods.workstation-optimization.delay.default-delay-in-ticks", 10,
"The delay in ticks the plugin should wait before trying to optimize the closest villager on workstation place.\n" +
"Gives the villager time to claim the placed workstation. Minimum delay is 1 Tick (Not recommended)"), 1) * 50L;
this.resettable_delay_millis = Math.max(config.getInt("optimization-methods.workstation-optimization.delay.resettable-delay-in-ticks", 60,
"The delay in ticks the plugin should wait before trying to optimize a villager that can loose its profession\n" +
"by having their workstation destroyed.\n" +
"Intended to fix issues while trade rolling."), 1) * 50L;
this.pending_optimizations = Caffeine.newBuilder()
.expireAfterWrite(Duration.ofMillis(Math.max(resettable_delay_millis, delay_millis) + 500L))
.build();
this.search_radius = config.getDouble("optimization-methods.workstation-optimization.search-radius-in-blocks", 2.0, """
The radius in blocks a villager can be away from the player when he places a workstation.\s
The closest unoptimized villager to the player will be optimized.""");
this.search_radius = config.getDouble("optimization-methods.workstation-optimization.search-radius-in-blocks", 2.0,
"The radius in blocks a villager can be away from the player when he places a workstation.\n" +
"The closest unoptimized villager to the player will be optimized.");
this.search_radius_squared = NumberConversions.square(search_radius);
this.cooldown_millis = TimeUnit.SECONDS.toMillis(
config.getInt("optimization-methods.workstation-optimization.optimize-cooldown-seconds", 600, """
Cooldown in seconds until a villager can be optimized again using a workstation.\s
Here for configuration freedom. Recommended to leave as is to not enable any exploitable behavior."""));
config.getInt("optimization-methods.workstation-optimization.optimize-cooldown-seconds", 600,
"Cooldown in seconds until a villager can be optimized again using a workstation.\n" +
"Here for configuration freedom. Recommended to leave as is to not enable any exploitable behavior."));
this.only_while_sneaking = config.getBoolean("optimization-methods.workstation-optimization.only-when-sneaking", true,
"Only optimize/unoptimize by workstation when player is sneaking during place or break");
this.notify_player = config.getBoolean("optimization-methods.workstation-optimization.notify-player", true,
@ -132,14 +133,19 @@ public class OptimizeByWorkstation implements VillagerOptimizerModule, Listener
.matchLiteral("%time%")
.replacement(CommonUtil.formatDuration(Duration.ofMillis(finalToOptimize.getOptimizeCooldownMillis(cooldown_millis))))
.build();
VillagerOptimizer.getLang(player.locale()).nametag_on_optimize_cooldown.forEach(line -> player.sendMessage(line
.replaceText(timeLeft)
));
VillagerOptimizer.getLang(player.locale()).nametag_on_optimize_cooldown
.forEach(line -> KyoriUtil.sendMessage(player, line.replaceText(timeLeft)));
}
return;
}
VillagerOptimizeEvent optimizeEvent = new VillagerOptimizeEvent(finalToOptimize, OptimizationType.WORKSTATION, player, event.isAsynchronous());
VillagerOptimizeEvent optimizeEvent = new VillagerOptimizeEvent(
finalToOptimize,
OptimizationType.WORKSTATION,
player,
event.isAsynchronous()
);
if (!optimizeEvent.callEvent()) return;
finalToOptimize.setOptimizationType(optimizeEvent.getOptimizationType());
@ -154,16 +160,14 @@ public class OptimizeByWorkstation implements VillagerOptimizerModule, Listener
.matchLiteral("%workstation%")
.replacement(placed.getType().toString().toLowerCase())
.build();
VillagerOptimizer.getLang(player.locale()).workstation_optimize_success.forEach(line -> player.sendMessage(line
.replaceText(vilProfession)
.replaceText(placedWorkstation)
));
VillagerOptimizer.getLang(player.locale()).workstation_optimize_success
.forEach(line -> KyoriUtil.sendMessage(player, line.replaceText(vilProfession).replaceText(placedWorkstation)));
}
if (log_enabled) {
VillagerOptimizer.getLog().info(Component.text(player.getName() +
" optimized villager by workstation (" + placed.getType().toString().toLowerCase() + ") at " +
CommonUtil.formatLocation(finalToOptimize.villager().getLocation())).color(VillagerOptimizer.plugin_style.color()));
CommonUtil.formatLocation(finalToOptimize.villager().getLocation())).color(VillagerOptimizer.STYLE.color()));
}
}, toOptimize.canLooseProfession() ? resettable_delay_millis : delay_millis, TimeUnit.MILLISECONDS));
}
@ -201,7 +205,13 @@ public class OptimizeByWorkstation implements VillagerOptimizerModule, Listener
if (closestOptimizedVillager == null) return;
VillagerUnoptimizeEvent unOptimizeEvent = new VillagerUnoptimizeEvent(closestOptimizedVillager, player, OptimizationType.WORKSTATION, event.isAsynchronous());
VillagerUnoptimizeEvent unOptimizeEvent = new VillagerUnoptimizeEvent(
closestOptimizedVillager,
player,
OptimizationType.WORKSTATION,
event.isAsynchronous()
);
if (!unOptimizeEvent.callEvent()) return;
closestOptimizedVillager.setOptimizationType(OptimizationType.NONE);
@ -215,16 +225,14 @@ public class OptimizeByWorkstation implements VillagerOptimizerModule, Listener
.matchLiteral("%workstation%")
.replacement(broken.getType().toString().toLowerCase())
.build();
VillagerOptimizer.getLang(player.locale()).workstation_unoptimize_success.forEach(line -> player.sendMessage(line
.replaceText(vilProfession)
.replaceText(brokenWorkstation)
));
VillagerOptimizer.getLang(player.locale()).workstation_unoptimize_success
.forEach(line -> KyoriUtil.sendMessage(player, line.replaceText(vilProfession).replaceText(brokenWorkstation)));
}
if (log_enabled) {
VillagerOptimizer.getLog().info(Component.text(player.getName() +
" unoptimized villager by workstation (" + broken.getType().toString().toLowerCase() + ") at " +
CommonUtil.formatLocation(closestOptimizedVillager.villager().getLocation())).color(VillagerOptimizer.plugin_style.color()));
CommonUtil.formatLocation(closestOptimizedVillager.villager().getLocation())).color(VillagerOptimizer.STYLE.color()));
}
}
}

View File

@ -12,9 +12,9 @@ public class CommonUtil {
public static @NotNull String formatDuration(Duration duration) {
if (duration.isNegative()) duration = duration.negated();
final int seconds = duration.toSecondsPart();
final int minutes = duration.toMinutesPart();
final int hours = duration.toHoursPart();
final int seconds = (int) (duration.getSeconds() % 60);
final int minutes = (int) (duration.toMinutes() % 60);
final int hours = (int) (duration.toHours() % 24);
if (hours > 0) {
return String.format("%02dh %02dm %02ds", hours, minutes, seconds);
@ -49,21 +49,35 @@ public class CommonUtil {
}
public static Villager.Profession getWorkstationProfession(@NotNull Material workstation) {
return switch (workstation) {
case BARREL -> Villager.Profession.FISHERMAN;
case CARTOGRAPHY_TABLE -> Villager.Profession.CARTOGRAPHER;
case SMOKER -> Villager.Profession.BUTCHER;
case SMITHING_TABLE -> Villager.Profession.TOOLSMITH;
case GRINDSTONE -> Villager.Profession.WEAPONSMITH;
case BLAST_FURNACE -> Villager.Profession.ARMORER;
case CAULDRON -> Villager.Profession.LEATHERWORKER;
case BREWING_STAND -> Villager.Profession.CLERIC;
case COMPOSTER -> Villager.Profession.FARMER;
case FLETCHING_TABLE -> Villager.Profession.FLETCHER;
case LOOM -> Villager.Profession.SHEPHERD;
case LECTERN -> Villager.Profession.LIBRARIAN;
case STONECUTTER -> Villager.Profession.MASON;
default -> Villager.Profession.NONE;
};
switch (workstation) {
case BARREL:
return Villager.Profession.FISHERMAN;
case CARTOGRAPHY_TABLE:
return Villager.Profession.CARTOGRAPHER;
case SMOKER:
return Villager.Profession.BUTCHER;
case SMITHING_TABLE:
return Villager.Profession.TOOLSMITH;
case GRINDSTONE:
return Villager.Profession.WEAPONSMITH;
case BLAST_FURNACE:
return Villager.Profession.ARMORER;
case CAULDRON:
return Villager.Profession.LEATHERWORKER;
case BREWING_STAND:
return Villager.Profession.CLERIC;
case COMPOSTER:
return Villager.Profession.FARMER;
case FLETCHING_TABLE:
return Villager.Profession.FLETCHER;
case LOOM:
return Villager.Profession.SHEPHERD;
case LECTERN:
return Villager.Profession.LIBRARIAN;
case STONECUTTER:
return Villager.Profession.MASON;
default:
return Villager.Profession.NONE;
}
}
}

View File

@ -0,0 +1,24 @@
package me.xginko.villageroptimizer.utils;
import me.xginko.villageroptimizer.VillagerOptimizer;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.text.Component;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.Locale;
public class KyoriUtil {
public static void sendMessage(CommandSender sender, Component message) {
VillagerOptimizer.getAudiences().sender(sender).sendMessage(message);
}
public static void sendActionBar(CommandSender sender, Component message) {
VillagerOptimizer.getAudiences().sender(sender).sendActionBar(message);
}
public static Locale getLocale(Player player) {
return VillagerOptimizer.getAudiences().player(player).pointers().getOrDefault(Identity.LOCALE, Locale.US);
}
}