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

View File

@ -2,10 +2,8 @@ package me.xginko.villageroptimizer;
import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.Caffeine;
import org.bukkit.Bukkit;
import org.bukkit.entity.Villager; import org.bukkit.entity.Villager;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.time.Duration; import java.time.Duration;
import java.util.UUID; import java.util.UUID;
@ -23,11 +21,6 @@ public final class VillagerCache {
return this.villagerCache.asMap(); 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) { public @NotNull WrappedVillager getOrAdd(@NotNull Villager villager) {
WrappedVillager wrappedVillager = this.villagerCache.getIfPresent(villager.getUniqueId()); WrappedVillager wrappedVillager = this.villagerCache.getIfPresent(villager.getUniqueId());
return wrappedVillager == null ? this.add(new WrappedVillager(villager)) : this.add(wrappedVillager); 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.Config;
import me.xginko.villageroptimizer.config.LanguageCache; import me.xginko.villageroptimizer.config.LanguageCache;
import me.xginko.villageroptimizer.modules.VillagerOptimizerModule; import me.xginko.villageroptimizer.modules.VillagerOptimizerModule;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style; import net.kyori.adventure.text.format.Style;
@ -15,14 +16,12 @@ import org.bstats.bukkit.Metrics;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.Collections; import java.util.*;
import java.util.HashMap;
import java.util.Locale;
import java.util.Set;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -30,80 +29,99 @@ import java.util.stream.Collectors;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
public final class VillagerOptimizer extends JavaPlugin { 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 VillagerOptimizer instance;
private static VillagerCache villagerCache; private static VillagerCache villagerCache;
private static FoliaLib foliaLib; private static FoliaLib foliaLib;
private static HashMap<String, LanguageCache> languageCacheMap; private static Map<String, LanguageCache> languageCacheMap;
private static Config config; private static Config config;
private static BukkitAudiences audiences;
private static ComponentLogger logger; private static ComponentLogger logger;
@Override @Override
public void onEnable() { public void onEnable() {
instance = this; instance = this;
logger = ComponentLogger.logger(this.getName());
foliaLib = new FoliaLib(this); 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(STYLE));
logger.info(Component.text("│ │").style(plugin_style)); logger.info(Component.text("│ │").style(STYLE));
logger.info(Component.text("│ │").style(plugin_style)); logger.info(Component.text("│ │").style(STYLE));
logger.info(Component.text("│ _ __ _ __ __ │").style(plugin_style)); logger.info(Component.text("│ _ __ _ __ __ │").style(STYLE));
logger.info(Component.text("│ | | / /(_)/ // /___ _ ___ _ ___ ____ │").style(plugin_style)); logger.info(Component.text("│ | | / /(_)/ // /___ _ ___ _ ___ ____ │").style(STYLE));
logger.info(Component.text("│ | |/ // // // // _ `// _ `// -_)/ __/ │").style(plugin_style)); logger.info(Component.text("│ | |/ // // // // _ `// _ `// -_)/ __/ │").style(STYLE));
logger.info(Component.text("│ |___//_//_//_/ \\_,_/ \\_, / \\__//_/ │").style(plugin_style)); logger.info(Component.text("│ |___//_//_//_/ \\_,_/ \\_, / \\__//_/ │").style(STYLE));
logger.info(Component.text("│ ____ __ _ /___/_ │").style(plugin_style)); logger.info(Component.text("│ ____ __ _ /___/_ │").style(STYLE));
logger.info(Component.text("│ / __ \\ ___ / /_ (_)__ _ (_)___ ___ ____ │").style(plugin_style)); logger.info(Component.text("│ / __ \\ ___ / /_ (_)__ _ (_)___ ___ ____ │").style(STYLE));
logger.info(Component.text("│ / /_/ // _ \\/ __// // ' \\ / //_ // -_)/ __/ │").style(plugin_style)); logger.info(Component.text("│ / /_/ // _ \\/ __// // ' \\ / //_ // -_)/ __/ │").style(STYLE));
logger.info(Component.text("\\____// .__/\\__//_//_/_/_//_/ /__/\\__//_/ │").style(plugin_style)); logger.info(Component.text("\\____// .__/\\__//_//_/_/_//_/ /__/\\__//_/ │").style(STYLE));
logger.info(Component.text("│ /_/ by xGinko │").style(plugin_style)); logger.info(Component.text("│ /_/ by xGinko │").style(STYLE));
logger.info(Component.text("│ │").style(plugin_style)); logger.info(Component.text("│ │").style(STYLE));
logger.info(Component.text("│ │").style(plugin_style)); logger.info(Component.text("│ │").style(STYLE));
logger.info(Component.text("") logger.info(Component.text("")
.style(plugin_style).append(Component.text("https://github.com/xGinko/VillagerOptimizer") .style(STYLE).append(Component.text("https://github.com/xGinko/VillagerOptimizer")
.color(NamedTextColor.GRAY)).append(Component.text("").style(plugin_style))); .color(NamedTextColor.GRAY)).append(Component.text("").style(STYLE)));
logger.info(Component.text("│ │").style(plugin_style)); logger.info(Component.text("│ │").style(STYLE));
logger.info(Component.text("│ │").style(plugin_style)); logger.info(Component.text("│ │").style(STYLE));
logger.info(Component.text("") logger.info(Component.text("")
.style(plugin_style).append(Component.text(" ➤ Loading Translations...").style(plugin_style)) .style(STYLE).append(Component.text(" ➤ Loading Translations...").style(STYLE))
.append(Component.text("").style(plugin_style))); .append(Component.text("").style(STYLE)));
reloadLang(true); reloadLang(true);
logger.info(Component.text("") logger.info(Component.text("")
.style(plugin_style).append(Component.text(" ➤ Loading Config...").style(plugin_style)) .style(STYLE).append(Component.text(" ➤ Loading Config...").style(STYLE))
.append(Component.text("").style(plugin_style))); .append(Component.text("").style(STYLE)));
reloadConfiguration(); reloadConfiguration();
logger.info(Component.text("") logger.info(Component.text("")
.style(plugin_style).append(Component.text(" ✓ Done.").color(NamedTextColor.WHITE).decorate(TextDecoration.BOLD)) .style(STYLE).append(Component.text(" ✓ Done.").color(NamedTextColor.WHITE).decorate(TextDecoration.BOLD))
.append(Component.text("").style(plugin_style))); .append(Component.text("").style(STYLE)));
logger.info(Component.text("│ │").style(plugin_style)); logger.info(Component.text("│ │").style(STYLE));
logger.info(Component.text("│ │").style(plugin_style)); logger.info(Component.text("│ │").style(STYLE));
logger.info(Component.text("╰────────────────────────────────────────────────────────────╯").style(plugin_style)); logger.info(Component.text("╰────────────────────────────────────────────────────────────╯").style(STYLE));
new Metrics(this, 19954); 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; return instance;
} }
public static Config getConfiguration() { public static @NotNull Config getConfiguration() {
return config; return config;
} }
public static VillagerCache getCache() { public static @NotNull VillagerCache getCache() {
return villagerCache; return villagerCache;
} }
public static FoliaLib getFoliaLib() { public static @NotNull FoliaLib getFoliaLib() {
return foliaLib; return foliaLib;
} }
public static ComponentLogger getLog() { public static @NotNull ComponentLogger getLog() {
return logger; 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()); return getLang(locale.toString().toLowerCase());
} }
public static LanguageCache getLang(CommandSender commandSender) { public static @NotNull LanguageCache getLang(CommandSender commandSender) {
return commandSender instanceof Player player ? getLang(player.locale()) : getLang(config.default_lang); 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()); if (!config.auto_lang) return languageCacheMap.get(config.default_lang.toString().toLowerCase());
return languageCacheMap.getOrDefault(lang.replace("-", "_"), 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(); VillagerOptimizerCommand.reloadCommands();
VillagerOptimizerModule.reloadModules(); VillagerOptimizerModule.reloadModules();
config.saveConfig(); config.saveConfig();
} catch (Exception e) { } catch (Exception exception) {
logger.error("Error loading config! - " + e.getLocalizedMessage()); logger.error("Error loading config!", exception);
e.printStackTrace();
} }
} }
@ -134,9 +151,9 @@ public final class VillagerOptimizer extends JavaPlugin {
for (String fileName : getDefaultLanguageFiles()) { for (String fileName : getDefaultLanguageFiles()) {
final String localeString = fileName.substring(fileName.lastIndexOf(File.separator) + 1, fileName.lastIndexOf('.')); final String localeString = fileName.substring(fileName.lastIndexOf(File.separator) + 1, fileName.lastIndexOf('.'));
if (startup) logger.info( 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(" "+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)); else logger.info(String.format("Found language file for %s", localeString));
languageCacheMap.put(localeString, new LanguageCache(localeString)); languageCacheMap.put(localeString, new LanguageCache(localeString));
} }
@ -147,9 +164,9 @@ public final class VillagerOptimizer extends JavaPlugin {
String localeString = langMatcher.group(1).toLowerCase(); String localeString = langMatcher.group(1).toLowerCase();
if (!languageCacheMap.containsKey(localeString)) { // make sure it wasn't a default file that we already loaded if (!languageCacheMap.containsKey(localeString)) { // make sure it wasn't a default file that we already loaded
if (startup) logger.info( 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(" "+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)); else logger.info(String.format("Found language file for %s", localeString));
languageCacheMap.put(localeString, new LanguageCache(localeString)); languageCacheMap.put(localeString, new LanguageCache(localeString));
} }
@ -157,22 +174,21 @@ public final class VillagerOptimizer extends JavaPlugin {
} }
} catch (Exception e) { } catch (Exception e) {
if (startup) logger.error( 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("LANG ERROR").color(NamedTextColor.RED).decorate(TextDecoration.BOLD))
.append(Component.text("").style(plugin_style))); .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!"); 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);
e.printStackTrace();
} }
} }
private Set<String> getDefaultLanguageFiles() { private @NotNull Set<String> getDefaultLanguageFiles() {
try (final JarFile pluginJarFile = new JarFile(this.getFile())) { try (final JarFile pluginJarFile = new JarFile(this.getFile())) {
return pluginJarFile.stream() return pluginJarFile.stream()
.map(ZipEntry::getName) .map(ZipEntry::getName)
.filter(name -> name.startsWith("lang" + File.separator) && name.endsWith(".yml")) .filter(name -> name.startsWith("lang" + File.separator) && name.endsWith(".yml"))
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} catch (IOException e) { } catch (IOException ioException) {
logger.error("Failed getting default lang files! - "+e.getLocalizedMessage()); logger.error("Failed getting default lang files!", ioException);
return Collections.emptySet(); 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. * @return True if the villager is optimized by the supported plugin, otherwise false.
*/ */
public boolean isOptimized(Keyring.Spaces namespaces) { public boolean isOptimized(Keyring.Spaces namespace) {
return switch (namespaces) { if (namespace == Keyring.Spaces.VillagerOptimizer) {
case VillagerOptimizer -> dataContainer.has(Keyring.VillagerOptimizer.OPTIMIZATION_TYPE.getKey(), PersistentDataType.STRING); return dataContainer.has(Keyring.VillagerOptimizer.OPTIMIZATION_TYPE.getKey(), PersistentDataType.STRING);
case AntiVillagerLag -> dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_ANY.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_WORKSTATION.getKey(), PersistentDataType.STRING)
|| dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_BLOCK.getKey(), PersistentDataType.STRING); || dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_BLOCK.getKey(), PersistentDataType.STRING);
}; }
} }
/** /**
@ -92,12 +93,7 @@ public final class WrappedVillager {
// Keep repeating task until villager is no longer trading with a player // Keep repeating task until villager is no longer trading with a player
if (villager.isTrading()) return; if (villager.isTrading()) return;
switch (type) { if (type == OptimizationType.NONE) {
case NAMETAG, COMMAND, BLOCK, WORKSTATION -> {
dataContainer.set(Keyring.VillagerOptimizer.OPTIMIZATION_TYPE.getKey(), PersistentDataType.STRING, type.name());
villager.setAware(false);
}
case NONE -> {
if (isOptimized(Keyring.Spaces.VillagerOptimizer)) { if (isOptimized(Keyring.Spaces.VillagerOptimizer)) {
dataContainer.remove(Keyring.VillagerOptimizer.OPTIMIZATION_TYPE.getKey()); dataContainer.remove(Keyring.VillagerOptimizer.OPTIMIZATION_TYPE.getKey());
} }
@ -111,7 +107,9 @@ public final class WrappedVillager {
} }
villager.setAware(true); villager.setAware(true);
villager.setAI(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 // End repeating task once logic is finished
@ -134,26 +132,26 @@ public final class WrappedVillager {
} }
public @NotNull OptimizationType getOptimizationType(Keyring.Spaces namespaces) { public @NotNull OptimizationType getOptimizationType(Keyring.Spaces namespaces) {
return switch (namespaces) { if (namespaces == Keyring.Spaces.VillagerOptimizer) {
case VillagerOptimizer -> { if (!isOptimized(Keyring.Spaces.VillagerOptimizer)) {
if (isOptimized(Keyring.Spaces.VillagerOptimizer)) { return OptimizationType.valueOf(dataContainer.get(Keyring.VillagerOptimizer.OPTIMIZATION_TYPE.getKey(), PersistentDataType.STRING));
yield OptimizationType.valueOf(dataContainer.get(Keyring.VillagerOptimizer.OPTIMIZATION_TYPE.getKey(), PersistentDataType.STRING)); } else {
return OptimizationType.NONE;
} }
yield OptimizationType.NONE;
} }
case AntiVillagerLag -> { if (namespaces == Keyring.Spaces.AntiVillagerLag) {
if (dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_BLOCK.getKey(), PersistentDataType.STRING)) { if (dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_BLOCK.getKey(), PersistentDataType.STRING)) {
yield OptimizationType.BLOCK; return OptimizationType.BLOCK;
} }
if (dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_WORKSTATION.getKey(), PersistentDataType.STRING)) { if (dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_WORKSTATION.getKey(), PersistentDataType.STRING)) {
yield OptimizationType.WORKSTATION; return OptimizationType.WORKSTATION;
} }
if (dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_ANY.getKey(), PersistentDataType.STRING)) { if (dataContainer.has(Keyring.AntiVillagerLag.OPTIMIZED_ANY.getKey(), PersistentDataType.STRING)) {
yield OptimizationType.COMMAND; // Best we can do return OptimizationType.COMMAND; // Best we can do
} }
yield OptimizationType.NONE; 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.CommandMap;
import org.bukkit.command.TabCompleter; import org.bukkit.command.TabCompleter;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -17,7 +18,7 @@ public interface VillagerOptimizerCommand extends CommandExecutor, TabCompleter
String label(); String label();
List<String> NO_TABCOMPLETES = Collections.emptyList(); 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<>(); 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.Bypass;
import me.xginko.villageroptimizer.enums.permissions.Commands; import me.xginko.villageroptimizer.enums.permissions.Commands;
import me.xginko.villageroptimizer.events.VillagerOptimizeEvent; import me.xginko.villageroptimizer.events.VillagerOptimizeEvent;
import me.xginko.villageroptimizer.utils.KyoriUtil;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextReplacementConfig; import net.kyori.adventure.text.TextReplacementConfig;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
@ -32,9 +33,9 @@ public class OptVillagersRadius implements VillagerOptimizerCommand {
public OptVillagersRadius() { public OptVillagersRadius() {
Config config = VillagerOptimizer.getConfiguration(); Config config = VillagerOptimizer.getConfiguration();
this.max_radius = config.getInt("optimization-methods.commands.optimizevillagers.max-block-radius", 100); this.max_radius = config.getInt("optimization-methods.commands.optimizevillagers.max-block-radius", 100);
this.cooldown = config.getInt("optimization-methods.commands.optimizevillagers.cooldown-seconds", 600, """ 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 "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; "Here for configuration freedom. Recommended to leave as is to not enable any exploitable behavior.") * 1000L;
} }
@Override @Override
@ -50,18 +51,21 @@ public class OptVillagersRadius implements VillagerOptimizerCommand {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
if (!sender.hasPermission(Commands.OPTIMIZE_RADIUS.get())) { if (!sender.hasPermission(Commands.OPTIMIZE_RADIUS.get())) {
sender.sendMessage(VillagerOptimizer.getLang(sender).no_permission); KyoriUtil.sendMessage(sender, VillagerOptimizer.getLang(sender).no_permission);
return true; return true;
} }
if (!(sender instanceof Player player)) { if (!(sender instanceof Player)) {
sender.sendMessage(Component.text("This command can only be executed by a player.") KyoriUtil.sendMessage(sender, Component.text("This command can only be executed by a player.")
.color(NamedTextColor.RED).decorate(TextDecoration.BOLD)); .color(NamedTextColor.RED).decorate(TextDecoration.BOLD));
return true; return true;
} }
Player player = (Player) sender;
if (args.length != 1) { 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; return true;
} }
@ -71,7 +75,8 @@ public class OptVillagersRadius implements VillagerOptimizerCommand {
final int safeRadius = (int) Math.sqrt(specifiedRadius * specifiedRadius); final int safeRadius = (int) Math.sqrt(specifiedRadius * specifiedRadius);
if (safeRadius == 0) { 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; return true;
} }
@ -80,7 +85,8 @@ public class OptVillagersRadius implements VillagerOptimizerCommand {
.matchLiteral("%distance%") .matchLiteral("%distance%")
.replacement(Integer.toString(max_radius)) .replacement(Integer.toString(max_radius))
.build(); .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; return true;
} }
@ -114,7 +120,8 @@ public class OptVillagersRadius implements VillagerOptimizerCommand {
.matchLiteral("%radius%") .matchLiteral("%radius%")
.replacement(Integer.toString(safeRadius)) .replacement(Integer.toString(safeRadius))
.build(); .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; return true;
} }
@ -127,20 +134,20 @@ public class OptVillagersRadius implements VillagerOptimizerCommand {
.matchLiteral("%radius%") .matchLiteral("%radius%")
.replacement(Integer.toString(safeRadius)) .replacement(Integer.toString(safeRadius))
.build(); .build();
VillagerOptimizer.getLang(player.locale()).command_optimize_success.forEach(line -> player.sendMessage(line VillagerOptimizer.getLang(player.locale()).command_optimize_success
.replaceText(success_amount) .forEach(line -> KyoriUtil.sendMessage(player, line.replaceText(success_amount).replaceText(radius)));
.replaceText(radius)
));
} }
if (failCount > 0) { if (failCount > 0) {
final TextReplacementConfig alreadyOptimized = TextReplacementConfig.builder() final TextReplacementConfig alreadyOptimized = TextReplacementConfig.builder()
.matchLiteral("%amount%") .matchLiteral("%amount%")
.replacement(Integer.toString(failCount)) .replacement(Integer.toString(failCount))
.build(); .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) { } 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; 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.OptimizationType;
import me.xginko.villageroptimizer.enums.permissions.Commands; import me.xginko.villageroptimizer.enums.permissions.Commands;
import me.xginko.villageroptimizer.events.VillagerUnoptimizeEvent; import me.xginko.villageroptimizer.events.VillagerUnoptimizeEvent;
import me.xginko.villageroptimizer.utils.KyoriUtil;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextReplacementConfig; import net.kyori.adventure.text.TextReplacementConfig;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
@ -27,7 +28,8 @@ public class UnOptVillagersRadius implements VillagerOptimizerCommand {
private final int max_radius; private final int max_radius;
public UnOptVillagersRadius() { 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 @Override
@ -43,18 +45,21 @@ public class UnOptVillagersRadius implements VillagerOptimizerCommand {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
if (!sender.hasPermission(Commands.UNOPTIMIZE_RADIUS.get())) { if (!sender.hasPermission(Commands.UNOPTIMIZE_RADIUS.get())) {
sender.sendMessage(VillagerOptimizer.getLang(sender).no_permission); KyoriUtil.sendMessage(sender, VillagerOptimizer.getLang(sender).no_permission);
return true; return true;
} }
if (!(sender instanceof Player player)) { if (!(sender instanceof Player)) {
sender.sendMessage(Component.text("This command can only be executed by a player.") KyoriUtil.sendMessage(sender, Component.text("This command can only be executed by a player.")
.color(NamedTextColor.RED).decorate(TextDecoration.BOLD)); .color(NamedTextColor.RED).decorate(TextDecoration.BOLD));
return true; return true;
} }
Player player = (Player) sender;
if (args.length != 1) { 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; return true;
} }
@ -64,7 +69,8 @@ public class UnOptVillagersRadius implements VillagerOptimizerCommand {
final int safeRadius = (int) Math.sqrt(specifiedRadius * specifiedRadius); final int safeRadius = (int) Math.sqrt(specifiedRadius * specifiedRadius);
if (safeRadius == 0) { 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; return true;
} }
@ -73,7 +79,8 @@ public class UnOptVillagersRadius implements VillagerOptimizerCommand {
.matchLiteral("%distance%") .matchLiteral("%distance%")
.replacement(Integer.toString(max_radius)) .replacement(Integer.toString(max_radius))
.build(); .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; return true;
} }
@ -102,7 +109,8 @@ public class UnOptVillagersRadius implements VillagerOptimizerCommand {
.matchLiteral("%radius%") .matchLiteral("%radius%")
.replacement(Integer.toString(safeRadius)) .replacement(Integer.toString(safeRadius))
.build(); .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 { } else {
final TextReplacementConfig success_amount = TextReplacementConfig.builder() final TextReplacementConfig success_amount = TextReplacementConfig.builder()
.matchLiteral("%amount%") .matchLiteral("%amount%")
@ -112,13 +120,12 @@ public class UnOptVillagersRadius implements VillagerOptimizerCommand {
.matchLiteral("%radius%") .matchLiteral("%radius%")
.replacement(Integer.toString(safeRadius)) .replacement(Integer.toString(safeRadius))
.build(); .build();
VillagerOptimizer.getLang(player.locale()).command_unoptimize_success.forEach(line -> player.sendMessage(line VillagerOptimizer.getLang(player.locale()).command_unoptimize_success
.replaceText(success_amount) .forEach(line -> KyoriUtil.sendMessage(player, line.replaceText(success_amount).replaceText(radius)));
.replaceText(radius)
));
} }
} catch (NumberFormatException e) { } 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; 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.ReloadSubCmd;
import me.xginko.villageroptimizer.commands.villageroptimizer.subcommands.VersionSubCmd; import me.xginko.villageroptimizer.commands.villageroptimizer.subcommands.VersionSubCmd;
import me.xginko.villageroptimizer.enums.permissions.Commands; import me.xginko.villageroptimizer.enums.permissions.Commands;
import me.xginko.villageroptimizer.utils.KyoriUtil;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
public class VillagerOptimizerCmd implements VillagerOptimizerCommand { public class VillagerOptimizerCmd implements VillagerOptimizerCommand {
@ -21,8 +24,8 @@ public class VillagerOptimizerCmd implements VillagerOptimizerCommand {
private final List<String> tabCompleter; private final List<String> tabCompleter;
public VillagerOptimizerCmd() { public VillagerOptimizerCmd() {
subCommands = List.of(new ReloadSubCmd(), new VersionSubCmd(), new DisableSubCmd()); subCommands = Arrays.asList(new ReloadSubCmd(), new VersionSubCmd(), new DisableSubCmd());
tabCompleter = subCommands.stream().map(SubCommand::getLabel).toList(); tabCompleter = subCommands.stream().map(SubCommand::getLabel).collect(Collectors.toList());
} }
@Override @Override
@ -55,21 +58,21 @@ public class VillagerOptimizerCmd implements VillagerOptimizerCommand {
private void sendCommandOverview(CommandSender sender) { private void sendCommandOverview(CommandSender sender) {
if (!sender.hasPermission(Commands.RELOAD.get()) && !sender.hasPermission(Commands.VERSION.get())) return; if (!sender.hasPermission(Commands.RELOAD.get()) && !sender.hasPermission(Commands.VERSION.get())) return;
sender.sendMessage(Component.text("-----------------------------------------------------").color(NamedTextColor.GRAY)); KyoriUtil.sendMessage(sender, Component.text("-----------------------------------------------------").color(NamedTextColor.GRAY));
sender.sendMessage(Component.text("VillagerOptimizer Commands").color(VillagerOptimizer.plugin_style.color())); KyoriUtil.sendMessage(sender, Component.text("VillagerOptimizer Commands").color(VillagerOptimizer.STYLE.color()));
sender.sendMessage(Component.text("-----------------------------------------------------").color(NamedTextColor.GRAY)); KyoriUtil.sendMessage(sender, Component.text("-----------------------------------------------------").color(NamedTextColor.GRAY));
subCommands.forEach(subCommand -> sender.sendMessage( subCommands.forEach(subCommand -> KyoriUtil.sendMessage(sender,
subCommand.getSyntax().append(Component.text(" - ").color(NamedTextColor.DARK_GRAY)).append(subCommand.getDescription()))); subCommand.getSyntax().append(Component.text(" - ").color(NamedTextColor.DARK_GRAY)).append(subCommand.getDescription())));
sender.sendMessage( KyoriUtil.sendMessage(sender,
Component.text("/optimizevillagers <blockradius>").color(VillagerOptimizer.plugin_style.color()) Component.text("/optimizevillagers <blockradius>").color(VillagerOptimizer.STYLE.color())
.append(Component.text(" - ").color(NamedTextColor.DARK_GRAY)) .append(Component.text(" - ").color(NamedTextColor.DARK_GRAY))
.append(Component.text("Optimize villagers in a radius").color(NamedTextColor.GRAY)) .append(Component.text("Optimize villagers in a radius").color(NamedTextColor.GRAY))
); );
sender.sendMessage( KyoriUtil.sendMessage(sender,
Component.text("/unoptmizevillagers <blockradius>").color(VillagerOptimizer.plugin_style.color()) Component.text("/unoptmizevillagers <blockradius>").color(VillagerOptimizer.STYLE.color())
.append(Component.text(" - ").color(NamedTextColor.DARK_GRAY)) .append(Component.text(" - ").color(NamedTextColor.DARK_GRAY))
.append(Component.text("Unoptimize villagers in a radius").color(NamedTextColor.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.commands.SubCommand;
import me.xginko.villageroptimizer.enums.permissions.Commands; import me.xginko.villageroptimizer.enums.permissions.Commands;
import me.xginko.villageroptimizer.modules.VillagerOptimizerModule; import me.xginko.villageroptimizer.modules.VillagerOptimizerModule;
import me.xginko.villageroptimizer.utils.KyoriUtil;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
@ -23,21 +24,21 @@ public class DisableSubCmd extends SubCommand {
@Override @Override
public TextComponent getSyntax() { public TextComponent getSyntax() {
return Component.text("/villageroptimizer disable").color(VillagerOptimizer.plugin_style.color()); return Component.text("/villageroptimizer disable").color(VillagerOptimizer.STYLE.color());
} }
@Override @Override
public void perform(CommandSender sender, String[] args) { public void perform(CommandSender sender, String[] args) {
if (!sender.hasPermission(Commands.DISABLE.get())) { if (!sender.hasPermission(Commands.DISABLE.get())) {
sender.sendMessage(VillagerOptimizer.getLang(sender).no_permission); KyoriUtil.sendMessage(sender, VillagerOptimizer.getLang(sender).no_permission);
return; 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.forEach(VillagerOptimizerModule::disable);
VillagerOptimizerModule.modules.clear(); VillagerOptimizerModule.modules.clear();
VillagerOptimizer.getCache().cacheMap().clear(); VillagerOptimizer.getCache().cacheMap().clear();
sender.sendMessage(Component.text("Disabled all plugin listeners and tasks.").color(NamedTextColor.GREEN)); KyoriUtil.sendMessage(sender, 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("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.VillagerOptimizer;
import me.xginko.villageroptimizer.commands.SubCommand; import me.xginko.villageroptimizer.commands.SubCommand;
import me.xginko.villageroptimizer.enums.permissions.Commands; import me.xginko.villageroptimizer.enums.permissions.Commands;
import me.xginko.villageroptimizer.utils.KyoriUtil;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
@ -22,20 +23,20 @@ public class ReloadSubCmd extends SubCommand {
@Override @Override
public TextComponent getSyntax() { public TextComponent getSyntax() {
return Component.text("/villageroptimizer reload").color(VillagerOptimizer.plugin_style.color()); return Component.text("/villageroptimizer reload").color(VillagerOptimizer.STYLE.color());
} }
@Override @Override
public void perform(CommandSender sender, String[] args) { public void perform(CommandSender sender, String[] args) {
if (!sender.hasPermission(Commands.RELOAD.get())) { if (!sender.hasPermission(Commands.RELOAD.get())) {
sender.sendMessage(VillagerOptimizer.getLang(sender).no_permission); KyoriUtil.sendMessage(sender, VillagerOptimizer.getLang(sender).no_permission);
return; 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.getFoliaLib().getImpl().runNextTick(reload -> { // Reload in sync with the server
VillagerOptimizer.getInstance().reloadPlugin(); 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.VillagerOptimizer;
import me.xginko.villageroptimizer.commands.SubCommand; import me.xginko.villageroptimizer.commands.SubCommand;
import me.xginko.villageroptimizer.enums.permissions.Commands; import me.xginko.villageroptimizer.enums.permissions.Commands;
import me.xginko.villageroptimizer.utils.KyoriUtil;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.event.ClickEvent;
@ -25,14 +26,14 @@ public class VersionSubCmd extends SubCommand {
@Override @Override
public TextComponent getSyntax() { public TextComponent getSyntax() {
return Component.text("/villageroptimizer version").color(VillagerOptimizer.plugin_style.color()); return Component.text("/villageroptimizer version").color(VillagerOptimizer.STYLE.color());
} }
@Override @Override
@SuppressWarnings({"deprecation", "UnstableApiUsage"}) @SuppressWarnings({"deprecation", "UnstableApiUsage"})
public void perform(CommandSender sender, String[] args) { public void perform(CommandSender sender, String[] args) {
if (!sender.hasPermission(Commands.VERSION.get())) { if (!sender.hasPermission(Commands.VERSION.get())) {
sender.sendMessage(VillagerOptimizer.getLang(sender).no_permission); KyoriUtil.sendMessage(sender, VillagerOptimizer.getLang(sender).no_permission);
return; return;
} }
@ -52,10 +53,10 @@ public class VersionSubCmd extends SubCommand {
author = pluginYML.getAuthors().get(0); author = pluginYML.getAuthors().get(0);
} }
sender.sendMessage(Component.newline() KyoriUtil.sendMessage(sender, Component.newline()
.append( .append(
Component.text(name + " " + version) Component.text(name + " " + version)
.style(VillagerOptimizer.plugin_style) .style(VillagerOptimizer.STYLE)
.clickEvent(ClickEvent.openUrl(website)) .clickEvent(ClickEvent.openUrl(website))
) )
.append(Component.text(" by ").color(NamedTextColor.GRAY)) .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"); "If set to true, will display messages based on client language");
this.cache_keep_time_seconds = getInt("general.cache-keep-time-seconds", 30, 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."); "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, """ 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 "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."""); "Tries to read pre-existing info like optimization state so players don't need to reoptimize their villagers.");
} }
public void saveConfig() { public void saveConfig() {
try { try {
this.config.save(); this.config.save();
} catch (Exception e) { } catch (Throwable throwable) {
VillagerOptimizer.getLog().error("Failed to save config file! - " + e.getLocalizedMessage()); VillagerOptimizer.getLog().error("Failed to save config file!", throwable);
} }
} }
@ -51,11 +51,10 @@ public class Config {
this.createTitledSection("General", "general"); this.createTitledSection("General", "general");
this.createTitledSection("Optimization", "optimization-methods"); this.createTitledSection("Optimization", "optimization-methods");
this.config.addDefault("optimization-methods.commands.unoptimizevillagers", null); this.config.addDefault("optimization-methods.commands.unoptimizevillagers", null);
this.config.addComment("optimization-methods.commands", """ this.config.addComment("optimization-methods.commands",
If you want to disable commands, negate the following permissions:\s "If you want to disable commands, negate the following permissions:\n" +
villageroptimizer.cmd.optimize\s "villageroptimizer.cmd.optimize\n" +
villageroptimizer.cmd.unoptimize "villageroptimizer.cmd.unoptimize");
""");
this.config.addDefault("optimization-methods.nametag-optimization.enable", true); this.config.addDefault("optimization-methods.nametag-optimization.enable", true);
this.createTitledSection("Villager Chunk Limit", "villager-chunk-limit"); this.createTitledSection("Villager Chunk Limit", "villager-chunk-limit");
this.createTitledSection("Gameplay", "gameplay"); this.createTitledSection("Gameplay", "gameplay");

View File

@ -7,7 +7,9 @@ import net.kyori.adventure.text.minimessage.MiniMessage;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.File; import java.io.File;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
public class LanguageCache { public class LanguageCache {
@ -28,62 +30,62 @@ public class LanguageCache {
File parent = langYML.getParentFile(); File parent = langYML.getParentFile();
if (!parent.exists() && !parent.mkdir()) if (!parent.exists() && !parent.mkdir())
VillagerOptimizer.getLog().error("Failed to create lang directory."); VillagerOptimizer.getLog().error("Failed to create lang directory.");
// Check if the file already exists and save the one from the plugins resources folder if it does not // Check if the file already exists and save the one from the plugin's resources folder if it does not
if (!langYML.exists()) if (!langYML.exists())
plugin.saveResource("lang" + File.separator + locale + ".yml", false); 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); this.lang = ConfigFile.loadConfig(langYML);
// General // General
this.no_permission = getTranslation("messages.no-permission", this.no_permission = getTranslation("messages.no-permission",
"<red>You don't have permission to use this command."); "<red>You don't have permission to use this command.");
this.trades_restocked = getListTranslation("messages.trades-restocked", 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", 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", 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 // Nametag
this.nametag_optimize_success = getListTranslation("messages.nametag.optimize-success", 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", 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", 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 // Block
this.block_optimize_success = getListTranslation("messages.block.optimize-success", 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", 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", 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 // Workstation
this.workstation_optimize_success = getListTranslation("messages.workstation.optimize-success", 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", 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", 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 // Command
this.command_optimize_success = getListTranslation("messages.command.optimize-success", 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", 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", 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", 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", 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", 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", 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 { try {
this.lang.save(); this.lang.save();
} catch (Exception e) { } catch (Throwable throwable) {
VillagerOptimizer.getLog().error("Failed to save language file: "+ langYML.getName() +" - " + e.getLocalizedMessage()); 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)); return MiniMessage.miniMessage().deserialize(this.lang.getString(path, defaultTranslation));
} }
public @NotNull Component getTranslation(@NotNull String path, @NotNull String defaultTranslation, @NotNull String comment) { public @NotNull List<Component> getListTranslation(@NotNull String path, @NotNull String... defaultTranslation) {
this.lang.addDefault(path, defaultTranslation, comment); this.lang.addDefault(path, Arrays.asList(defaultTranslation));
return MiniMessage.miniMessage().deserialize(this.lang.getString(path, defaultTranslation)); return this.lang.getStringList(path).stream().map(MiniMessage.miniMessage()::deserialize).collect(Collectors.toList());
}
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();
} }
} }

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 * @return a {@link NamespacedKey} that can be used to test for and read data stored by plugins
* from a {@link PersistentDataContainer} * from a {@link PersistentDataContainer}
*/ */
@SuppressWarnings("deprecation")
public static NamespacedKey getKey(@NotNull String pluginName, @NotNull String key) { public static NamespacedKey getKey(@NotNull String pluginName, @NotNull String key) {
return new NamespacedKey(pluginName.toLowerCase(Locale.ROOT), key); return new NamespacedKey(pluginName.toLowerCase(Locale.ROOT), key);
} }
@ -58,7 +57,6 @@ public class Keyring {
private final @NotNull NamespacedKey key; private final @NotNull NamespacedKey key;
@SuppressWarnings("deprecation")
VillagerOptimizer(@NotNull String key) { VillagerOptimizer(@NotNull String key) {
this.key = new NamespacedKey(Spaces.VillagerOptimizer.namespace(), key); this.key = new NamespacedKey(Spaces.VillagerOptimizer.namespace(), key);
} }
@ -80,7 +78,6 @@ public class Keyring {
private final @NotNull NamespacedKey key; private final @NotNull NamespacedKey key;
@SuppressWarnings("deprecation")
AntiVillagerLag(@NotNull String avlKey) { AntiVillagerLag(@NotNull String avlKey) {
this.key = new NamespacedKey(Spaces.AntiVillagerLag.namespace(), 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 final @Nullable Player whoOptimised;
private boolean isCancelled = false; 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); super(isAsync);
this.wrappedVillager = wrappedVillager; this.wrappedVillager = wrappedVillager;
this.whoOptimised = whoOptimised; 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.wrappedVillager = wrappedVillager;
this.whoOptimised = whoOptimised; this.whoOptimised = whoOptimised;
if (optimizationType.equals(OptimizationType.NONE)) { if (optimizationType.equals(OptimizationType.NONE)) {

View File

@ -17,14 +17,23 @@ public class VillagerUnoptimizeEvent extends Event implements Cancellable {
private final @Nullable Player whoUnoptimized; private final @Nullable Player whoUnoptimized;
private boolean isCancelled = false; 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); super(isAsync);
this.wrappedVillager = wrappedVillager; this.wrappedVillager = wrappedVillager;
this.whoUnoptimized = whoUnoptimized; this.whoUnoptimized = whoUnoptimized;
this.unOptimizeType = unOptimizeType; 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.wrappedVillager = wrappedVillager;
this.whoUnoptimized = whoUnoptimized; this.whoUnoptimized = whoUnoptimized;
this.unOptimizeType = unOptimizeType; 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.bukkit.event.player.PlayerInteractEntityEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.*;
import java.util.Comparator; import java.util.stream.Collectors;
import java.util.List;
import java.util.Objects;
public class VillagerChunkLimit implements VillagerOptimizerModule, Listener { public class VillagerChunkLimit implements VillagerOptimizerModule, Listener {
@ -41,23 +39,23 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener {
this.scheduler = VillagerOptimizer.getFoliaLib().getImpl(); this.scheduler = VillagerOptimizer.getFoliaLib().getImpl();
this.villagerCache = VillagerOptimizer.getCache(); this.villagerCache = VillagerOptimizer.getCache();
Config config = VillagerOptimizer.getConfiguration(); Config config = VillagerOptimizer.getConfiguration();
config.master().addComment("villager-chunk-limit.enable", """ config.master().addComment("villager-chunk-limit.enable",
Checks chunks for too many villagers and removes excess villagers based on priority."""); "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, """ 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 "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.\s "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."""); "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, 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."); "Does not check chunks that don't have their entities loaded.");
this.log_enabled = config.getBoolean("villager-chunk-limit.log-removals", true); 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, this.non_optimized_max_per_chunk = config.getInt("villager-chunk-limit.unoptimized.max-per-chunk", 20,
"The maximum amount of unoptimized villagers per chunk."); "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", "NONE", "NITWIT", "SHEPHERD", "FISHERMAN", "BUTCHER", "CARTOGRAPHER", "LEATHERWORKER",
"FLETCHER", "MASON", "FARMER", "ARMORER", "TOOLSMITH", "WEAPONSMITH", "CLERIC", "LIBRARIAN" "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 "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""" "Use enums from https://jd.papermc.io/paper/1.20/org/bukkit/entity/Villager.Profession.html"
).stream().map(configuredProfession -> { ).stream().map(configuredProfession -> {
try { try {
return Villager.Profession.valueOf(configuredProfession); 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."); "https://jd.papermc.io/paper/1.20/org/bukkit/entity/Villager.Profession.html.");
return null; 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, this.optimized_max_per_chunk = config.getInt("villager-chunk-limit.optimized.max-per-chunk", 60,
"The maximum amount of optimized villagers per chunk."); "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", "NONE", "NITWIT", "SHEPHERD", "FISHERMAN", "BUTCHER", "CARTOGRAPHER", "LEATHERWORKER",
"FLETCHER", "MASON", "FARMER", "ARMORER", "TOOLSMITH", "WEAPONSMITH", "CLERIC", "LIBRARIAN" "FLETCHER", "MASON", "FARMER", "ARMORER", "TOOLSMITH", "WEAPONSMITH", "CLERIC", "LIBRARIAN"
)).stream().map(configuredProfession -> { )).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."); "https://jd.papermc.io/paper/1.20/org/bukkit/entity/Villager.Profession.html.");
return null; return null;
} }
}).filter(Objects::nonNull).toList(); }).filter(Objects::nonNull).collect(Collectors.toList());
} }
@Override @Override
@ -159,7 +157,7 @@ public class VillagerChunkLimit implements VillagerOptimizerModule, Listener {
if (log_enabled) { if (log_enabled) {
VillagerOptimizer.getLog().info(Component.text( VillagerOptimizer.getLog().info(Component.text(
"Removed unoptimized villager with profession '" + villager.getProfession().name() + "' at " + "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) { if (log_enabled) {
VillagerOptimizer.getLog().info(Component.text("Removed optimized villager with profession '" + VillagerOptimizer.getLog().info(Component.text("Removed optimized villager with profession '" +
villager.getProfession().name() + "' at " + 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.scheduler = VillagerOptimizer.getFoliaLib().getImpl();
this.villagerCache = VillagerOptimizer.getCache(); this.villagerCache = VillagerOptimizer.getCache();
Config config = VillagerOptimizer.getConfiguration(); Config config = VillagerOptimizer.getConfiguration();
config.master().addComment("gameplay.villagers-can-be-leashed.enable", """ 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."""); "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, this.only_optimized = config.getBoolean("gameplay.villagers-can-be-leashed.only-optimized", false,
"If set to true, only optimized villagers can be leashed."); "If set to true, only optimized villagers can be leashed.");
this.log_enabled = config.getBoolean("gameplay.villagers-can-be-leashed.log", false); 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) { if (log_enabled) {
VillagerOptimizer.getLog().info(Component.text(player.getName() + " leashed a villager at " + 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.WrappedVillager;
import me.xginko.villageroptimizer.modules.VillagerOptimizerModule; import me.xginko.villageroptimizer.modules.VillagerOptimizerModule;
import me.xginko.villageroptimizer.utils.CommonUtil; import me.xginko.villageroptimizer.utils.CommonUtil;
import me.xginko.villageroptimizer.utils.KyoriUtil;
import net.kyori.adventure.text.TextReplacementConfig; import net.kyori.adventure.text.TextReplacementConfig;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.entity.Villager; import org.bukkit.entity.Villager;
@ -34,13 +35,13 @@ public class LevelOptimizedProfession implements VillagerOptimizerModule, Listen
this.scheduler = VillagerOptimizer.getFoliaLib().getImpl(); this.scheduler = VillagerOptimizer.getFoliaLib().getImpl();
this.villagerCache = VillagerOptimizer.getCache(); this.villagerCache = VillagerOptimizer.getCache();
Config config = VillagerOptimizer.getConfiguration(); Config config = VillagerOptimizer.getConfiguration();
config.master().addComment("gameplay.level-optimized-profession", """ config.master().addComment("gameplay.level-optimized-profession",
This is needed to allow optimized villagers to level up.\s "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."""); "Temporarily enables the villagers AI to allow it to level up and then disables it again.");
this.cooldown_millis = TimeUnit.SECONDS.toMillis( this.cooldown_millis = TimeUnit.SECONDS.toMillis(
config.getInt("gameplay.level-optimized-profession.level-check-cooldown-seconds", 5, """ 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 "Cooldown in seconds until the level of a villager will be checked and updated again.\n" +
Recommended to leave as is.""")); "Recommended to leave as is."));
this.notify_player = config.getBoolean("gameplay.level-optimized-profession.notify-player", true, this.notify_player = config.getBoolean("gameplay.level-optimized-profession.notify-player", true,
"Tell players to wait when a villager is leveling up."); "Tell players to wait when a villager is leveling up.");
} }
@ -61,12 +62,13 @@ public class LevelOptimizedProfession implements VillagerOptimizerModule, Listen
return true; return true;
} }
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onTradeScreenClose(InventoryCloseEvent event) { private void onTradeScreenClose(InventoryCloseEvent event) {
if ( if (
event.getInventory().getType().equals(InventoryType.MERCHANT) 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); WrappedVillager wVillager = villagerCache.getOrAdd(villager);
if (!wVillager.isOptimized()) return; if (!wVillager.isOptimized()) return;
@ -89,7 +91,8 @@ public class LevelOptimizedProfession implements VillagerOptimizerModule, Listen
.matchLiteral("%time%") .matchLiteral("%time%")
.replacement(CommonUtil.formatDuration(Duration.ofMillis(wVillager.getLevelCooldownMillis(cooldown_millis)))) .replacement(CommonUtil.formatDuration(Duration.ofMillis(wVillager.getLevelCooldownMillis(cooldown_millis))))
.build(); .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 @Override
public boolean shouldEnable() { public boolean shouldEnable() {
return VillagerOptimizer.getConfiguration().getBoolean("gameplay.villagers-spawn-as-adults.enable", false, """ return VillagerOptimizer.getConfiguration().getBoolean("gameplay.villagers-spawn-as-adults.enable", false,
Spawned villagers will immediately be adults.\s "Spawned villagers will immediately be adults.\n" +
This is to save some more resources as players don't have to keep unoptimized\s "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\s "villagers loaded because they have to wait for them to turn into adults before they can\n" +
optimize them."""); "optimize them.");
} }
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onVillagerSpawn(CreatureSpawnEvent event) { private void onVillagerSpawn(CreatureSpawnEvent event) {
if (event.getEntityType() == EntityType.VILLAGER) { if (event.getEntityType().equals(EntityType.VILLAGER)) {
Villager villager = (Villager) event.getEntity(); Villager villager = (Villager) event.getEntity();
if (!villager.isAdult()) villager.setAdult(); if (!villager.isAdult()) villager.setAdult();
} }

View File

@ -23,7 +23,7 @@ public class PreventOptimizedDamage implements VillagerOptimizerModule, Listener
private final VillagerCache villagerCache; private final VillagerCache villagerCache;
private final Set<EntityDamageEvent.DamageCause> damage_causes_to_cancel; private final Set<EntityDamageEvent.DamageCause> damage_causes_to_cancel;
private final boolean cancelKnockback; private final boolean cancel_knockback;
public PreventOptimizedDamage() { public PreventOptimizedDamage() {
shouldEnable(); shouldEnable();
@ -31,13 +31,13 @@ public class PreventOptimizedDamage implements VillagerOptimizerModule, Listener
Config config = VillagerOptimizer.getConfiguration(); Config config = VillagerOptimizer.getConfiguration();
config.master().addComment("gameplay.prevent-damage-to-optimized.enable", config.master().addComment("gameplay.prevent-damage-to-optimized.enable",
"Configure what kind of damage you want to cancel for optimized villagers here."); "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"); "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", 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(), """ 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.\s "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:\s "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""" "https://jd.papermc.io/paper/1.20/org/bukkit/event/entity/EntityDamageEvent.DamageCause.html"
).stream().map(configuredDamageCause -> { ).stream().map(configuredDamageCause -> {
try { try {
return EntityDamageEvent.DamageCause.valueOf(configuredDamageCause); return EntityDamageEvent.DamageCause.valueOf(configuredDamageCause);
@ -80,7 +80,7 @@ public class PreventOptimizedDamage implements VillagerOptimizerModule, Listener
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onKnockbackByEntity(EntityKnockbackByEntityEvent event) { private void onKnockbackByEntity(EntityKnockbackByEntityEvent event) {
if ( if (
cancelKnockback cancel_knockback
&& event.getEntityType().equals(EntityType.VILLAGER) && event.getEntityType().equals(EntityType.VILLAGER)
&& villagerCache.getOrAdd((Villager) event.getEntity()).isOptimized() && villagerCache.getOrAdd((Villager) event.getEntity()).isOptimized()
) { ) {

View File

@ -42,11 +42,10 @@ public class PreventOptimizedTargeting implements VillagerOptimizerModule, Liste
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onTarget(EntityTargetEvent event) { private void onTarget(EntityTargetEvent event) {
// Yes, instanceof checks would look way more beautiful here but checking type is much faster
Entity target = event.getTarget(); Entity target = event.getTarget();
if ( if (
target != null target != null
&& target.getType() == EntityType.VILLAGER && target.getType().equals(EntityType.VILLAGER)
&& villagerCache.getOrAdd((Villager) target).isOptimized() && villagerCache.getOrAdd((Villager) target).isOptimized()
) { ) {
event.setTarget(null); event.setTarget(null);
@ -59,7 +58,7 @@ public class PreventOptimizedTargeting implements VillagerOptimizerModule, Liste
Entity target = event.getTargetEntity(); Entity target = event.getTargetEntity();
if ( if (
target != null target != null
&& target.getType() == EntityType.VILLAGER && target.getType().equals(EntityType.VILLAGER)
&& villagerCache.getOrAdd((Villager) target).isOptimized() && villagerCache.getOrAdd((Villager) target).isOptimized()
) { ) {
event.setCancelled(true); event.setCancelled(true);
@ -69,11 +68,11 @@ public class PreventOptimizedTargeting implements VillagerOptimizerModule, Liste
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
private void onEntityAttackVillager(EntityDamageByEntityEvent event) { private void onEntityAttackVillager(EntityDamageByEntityEvent event) {
if ( if (
event.getEntityType() == EntityType.VILLAGER event.getEntityType().equals(EntityType.VILLAGER)
&& event.getDamager() instanceof Mob attacker && event.getDamager() instanceof Mob
&& villagerCache.getOrAdd((Villager) event.getEntity()).isOptimized() && 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.config.Config;
import me.xginko.villageroptimizer.enums.permissions.Bypass; import me.xginko.villageroptimizer.enums.permissions.Bypass;
import me.xginko.villageroptimizer.modules.VillagerOptimizerModule; import me.xginko.villageroptimizer.modules.VillagerOptimizerModule;
import me.xginko.villageroptimizer.utils.KyoriUtil;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.entity.Villager; import org.bukkit.entity.Villager;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@ -24,10 +25,10 @@ public class PreventUnoptimizedTrading implements VillagerOptimizerModule, Liste
shouldEnable(); shouldEnable();
this.villagerCache = VillagerOptimizer.getCache(); this.villagerCache = VillagerOptimizer.getCache();
Config config = VillagerOptimizer.getConfiguration(); Config config = VillagerOptimizer.getConfiguration();
config.master().addComment("gameplay.prevent-trading-with-unoptimized.enable", """ config.master().addComment("gameplay.prevent-trading-with-unoptimized.enable",
Will prevent players from selecting and using trades of unoptimized villagers.\s "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.\s "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."""); "Inventories can still be opened so players can move villagers around.");
this.notify_player = config.getBoolean("gameplay.prevent-trading-with-unoptimized.notify-player", true, 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."); "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); 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) { private void onTradeOpen(TradeSelectEvent event) {
if (!event.getInventory().getType().equals(InventoryType.MERCHANT)) return;
if (event.getWhoClicked().hasPermission(Bypass.TRADE_PREVENTION.get())) return; if (event.getWhoClicked().hasPermission(Bypass.TRADE_PREVENTION.get())) return;
if (!(event.getInventory().getHolder() instanceof Villager)) return;
if ( Villager villager = (Villager) event.getInventory().getHolder();
event.getInventory().getType().equals(InventoryType.MERCHANT)
&& event.getInventory().getHolder() instanceof Villager villager if (!villagerCache.getOrAdd(villager).isOptimized()) {
&& !villagerCache.getOrAdd(villager).isOptimized()
) {
event.setCancelled(true); event.setCancelled(true);
if (notify_player) { if (notify_player) {
Player player = (Player) event.getWhoClicked(); 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) { private void onInventoryClick(InventoryClickEvent event) {
if (!event.getInventory().getType().equals(InventoryType.MERCHANT)) return;
if (event.getWhoClicked().hasPermission(Bypass.TRADE_PREVENTION.get())) return; if (event.getWhoClicked().hasPermission(Bypass.TRADE_PREVENTION.get())) return;
if (!(event.getInventory().getHolder() instanceof Villager)) return;
if ( Villager villager = (Villager) event.getInventory().getHolder();
event.getInventory().getType().equals(InventoryType.MERCHANT)
&& event.getInventory().getHolder() instanceof Villager villager if (!villagerCache.getOrAdd(villager).isOptimized()) {
&& !villagerCache.getOrAdd(villager).isOptimized()
) {
event.setCancelled(true); event.setCancelled(true);
if (notify_player) { if (notify_player) {
Player player = (Player) event.getWhoClicked(); 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(); shouldEnable();
this.scheduler = VillagerOptimizer.getFoliaLib().getImpl(); this.scheduler = VillagerOptimizer.getFoliaLib().getImpl();
Config config = VillagerOptimizer.getConfiguration(); Config config = VillagerOptimizer.getConfiguration();
config.master().addComment("gameplay.rename-optimized-villagers.enable", """ config.master().addComment("gameplay.rename-optimized-villagers.enable",
Will change a villager's name to the name configured below when they are optimized.\s "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."""); "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", 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.")); "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, 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."); "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.enums.permissions.Bypass;
import me.xginko.villageroptimizer.modules.VillagerOptimizerModule; import me.xginko.villageroptimizer.modules.VillagerOptimizerModule;
import me.xginko.villageroptimizer.utils.CommonUtil; import me.xginko.villageroptimizer.utils.CommonUtil;
import me.xginko.villageroptimizer.utils.KyoriUtil;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextReplacementConfig; import net.kyori.adventure.text.TextReplacementConfig;
import org.bukkit.Location;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.entity.Villager; import org.bukkit.entity.Villager;
@ -31,9 +31,9 @@ public class RestockOptimizedTrades implements VillagerOptimizerModule, Listener
shouldEnable(); shouldEnable();
this.villagerCache = VillagerOptimizer.getCache(); this.villagerCache = VillagerOptimizer.getCache();
Config config = VillagerOptimizer.getConfiguration(); Config config = VillagerOptimizer.getConfiguration();
config.master().addComment("gameplay.restock-optimized-trades", """ config.master().addComment("gameplay.restock-optimized-trades",
This is for automatic restocking of trades for optimized villagers. Optimized Villagers\s "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."""); "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, 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; "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, 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) { if (wVillager.canRestock(restock_delay_millis) || player_bypassing) {
wVillager.restock(); wVillager.restock();
wVillager.saveRestockTime(); wVillager.saveRestockTime();
if (notify_player && !player_bypassing) { if (notify_player && !player_bypassing) {
final TextReplacementConfig timeLeft = TextReplacementConfig.builder() final TextReplacementConfig timeLeft = TextReplacementConfig.builder()
.matchLiteral("%time%") .matchLiteral("%time%")
.replacement(CommonUtil.formatDuration(Duration.ofMillis(wVillager.getRestockCooldownMillis(restock_delay_millis)))) .replacement(CommonUtil.formatDuration(Duration.ofMillis(wVillager.getRestockCooldownMillis(restock_delay_millis))))
.build(); .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) { if (log_enabled) {
final Location location = wVillager.villager().getLocation();
VillagerOptimizer.getLog().info(Component.text("Restocked optimized villager at " + VillagerOptimizer.getLog().info(Component.text("Restocked optimized villager at " +
"x=" + location.getBlockX() + ", y=" + location.getBlockY() + ", z=" + location.getBlockZ() + CommonUtil.formatLocation(wVillager.villager().getLocation())).style(VillagerOptimizer.STYLE));
", world=" + location.getWorld().getName()).style(VillagerOptimizer.plugin_style));
} }
} }
} }

View File

@ -11,6 +11,7 @@ import me.xginko.villageroptimizer.events.VillagerOptimizeEvent;
import me.xginko.villageroptimizer.events.VillagerUnoptimizeEvent; import me.xginko.villageroptimizer.events.VillagerUnoptimizeEvent;
import me.xginko.villageroptimizer.modules.VillagerOptimizerModule; import me.xginko.villageroptimizer.modules.VillagerOptimizerModule;
import me.xginko.villageroptimizer.utils.CommonUtil; import me.xginko.villageroptimizer.utils.CommonUtil;
import me.xginko.villageroptimizer.utils.KyoriUtil;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextReplacementConfig; import net.kyori.adventure.text.TextReplacementConfig;
import org.bukkit.Location; import org.bukkit.Location;
@ -28,8 +29,8 @@ import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.BlockPlaceEvent;
import java.time.Duration; import java.time.Duration;
import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -47,10 +48,10 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener {
shouldEnable(); shouldEnable();
this.villagerCache = VillagerOptimizer.getCache(); this.villagerCache = VillagerOptimizer.getCache();
Config config = VillagerOptimizer.getConfiguration(); Config config = VillagerOptimizer.getConfiguration();
config.master().addComment("optimization-methods.block-optimization.enable", """ config.master().addComment("optimization-methods.block-optimization.enable",
When enabled, the closest villager standing near a configured block being placed will be optimized.\s "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."""); "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( this.blocks_that_disable = config.getList("optimization-methods.block-optimization.materials", Arrays.asList(
"LAPIS_BLOCK", "GLOWSTONE", "IRON_BLOCK" "LAPIS_BLOCK", "GLOWSTONE", "IRON_BLOCK"
), "Values here need to be valid bukkit Material enums for your server version." ), "Values here need to be valid bukkit Material enums for your server version."
).stream().map(configuredMaterial -> { ).stream().map(configuredMaterial -> {
@ -64,12 +65,12 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener {
} }
}).filter(Objects::nonNull).collect(Collectors.toCollection(HashSet::new)); }).filter(Objects::nonNull).collect(Collectors.toCollection(HashSet::new));
this.cooldown_millis = TimeUnit.SECONDS.toMillis( this.cooldown_millis = TimeUnit.SECONDS.toMillis(
config.getInt("optimization-methods.block-optimization.optimize-cooldown-seconds", 600, """ 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 "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.""")); "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, """ 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 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; "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, 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."); "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, 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 == null) return;
if (closestOptimizableVillager.canOptimize(cooldown_millis) || player.hasPermission(Bypass.BLOCK_COOLDOWN.get())) { 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; if (!optimizeEvent.callEvent()) return;
closestOptimizableVillager.setOptimizationType(optimizeEvent.getOptimizationType()); closestOptimizableVillager.setOptimizationType(optimizeEvent.getOptimizationType());
@ -138,15 +145,13 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener {
.matchLiteral("%blocktype%") .matchLiteral("%blocktype%")
.replacement(placed.getType().toString().toLowerCase()) .replacement(placed.getType().toString().toLowerCase())
.build(); .build();
VillagerOptimizer.getLang(player.locale()).block_optimize_success.forEach(line -> player.sendMessage(line VillagerOptimizer.getLang(player.locale()).block_optimize_success
.replaceText(vilProfession) .forEach(line -> KyoriUtil.sendMessage(player, line.replaceText(vilProfession).replaceText(placedMaterial)));
.replaceText(placedMaterial)
));
} }
if (log_enabled) { if (log_enabled) {
VillagerOptimizer.getLog().info(Component.text(player.getName() + " optimized villager by block at " + 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 { } else {
CommonUtil.shakeHead(closestOptimizableVillager.villager()); CommonUtil.shakeHead(closestOptimizableVillager.villager());
@ -155,7 +160,8 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener {
.matchLiteral("%time%") .matchLiteral("%time%")
.replacement(CommonUtil.formatDuration(Duration.ofMillis(closestOptimizableVillager.getOptimizeCooldownMillis(cooldown_millis)))) .replacement(CommonUtil.formatDuration(Duration.ofMillis(closestOptimizableVillager.getOptimizeCooldownMillis(cooldown_millis))))
.build(); .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; 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; if (!unOptimizeEvent.callEvent()) return;
closestOptimizedVillager.setOptimizationType(OptimizationType.NONE); closestOptimizedVillager.setOptimizationType(OptimizationType.NONE);
@ -201,15 +213,13 @@ public class OptimizeByBlock implements VillagerOptimizerModule, Listener {
.matchLiteral("%blocktype%") .matchLiteral("%blocktype%")
.replacement(broken.getType().toString().toLowerCase()) .replacement(broken.getType().toString().toLowerCase())
.build(); .build();
VillagerOptimizer.getLang(player.locale()).block_unoptimize_success.forEach(line -> player.sendMessage(line VillagerOptimizer.getLang(player.locale()).block_unoptimize_success
.replaceText(vilProfession) .forEach(line -> KyoriUtil.sendMessage(player, line.replaceText(vilProfession).replaceText(brokenMaterial)));
.replaceText(brokenMaterial)
));
} }
if (log_enabled) { if (log_enabled) {
VillagerOptimizer.getLog().info(Component.text(player.getName() + " unoptimized villager by block at " + 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.events.VillagerUnoptimizeEvent;
import me.xginko.villageroptimizer.modules.VillagerOptimizerModule; import me.xginko.villageroptimizer.modules.VillagerOptimizerModule;
import me.xginko.villageroptimizer.utils.CommonUtil; import me.xginko.villageroptimizer.utils.CommonUtil;
import me.xginko.villageroptimizer.utils.KyoriUtil;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextReplacementConfig; import net.kyori.adventure.text.TextReplacementConfig;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
@ -27,13 +28,15 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
import java.time.Duration; import java.time.Duration;
import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class OptimizeByNametag implements VillagerOptimizerModule, Listener { public class OptimizeByNametag implements VillagerOptimizerModule, Listener {
private final PlainTextComponentSerializer plainTextSerializer;
private final VillagerCache villagerCache; private final VillagerCache villagerCache;
private final Set<String> nametags; private final Set<String> nametags;
private final long cooldown; private final long cooldown;
@ -41,19 +44,21 @@ public class OptimizeByNametag implements VillagerOptimizerModule, Listener {
public OptimizeByNametag() { public OptimizeByNametag() {
shouldEnable(); shouldEnable();
this.plainTextSerializer = PlainTextComponentSerializer.plainText();
this.villagerCache = VillagerOptimizer.getCache(); this.villagerCache = VillagerOptimizer.getCache();
Config config = VillagerOptimizer.getConfiguration(); Config config = VillagerOptimizer.getConfiguration();
config.master().addComment("optimization-methods.nametag-optimization.enable", """ config.master().addComment("optimization-methods.nametag-optimization.enable",
Enable optimization by naming villagers to one of the names configured below.\s "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."""); "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"), this.nametags = config.getList("optimization-methods.nametag-optimization.names", Arrays.asList("Optimize", "DisableAI"),
"Names are case insensitive, capital letters won't matter.") "Names are case insensitive, capital letters won't matter.")
.stream().map(String::toLowerCase).collect(Collectors.toCollection(HashSet::new)); .stream().map(String::toLowerCase).collect(Collectors.toCollection(HashSet::new));
this.consume_nametag = config.getBoolean("optimization-methods.nametag-optimization.nametags-get-consumed", true, this.consume_nametag = config.getBoolean("optimization-methods.nametag-optimization.nametags-get-consumed", true,
"Enable or disable consumption of the used nametag item."); "Enable or disable consumption of the used nametag item.");
this.cooldown = config.getInt("optimization-methods.nametag-optimization.optimize-cooldown-seconds", 600, """ this.cooldown = TimeUnit.SECONDS.toMillis(
Cooldown in seconds until a villager can be optimized again using a nametag.\s config.getInt("optimization-methods.nametag-optimization.optimize-cooldown-seconds", 600,
Here for configuration freedom. Recommended to leave as is to not enable any exploitable behavior.""") * 1000L; "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, this.notify_player = config.getBoolean("optimization-methods.nametag-optimization.notify-player", true,
"Sends players a message when they successfully optimized a villager."); "Sends players a message when they successfully optimized a villager.");
this.log_enabled = config.getBoolean("optimization-methods.nametag-optimization.log", false); 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; if (!player.hasPermission(Optimize.NAMETAG.get())) return;
ItemStack usedItem = player.getInventory().getItem(event.getHand()); 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(); ItemMeta meta = usedItem.getItemMeta();
if (!meta.hasDisplayName()) return; if (!meta.hasDisplayName()) return;
// Get component name first, so we can manually name the villager when canceling the event to avoid item consumption. // Get component name first, so we can manually name the villager when canceling the event to avoid item consumption.
Component newVillagerName = meta.displayName(); Component newVillagerName = meta.displayName();
assert newVillagerName != null; // Legitimate since we checked for hasDisplayName() 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(); Villager villager = (Villager) event.getRightClicked();
WrappedVillager wVillager = villagerCache.getOrAdd(villager); WrappedVillager wVillager = villagerCache.getOrAdd(villager);
if (nametags.contains(name.toLowerCase())) { if (nametags.contains(name.toLowerCase())) {
if (wVillager.canOptimize(cooldown) || player.hasPermission(Bypass.NAMETAG_COOLDOWN.get())) { 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 (!optimizeEvent.callEvent()) return;
if (!consume_nametag) { if (!consume_nametag) {
@ -107,13 +119,14 @@ public class OptimizeByNametag implements VillagerOptimizerModule, Listener {
wVillager.saveOptimizeTime(); wVillager.saveOptimizeTime();
if (notify_player) { 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) { if (log_enabled) {
VillagerOptimizer.getLog().info(Component.text(player.getName() + VillagerOptimizer.getLog().info(Component.text(player.getName() +
" optimized villager by nametag '" + name + "' at " + " 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 { } else {
event.setCancelled(true); event.setCancelled(true);
@ -123,7 +136,8 @@ public class OptimizeByNametag implements VillagerOptimizerModule, Listener {
.matchLiteral("%time%") .matchLiteral("%time%")
.replacement(CommonUtil.formatDuration(Duration.ofMillis(wVillager.getOptimizeCooldownMillis(cooldown)))) .replacement(CommonUtil.formatDuration(Duration.ofMillis(wVillager.getOptimizeCooldownMillis(cooldown))))
.build(); .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 { } else {
@ -134,13 +148,14 @@ public class OptimizeByNametag implements VillagerOptimizerModule, Listener {
wVillager.setOptimizationType(OptimizationType.NONE); wVillager.setOptimizationType(OptimizationType.NONE);
if (notify_player) { 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) { if (log_enabled) {
VillagerOptimizer.getLog().info(Component.text(player.getName() + VillagerOptimizer.getLog().info(Component.text(player.getName() +
" unoptimized villager by nametag '" + name + "' at " + " 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.events.VillagerUnoptimizeEvent;
import me.xginko.villageroptimizer.modules.VillagerOptimizerModule; import me.xginko.villageroptimizer.modules.VillagerOptimizerModule;
import me.xginko.villageroptimizer.utils.CommonUtil; import me.xginko.villageroptimizer.utils.CommonUtil;
import me.xginko.villageroptimizer.utils.KyoriUtil;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextReplacementConfig; import net.kyori.adventure.text.TextReplacementConfig;
import org.bukkit.Location; import org.bukkit.Location;
@ -48,27 +49,27 @@ public class OptimizeByWorkstation implements VillagerOptimizerModule, Listener
this.scheduler = VillagerOptimizer.getFoliaLib().getImpl(); this.scheduler = VillagerOptimizer.getFoliaLib().getImpl();
this.villagerCache = VillagerOptimizer.getCache(); this.villagerCache = VillagerOptimizer.getCache();
Config config = VillagerOptimizer.getConfiguration(); Config config = VillagerOptimizer.getConfiguration();
config.master().addComment("optimization-methods.workstation-optimization.enable", """ 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 "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."""); "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, """ 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 "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; "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, """ 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 "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.\s "by having their workstation destroyed.\n" +
Intended to fix issues while trade rolling."""), 1) * 50L; "Intended to fix issues while trade rolling."), 1) * 50L;
this.pending_optimizations = Caffeine.newBuilder() this.pending_optimizations = Caffeine.newBuilder()
.expireAfterWrite(Duration.ofMillis(Math.max(resettable_delay_millis, delay_millis) + 500L)) .expireAfterWrite(Duration.ofMillis(Math.max(resettable_delay_millis, delay_millis) + 500L))
.build(); .build();
this.search_radius = config.getDouble("optimization-methods.workstation-optimization.search-radius-in-blocks", 2.0, """ 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 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."""); "The closest unoptimized villager to the player will be optimized.");
this.search_radius_squared = NumberConversions.square(search_radius); this.search_radius_squared = NumberConversions.square(search_radius);
this.cooldown_millis = TimeUnit.SECONDS.toMillis( this.cooldown_millis = TimeUnit.SECONDS.toMillis(
config.getInt("optimization-methods.workstation-optimization.optimize-cooldown-seconds", 600, """ config.getInt("optimization-methods.workstation-optimization.optimize-cooldown-seconds", 600,
Cooldown in seconds until a villager can be optimized again using a workstation.\s "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.""")); "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, 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"); "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, this.notify_player = config.getBoolean("optimization-methods.workstation-optimization.notify-player", true,
@ -132,14 +133,19 @@ public class OptimizeByWorkstation implements VillagerOptimizerModule, Listener
.matchLiteral("%time%") .matchLiteral("%time%")
.replacement(CommonUtil.formatDuration(Duration.ofMillis(finalToOptimize.getOptimizeCooldownMillis(cooldown_millis)))) .replacement(CommonUtil.formatDuration(Duration.ofMillis(finalToOptimize.getOptimizeCooldownMillis(cooldown_millis))))
.build(); .build();
VillagerOptimizer.getLang(player.locale()).nametag_on_optimize_cooldown.forEach(line -> player.sendMessage(line VillagerOptimizer.getLang(player.locale()).nametag_on_optimize_cooldown
.replaceText(timeLeft) .forEach(line -> KyoriUtil.sendMessage(player, line.replaceText(timeLeft)));
));
} }
return; 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; if (!optimizeEvent.callEvent()) return;
finalToOptimize.setOptimizationType(optimizeEvent.getOptimizationType()); finalToOptimize.setOptimizationType(optimizeEvent.getOptimizationType());
@ -154,16 +160,14 @@ public class OptimizeByWorkstation implements VillagerOptimizerModule, Listener
.matchLiteral("%workstation%") .matchLiteral("%workstation%")
.replacement(placed.getType().toString().toLowerCase()) .replacement(placed.getType().toString().toLowerCase())
.build(); .build();
VillagerOptimizer.getLang(player.locale()).workstation_optimize_success.forEach(line -> player.sendMessage(line VillagerOptimizer.getLang(player.locale()).workstation_optimize_success
.replaceText(vilProfession) .forEach(line -> KyoriUtil.sendMessage(player, line.replaceText(vilProfession).replaceText(placedWorkstation)));
.replaceText(placedWorkstation)
));
} }
if (log_enabled) { if (log_enabled) {
VillagerOptimizer.getLog().info(Component.text(player.getName() + VillagerOptimizer.getLog().info(Component.text(player.getName() +
" optimized villager by workstation (" + placed.getType().toString().toLowerCase() + ") at " + " 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)); }, toOptimize.canLooseProfession() ? resettable_delay_millis : delay_millis, TimeUnit.MILLISECONDS));
} }
@ -201,7 +205,13 @@ public class OptimizeByWorkstation implements VillagerOptimizerModule, Listener
if (closestOptimizedVillager == null) return; 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; if (!unOptimizeEvent.callEvent()) return;
closestOptimizedVillager.setOptimizationType(OptimizationType.NONE); closestOptimizedVillager.setOptimizationType(OptimizationType.NONE);
@ -215,16 +225,14 @@ public class OptimizeByWorkstation implements VillagerOptimizerModule, Listener
.matchLiteral("%workstation%") .matchLiteral("%workstation%")
.replacement(broken.getType().toString().toLowerCase()) .replacement(broken.getType().toString().toLowerCase())
.build(); .build();
VillagerOptimizer.getLang(player.locale()).workstation_unoptimize_success.forEach(line -> player.sendMessage(line VillagerOptimizer.getLang(player.locale()).workstation_unoptimize_success
.replaceText(vilProfession) .forEach(line -> KyoriUtil.sendMessage(player, line.replaceText(vilProfession).replaceText(brokenWorkstation)));
.replaceText(brokenWorkstation)
));
} }
if (log_enabled) { if (log_enabled) {
VillagerOptimizer.getLog().info(Component.text(player.getName() + VillagerOptimizer.getLog().info(Component.text(player.getName() +
" unoptimized villager by workstation (" + broken.getType().toString().toLowerCase() + ") at " + " 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) { public static @NotNull String formatDuration(Duration duration) {
if (duration.isNegative()) duration = duration.negated(); if (duration.isNegative()) duration = duration.negated();
final int seconds = duration.toSecondsPart(); final int seconds = (int) (duration.getSeconds() % 60);
final int minutes = duration.toMinutesPart(); final int minutes = (int) (duration.toMinutes() % 60);
final int hours = duration.toHoursPart(); final int hours = (int) (duration.toHours() % 24);
if (hours > 0) { if (hours > 0) {
return String.format("%02dh %02dm %02ds", hours, minutes, seconds); return String.format("%02dh %02dm %02ds", hours, minutes, seconds);
@ -49,21 +49,35 @@ public class CommonUtil {
} }
public static Villager.Profession getWorkstationProfession(@NotNull Material workstation) { public static Villager.Profession getWorkstationProfession(@NotNull Material workstation) {
return switch (workstation) { switch (workstation) {
case BARREL -> Villager.Profession.FISHERMAN; case BARREL:
case CARTOGRAPHY_TABLE -> Villager.Profession.CARTOGRAPHER; return Villager.Profession.FISHERMAN;
case SMOKER -> Villager.Profession.BUTCHER; case CARTOGRAPHY_TABLE:
case SMITHING_TABLE -> Villager.Profession.TOOLSMITH; return Villager.Profession.CARTOGRAPHER;
case GRINDSTONE -> Villager.Profession.WEAPONSMITH; case SMOKER:
case BLAST_FURNACE -> Villager.Profession.ARMORER; return Villager.Profession.BUTCHER;
case CAULDRON -> Villager.Profession.LEATHERWORKER; case SMITHING_TABLE:
case BREWING_STAND -> Villager.Profession.CLERIC; return Villager.Profession.TOOLSMITH;
case COMPOSTER -> Villager.Profession.FARMER; case GRINDSTONE:
case FLETCHING_TABLE -> Villager.Profession.FLETCHER; return Villager.Profession.WEAPONSMITH;
case LOOM -> Villager.Profession.SHEPHERD; case BLAST_FURNACE:
case LECTERN -> Villager.Profession.LIBRARIAN; return Villager.Profession.ARMORER;
case STONECUTTER -> Villager.Profession.MASON; case CAULDRON:
default -> Villager.Profession.NONE; 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);
}
}