From b317749ed07d748ae61fe699943474b2da5380ab Mon Sep 17 00:00:00 2001 From: tenkuma Date: Wed, 19 Mar 2025 06:35:42 -0300 Subject: [PATCH 01/22] Reworked classe. --- .classpath | 11 +++++ .gitignore | 1 + .project | 17 ++++++++ .settings/org.eclipse.jdt.core.prefs | 5 +++ src/gd/rf/adrianvictor/lib/Color.java | 28 ++++++++++--- ...onfiguration.java => ConfigurationEx.java} | 42 +++++++++++-------- src/gd/rf/adrianvictor/lib/Log.java | 35 ++++++++++------ src/gd/rf/adrianvictor/lib/Main.java | 16 +++++-- src/gd/rf/adrianvictor/lib/PlayerEx.java | 8 ++++ src/gd/rf/adrianvictor/lib/Text.java | 20 +++++++++ 10 files changed, 145 insertions(+), 38 deletions(-) create mode 100644 .classpath create mode 100644 .gitignore create mode 100644 .project create mode 100644 .settings/org.eclipse.jdt.core.prefs rename src/gd/rf/adrianvictor/lib/{Configuration.java => ConfigurationEx.java} (64%) create mode 100644 src/gd/rf/adrianvictor/lib/PlayerEx.java create mode 100644 src/gd/rf/adrianvictor/lib/Text.java diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..dd8cdcc --- /dev/null +++ b/.classpath @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ae3c172 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/.project b/.project new file mode 100644 index 0000000..133f5c7 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + tenkumaLib + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..2784463 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/src/gd/rf/adrianvictor/lib/Color.java b/src/gd/rf/adrianvictor/lib/Color.java index 0ec68d2..9c351b1 100644 --- a/src/gd/rf/adrianvictor/lib/Color.java +++ b/src/gd/rf/adrianvictor/lib/Color.java @@ -1,13 +1,31 @@ package gd.rf.adrianvictor.lib; +import org.bukkit.ChatColor; + public class Color { - public String formatColors(String message) { - return message.replaceAll("&([0-9a-fk-or])", "§$1"); + public static String formatColors(String message) { + return message.replaceAll("&", "§"); } - public Object[] ignoreColors(String message) { + public static Object[] ignoreColors(String message) { String parsed = message.replaceAll("&([0-9a-fk-or])", ""); - Boolean changed = parsed == message; + Boolean changed = parsed.equals(message); return new Object[] {parsed, changed}; } -} \ No newline at end of file + + public static String rainbow(String message) { + ChatColor[] rainbowColors = { ChatColor.RED, ChatColor.GOLD, ChatColor.YELLOW, ChatColor.GREEN, ChatColor.AQUA, ChatColor.BLUE, ChatColor.LIGHT_PURPLE }; + int colorIndex = 0; + String coloredMessage = ""; + + for (char c : message.toCharArray()) { + ChatColor currentColor = rainbowColors[colorIndex]; + coloredMessage += currentColor + String.valueOf(c); + colorIndex++; + if (colorIndex >= rainbowColors.length) { + colorIndex = 0; + } + } + return coloredMessage; + } +} diff --git a/src/gd/rf/adrianvictor/lib/Configuration.java b/src/gd/rf/adrianvictor/lib/ConfigurationEx.java similarity index 64% rename from src/gd/rf/adrianvictor/lib/Configuration.java rename to src/gd/rf/adrianvictor/lib/ConfigurationEx.java index 7943e93..61b85d2 100644 --- a/src/gd/rf/adrianvictor/lib/Configuration.java +++ b/src/gd/rf/adrianvictor/lib/ConfigurationEx.java @@ -11,16 +11,17 @@ import java.nio.file.Files; /** * Utility class for managing plugin configuration files. *

- * This class extends {@link Configuration} to provide custom methods for loading, saving, and managing + * This class extends {@link ConfigurationEx} to provide custom methods for loading, saving, and managing * configuration files. It automatically handles the creation of parent directories and copies default configuration * files from the plugin's resources if they do not exist. *

* Note: This class allows for flexible management of multiple configuration files, specified by their file name. */ -public class Configuration extends Configuration { +public class ConfigurationEx extends Configuration { private final File configFile; - private final String pluginName; + private Log logger; + JavaPlugin plugin; /** * Constructs a new instance of {@code ConfigUtil}. @@ -28,10 +29,11 @@ public class Configuration extends Configuration { * @param plugin the plugin instance using this configuration utility * @param fileName the name of the configuration file to manage (e.g., "config.yml", "settings.yml") */ - public Configuration(JavaPlugin plugin, String fileName) { - super(new File(plugin.getDataFolder(), fileName)); + public ConfigurationEx(JavaPlugin _plugin, String fileName, Log _logger) { + super(new File(_plugin.getDataFolder(), fileName)); + plugin = _plugin; + logger = _logger; this.configFile = new File(plugin.getDataFolder(), fileName); - this.pluginName = plugin.getDescription().getName(); } /** @@ -54,7 +56,7 @@ public class Configuration extends Configuration { try { super.load(); } catch (Exception e) { - Logger.severe(String.format("[%s] Failed to load config '%s': %s", pluginName, configFile.getName(), e.getMessage())); + logger.severe(String.format("Failed to load config '%s': %s", configFile.getName(), e.getMessage())); } } @@ -67,7 +69,7 @@ public class Configuration extends Configuration { try { Files.createDirectories(configFile.getParentFile().toPath()); } catch (IOException e) { - Logger.severe(String.format("[%s] Failed to generate default config directory: %s", pluginName, e.getMessage())); + logger.severe(String.format("Failed to generate default config directory: %s", e.getMessage())); } } @@ -80,19 +82,23 @@ public class Configuration extends Configuration { * Logs an error if the default configuration file cannot be found or copied. */ private void copyDefaultConfig() { - // Adjust the path to ensure it's correct for your JAR structure - String resourcePath = "/" + configFile.getName(); + // Load the config from the JAR directly (it is located at the root level) + String resourcePath = "/" + configFile.getName(); // Root path of JAR - try (InputStream input = getClass().getResourceAsStream(resourcePath)) { + try (InputStream input = plugin.getClass().getResourceAsStream(resourcePath)) { if (input == null) { - Logger.severe(String.format("[%s] Default config '%s' wasn't found.", pluginName, configFile.getName())); + logger.severe(String.format("Default config '%s' wasn't found in the JAR.", configFile.getName())); return; } Files.copy(input, configFile.toPath()); - Logger.info(String.format("[%s] Default config '%s' generated successfully.", pluginName, configFile.getName())); + if (Files.exists(configFile.toPath())) { + logger.info(String.format("Default config '%s' generated successfully.", configFile.getName())); + } else { + logger.warning("We tried to generate the default config file, but it was not found even after the creation. Maybe your permissions are broken?"); + } } catch (IOException e) { - Logger.severe(String.format("[%s] Failed to generate default config '%s': %s", pluginName, configFile.getName(), e.getMessage())); + logger.severe(String.format("Failed to generate default config '%s': %s", configFile.getName(), e.getMessage())); } } @@ -104,9 +110,9 @@ public class Configuration extends Configuration { public void loadConfig() { try { this.load(); - Logger.info(String.format("[%s] Config '%s' loaded successfully.", pluginName, configFile.getName())); + logger.info(String.format("Config '%s' loaded successfully.", configFile.getName())); } catch (Exception e) { - Logger.severe(String.format("[%s] Failed to load config '%s': %s", pluginName, configFile.getName(), e.getMessage())); + logger.severe(String.format("Failed to load config '%s': %s", configFile.getName(), e.getMessage())); } } @@ -118,9 +124,9 @@ public class Configuration extends Configuration { public void saveConfig() { try { this.save(); - Logger.info(String.format("[%s] Config '%s' saved successfully.", pluginName, configFile.getName())); + logger.info(String.format("Config '%s' saved successfully.", configFile.getName())); } catch (Exception e) { - Logger.severe(String.format("[%s] Failed to save config '%s': %s", pluginName, configFile.getName(), e.getMessage())); + logger.severe(String.format("Failed to save config '%s': %s", configFile.getName(), e.getMessage())); } } diff --git a/src/gd/rf/adrianvictor/lib/Log.java b/src/gd/rf/adrianvictor/lib/Log.java index f04ea93..e276349 100644 --- a/src/gd/rf/adrianvictor/lib/Log.java +++ b/src/gd/rf/adrianvictor/lib/Log.java @@ -1,28 +1,39 @@ package gd.rf.adrianvictor.lib; import static org.bukkit.Bukkit.getServer; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.java.JavaPlugin; + public class Log { - public static void info(String message) { - getServer().getLogger().info(message); + JavaPlugin plugin; + PluginDescriptionFile pdf; + + public Log(JavaPlugin _plugin) { + plugin = _plugin; + pdf = plugin.getDescription(); } - public static void infoc(String message) { - getServer().getLogger().info(message); + public void info(String message) { + getServer().getLogger().info("[" + pdf.getName() + "] " + message); } - public static void warning(String message) { - getServer().getLogger().warning(message); + public void infoc(String message) { + getServer().getLogger().info("[" + pdf.getName() + "] " + Color.formatColors(message)); } - public static void warningc(String message) { - getServer().getLogger().warning(message); + public void warning(String message) { + getServer().getLogger().warning("[" + pdf.getName() + "] " + message); } - public static void severe(String message) { - getServer().getLogger().severe(message); + public void warningc(String message) { + getServer().getLogger().warning("[" + pdf.getName() + "] " + Color.formatColors(message)); } - public static void severec(String message) { - getServer().getLogger().severe(message); + public void severe(String message) { + getServer().getLogger().severe("[" + pdf.getName() + "] " + message); + } + + public void severec(String message) { + getServer().getLogger().severe("[" + pdf.getName() + "] " + Color.formatColors(message)); } } \ No newline at end of file diff --git a/src/gd/rf/adrianvictor/lib/Main.java b/src/gd/rf/adrianvictor/lib/Main.java index c31100e..b5d59f0 100644 --- a/src/gd/rf/adrianvictor/lib/Main.java +++ b/src/gd/rf/adrianvictor/lib/Main.java @@ -2,7 +2,17 @@ package gd.rf.adrianvictor.lib; import org.bukkit.plugin.java.JavaPlugin; public class Main extends JavaPlugin { - public void onLoad() { - Log.info(this + " is loading."); - } + + Log logger; + + @Override + public void onDisable() { + logger.info("is being disabled."); + } + + @Override + public void onEnable() { + logger = new Log(this); + logger.info("is loading."); + } } \ No newline at end of file diff --git a/src/gd/rf/adrianvictor/lib/PlayerEx.java b/src/gd/rf/adrianvictor/lib/PlayerEx.java new file mode 100644 index 0000000..d5388df --- /dev/null +++ b/src/gd/rf/adrianvictor/lib/PlayerEx.java @@ -0,0 +1,8 @@ +package gd.rf.adrianvictor.lib; +import org.bukkit.entity.Player; + +public class PlayerEx { + public static void strikeLightning(Player player) { + player.getWorld().strikeLightning(player.getLocation()); + } +} diff --git a/src/gd/rf/adrianvictor/lib/Text.java b/src/gd/rf/adrianvictor/lib/Text.java new file mode 100644 index 0000000..82c2f0b --- /dev/null +++ b/src/gd/rf/adrianvictor/lib/Text.java @@ -0,0 +1,20 @@ +package gd.rf.adrianvictor.lib; + +import java.util.Random; + +import org.bukkit.ChatColor; + +public class Text { + public static String generateRandomString(int length) { + String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + Random random = new Random(); + StringBuilder randomString = new StringBuilder(length); + + for (int i = 0; i < length; i++) { + int randomIndex = random.nextInt(characters.length()); + randomString.append(characters.charAt(randomIndex)); + } + + return randomString.toString(); + } +} From 134eb44f74486b347986946013c8c3d7113a9bd2 Mon Sep 17 00:00:00 2001 From: adrian Date: Mon, 15 Dec 2025 23:53:37 -0300 Subject: [PATCH 02/22] Converted project to Gradle. --- .classpath | 11 - .gitattributes | 12 + .gitignore | 13 +- .project | 17 -- .settings/org.eclipse.jdt.core.prefs | 5 - app/build.gradle.kts | 43 +++ app/src/main/java/org/example/App.java | 14 + app/src/test/java/org/example/AppTest.java | 14 + build.gradle.kts | 10 + gradle.properties | 5 + gradle/libs.versions.toml | 10 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 45633 bytes gradle/wrapper/gradle-wrapper.properties | 7 + gradlew | 248 ++++++++++++++++++ gradlew.bat | 93 +++++++ settings.gradle.kts | 13 + .../java}/gd/rf/adrianvictor/lib/Color.java | 0 .../rf/adrianvictor/lib/ConfigurationEx.java | 2 +- .../java}/gd/rf/adrianvictor/lib/Log.java | 0 .../java}/gd/rf/adrianvictor/lib/Main.java | 0 .../gd/rf/adrianvictor/lib/PlayerEx.java | 0 .../java}/gd/rf/adrianvictor/lib/Text.java | 0 plugin.yml => src/main/resources/plugin.yml | 0 23 files changed, 482 insertions(+), 35 deletions(-) delete mode 100644 .classpath create mode 100644 .gitattributes delete mode 100644 .project delete mode 100644 .settings/org.eclipse.jdt.core.prefs create mode 100644 app/build.gradle.kts create mode 100644 app/src/main/java/org/example/App.java create mode 100644 app/src/test/java/org/example/AppTest.java create mode 100644 build.gradle.kts create mode 100644 gradle.properties create mode 100644 gradle/libs.versions.toml create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle.kts rename src/{ => main/java}/gd/rf/adrianvictor/lib/Color.java (100%) rename src/{ => main/java}/gd/rf/adrianvictor/lib/ConfigurationEx.java (98%) rename src/{ => main/java}/gd/rf/adrianvictor/lib/Log.java (100%) rename src/{ => main/java}/gd/rf/adrianvictor/lib/Main.java (100%) rename src/{ => main/java}/gd/rf/adrianvictor/lib/PlayerEx.java (100%) rename src/{ => main/java}/gd/rf/adrianvictor/lib/Text.java (100%) rename plugin.yml => src/main/resources/plugin.yml (100%) diff --git a/.classpath b/.classpath deleted file mode 100644 index dd8cdcc..0000000 --- a/.classpath +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..f91f646 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,12 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# Linux start script should use lf +/gradlew text eol=lf + +# These are Windows script files and should use crlf +*.bat text eol=crlf + +# Binary files should be left untouched +*.jar binary + diff --git a/.gitignore b/.gitignore index ae3c172..46bcab9 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,12 @@ -/bin/ +bin/ +libs/ +.idea/ +.gradle/ +build/ +out/ + +# Ignore Gradle project-specific cache directory +.gradle + +# Ignore Gradle build output directory +build diff --git a/.project b/.project deleted file mode 100644 index 133f5c7..0000000 --- a/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - tenkumaLib - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 2784463..0000000 --- a/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,5 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 -org.eclipse.jdt.core.compiler.compliance=1.8 -org.eclipse.jdt.core.compiler.release=enabled -org.eclipse.jdt.core.compiler.source=1.8 diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..b8b5f6a --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,43 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This generated file contains a sample Java application project to get you started. + * For more details on building Java & JVM projects, please refer to https://docs.gradle.org/9.2.1/userguide/building_java_projects.html in the Gradle documentation. + */ + +plugins { + // Apply the application plugin to add support for building a CLI application in Java. + application +} + +repositories { + // Use Maven Central for resolving dependencies. + mavenCentral() +} + +dependencies { + // Use JUnit Jupiter for testing. + testImplementation(libs.junit.jupiter) + + testRuntimeOnly("org.junit.platform:junit-platform-launcher") + + // This dependency is used by the application. + implementation(libs.guava) +} + +// Apply a specific Java toolchain to ease working on different environments. +java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } +} + +application { + // Define the main class for the application. + mainClass = "org.example.App" +} + +tasks.named("test") { + // Use JUnit Platform for unit tests. + useJUnitPlatform() +} diff --git a/app/src/main/java/org/example/App.java b/app/src/main/java/org/example/App.java new file mode 100644 index 0000000..e7e1af9 --- /dev/null +++ b/app/src/main/java/org/example/App.java @@ -0,0 +1,14 @@ +/* + * This source file was generated by the Gradle 'init' task + */ +package org.example; + +public class App { + public String getGreeting() { + return "Hello World!"; + } + + public static void main(String[] args) { + System.out.println(new App().getGreeting()); + } +} diff --git a/app/src/test/java/org/example/AppTest.java b/app/src/test/java/org/example/AppTest.java new file mode 100644 index 0000000..f5ce33d --- /dev/null +++ b/app/src/test/java/org/example/AppTest.java @@ -0,0 +1,14 @@ +/* + * This source file was generated by the Gradle 'init' task + */ +package org.example; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class AppTest { + @Test void appHasAGreeting() { + App classUnderTest = new App(); + assertNotNull(classUnderTest.getGreeting(), "app should have a greeting"); + } +} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..4753748 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,10 @@ +group = "io.github.adrianvic" +version = "1.2" + +plugins { + `java-library` +} + +dependencies { + compileOnly(files("libs/craftbukkit-1060.jar")) +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..377538c --- /dev/null +++ b/gradle.properties @@ -0,0 +1,5 @@ +# This file was generated by the Gradle 'init' task. +# https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties + +org.gradle.configuration-cache=true + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..8da4f83 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,10 @@ +# This file was generated by the Gradle 'init' task. +# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format + +[versions] +guava = "33.4.6-jre" +junit-jupiter = "5.12.1" + +[libraries] +guava = { module = "com.google.guava:guava", version.ref = "guava" } +junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..f8e1ee3125fe0768e9a76ee977ac089eb657005e GIT binary patch literal 45633 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..c4bdd3a --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,93 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..2cbf57b --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,13 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * For more detailed information on multi-project builds, please refer to https://docs.gradle.org/9.2.1/userguide/multi_project_builds.html in the Gradle documentation. + */ + + +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" +} + +rootProject.name = "tenkumaLib" \ No newline at end of file diff --git a/src/gd/rf/adrianvictor/lib/Color.java b/src/main/java/gd/rf/adrianvictor/lib/Color.java similarity index 100% rename from src/gd/rf/adrianvictor/lib/Color.java rename to src/main/java/gd/rf/adrianvictor/lib/Color.java diff --git a/src/gd/rf/adrianvictor/lib/ConfigurationEx.java b/src/main/java/gd/rf/adrianvictor/lib/ConfigurationEx.java similarity index 98% rename from src/gd/rf/adrianvictor/lib/ConfigurationEx.java rename to src/main/java/gd/rf/adrianvictor/lib/ConfigurationEx.java index 61b85d2..d6672d5 100644 --- a/src/gd/rf/adrianvictor/lib/ConfigurationEx.java +++ b/src/main/java/gd/rf/adrianvictor/lib/ConfigurationEx.java @@ -26,7 +26,7 @@ public class ConfigurationEx extends Configuration { /** * Constructs a new instance of {@code ConfigUtil}. * - * @param plugin the plugin instance using this configuration utility + * @param _plugin the plugin instance using this configuration utility * @param fileName the name of the configuration file to manage (e.g., "config.yml", "settings.yml") */ public ConfigurationEx(JavaPlugin _plugin, String fileName, Log _logger) { diff --git a/src/gd/rf/adrianvictor/lib/Log.java b/src/main/java/gd/rf/adrianvictor/lib/Log.java similarity index 100% rename from src/gd/rf/adrianvictor/lib/Log.java rename to src/main/java/gd/rf/adrianvictor/lib/Log.java diff --git a/src/gd/rf/adrianvictor/lib/Main.java b/src/main/java/gd/rf/adrianvictor/lib/Main.java similarity index 100% rename from src/gd/rf/adrianvictor/lib/Main.java rename to src/main/java/gd/rf/adrianvictor/lib/Main.java diff --git a/src/gd/rf/adrianvictor/lib/PlayerEx.java b/src/main/java/gd/rf/adrianvictor/lib/PlayerEx.java similarity index 100% rename from src/gd/rf/adrianvictor/lib/PlayerEx.java rename to src/main/java/gd/rf/adrianvictor/lib/PlayerEx.java diff --git a/src/gd/rf/adrianvictor/lib/Text.java b/src/main/java/gd/rf/adrianvictor/lib/Text.java similarity index 100% rename from src/gd/rf/adrianvictor/lib/Text.java rename to src/main/java/gd/rf/adrianvictor/lib/Text.java diff --git a/plugin.yml b/src/main/resources/plugin.yml similarity index 100% rename from plugin.yml rename to src/main/resources/plugin.yml From dbd8c6a83e8166aa505ecdd2f8e4f9cbdd0146b3 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Sat, 23 May 2026 12:23:40 -0300 Subject: [PATCH 03/22] Overall refactor and add reflection. --- build.gradle.kts | 121 +++++++++++++- .../lib/configuration/Configuration.java | 135 ++++++++++++++++ .../org/adrianvictor/lib/text/TextColor.java | 10 ++ .../java/gd/rf/adrianvictor/lib/Color.java | 31 ---- .../rf/adrianvictor/lib/ConfigurationEx.java | 141 ---------------- src/main/java/gd/rf/adrianvictor/lib/Log.java | 39 ----- .../java/gd/rf/adrianvictor/lib/Main.java | 18 --- .../java/gd/rf/adrianvictor/lib/PlayerEx.java | 8 - src/main/java/org/adrianvictor/lib/Main.java | 45 ++++++ .../lib/configuration/Configuration.java | 10 ++ .../InvalidConfigurationException.java | 11 ++ .../provider/ConfigurationProvider.java | 33 ++++ .../reflection/ImplementationRegistry.java | 24 +++ .../lib/reflection/VersionMatcher.java | 116 +++++++++++++ .../org/adrianvictor/lib/text/TextColor.java | 10 ++ .../adrianvictor/lib/text/TextColorUtils.java | 31 ++++ .../adrianvictor/lib/text/TextUtils.java} | 6 +- .../lib/text/provider/TextColorProvider.java | 5 + src/main/resources/plugin.yml | 9 +- .../lib/configuration/Configuration.java | 153 ++++++++++++++++++ .../org/adrianvictor/lib/text/TextColor.java | 10 ++ 21 files changed, 716 insertions(+), 250 deletions(-) create mode 100644 src/b1_7_3/java/org/adrianvictor/lib/configuration/Configuration.java create mode 100644 src/b1_7_3/java/org/adrianvictor/lib/text/TextColor.java delete mode 100644 src/main/java/gd/rf/adrianvictor/lib/Color.java delete mode 100644 src/main/java/gd/rf/adrianvictor/lib/ConfigurationEx.java delete mode 100644 src/main/java/gd/rf/adrianvictor/lib/Log.java delete mode 100644 src/main/java/gd/rf/adrianvictor/lib/Main.java delete mode 100644 src/main/java/gd/rf/adrianvictor/lib/PlayerEx.java create mode 100644 src/main/java/org/adrianvictor/lib/Main.java create mode 100644 src/main/java/org/adrianvictor/lib/configuration/Configuration.java create mode 100644 src/main/java/org/adrianvictor/lib/configuration/exception/InvalidConfigurationException.java create mode 100644 src/main/java/org/adrianvictor/lib/configuration/provider/ConfigurationProvider.java create mode 100644 src/main/java/org/adrianvictor/lib/reflection/ImplementationRegistry.java create mode 100644 src/main/java/org/adrianvictor/lib/reflection/VersionMatcher.java create mode 100644 src/main/java/org/adrianvictor/lib/text/TextColor.java create mode 100644 src/main/java/org/adrianvictor/lib/text/TextColorUtils.java rename src/main/java/{gd/rf/adrianvictor/lib/Text.java => org/adrianvictor/lib/text/TextUtils.java} (86%) create mode 100644 src/main/java/org/adrianvictor/lib/text/provider/TextColorProvider.java create mode 100644 src/r1_21/java/org/adrianvictor/lib/configuration/Configuration.java create mode 100644 src/r1_21/java/org/adrianvictor/lib/text/TextColor.java diff --git a/build.gradle.kts b/build.gradle.kts index 4753748..f8cac5a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,10 +1,121 @@ -group = "io.github.adrianvic" -version = "1.2" - plugins { - `java-library` + java + id("xyz.jpenilla.run-paper") version "2.3.1" } +group = "org.adrianvictor" +version = System.getenv("VERSION") ?: "unknown" +val buildEnv = System.getenv("BUILD_CHANNEL") + ?: if (System.getenv("JITPACK") != null) "jitpack" else "local" + +repositories { + mavenCentral() + maven("https://repo.papermc.io/repository/maven-public/") +} + +/* ----------------------------------------- */ +/* SUPPORTED VERSIONS */ +/* ----------------------------------------- */ + +val mcVersions = listOf( + "b1_7_3", + "r1_21" +) + +/* ----------------------------------------- */ +/* CREATE SOURCE SET PER VERSION */ +/* ----------------------------------------- */ + +//tasks.withType { +// inputs.property("version", project.version) +// +// filesMatching("plugin.yml") { +// expand("version" to project.version) +// } +//} + +mcVersions.forEach { ver -> + val ss = sourceSets.create(ver) { + java.srcDir("src/$ver/java") + + resources.setSrcDirs( + listOf( + "src/$ver/resources", + "src/main/resources" + ) + ) + + compileClasspath += sourceSets["main"].output + runtimeClasspath += output + compileClasspath + } + + if (ver == "r1_21") { + configurations[ss.compileOnlyConfigurationName] + .extendsFrom(configurations["compileOnly"]) + } +} + +/* ----------------------------------------- */ +/* DEPENDENCIES */ +/* ----------------------------------------- */ + dependencies { - compileOnly(files("libs/craftbukkit-1060.jar")) + add("compileOnly", "io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT") + add("r1_21CompileOnly", "io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT") + add("b1_7_3CompileOnly", files("libs/craftbukkit-1060.jar")) +} + +/* ----------------------------------------- */ +/* BUILD TASKS */ +/* ----------------------------------------- */ + +mcVersions.forEach { ver -> + tasks.register("jar${ver.replace(".", "_").replace("-", "_").replace("/", "_").capitalize()}") { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + from(sourceSets["main"].output) + from(sourceSets[ver].output) + archiveClassifier.set(ver) + + manifest { + attributes( + "TLib-Impl-Version" to ver, + "TLib-Environment" to buildEnv + ) + } + + } +} + +tasks.register("buildAll") { + dependsOn(tasks.withType()) +} + +tasks.register("bundleAll") { + from(sourceSets["main"].output) + mcVersions.forEach { ver -> + from(sourceSets[ver].output) + } + + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + + archiveClassifier.set("all-implementations") + archiveVersion.set(project.version.toString()) + + manifest { + attributes( + "Implemented-Versions" to mcVersions.joinToString(",") + ) + } +} + +/* ----------------------------------------- */ +/* JAVA SETTINGS */ +/* ----------------------------------------- */ + +java { + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) +} + +tasks.withType { + options.encoding = "UTF-8" } \ No newline at end of file diff --git a/src/b1_7_3/java/org/adrianvictor/lib/configuration/Configuration.java b/src/b1_7_3/java/org/adrianvictor/lib/configuration/Configuration.java new file mode 100644 index 0000000..04a8461 --- /dev/null +++ b/src/b1_7_3/java/org/adrianvictor/lib/configuration/Configuration.java @@ -0,0 +1,135 @@ +package org.adrianvictor.lib.configuration; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Map; + +public class Configuration implements org.adrianvictor.lib.configuration.provider.ConfigurationProvider { + org.bukkit.util.config.Configuration config; + + @Override + public void load(File file) { + config = new org.bukkit.util.config.Configuration(file); + } + + @Override + public void load(String contents) throws IOException { + String timestamp = LocalDateTime.now() + .format(DateTimeFormatter.ofPattern("HH-mm-dd-MM-yyyy")); + Path temp = Files.createTempFile("tlib-configprovider-tmp-%s".formatted(timestamp), ".yml"); + Files.writeString(temp, contents); + File file = temp.toFile(); + load(file); + } + + @Override + public void load(Reader reader) throws IOException { + String timestamp = LocalDateTime.now() + .format(DateTimeFormatter.ofPattern("HH-mm-dd-MM-yyyy")); + Path temp = Files.createTempFile("tlib-configprovider-tmp-%s".formatted(timestamp), ".yml"); + + try (BufferedWriter writer = Files.newBufferedWriter(temp)) { + reader.transferTo(writer); + } + + File file = temp.toFile(); + load(file); + } + + @Override + public boolean save(File file) { + return config.save(); + } + + @Override + public boolean save() { + return config.save(); + } + + @Override + public String saveToString() { + return config.toString(); + } + + @Override + public Object getProperty(String path) { + return config.getProperty(path); + } + + @Override + public void setProperty(String path, Object value) { + config.setProperty(path, value); + } + + @Override + public Map getAll() { + return config.getAll(); + } + + @Override + public boolean getBoolean(String path, boolean def) { + return config.getBoolean(path, def); + } + + @Override + public List getBooleanList(String path, List def) { + return config.getBooleanList(path, def); + } + + @Override + public double getDouble(String path, double def) { + return config.getDouble(path, def); + } + + @Override + public List getDoubleList(String path, List def) { + return config.getDoubleList(path, def); + } + + @Override + public int getInt(String path, int def) { + return config.getInt(path, def); + } + + @Override + public String getString(String path) { + return config.getString(path); + } + + @Override + public String getString(String path, String def) { + return config.getString(path, def); + } + + @Override + public List getStringList(String path, List def) { + return config.getStringList(path, def); + } + + @Override + public List getIntList(String path, List def) { + return config.getIntList(path, def); + } + + @Override + public List getKeys(String path) { + return config.getKeys(path); + } + + @Override + public List getKeys() { + return config.getKeys(); + } + + @Override + public List getList(String path) { + return config.getList(path); + } +} diff --git a/src/b1_7_3/java/org/adrianvictor/lib/text/TextColor.java b/src/b1_7_3/java/org/adrianvictor/lib/text/TextColor.java new file mode 100644 index 0000000..c150502 --- /dev/null +++ b/src/b1_7_3/java/org/adrianvictor/lib/text/TextColor.java @@ -0,0 +1,10 @@ +package org.adrianvictor.lib.text; + +import org.bukkit.ChatColor; + +public class TextColor implements org.adrianvictor.lib.text.provider.TextColorProvider { + @Override + public String colorize(String colorCode) { + return ChatColor.valueOf(colorCode).toString(); + } +} diff --git a/src/main/java/gd/rf/adrianvictor/lib/Color.java b/src/main/java/gd/rf/adrianvictor/lib/Color.java deleted file mode 100644 index 9c351b1..0000000 --- a/src/main/java/gd/rf/adrianvictor/lib/Color.java +++ /dev/null @@ -1,31 +0,0 @@ -package gd.rf.adrianvictor.lib; - -import org.bukkit.ChatColor; - -public class Color { - public static String formatColors(String message) { - return message.replaceAll("&", "§"); - } - - public static Object[] ignoreColors(String message) { - String parsed = message.replaceAll("&([0-9a-fk-or])", ""); - Boolean changed = parsed.equals(message); - return new Object[] {parsed, changed}; - } - - public static String rainbow(String message) { - ChatColor[] rainbowColors = { ChatColor.RED, ChatColor.GOLD, ChatColor.YELLOW, ChatColor.GREEN, ChatColor.AQUA, ChatColor.BLUE, ChatColor.LIGHT_PURPLE }; - int colorIndex = 0; - String coloredMessage = ""; - - for (char c : message.toCharArray()) { - ChatColor currentColor = rainbowColors[colorIndex]; - coloredMessage += currentColor + String.valueOf(c); - colorIndex++; - if (colorIndex >= rainbowColors.length) { - colorIndex = 0; - } - } - return coloredMessage; - } -} diff --git a/src/main/java/gd/rf/adrianvictor/lib/ConfigurationEx.java b/src/main/java/gd/rf/adrianvictor/lib/ConfigurationEx.java deleted file mode 100644 index d6672d5..0000000 --- a/src/main/java/gd/rf/adrianvictor/lib/ConfigurationEx.java +++ /dev/null @@ -1,141 +0,0 @@ -package gd.rf.adrianvictor.lib; - -import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.util.config.Configuration; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; - -/** - * Utility class for managing plugin configuration files. - *

- * This class extends {@link ConfigurationEx} to provide custom methods for loading, saving, and managing - * configuration files. It automatically handles the creation of parent directories and copies default configuration - * files from the plugin's resources if they do not exist. - *

- * Note: This class allows for flexible management of multiple configuration files, specified by their file name. - */ -public class ConfigurationEx extends Configuration { - - private final File configFile; - private Log logger; - JavaPlugin plugin; - - /** - * Constructs a new instance of {@code ConfigUtil}. - * - * @param _plugin the plugin instance using this configuration utility - * @param fileName the name of the configuration file to manage (e.g., "config.yml", "settings.yml") - */ - public ConfigurationEx(JavaPlugin _plugin, String fileName, Log _logger) { - super(new File(_plugin.getDataFolder(), fileName)); - plugin = _plugin; - logger = _logger; - this.configFile = new File(plugin.getDataFolder(), fileName); - } - - /** - * Loads the configuration file. - *

    - *
  • Creates parent directories if they do not exist.
  • - *
  • Copies the default configuration file from the plugin's resources if the configuration file does not exist.
  • - *
  • Attempts to load the configuration by calling the superclass' {@code load()} method.
  • - *
  • Logs errors if the configuration file cannot be loaded.
  • - *
- */ - @Override - public void load() { - createParentDirectories(); - - if (!configFile.exists()) { - copyDefaultConfig(); - } - - try { - super.load(); - } catch (Exception e) { - logger.severe(String.format("Failed to load config '%s': %s", configFile.getName(), e.getMessage())); - } - } - - /** - * Creates the parent directories for the configuration file if they do not exist. - *

- * Logs an error if the directories cannot be created. - */ - private void createParentDirectories() { - try { - Files.createDirectories(configFile.getParentFile().toPath()); - } catch (IOException e) { - logger.severe(String.format("Failed to generate default config directory: %s", e.getMessage())); - } - } - - /** - * Copies the default configuration file from the plugin's resources to the target location. - *

- * This method looks for a file in the plugin's resources with the same name as the configuration file being managed. - * If found, it copies this file to the plugin's data folder. - *

- * Logs an error if the default configuration file cannot be found or copied. - */ - private void copyDefaultConfig() { - // Load the config from the JAR directly (it is located at the root level) - String resourcePath = "/" + configFile.getName(); // Root path of JAR - - try (InputStream input = plugin.getClass().getResourceAsStream(resourcePath)) { - if (input == null) { - logger.severe(String.format("Default config '%s' wasn't found in the JAR.", configFile.getName())); - return; - } - - Files.copy(input, configFile.toPath()); - if (Files.exists(configFile.toPath())) { - logger.info(String.format("Default config '%s' generated successfully.", configFile.getName())); - } else { - logger.warning("We tried to generate the default config file, but it was not found even after the creation. Maybe your permissions are broken?"); - } - } catch (IOException e) { - logger.severe(String.format("Failed to generate default config '%s': %s", configFile.getName(), e.getMessage())); - } - } - - /** - * Loads the configuration file and logs the result. - *

- * Logs a message indicating whether the configuration was loaded successfully. - */ - public void loadConfig() { - try { - this.load(); - logger.info(String.format("Config '%s' loaded successfully.", configFile.getName())); - } catch (Exception e) { - logger.severe(String.format("Failed to load config '%s': %s", configFile.getName(), e.getMessage())); - } - } - - /** - * Saves the configuration file and logs the result. - *

- * Logs a message indicating whether the configuration was saved successfully. - */ - public void saveConfig() { - try { - this.save(); - logger.info(String.format("Config '%s' saved successfully.", configFile.getName())); - } catch (Exception e) { - logger.severe(String.format("Failed to save config '%s': %s", configFile.getName(), e.getMessage())); - } - } - - /** - * Returns the configuration file managed by this utility. - * - * @return the configuration file - */ - public File getConfig() { - return configFile; - } -} \ No newline at end of file diff --git a/src/main/java/gd/rf/adrianvictor/lib/Log.java b/src/main/java/gd/rf/adrianvictor/lib/Log.java deleted file mode 100644 index e276349..0000000 --- a/src/main/java/gd/rf/adrianvictor/lib/Log.java +++ /dev/null @@ -1,39 +0,0 @@ -package gd.rf.adrianvictor.lib; -import static org.bukkit.Bukkit.getServer; - -import org.bukkit.plugin.PluginDescriptionFile; -import org.bukkit.plugin.java.JavaPlugin; - -public class Log { - JavaPlugin plugin; - PluginDescriptionFile pdf; - - public Log(JavaPlugin _plugin) { - plugin = _plugin; - pdf = plugin.getDescription(); - } - - public void info(String message) { - getServer().getLogger().info("[" + pdf.getName() + "] " + message); - } - - public void infoc(String message) { - getServer().getLogger().info("[" + pdf.getName() + "] " + Color.formatColors(message)); - } - - public void warning(String message) { - getServer().getLogger().warning("[" + pdf.getName() + "] " + message); - } - - public void warningc(String message) { - getServer().getLogger().warning("[" + pdf.getName() + "] " + Color.formatColors(message)); - } - - public void severe(String message) { - getServer().getLogger().severe("[" + pdf.getName() + "] " + message); - } - - public void severec(String message) { - getServer().getLogger().severe("[" + pdf.getName() + "] " + Color.formatColors(message)); - } -} \ No newline at end of file diff --git a/src/main/java/gd/rf/adrianvictor/lib/Main.java b/src/main/java/gd/rf/adrianvictor/lib/Main.java deleted file mode 100644 index b5d59f0..0000000 --- a/src/main/java/gd/rf/adrianvictor/lib/Main.java +++ /dev/null @@ -1,18 +0,0 @@ -package gd.rf.adrianvictor.lib; -import org.bukkit.plugin.java.JavaPlugin; - -public class Main extends JavaPlugin { - - Log logger; - - @Override - public void onDisable() { - logger.info("is being disabled."); - } - - @Override - public void onEnable() { - logger = new Log(this); - logger.info("is loading."); - } -} \ No newline at end of file diff --git a/src/main/java/gd/rf/adrianvictor/lib/PlayerEx.java b/src/main/java/gd/rf/adrianvictor/lib/PlayerEx.java deleted file mode 100644 index d5388df..0000000 --- a/src/main/java/gd/rf/adrianvictor/lib/PlayerEx.java +++ /dev/null @@ -1,8 +0,0 @@ -package gd.rf.adrianvictor.lib; -import org.bukkit.entity.Player; - -public class PlayerEx { - public static void strikeLightning(Player player) { - player.getWorld().strikeLightning(player.getLocation()); - } -} diff --git a/src/main/java/org/adrianvictor/lib/Main.java b/src/main/java/org/adrianvictor/lib/Main.java new file mode 100644 index 0000000..c8cb630 --- /dev/null +++ b/src/main/java/org/adrianvictor/lib/Main.java @@ -0,0 +1,45 @@ +package org.adrianvictor.lib; +import org.adrianvictor.lib.reflection.ImplementationRegistry; +import org.adrianvictor.lib.reflection.VersionMatcher; +import org.bukkit.plugin.java.JavaPlugin; + +public class Main extends JavaPlugin { + VersionMatcher matcher; + String serverVersion; + ImplementationRegistry registry; + private static Main instance; + + @Override + public void onDisable() { + getLogger().info("is being disabled."); + } + + @Override + public void onEnable() { + getLogger().info("is loading."); + + instance = this; + matcher = new VersionMatcher(); + serverVersion = matcher.getServerVersion(); + + String classSuffix = matcher.getClassSuffix("release", serverVersion); + + if (classSuffix == null) { + classSuffix = matcher.getClassSuffix("beta", serverVersion); + } + + if (classSuffix == null) { + throw new IllegalStateException("No suitable implementation found for version " + serverVersion); + } + + registry = new ImplementationRegistry(classSuffix); + } + + public static Main getInstance() { + return instance; + } + + public ImplementationRegistry getRegistry() { + return registry; + } +} \ No newline at end of file diff --git a/src/main/java/org/adrianvictor/lib/configuration/Configuration.java b/src/main/java/org/adrianvictor/lib/configuration/Configuration.java new file mode 100644 index 0000000..e641f75 --- /dev/null +++ b/src/main/java/org/adrianvictor/lib/configuration/Configuration.java @@ -0,0 +1,10 @@ +package org.adrianvictor.lib.configuration; + +import org.adrianvictor.lib.Main; +import org.adrianvictor.lib.configuration.provider.ConfigurationProvider; + +public class Configuration { + public static ConfigurationProvider create() { + return Main.getInstance().getRegistry().getInstance(ConfigurationProvider.class, "org.adrianvictor.lib.configuraion"); + } +} \ No newline at end of file diff --git a/src/main/java/org/adrianvictor/lib/configuration/exception/InvalidConfigurationException.java b/src/main/java/org/adrianvictor/lib/configuration/exception/InvalidConfigurationException.java new file mode 100644 index 0000000..eda57f7 --- /dev/null +++ b/src/main/java/org/adrianvictor/lib/configuration/exception/InvalidConfigurationException.java @@ -0,0 +1,11 @@ +package org.adrianvictor.lib.configuration.exception; + +public class InvalidConfigurationException extends Exception { + public InvalidConfigurationException(String message) { + super(message); + } + + public InvalidConfigurationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/adrianvictor/lib/configuration/provider/ConfigurationProvider.java b/src/main/java/org/adrianvictor/lib/configuration/provider/ConfigurationProvider.java new file mode 100644 index 0000000..718212e --- /dev/null +++ b/src/main/java/org/adrianvictor/lib/configuration/provider/ConfigurationProvider.java @@ -0,0 +1,33 @@ +package org.adrianvictor.lib.configuration.provider; + +import org.adrianvictor.lib.configuration.exception.InvalidConfigurationException; + +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.util.List; +import java.util.Map; + +public interface ConfigurationProvider { + void load(File file) throws IOException, InvalidConfigurationException; + void load(String contents) throws IOException, InvalidConfigurationException; + void load(Reader reader) throws IOException, InvalidConfigurationException; + boolean save(File file); + boolean save(); + String saveToString(); + Object getProperty(String path); + void setProperty(String path, Object value); + Map getAll(); + boolean getBoolean(String path, boolean def); + List getBooleanList(String path, List def); + double getDouble(String path, double def); + List getDoubleList(String path, List def); + int getInt(String path, int def); + String getString(String path); + String getString(String path, String def); + List getStringList(String path, List def); + List getIntList(String path, List def); + List getKeys(String path); + List getKeys(); + List getList(String path); +} \ No newline at end of file diff --git a/src/main/java/org/adrianvictor/lib/reflection/ImplementationRegistry.java b/src/main/java/org/adrianvictor/lib/reflection/ImplementationRegistry.java new file mode 100644 index 0000000..fcdf626 --- /dev/null +++ b/src/main/java/org/adrianvictor/lib/reflection/ImplementationRegistry.java @@ -0,0 +1,24 @@ +package org.adrianvictor.lib.reflection; + +public class ImplementationRegistry { + + private final String classSuffix; + + public ImplementationRegistry(String classSuffix) { + this.classSuffix = classSuffix; + } + + public T getInstance(Class interfaceClass, String packagePath) { + String implClassName = packagePath.replace( + "org.adrianvictor.lib", + "org.adrianvictor.lib.impl." + classSuffix + ); + + try { + Class implClass = Class.forName(implClassName); + return interfaceClass.cast(implClass.getDeclaredConstructor().newInstance()); + } catch (ReflectiveOperationException e) { + throw new IllegalStateException("Cannot load " + implClassName, e); + } + } +} diff --git a/src/main/java/org/adrianvictor/lib/reflection/VersionMatcher.java b/src/main/java/org/adrianvictor/lib/reflection/VersionMatcher.java new file mode 100644 index 0000000..6c80828 --- /dev/null +++ b/src/main/java/org/adrianvictor/lib/reflection/VersionMatcher.java @@ -0,0 +1,116 @@ +package org.adrianvictor.lib.reflection; + +import org.bukkit.Bukkit; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +public class VersionMatcher { + + public VersionMatcher() {} + + private record Entry(String pattern, String classSuffix) {} + + public String getClassSuffix(String type, String serverVersion) { + if (type == null || serverVersion == null) { + return null; + } + + Map> map = populateMap(); + List entries = map.get(type.toLowerCase()); + if (entries == null || entries.isEmpty()) { + return null; + } + + // Exact match + for (Entry e : entries) { + if (safeMatches(e.pattern, serverVersion)) { + return e.classSuffix; + } + } + + // Fallback to closest version + List sorted = new ArrayList<>(entries); + Collections.sort(sorted, (a, b) -> compareVersions(a.pattern, b.pattern)); + + Entry oldest = sorted.get(0); + Entry newest = sorted.get(sorted.size() - 1); + + int cmpOldest = compareVersions(serverVersion, oldest.pattern); + int cmpNewest = compareVersions(serverVersion, newest.pattern); + + if (cmpOldest < 0) { + return oldest.classSuffix; + } else if (cmpNewest > 0) { + return newest.classSuffix; + } + + return newest.classSuffix; + } + + public String getServerVersion() { + String rawVersion = null; + try { + rawVersion = Bukkit.getMinecraftVersion(); + } catch (NoSuchMethodError ignored) {} + + if (rawVersion == null || rawVersion.isEmpty()) { + String v = Bukkit.getVersion(); + int start = v.lastIndexOf("MC: "); + if (start != -1) { + rawVersion = v.substring(start + 4, v.length() - 1); + } else { + rawVersion = "unknown"; + } + } + + return rawVersion; + } + + private Map> populateMap() { + Map> map = new HashMap<>(); + map.put("release", List.of( + new Entry("^1\\.21\\..*$", "r1_21") + )); + map.put("beta", List.of( + new Entry("^1\\.7\\.3$", "b1_7_3") + )); + return map; + } + + private int compareVersions(String v1, String v2) { + String clean1 = v1.replaceAll("[^0-9.]", ""); + String clean2 = v2.replaceAll("[^0-9.]", ""); + + String[] a1 = clean1.split("\\."); + String[] a2 = clean2.split("\\."); + + int len = Math.max(a1.length, a2.length); + for (int i = 0; i < len; i++) { + int n1 = i < a1.length ? parseInt(a1[i]) : 0; + int n2 = i < a2.length ? parseInt(a2[i]) : 0; + if (n1 != n2) { + return n1 - n2; + } + } + return 0; + } + + private int parseInt(String s) { + try { + return Integer.parseInt(s); + } catch (NumberFormatException e) { + return 0; + } + } + + private static boolean safeMatches(String expression, String against) { + String cleanPattern = expression.trim(); + Pattern pattern = Pattern.compile(cleanPattern, Pattern.CASE_INSENSITIVE); + return pattern.matcher(against).matches(); + } +} \ No newline at end of file diff --git a/src/main/java/org/adrianvictor/lib/text/TextColor.java b/src/main/java/org/adrianvictor/lib/text/TextColor.java new file mode 100644 index 0000000..98ca014 --- /dev/null +++ b/src/main/java/org/adrianvictor/lib/text/TextColor.java @@ -0,0 +1,10 @@ +package org.adrianvictor.lib.text; + +import org.adrianvictor.lib.Main; +import org.adrianvictor.lib.text.provider.TextColorProvider; + +public class TextColor { + public static TextColorProvider create() { + return Main.getInstance().getRegistry().getInstance(TextColorProvider.class, "org.adrianvictor.lib.text"); + } +} diff --git a/src/main/java/org/adrianvictor/lib/text/TextColorUtils.java b/src/main/java/org/adrianvictor/lib/text/TextColorUtils.java new file mode 100644 index 0000000..ba0fe58 --- /dev/null +++ b/src/main/java/org/adrianvictor/lib/text/TextColorUtils.java @@ -0,0 +1,31 @@ +package org.adrianvictor.lib.text; + +import org.adrianvictor.lib.text.provider.TextColorProvider; + +public class TextColorUtils { + private static final TextColorProvider provider = TextColor.create(); + + public static String formatColors(String message) { + return message.replace("&", "§"); + } + + public static String ignoreColors(String message) { + return message.replaceAll("&([0-9a-fk-or])", ""); + } + + public static String rainbow(String message) { + String[] colorNames = {"RED", "GOLD", "YELLOW", "GREEN", "AQUA", "BLUE", "LIGHT_PURPLE"}; + int colorIndex = 0; + String coloredMessage = ""; + + for (char c : message.toCharArray()) { + String currentColor = provider.colorize(colorNames[colorIndex]); + coloredMessage += currentColor + c; + colorIndex++; + if (colorIndex >= colorNames.length) { + colorIndex = 0; + } + } + return coloredMessage; + } +} diff --git a/src/main/java/gd/rf/adrianvictor/lib/Text.java b/src/main/java/org/adrianvictor/lib/text/TextUtils.java similarity index 86% rename from src/main/java/gd/rf/adrianvictor/lib/Text.java rename to src/main/java/org/adrianvictor/lib/text/TextUtils.java index 82c2f0b..84033bf 100644 --- a/src/main/java/gd/rf/adrianvictor/lib/Text.java +++ b/src/main/java/org/adrianvictor/lib/text/TextUtils.java @@ -1,10 +1,8 @@ -package gd.rf.adrianvictor.lib; +package org.adrianvictor.lib.text; import java.util.Random; -import org.bukkit.ChatColor; - -public class Text { +public class TextUtils { public static String generateRandomString(int length) { String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; Random random = new Random(); diff --git a/src/main/java/org/adrianvictor/lib/text/provider/TextColorProvider.java b/src/main/java/org/adrianvictor/lib/text/provider/TextColorProvider.java new file mode 100644 index 0000000..a106e58 --- /dev/null +++ b/src/main/java/org/adrianvictor/lib/text/provider/TextColorProvider.java @@ -0,0 +1,5 @@ +package org.adrianvictor.lib.text.provider; + +public interface TextColorProvider { + String colorize(String colorCode); +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 643f8ef..5585d53 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,7 +1,8 @@ +name: tenkumaLib author: tenkuma database: false -main: gd.rf.adrianvictor.lib.Main -name: tenkumaLib +main: org.adrianvictor.lib.Main startup: startup -url: https://adrianvictor.rf.gd -version: '1.0' \ No newline at end of file +url: https://adrianvic.github.io +version: '2.0' +api-version: '1.13' \ No newline at end of file diff --git a/src/r1_21/java/org/adrianvictor/lib/configuration/Configuration.java b/src/r1_21/java/org/adrianvictor/lib/configuration/Configuration.java new file mode 100644 index 0000000..a6eb3d0 --- /dev/null +++ b/src/r1_21/java/org/adrianvictor/lib/configuration/Configuration.java @@ -0,0 +1,153 @@ +package org.adrianvictor.lib.configuration; + +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.util.*; + +public class Configuration implements org.adrianvictor.lib.configuration.provider.ConfigurationProvider { + YamlConfiguration config = new YamlConfiguration(); + File configFile; + + @Override + public void load(File file) throws IOException, org.adrianvictor.lib.configuration.exception.InvalidConfigurationException { + try { + config.load(file); + configFile = file; + } catch (InvalidConfigurationException e) { + throw new org.adrianvictor.lib.configuration.exception.InvalidConfigurationException(e.getMessage()); + } + } + + @Override + public void load(String contents) throws IOException, org.adrianvictor.lib.configuration.exception.InvalidConfigurationException { + try { + config.load(contents); + } catch (InvalidConfigurationException e) { + throw new org.adrianvictor.lib.configuration.exception.InvalidConfigurationException(e.getMessage()); + } + } + + @Override + public void load(Reader reader) throws IOException, org.adrianvictor.lib.configuration.exception.InvalidConfigurationException { + try { + config.load(reader); + } catch (InvalidConfigurationException e) { + throw new org.adrianvictor.lib.configuration.exception.InvalidConfigurationException(e.getMessage()); + } + } + + @Override + public boolean save(File file) { + try { + config.save(file); + return true; + } catch (IOException e) { + return false; + } + } + + @Override + public boolean save() { + try { + config.save(configFile); + return true; + } catch (IOException e) { + return false; + } + } + + @Override + public String saveToString() { + return config.saveToString(); + } + + @Override + public Object getProperty(String path) { + return config.get(path); + } + + @Override + public void setProperty(String path, Object value) { + config.set(path, value); + } + + @Override + public Map getAll() { + assert config.getRoot() != null; + return config.getRoot().getValues(true); + } + + @Override + public boolean getBoolean(String path, boolean def) { + return config.getBoolean(path, def); + } + + @Override + public List getBooleanList(String path, List def) { + List result = config.getBooleanList(path); + if (result.isEmpty()) result = def; + return result; + } + + @Override + public double getDouble(String path, double def) { + return config.getDouble(path, def); + } + + @Override + public List getDoubleList(String path, List def) { + List result = config.getDoubleList(path); + if (result.isEmpty()) result = def; + return result; + } + + @Override + public int getInt(String path, int def) { + return config.getInt(path, def); + } + + @Override + public String getString(String path) { + return config.getString(path); + } + + @Override + public String getString(String path, String def) { + return config.getString(path, def); + } + + @Override + public List getStringList(String path, List def) { + List result = config.getStringList(path); + if (result.isEmpty()) result = def; + return result; + } + + @Override + public List getIntList(String path, List def) { + List result = config.getIntegerList(path); + if (result.isEmpty()) result = def; + return result; + } + + @Override + public List getKeys(String path) { + Set set = Objects.requireNonNull(config.getConfigurationSection(path)).getKeys(true); + return set.stream().toList(); + } + + @Override + public List getKeys() { + Set set = Objects.requireNonNull(config.getRoot()).getKeys(true); + return set.stream().toList(); + } + + @Override + public List getList(String path) { + return Collections.singletonList(config.getList(path)); + } +} diff --git a/src/r1_21/java/org/adrianvictor/lib/text/TextColor.java b/src/r1_21/java/org/adrianvictor/lib/text/TextColor.java new file mode 100644 index 0000000..f1b76dc --- /dev/null +++ b/src/r1_21/java/org/adrianvictor/lib/text/TextColor.java @@ -0,0 +1,10 @@ +package org.adrianvictor.lib.text; + +import net.kyori.adventure.text.format.NamedTextColor; + +public class TextColor implements org.adrianvictor.lib.text.provider.TextColorProvider { + @Override + public String colorize(String colorCode) { + return NamedTextColor.NAMES.valueOr(colorCode, NamedTextColor.WHITE).toString(); + } +} From 2e0745d505f8df51a18f25b2ff4a1b1bd7174a4b Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Sat, 23 May 2026 13:25:11 -0300 Subject: [PATCH 04/22] Add duplicatesStrategy to ProcessResources --- build.gradle.kts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index f8cac5a..ecd5724 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -108,6 +108,14 @@ tasks.register("bundleAll") { } } +tasks.withType().configureEach { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE +} + +tasks.withType().configureEach { + options.encoding = "UTF-8" +} + /* ----------------------------------------- */ /* JAVA SETTINGS */ /* ----------------------------------------- */ From c36d00195467b9f0410247d54a8588ae1fc72bf8 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Sat, 23 May 2026 14:36:04 -0300 Subject: [PATCH 05/22] Convert to an actual library --- src/main/java/org/adrianvictor/lib/Main.java | 45 ------------------- .../lib/configuration/Configuration.java | 6 +-- .../org/adrianvictor/lib/text/TextColor.java | 6 +-- .../adrianvictor/lib/text/TextColorUtils.java | 13 ++++-- 4 files changed, 15 insertions(+), 55 deletions(-) delete mode 100644 src/main/java/org/adrianvictor/lib/Main.java diff --git a/src/main/java/org/adrianvictor/lib/Main.java b/src/main/java/org/adrianvictor/lib/Main.java deleted file mode 100644 index c8cb630..0000000 --- a/src/main/java/org/adrianvictor/lib/Main.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.adrianvictor.lib; -import org.adrianvictor.lib.reflection.ImplementationRegistry; -import org.adrianvictor.lib.reflection.VersionMatcher; -import org.bukkit.plugin.java.JavaPlugin; - -public class Main extends JavaPlugin { - VersionMatcher matcher; - String serverVersion; - ImplementationRegistry registry; - private static Main instance; - - @Override - public void onDisable() { - getLogger().info("is being disabled."); - } - - @Override - public void onEnable() { - getLogger().info("is loading."); - - instance = this; - matcher = new VersionMatcher(); - serverVersion = matcher.getServerVersion(); - - String classSuffix = matcher.getClassSuffix("release", serverVersion); - - if (classSuffix == null) { - classSuffix = matcher.getClassSuffix("beta", serverVersion); - } - - if (classSuffix == null) { - throw new IllegalStateException("No suitable implementation found for version " + serverVersion); - } - - registry = new ImplementationRegistry(classSuffix); - } - - public static Main getInstance() { - return instance; - } - - public ImplementationRegistry getRegistry() { - return registry; - } -} \ No newline at end of file diff --git a/src/main/java/org/adrianvictor/lib/configuration/Configuration.java b/src/main/java/org/adrianvictor/lib/configuration/Configuration.java index e641f75..9bb76b5 100644 --- a/src/main/java/org/adrianvictor/lib/configuration/Configuration.java +++ b/src/main/java/org/adrianvictor/lib/configuration/Configuration.java @@ -1,10 +1,10 @@ package org.adrianvictor.lib.configuration; -import org.adrianvictor.lib.Main; import org.adrianvictor.lib.configuration.provider.ConfigurationProvider; +import org.adrianvictor.lib.reflection.ImplementationRegistry; public class Configuration { - public static ConfigurationProvider create() { - return Main.getInstance().getRegistry().getInstance(ConfigurationProvider.class, "org.adrianvictor.lib.configuraion"); + public static ConfigurationProvider create(ImplementationRegistry registry) { + return registry.getInstance(ConfigurationProvider.class, "org.adrianvictor.lib.configuraion"); } } \ No newline at end of file diff --git a/src/main/java/org/adrianvictor/lib/text/TextColor.java b/src/main/java/org/adrianvictor/lib/text/TextColor.java index 98ca014..67629a5 100644 --- a/src/main/java/org/adrianvictor/lib/text/TextColor.java +++ b/src/main/java/org/adrianvictor/lib/text/TextColor.java @@ -1,10 +1,10 @@ package org.adrianvictor.lib.text; -import org.adrianvictor.lib.Main; +import org.adrianvictor.lib.reflection.ImplementationRegistry; import org.adrianvictor.lib.text.provider.TextColorProvider; public class TextColor { - public static TextColorProvider create() { - return Main.getInstance().getRegistry().getInstance(TextColorProvider.class, "org.adrianvictor.lib.text"); + public static TextColorProvider create(ImplementationRegistry registry) { + return registry.getInstance(TextColorProvider.class, "org.adrianvictor.lib.text"); } } diff --git a/src/main/java/org/adrianvictor/lib/text/TextColorUtils.java b/src/main/java/org/adrianvictor/lib/text/TextColorUtils.java index ba0fe58..1e1183e 100644 --- a/src/main/java/org/adrianvictor/lib/text/TextColorUtils.java +++ b/src/main/java/org/adrianvictor/lib/text/TextColorUtils.java @@ -1,19 +1,24 @@ package org.adrianvictor.lib.text; +import org.adrianvictor.lib.reflection.ImplementationRegistry; import org.adrianvictor.lib.text.provider.TextColorProvider; public class TextColorUtils { - private static final TextColorProvider provider = TextColor.create(); + private final TextColorProvider provider; - public static String formatColors(String message) { + public TextColorUtils(ImplementationRegistry registry) { + provider = TextColor.create(registry); + } + + public String formatColors(String message) { return message.replace("&", "§"); } - public static String ignoreColors(String message) { + public String ignoreColors(String message) { return message.replaceAll("&([0-9a-fk-or])", ""); } - public static String rainbow(String message) { + public String rainbow(String message) { String[] colorNames = {"RED", "GOLD", "YELLOW", "GREEN", "AQUA", "BLUE", "LIGHT_PURPLE"}; int colorIndex = 0; String coloredMessage = ""; From 623e581623f73563f74eed51e8e601c701ddcf27 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Sat, 23 May 2026 15:09:19 -0300 Subject: [PATCH 06/22] Fix ImplementationRegistry --- .../b1_7_3}/configuration/Configuration.java | 2 +- .../lib/{ => impl/b1_7_3}/text/TextColor.java | 2 +- .../lib/configuration/Configuration.java | 2 +- .../reflection/ImplementationRegistry.java | 28 +++++++++++++------ .../org/adrianvictor/lib/text/TextColor.java | 2 +- .../r1_21}/configuration/Configuration.java | 2 +- .../lib/{ => impl/r1_21}/text/TextColor.java | 2 +- 7 files changed, 26 insertions(+), 14 deletions(-) rename src/b1_7_3/java/org/adrianvictor/lib/{ => impl/b1_7_3}/configuration/Configuration.java (98%) rename src/b1_7_3/java/org/adrianvictor/lib/{ => impl/b1_7_3}/text/TextColor.java (83%) rename src/r1_21/java/org/adrianvictor/lib/{ => impl/r1_21}/configuration/Configuration.java (98%) rename src/r1_21/java/org/adrianvictor/lib/{ => impl/r1_21}/text/TextColor.java (86%) diff --git a/src/b1_7_3/java/org/adrianvictor/lib/configuration/Configuration.java b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/configuration/Configuration.java similarity index 98% rename from src/b1_7_3/java/org/adrianvictor/lib/configuration/Configuration.java rename to src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/configuration/Configuration.java index 04a8461..9e8c31b 100644 --- a/src/b1_7_3/java/org/adrianvictor/lib/configuration/Configuration.java +++ b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/configuration/Configuration.java @@ -1,4 +1,4 @@ -package org.adrianvictor.lib.configuration; +package org.adrianvictor.lib.impl.b1_7_3.configuration; import java.io.BufferedWriter; import java.io.File; diff --git a/src/b1_7_3/java/org/adrianvictor/lib/text/TextColor.java b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/text/TextColor.java similarity index 83% rename from src/b1_7_3/java/org/adrianvictor/lib/text/TextColor.java rename to src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/text/TextColor.java index c150502..8fa20ce 100644 --- a/src/b1_7_3/java/org/adrianvictor/lib/text/TextColor.java +++ b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/text/TextColor.java @@ -1,4 +1,4 @@ -package org.adrianvictor.lib.text; +package org.adrianvictor.lib.impl.b1_7_3.text; import org.bukkit.ChatColor; diff --git a/src/main/java/org/adrianvictor/lib/configuration/Configuration.java b/src/main/java/org/adrianvictor/lib/configuration/Configuration.java index 9bb76b5..fa005b5 100644 --- a/src/main/java/org/adrianvictor/lib/configuration/Configuration.java +++ b/src/main/java/org/adrianvictor/lib/configuration/Configuration.java @@ -5,6 +5,6 @@ import org.adrianvictor.lib.reflection.ImplementationRegistry; public class Configuration { public static ConfigurationProvider create(ImplementationRegistry registry) { - return registry.getInstance(ConfigurationProvider.class, "org.adrianvictor.lib.configuraion"); + return registry.getInstance(ConfigurationProvider.class); } } \ No newline at end of file diff --git a/src/main/java/org/adrianvictor/lib/reflection/ImplementationRegistry.java b/src/main/java/org/adrianvictor/lib/reflection/ImplementationRegistry.java index fcdf626..c9afd44 100644 --- a/src/main/java/org/adrianvictor/lib/reflection/ImplementationRegistry.java +++ b/src/main/java/org/adrianvictor/lib/reflection/ImplementationRegistry.java @@ -8,17 +8,29 @@ public class ImplementationRegistry { this.classSuffix = classSuffix; } - public T getInstance(Class interfaceClass, String packagePath) { - String implClassName = packagePath.replace( - "org.adrianvictor.lib", - "org.adrianvictor.lib.impl." + classSuffix - ); + public T getInstance(Class apiClass, String target, String replacement) { + String implName = + apiClass.getName() + .replace( + target, + replacement + classSuffix + "." + ); try { - Class implClass = Class.forName(implClassName); - return interfaceClass.cast(implClass.getDeclaredConstructor().newInstance()); + Class implClass = Class.forName(implName); + + return apiClass.cast( + implClass.getDeclaredConstructor().newInstance() + ); } catch (ReflectiveOperationException e) { - throw new IllegalStateException("Cannot load " + implClassName, e); + throw new IllegalStateException( + "Cannot load " + implName, + e + ); } } + + public T getInstance(Class apiClass) { + return getInstance(apiClass, "org.adrianvictor.lib", "org.adrianvictor.lib.impl."); + } } diff --git a/src/main/java/org/adrianvictor/lib/text/TextColor.java b/src/main/java/org/adrianvictor/lib/text/TextColor.java index 67629a5..9252de4 100644 --- a/src/main/java/org/adrianvictor/lib/text/TextColor.java +++ b/src/main/java/org/adrianvictor/lib/text/TextColor.java @@ -5,6 +5,6 @@ import org.adrianvictor.lib.text.provider.TextColorProvider; public class TextColor { public static TextColorProvider create(ImplementationRegistry registry) { - return registry.getInstance(TextColorProvider.class, "org.adrianvictor.lib.text"); + return registry.getInstance(TextColorProvider.class); } } diff --git a/src/r1_21/java/org/adrianvictor/lib/configuration/Configuration.java b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/configuration/Configuration.java similarity index 98% rename from src/r1_21/java/org/adrianvictor/lib/configuration/Configuration.java rename to src/r1_21/java/org/adrianvictor/lib/impl/r1_21/configuration/Configuration.java index a6eb3d0..0fe9c3d 100644 --- a/src/r1_21/java/org/adrianvictor/lib/configuration/Configuration.java +++ b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/configuration/Configuration.java @@ -1,4 +1,4 @@ -package org.adrianvictor.lib.configuration; +package org.adrianvictor.lib.impl.r1_21.configuration; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.YamlConfiguration; diff --git a/src/r1_21/java/org/adrianvictor/lib/text/TextColor.java b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/text/TextColor.java similarity index 86% rename from src/r1_21/java/org/adrianvictor/lib/text/TextColor.java rename to src/r1_21/java/org/adrianvictor/lib/impl/r1_21/text/TextColor.java index f1b76dc..68fa062 100644 --- a/src/r1_21/java/org/adrianvictor/lib/text/TextColor.java +++ b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/text/TextColor.java @@ -1,4 +1,4 @@ -package org.adrianvictor.lib.text; +package org.adrianvictor.lib.impl.r1_21.text; import net.kyori.adventure.text.format.NamedTextColor; From 811b54517a403e190f0955638f281d95c9ef0702 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Sat, 23 May 2026 15:12:13 -0300 Subject: [PATCH 07/22] Safer classpath building in ImplementationRegistry --- .../lib/reflection/ImplementationRegistry.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/adrianvictor/lib/reflection/ImplementationRegistry.java b/src/main/java/org/adrianvictor/lib/reflection/ImplementationRegistry.java index c9afd44..cac780a 100644 --- a/src/main/java/org/adrianvictor/lib/reflection/ImplementationRegistry.java +++ b/src/main/java/org/adrianvictor/lib/reflection/ImplementationRegistry.java @@ -9,12 +9,13 @@ public class ImplementationRegistry { } public T getInstance(Class apiClass, String target, String replacement) { + String base = apiClass.getName(); + String implName = - apiClass.getName() - .replace( - target, - replacement + classSuffix + "." - ); + base.replaceFirst( + "org\\.adrianvictor\\.lib", + "org.adrianvictor.lib.impl." + classSuffix + ); try { Class implClass = Class.forName(implName); @@ -23,10 +24,7 @@ public class ImplementationRegistry { implClass.getDeclaredConstructor().newInstance() ); } catch (ReflectiveOperationException e) { - throw new IllegalStateException( - "Cannot load " + implName, - e - ); + throw new IllegalStateException("Cannot load " + implName, e); } } From eec7b2fcbbe079d34ca4906278b845fb4238c0a1 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Sat, 23 May 2026 17:59:40 -0300 Subject: [PATCH 08/22] Remove reflection and add VersionedService to provide multiple versions support --- .../DefaultVersionedServiceFactoryTest.java | 126 ++++++++++++++++++ .../lib/impl/b1_7_3/B1_7_3Registrar.java | 20 +++ ...adrianvictor.lib.VersionedServiceRegistrar | 1 + .../lib/DefaultVersionedServiceFactory.java | 36 +++++ src/main/java/org/adrianvictor/lib/Main.java | 46 +++++++ .../lib/VersionedServiceFactory.java | 8 ++ .../lib/VersionedServiceRegistrar.java | 5 + .../lib/configuration/Configuration.java | 6 +- .../reflection/ImplementationRegistry.java | 34 ----- .../lib/reflection/VersionMatcher.java | 110 ++++++--------- .../org/adrianvictor/lib/text/TextColor.java | 6 +- .../adrianvictor/lib/text/TextColorUtils.java | 6 +- .../lib/impl/r1_21/R1_21Registrar.java | 16 +++ ...adrianvictor.lib.VersionedServiceRegistrar | 1 + 14 files changed, 310 insertions(+), 111 deletions(-) create mode 100644 app/src/test/java/org/adrianvictor/lib/DefaultVersionedServiceFactoryTest.java create mode 100644 src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/B1_7_3Registrar.java create mode 100644 src/b1_7_3/resources/META-INF/services/org.adrianvictor.lib.VersionedServiceRegistrar create mode 100644 src/main/java/org/adrianvictor/lib/DefaultVersionedServiceFactory.java create mode 100644 src/main/java/org/adrianvictor/lib/Main.java create mode 100644 src/main/java/org/adrianvictor/lib/VersionedServiceFactory.java create mode 100644 src/main/java/org/adrianvictor/lib/VersionedServiceRegistrar.java delete mode 100644 src/main/java/org/adrianvictor/lib/reflection/ImplementationRegistry.java create mode 100644 src/r1_21/java/org/adrianvictor/lib/impl/r1_21/R1_21Registrar.java create mode 100644 src/r1_21/resources/META-INF/services/org.adrianvictor.lib.VersionedServiceRegistrar diff --git a/app/src/test/java/org/adrianvictor/lib/DefaultVersionedServiceFactoryTest.java b/app/src/test/java/org/adrianvictor/lib/DefaultVersionedServiceFactoryTest.java new file mode 100644 index 0000000..ca7b1b6 --- /dev/null +++ b/app/src/test/java/org/adrianvictor/lib/DefaultVersionedServiceFactoryTest.java @@ -0,0 +1,126 @@ +package org.adrianvictor.lib; + +import org.adrianvictor.lib.reflection.VersionMatcher; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.function.Supplier; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +// Define dummy interfaces for testing +interface TestService {} +class TestServiceB1_7_3 implements TestService {} +class TestServiceR1_21 implements TestService {} + +// Dummy registrar for testing +class DummyRegistrarB1_7_3 implements VersionedServiceRegistrar { + @Override + public void register(VersionedServiceFactory factory) { + factory.register(TestService.class, "b1_7_3", TestServiceB1_7_3::new); + factory.register(TestService.class, "b1_8_compat", TestServiceB1_7_3::new); // Register for b1_8_compat + } +} + +class DummyRegistrarR1_21 implements VersionedServiceRegistrar { + @Override + public void register(VersionedServiceFactory factory) { + factory.register(TestService.class, "r1_21", TestServiceR1_21::new); + } +} + + +public class DefaultVersionedServiceFactoryTest { + + private VersionMatcher mockVersionMatcher; + private DefaultVersionedServiceFactory factory; + + @BeforeEach + void setUp() { + mockVersionMatcher = mock(VersionMatcher.class); + factory = new DefaultVersionedServiceFactory(mockVersionMatcher); + } + + @Test + void testServiceRegistrationAndRetrievalForB1_7_3() { + // Mock VersionMatcher to return b1_7_3 suffix + when(mockVersionMatcher.getServerVersion()).thenReturn("1.7.3"); + when(mockVersionMatcher.getClassSuffix(eq("1.7.3"))).thenReturn("b1_7_3"); + + // Register the dummy service for b1_7_3 + new DummyRegistrarB1_7_3().register(factory); + + // Retrieve the service + TestService service = factory.getService(TestService.class); + + // Assert that the correct version is returned + assertNotNull(service); + assertTrue(service instanceof TestServiceB1_7_3); + } + + @Test + void testServiceRegistrationAndRetrievalForR1_21() { + // Mock VersionMatcher to return r1_21 suffix + when(mockVersionMatcher.getServerVersion()).thenReturn("1.21.0"); + when(mockVersionMatcher.getClassSuffix(eq("1.21.0"))).thenReturn("r1_21"); + + // Register the dummy service for r1_21 + new DummyRegistrarR1_21().register(factory); + + // Retrieve the service + TestService service = factory.getService(TestService.class); + + // Assert that the correct version is returned + assertNotNull(service); + assertTrue(service instanceof TestServiceR1_21); + } + + @Test + void testServiceRegistrationAndRetrievalForB1_8_Compat() { + // Mock VersionMatcher to return b1_8_compat suffix for a 1.16.5 server + when(mockVersionMatcher.getServerVersion()).thenReturn("1.16.5"); + when(mockVersionMatcher.getClassSuffix(eq("1.16.5"))).thenReturn("b1_8_compat"); + + // Register the dummy service for b1_7_3 (which also covers b1_8_compat) + new DummyRegistrarB1_7_3().register(factory); + + // Retrieve the service + TestService service = factory.getService(TestService.class); + + // Assert that the correct compatible version is returned + assertNotNull(service); + assertTrue(service instanceof TestServiceB1_7_3); + } + + @Test + void testNoServiceRegisteredThrowsException() { + // Mock VersionMatcher to return an unknown suffix + when(mockVersionMatcher.getServerVersion()).thenReturn("unknown"); + when(mockVersionMatcher.getClassSuffix(anyString())).thenReturn("unknown_version"); + + // Attempt to retrieve a service without any registration + IllegalStateException exception = assertThrows(IllegalStateException.class, () -> + factory.getService(TestService.class) + ); + + assertTrue(exception.getMessage().contains("No service registered for type")); + } + + @Test + void testSpecificServiceNotRegisteredForVersionThrowsException() { + // Mock VersionMatcher to return b1_7_3 suffix + when(mockVersionMatcher.getServerVersion()).thenReturn("1.7.3"); + when(mockVersionMatcher.getClassSuffix(eq("1.7.3"))).thenReturn("b1_7_3"); + + // Register R1_21 service, but not B1_7_3 + new DummyRegistrarR1_21().register(factory); + + // Attempt to retrieve b1_7_3 service + IllegalStateException exception = assertThrows(IllegalStateException.class, () -> + factory.getService(TestService.class) + ); + + assertTrue(exception.getMessage().contains("No service registered for type")); + } +} diff --git a/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/B1_7_3Registrar.java b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/B1_7_3Registrar.java new file mode 100644 index 0000000..197a704 --- /dev/null +++ b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/B1_7_3Registrar.java @@ -0,0 +1,20 @@ +package org.adrianvictor.lib.impl.b1_7_3; + +import org.adrianvictor.lib.VersionedServiceFactory; +import org.adrianvictor.lib.VersionedServiceRegistrar; +import org.adrianvictor.lib.configuration.provider.ConfigurationProvider; +import org.adrianvictor.lib.impl.b1_7_3.configuration.Configuration; +import org.adrianvictor.lib.text.provider.TextColorProvider; +import org.adrianvictor.lib.impl.b1_7_3.text.TextColor; + +public class B1_7_3Registrar implements VersionedServiceRegistrar { + @Override + public void register(VersionedServiceFactory factory) { + factory.register(ConfigurationProvider.class, "b1_7_3", Configuration::new); + factory.register(TextColorProvider.class, "b1_7_3", TextColor::new); + + // Register for b1_8_compat as well, assuming compatibility + factory.register(ConfigurationProvider.class, "b1_8_compat", Configuration::new); + factory.register(TextColorProvider.class, "b1_8_compat", TextColor::new); + } +} diff --git a/src/b1_7_3/resources/META-INF/services/org.adrianvictor.lib.VersionedServiceRegistrar b/src/b1_7_3/resources/META-INF/services/org.adrianvictor.lib.VersionedServiceRegistrar new file mode 100644 index 0000000..04d62e3 --- /dev/null +++ b/src/b1_7_3/resources/META-INF/services/org.adrianvictor.lib.VersionedServiceRegistrar @@ -0,0 +1 @@ +org.adrianvictor.lib.impl.b1_7_3.B1_7_3Registrar \ No newline at end of file diff --git a/src/main/java/org/adrianvictor/lib/DefaultVersionedServiceFactory.java b/src/main/java/org/adrianvictor/lib/DefaultVersionedServiceFactory.java new file mode 100644 index 0000000..6be8582 --- /dev/null +++ b/src/main/java/org/adrianvictor/lib/DefaultVersionedServiceFactory.java @@ -0,0 +1,36 @@ +package org.adrianvictor.lib; + +import org.adrianvictor.lib.reflection.VersionMatcher; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +public class DefaultVersionedServiceFactory implements VersionedServiceFactory { + private final VersionMatcher versionMatcher; + private final Map, Map>> serviceRegistrations = new HashMap<>(); + + public DefaultVersionedServiceFactory(VersionMatcher versionMatcher) { + this.versionMatcher = versionMatcher; + } + + @Override + public void register(Class serviceType, String versionSuffix, Supplier supplier) { + serviceRegistrations + .computeIfAbsent(serviceType, k -> new HashMap<>()) + .put(versionSuffix, supplier); + } + + @Override + @SuppressWarnings("unchecked") + public T getService(Class serviceType) { + String serverVersion = versionMatcher.getServerVersion(); + // Determine the correct suffix using VersionMatcher (adapted) + String versionSuffix = versionMatcher.getClassSuffix(serverVersion); // Removed "release" parameter + + Map> versionSuppliers = serviceRegistrations.get(serviceType); + if (versionSuppliers == null || !versionSuppliers.containsKey(versionSuffix)) { + throw new IllegalStateException("No service registered for type " + serviceType.getName() + " and version " + versionSuffix + ". Current server version: " + serverVersion); + } + return (T) versionSuppliers.get(versionSuffix).get(); + } +} diff --git a/src/main/java/org/adrianvictor/lib/Main.java b/src/main/java/org/adrianvictor/lib/Main.java new file mode 100644 index 0000000..2371249 --- /dev/null +++ b/src/main/java/org/adrianvictor/lib/Main.java @@ -0,0 +1,46 @@ +package org.adrianvictor.lib; + +import org.adrianvictor.lib.reflection.VersionMatcher; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.ServiceLoader; + +public class Main extends JavaPlugin { + + private static VersionedServiceFactory versionedServiceFactory; + private static VersionMatcher versionMatcher; + + @Override + public void onEnable() { + // Initialize VersionMatcher + versionMatcher = new VersionMatcher(); + + // Initialize DefaultVersionedServiceFactory + versionedServiceFactory = new DefaultVersionedServiceFactory(versionMatcher); + + // Discover and register version-specific implementations using ServiceLoader + ServiceLoader registrars = ServiceLoader.load(VersionedServiceRegistrar.class); + for (VersionedServiceRegistrar registrar : registrars) { + registrar.register(versionedServiceFactory); + } + + // Example usage (for demonstration, can be removed later) + // ConfigurationProvider configProvider = Configuration.create(versionedServiceFactory); + // TextColorProvider textColorProvider = TextColor.create(versionedServiceFactory); + + getLogger().info("tenkumaLib has been enabled!"); + } + + @Override + public void onDisable() { + getLogger().info("tenkumaLib has been disabled!"); + } + + public static VersionedServiceFactory getVersionedServiceFactory() { + return versionedServiceFactory; + } + + public static VersionMatcher getVersionMatcher() { + return versionMatcher; + } +} diff --git a/src/main/java/org/adrianvictor/lib/VersionedServiceFactory.java b/src/main/java/org/adrianvictor/lib/VersionedServiceFactory.java new file mode 100644 index 0000000..749689d --- /dev/null +++ b/src/main/java/org/adrianvictor/lib/VersionedServiceFactory.java @@ -0,0 +1,8 @@ +package org.adrianvictor.lib; + +import java.util.function.Supplier; + +public interface VersionedServiceFactory { + void register(Class serviceType, String versionSuffix, Supplier supplier); + T getService(Class serviceType); +} diff --git a/src/main/java/org/adrianvictor/lib/VersionedServiceRegistrar.java b/src/main/java/org/adrianvictor/lib/VersionedServiceRegistrar.java new file mode 100644 index 0000000..ed78bc4 --- /dev/null +++ b/src/main/java/org/adrianvictor/lib/VersionedServiceRegistrar.java @@ -0,0 +1,5 @@ +package org.adrianvictor.lib; + +public interface VersionedServiceRegistrar { + void register(VersionedServiceFactory factory); +} diff --git a/src/main/java/org/adrianvictor/lib/configuration/Configuration.java b/src/main/java/org/adrianvictor/lib/configuration/Configuration.java index fa005b5..fa8999b 100644 --- a/src/main/java/org/adrianvictor/lib/configuration/Configuration.java +++ b/src/main/java/org/adrianvictor/lib/configuration/Configuration.java @@ -1,10 +1,10 @@ package org.adrianvictor.lib.configuration; +import org.adrianvictor.lib.VersionedServiceFactory; import org.adrianvictor.lib.configuration.provider.ConfigurationProvider; -import org.adrianvictor.lib.reflection.ImplementationRegistry; public class Configuration { - public static ConfigurationProvider create(ImplementationRegistry registry) { - return registry.getInstance(ConfigurationProvider.class); + public static ConfigurationProvider create(VersionedServiceFactory factory) { + return factory.getService(ConfigurationProvider.class); } } \ No newline at end of file diff --git a/src/main/java/org/adrianvictor/lib/reflection/ImplementationRegistry.java b/src/main/java/org/adrianvictor/lib/reflection/ImplementationRegistry.java deleted file mode 100644 index cac780a..0000000 --- a/src/main/java/org/adrianvictor/lib/reflection/ImplementationRegistry.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.adrianvictor.lib.reflection; - -public class ImplementationRegistry { - - private final String classSuffix; - - public ImplementationRegistry(String classSuffix) { - this.classSuffix = classSuffix; - } - - public T getInstance(Class apiClass, String target, String replacement) { - String base = apiClass.getName(); - - String implName = - base.replaceFirst( - "org\\.adrianvictor\\.lib", - "org.adrianvictor.lib.impl." + classSuffix - ); - - try { - Class implClass = Class.forName(implName); - - return apiClass.cast( - implClass.getDeclaredConstructor().newInstance() - ); - } catch (ReflectiveOperationException e) { - throw new IllegalStateException("Cannot load " + implName, e); - } - } - - public T getInstance(Class apiClass) { - return getInstance(apiClass, "org.adrianvictor.lib", "org.adrianvictor.lib.impl."); - } -} diff --git a/src/main/java/org/adrianvictor/lib/reflection/VersionMatcher.java b/src/main/java/org/adrianvictor/lib/reflection/VersionMatcher.java index 6c80828..1a77a2b 100644 --- a/src/main/java/org/adrianvictor/lib/reflection/VersionMatcher.java +++ b/src/main/java/org/adrianvictor/lib/reflection/VersionMatcher.java @@ -2,54 +2,53 @@ package org.adrianvictor.lib.reflection; import org.bukkit.Bukkit; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; +import java.util.Comparator; import java.util.List; +import java.util.ArrayList; import java.util.Map; +import java.util.Optional; import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.util.Collections; public class VersionMatcher { public VersionMatcher() {} - private record Entry(String pattern, String classSuffix) {} + private record Entry(String pattern, String classSuffix, int minMajor, int minMinor, int maxMajor, int maxMinor) {} - public String getClassSuffix(String type, String serverVersion) { - if (type == null || serverVersion == null) { + public String getClassSuffix(String serverVersion) { + if (serverVersion == null) { return null; } - Map> map = populateMap(); - List entries = map.get(type.toLowerCase()); - if (entries == null || entries.isEmpty()) { - return null; - } + List entries = populateEntries(); + List applicableEntries = new ArrayList<>(); - // Exact match - for (Entry e : entries) { - if (safeMatches(e.pattern, serverVersion)) { - return e.classSuffix; + int[] parsedServerVersion = parseVersion(serverVersion); + int serverMajor = parsedServerVersion[0]; + int serverMinor = parsedServerVersion[1]; + + for (Entry entry : entries) { + Pattern p = Pattern.compile(entry.pattern()); + if (p.matcher(serverVersion).matches()) { + if (serverMajor >= entry.minMajor() && serverMinor >= entry.minMinor() && + serverMajor <= entry.maxMajor() && serverMinor <= entry.maxMinor()) { + applicableEntries.add(entry); + } } } - // Fallback to closest version - List sorted = new ArrayList<>(entries); - Collections.sort(sorted, (a, b) -> compareVersions(a.pattern, b.pattern)); + // Sort by version (highest minor first for best match within range) + applicableEntries.sort(Comparator + .comparing(Entry::maxMajor).reversed() + .thenComparing(Entry::maxMinor).reversed() + .thenComparing(Entry::minMajor).reversed() + .thenComparing(Entry::minMinor).reversed()); - Entry oldest = sorted.get(0); - Entry newest = sorted.get(sorted.size() - 1); + Optional bestMatch = applicableEntries.stream().findFirst(); - int cmpOldest = compareVersions(serverVersion, oldest.pattern); - int cmpNewest = compareVersions(serverVersion, newest.pattern); - - if (cmpOldest < 0) { - return oldest.classSuffix; - } else if (cmpNewest > 0) { - return newest.classSuffix; - } - - return newest.classSuffix; + return bestMatch.map(Entry::classSuffix).orElse(null); } public String getServerVersion() { @@ -71,46 +70,21 @@ public class VersionMatcher { return rawVersion; } - private Map> populateMap() { - Map> map = new HashMap<>(); - map.put("release", List.of( - new Entry("^1\\.21\\..*$", "r1_21") - )); - map.put("beta", List.of( - new Entry("^1\\.7\\.3$", "b1_7_3") - )); - return map; + private List populateEntries() { + return List.of( + new Entry("^1\\.7\\.3$", "b1_7_3", 1, 7, 1, 7), + // Covers 1.8 up to 1.16 for compatibility (example for user request) + new Entry("^1\\.(8|9|10|11|12|13|14|15|16)\\..*$", "b1_8_compat", 1, 8, 1, 16), + new Entry("^1\\.21\\..*$", "r1_21", 1, 21, Integer.MAX_VALUE, Integer.MAX_VALUE) // Open-ended for future 1.21.x + ); } - private int compareVersions(String v1, String v2) { - String clean1 = v1.replaceAll("[^0-9.]", ""); - String clean2 = v2.replaceAll("[^0-9.]", ""); - - String[] a1 = clean1.split("\\."); - String[] a2 = clean2.split("\\."); - - int len = Math.max(a1.length, a2.length); - for (int i = 0; i < len; i++) { - int n1 = i < a1.length ? parseInt(a1[i]) : 0; - int n2 = i < a2.length ? parseInt(a2[i]) : 0; - if (n1 != n2) { - return n1 - n2; - } + // Helper to parse major and minor version from a string (e.g., "1.16.5" -> [1, 16]) + private int[] parseVersion(String versionString) { + Matcher matcher = Pattern.compile("^(\\d+)\\.(\\d+).*").matcher(versionString); + if (matcher.find()) { + return new int[]{Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2))}; } - return 0; - } - - private int parseInt(String s) { - try { - return Integer.parseInt(s); - } catch (NumberFormatException e) { - return 0; - } - } - - private static boolean safeMatches(String expression, String against) { - String cleanPattern = expression.trim(); - Pattern pattern = Pattern.compile(cleanPattern, Pattern.CASE_INSENSITIVE); - return pattern.matcher(against).matches(); + return new int[]{0, 0}; // Default for unknown format } } \ No newline at end of file diff --git a/src/main/java/org/adrianvictor/lib/text/TextColor.java b/src/main/java/org/adrianvictor/lib/text/TextColor.java index 9252de4..26bb87a 100644 --- a/src/main/java/org/adrianvictor/lib/text/TextColor.java +++ b/src/main/java/org/adrianvictor/lib/text/TextColor.java @@ -1,10 +1,10 @@ package org.adrianvictor.lib.text; -import org.adrianvictor.lib.reflection.ImplementationRegistry; +import org.adrianvictor.lib.VersionedServiceFactory; import org.adrianvictor.lib.text.provider.TextColorProvider; public class TextColor { - public static TextColorProvider create(ImplementationRegistry registry) { - return registry.getInstance(TextColorProvider.class); + public static TextColorProvider create(VersionedServiceFactory factory) { + return factory.getService(TextColorProvider.class); } } diff --git a/src/main/java/org/adrianvictor/lib/text/TextColorUtils.java b/src/main/java/org/adrianvictor/lib/text/TextColorUtils.java index 1e1183e..b2d109e 100644 --- a/src/main/java/org/adrianvictor/lib/text/TextColorUtils.java +++ b/src/main/java/org/adrianvictor/lib/text/TextColorUtils.java @@ -1,13 +1,13 @@ package org.adrianvictor.lib.text; -import org.adrianvictor.lib.reflection.ImplementationRegistry; +import org.adrianvictor.lib.VersionedServiceFactory; import org.adrianvictor.lib.text.provider.TextColorProvider; public class TextColorUtils { private final TextColorProvider provider; - public TextColorUtils(ImplementationRegistry registry) { - provider = TextColor.create(registry); + public TextColorUtils(VersionedServiceFactory factory) { + provider = TextColor.create(factory); } public String formatColors(String message) { diff --git a/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/R1_21Registrar.java b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/R1_21Registrar.java new file mode 100644 index 0000000..d289886 --- /dev/null +++ b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/R1_21Registrar.java @@ -0,0 +1,16 @@ +package org.adrianvictor.lib.impl.r1_21; + +import org.adrianvictor.lib.VersionedServiceFactory; +import org.adrianvictor.lib.VersionedServiceRegistrar; +import org.adrianvictor.lib.configuration.provider.ConfigurationProvider; +import org.adrianvictor.lib.impl.r1_21.configuration.Configuration; +import org.adrianvictor.lib.text.provider.TextColorProvider; +import org.adrianvictor.lib.impl.r1_21.text.TextColor; + +public class R1_21Registrar implements VersionedServiceRegistrar { + @Override + public void register(VersionedServiceFactory factory) { + factory.register(ConfigurationProvider.class, "r1_21", Configuration::new); + factory.register(TextColorProvider.class, "r1_21", TextColor::new); + } +} diff --git a/src/r1_21/resources/META-INF/services/org.adrianvictor.lib.VersionedServiceRegistrar b/src/r1_21/resources/META-INF/services/org.adrianvictor.lib.VersionedServiceRegistrar new file mode 100644 index 0000000..8f5e531 --- /dev/null +++ b/src/r1_21/resources/META-INF/services/org.adrianvictor.lib.VersionedServiceRegistrar @@ -0,0 +1 @@ +org.adrianvictor.lib.impl.r1_21.R1_21Registrar \ No newline at end of file From 0b753d6a4ed911c497722a9b2297c3990b8b0b42 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Sat, 23 May 2026 21:41:17 -0300 Subject: [PATCH 09/22] Finish factory implementation --- .gitignore | 2 + .../DefaultVersionedServiceFactoryTest.java | 65 ++++++++++++------- build.gradle.kts | 13 +++- .../lib/impl/b1_7_3/B1_7_3Registrar.java | 4 +- ....lib.versioning.VersionedServiceRegistrar} | 0 src/main/java/org/adrianvictor/lib/Main.java | 12 ++-- .../lib/configuration/Configuration.java | 2 +- .../org/adrianvictor/lib/text/TextColor.java | 2 +- .../adrianvictor/lib/text/TextColorUtils.java | 2 +- .../DefaultVersionedServiceFactory.java | 6 +- .../VersionMatcher.java | 15 ++--- .../VersionedServiceFactory.java | 2 +- .../VersionedServiceRegistrar.java | 2 +- .../lib/impl/r1_21/R1_21Registrar.java | 8 +-- ....lib.versioning.VersionedServiceRegistrar} | 0 15 files changed, 79 insertions(+), 56 deletions(-) rename src/b1_7_3/resources/META-INF/services/{org.adrianvictor.lib.VersionedServiceRegistrar => org.adrianvictor.lib.versioning.VersionedServiceRegistrar} (100%) rename src/main/java/org/adrianvictor/lib/{ => versioning}/DefaultVersionedServiceFactory.java (87%) rename src/main/java/org/adrianvictor/lib/{reflection => versioning}/VersionMatcher.java (82%) rename src/main/java/org/adrianvictor/lib/{ => versioning}/VersionedServiceFactory.java (84%) rename src/main/java/org/adrianvictor/lib/{ => versioning}/VersionedServiceRegistrar.java (70%) rename src/r1_21/resources/META-INF/services/{org.adrianvictor.lib.VersionedServiceRegistrar => org.adrianvictor.lib.versioning.VersionedServiceRegistrar} (100%) diff --git a/.gitignore b/.gitignore index 46bcab9..df892ec 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ out/ # Ignore Gradle build output directory build + +run/ \ No newline at end of file diff --git a/app/src/test/java/org/adrianvictor/lib/DefaultVersionedServiceFactoryTest.java b/app/src/test/java/org/adrianvictor/lib/DefaultVersionedServiceFactoryTest.java index ca7b1b6..a624d68 100644 --- a/app/src/test/java/org/adrianvictor/lib/DefaultVersionedServiceFactoryTest.java +++ b/app/src/test/java/org/adrianvictor/lib/DefaultVersionedServiceFactoryTest.java @@ -1,11 +1,12 @@ package org.adrianvictor.lib; -import org.adrianvictor.lib.reflection.VersionMatcher; +import org.adrianvictor.lib.versioning.DefaultVersionedServiceFactory; +import org.adrianvictor.lib.versioning.VersionMatcher; +import org.adrianvictor.lib.versioning.VersionedServiceFactory; +import org.adrianvictor.lib.versioning.VersionedServiceRegistrar; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.util.function.Supplier; - import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @@ -19,14 +20,15 @@ class DummyRegistrarB1_7_3 implements VersionedServiceRegistrar { @Override public void register(VersionedServiceFactory factory) { factory.register(TestService.class, "b1_7_3", TestServiceB1_7_3::new); - factory.register(TestService.class, "b1_8_compat", TestServiceB1_7_3::new); // Register for b1_8_compat } } class DummyRegistrarR1_21 implements VersionedServiceRegistrar { @Override public void register(VersionedServiceFactory factory) { - factory.register(TestService.class, "r1_21", TestServiceR1_21::new); + factory.register(TestService.class, "r1_1", TestServiceR1_21::new); // Registers for r1_1 compat + factory.register(TestService.class, "r1_16_5", TestServiceR1_21::new); // Registers for r1_16_5 compat + factory.register(TestService.class, "r1_21", TestServiceR1_21::new); // Registers for r1_21 } } @@ -59,6 +61,40 @@ public class DefaultVersionedServiceFactoryTest { assertTrue(service instanceof TestServiceB1_7_3); } + @Test + void testServiceRegistrationAndRetrievalForR1_1() { + // Mock VersionMatcher to return r1_1 suffix for a 1.10.2 server + when(mockVersionMatcher.getServerVersion()).thenReturn("1.10.2"); + when(mockVersionMatcher.getClassSuffix(eq("1.10.2"))).thenReturn("r1_1"); + + // Register the dummy service for R1_21 (which covers r1_1 compat) + new DummyRegistrarR1_21().register(factory); + + // Retrieve the service + TestService service = factory.getService(TestService.class); + + // Assert that the correct compatible version is returned + assertNotNull(service); + assertTrue(service instanceof TestServiceR1_21); + } + + @Test + void testServiceRegistrationAndRetrievalForR1_16_5() { + // Mock VersionMatcher to return r1_16_5 suffix for a 1.18.1 server + when(mockVersionMatcher.getServerVersion()).thenReturn("1.18.1"); + when(mockVersionMatcher.getClassSuffix(eq("1.18.1"))).thenReturn("r1_16_5"); + + // Register the dummy service for R1_21 (which covers r1_16_5 compat) + new DummyRegistrarR1_21().register(factory); + + // Retrieve the service + TestService service = factory.getService(TestService.class); + + // Assert that the correct compatible version is returned + assertNotNull(service); + assertTrue(service instanceof TestServiceR1_21); + } + @Test void testServiceRegistrationAndRetrievalForR1_21() { // Mock VersionMatcher to return r1_21 suffix @@ -76,23 +112,6 @@ public class DefaultVersionedServiceFactoryTest { assertTrue(service instanceof TestServiceR1_21); } - @Test - void testServiceRegistrationAndRetrievalForB1_8_Compat() { - // Mock VersionMatcher to return b1_8_compat suffix for a 1.16.5 server - when(mockVersionMatcher.getServerVersion()).thenReturn("1.16.5"); - when(mockVersionMatcher.getClassSuffix(eq("1.16.5"))).thenReturn("b1_8_compat"); - - // Register the dummy service for b1_7_3 (which also covers b1_8_compat) - new DummyRegistrarB1_7_3().register(factory); - - // Retrieve the service - TestService service = factory.getService(TestService.class); - - // Assert that the correct compatible version is returned - assertNotNull(service); - assertTrue(service instanceof TestServiceB1_7_3); - } - @Test void testNoServiceRegisteredThrowsException() { // Mock VersionMatcher to return an unknown suffix @@ -114,7 +133,7 @@ public class DefaultVersionedServiceFactoryTest { when(mockVersionMatcher.getClassSuffix(eq("1.7.3"))).thenReturn("b1_7_3"); // Register R1_21 service, but not B1_7_3 - new DummyRegistrarR1_21().register(factory); + new DummyRegistrarR1_21().register(factory); // R1_21 registrar doesn't register b1_7_3 // Attempt to retrieve b1_7_3 service IllegalStateException exception = assertThrows(IllegalStateException.class, () -> diff --git a/build.gradle.kts b/build.gradle.kts index ecd5724..3e973c0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,6 @@ plugins { java - id("xyz.jpenilla.run-paper") version "2.3.1" + id("xyz.jpenilla.run-paper") version "3.0.1" } group = "org.adrianvictor" @@ -70,7 +70,7 @@ dependencies { /* ----------------------------------------- */ mcVersions.forEach { ver -> - tasks.register("jar${ver.replace(".", "_").replace("-", "_").replace("/", "_").capitalize()}") { + tasks.register("jar${ver.replace(".", "_").replace("-", "_").replace("/", "_").replaceFirstChar { it.uppercase() }}") { duplicatesStrategy = DuplicatesStrategy.EXCLUDE from(sourceSets["main"].output) from(sourceSets[ver].output) @@ -126,4 +126,13 @@ java { tasks.withType { options.encoding = "UTF-8" +} + +/* ----------------------------------------- */ +/* RUN SETTINGS */ +/* ----------------------------------------- */ + +tasks.runServer { + minecraftVersion("1.21.1") + pluginJars.from(tasks.named("jarR1_21")) } \ No newline at end of file diff --git a/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/B1_7_3Registrar.java b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/B1_7_3Registrar.java index 197a704..2475ca3 100644 --- a/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/B1_7_3Registrar.java +++ b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/B1_7_3Registrar.java @@ -1,7 +1,7 @@ package org.adrianvictor.lib.impl.b1_7_3; -import org.adrianvictor.lib.VersionedServiceFactory; -import org.adrianvictor.lib.VersionedServiceRegistrar; +import org.adrianvictor.lib.versioning.VersionedServiceFactory; +import org.adrianvictor.lib.versioning.VersionedServiceRegistrar; import org.adrianvictor.lib.configuration.provider.ConfigurationProvider; import org.adrianvictor.lib.impl.b1_7_3.configuration.Configuration; import org.adrianvictor.lib.text.provider.TextColorProvider; diff --git a/src/b1_7_3/resources/META-INF/services/org.adrianvictor.lib.VersionedServiceRegistrar b/src/b1_7_3/resources/META-INF/services/org.adrianvictor.lib.versioning.VersionedServiceRegistrar similarity index 100% rename from src/b1_7_3/resources/META-INF/services/org.adrianvictor.lib.VersionedServiceRegistrar rename to src/b1_7_3/resources/META-INF/services/org.adrianvictor.lib.versioning.VersionedServiceRegistrar diff --git a/src/main/java/org/adrianvictor/lib/Main.java b/src/main/java/org/adrianvictor/lib/Main.java index 2371249..a16c99d 100644 --- a/src/main/java/org/adrianvictor/lib/Main.java +++ b/src/main/java/org/adrianvictor/lib/Main.java @@ -1,6 +1,9 @@ package org.adrianvictor.lib; -import org.adrianvictor.lib.reflection.VersionMatcher; +import org.adrianvictor.lib.versioning.DefaultVersionedServiceFactory; +import org.adrianvictor.lib.versioning.VersionMatcher; +import org.adrianvictor.lib.versioning.VersionedServiceFactory; +import org.adrianvictor.lib.versioning.VersionedServiceRegistrar; import org.bukkit.plugin.java.JavaPlugin; import java.util.ServiceLoader; @@ -12,22 +15,15 @@ public class Main extends JavaPlugin { @Override public void onEnable() { - // Initialize VersionMatcher versionMatcher = new VersionMatcher(); - // Initialize DefaultVersionedServiceFactory versionedServiceFactory = new DefaultVersionedServiceFactory(versionMatcher); - // Discover and register version-specific implementations using ServiceLoader ServiceLoader registrars = ServiceLoader.load(VersionedServiceRegistrar.class); for (VersionedServiceRegistrar registrar : registrars) { registrar.register(versionedServiceFactory); } - // Example usage (for demonstration, can be removed later) - // ConfigurationProvider configProvider = Configuration.create(versionedServiceFactory); - // TextColorProvider textColorProvider = TextColor.create(versionedServiceFactory); - getLogger().info("tenkumaLib has been enabled!"); } diff --git a/src/main/java/org/adrianvictor/lib/configuration/Configuration.java b/src/main/java/org/adrianvictor/lib/configuration/Configuration.java index fa8999b..8cee615 100644 --- a/src/main/java/org/adrianvictor/lib/configuration/Configuration.java +++ b/src/main/java/org/adrianvictor/lib/configuration/Configuration.java @@ -1,6 +1,6 @@ package org.adrianvictor.lib.configuration; -import org.adrianvictor.lib.VersionedServiceFactory; +import org.adrianvictor.lib.versioning.VersionedServiceFactory; import org.adrianvictor.lib.configuration.provider.ConfigurationProvider; public class Configuration { diff --git a/src/main/java/org/adrianvictor/lib/text/TextColor.java b/src/main/java/org/adrianvictor/lib/text/TextColor.java index 26bb87a..c07ebdf 100644 --- a/src/main/java/org/adrianvictor/lib/text/TextColor.java +++ b/src/main/java/org/adrianvictor/lib/text/TextColor.java @@ -1,6 +1,6 @@ package org.adrianvictor.lib.text; -import org.adrianvictor.lib.VersionedServiceFactory; +import org.adrianvictor.lib.versioning.VersionedServiceFactory; import org.adrianvictor.lib.text.provider.TextColorProvider; public class TextColor { diff --git a/src/main/java/org/adrianvictor/lib/text/TextColorUtils.java b/src/main/java/org/adrianvictor/lib/text/TextColorUtils.java index b2d109e..81ba3cf 100644 --- a/src/main/java/org/adrianvictor/lib/text/TextColorUtils.java +++ b/src/main/java/org/adrianvictor/lib/text/TextColorUtils.java @@ -1,6 +1,6 @@ package org.adrianvictor.lib.text; -import org.adrianvictor.lib.VersionedServiceFactory; +import org.adrianvictor.lib.versioning.VersionedServiceFactory; import org.adrianvictor.lib.text.provider.TextColorProvider; public class TextColorUtils { diff --git a/src/main/java/org/adrianvictor/lib/DefaultVersionedServiceFactory.java b/src/main/java/org/adrianvictor/lib/versioning/DefaultVersionedServiceFactory.java similarity index 87% rename from src/main/java/org/adrianvictor/lib/DefaultVersionedServiceFactory.java rename to src/main/java/org/adrianvictor/lib/versioning/DefaultVersionedServiceFactory.java index 6be8582..9f528cf 100644 --- a/src/main/java/org/adrianvictor/lib/DefaultVersionedServiceFactory.java +++ b/src/main/java/org/adrianvictor/lib/versioning/DefaultVersionedServiceFactory.java @@ -1,6 +1,5 @@ -package org.adrianvictor.lib; +package org.adrianvictor.lib.versioning; -import org.adrianvictor.lib.reflection.VersionMatcher; import java.util.HashMap; import java.util.Map; import java.util.function.Supplier; @@ -24,8 +23,7 @@ public class DefaultVersionedServiceFactory implements VersionedServiceFactory { @SuppressWarnings("unchecked") public T getService(Class serviceType) { String serverVersion = versionMatcher.getServerVersion(); - // Determine the correct suffix using VersionMatcher (adapted) - String versionSuffix = versionMatcher.getClassSuffix(serverVersion); // Removed "release" parameter + String versionSuffix = versionMatcher.getClassSuffix(serverVersion); Map> versionSuppliers = serviceRegistrations.get(serviceType); if (versionSuppliers == null || !versionSuppliers.containsKey(versionSuffix)) { diff --git a/src/main/java/org/adrianvictor/lib/reflection/VersionMatcher.java b/src/main/java/org/adrianvictor/lib/versioning/VersionMatcher.java similarity index 82% rename from src/main/java/org/adrianvictor/lib/reflection/VersionMatcher.java rename to src/main/java/org/adrianvictor/lib/versioning/VersionMatcher.java index 1a77a2b..91fee05 100644 --- a/src/main/java/org/adrianvictor/lib/reflection/VersionMatcher.java +++ b/src/main/java/org/adrianvictor/lib/versioning/VersionMatcher.java @@ -1,15 +1,13 @@ -package org.adrianvictor.lib.reflection; +package org.adrianvictor.lib.versioning; import org.bukkit.Bukkit; import java.util.Comparator; import java.util.List; import java.util.ArrayList; -import java.util.Map; import java.util.Optional; import java.util.regex.Pattern; import java.util.regex.Matcher; -import java.util.Collections; public class VersionMatcher { @@ -39,7 +37,6 @@ public class VersionMatcher { } } - // Sort by version (highest minor first for best match within range) applicableEntries.sort(Comparator .comparing(Entry::maxMajor).reversed() .thenComparing(Entry::maxMinor).reversed() @@ -73,13 +70,15 @@ public class VersionMatcher { private List populateEntries() { return List.of( new Entry("^1\\.7\\.3$", "b1_7_3", 1, 7, 1, 7), - // Covers 1.8 up to 1.16 for compatibility (example for user request) - new Entry("^1\\.(8|9|10|11|12|13|14|15|16)\\..*$", "b1_8_compat", 1, 8, 1, 16), - new Entry("^1\\.21\\..*$", "r1_21", 1, 21, Integer.MAX_VALUE, Integer.MAX_VALUE) // Open-ended for future 1.21.x + // r1_1 covers versions from 1.1.x up to 1.16.4 + new Entry("^1\\.(1[0-5]|[1-9])(\\.\\d+)*$", "r1_1", 1, 1, 1, 16), + // r1_16_5 covers versions from 1.16.5 up to 1.20.x + new Entry("^1\\.(16\\.[5-9]|1[7-9]|20)(\\.\\d+)*$", "r1_16_5", 1, 16, 1, 20), + // r1_21 covers 1.21.x and above + new Entry("^1\\.21(\\.\\d+)*$", "r1_21", 1, 21, Integer.MAX_VALUE, Integer.MAX_VALUE) ); } - // Helper to parse major and minor version from a string (e.g., "1.16.5" -> [1, 16]) private int[] parseVersion(String versionString) { Matcher matcher = Pattern.compile("^(\\d+)\\.(\\d+).*").matcher(versionString); if (matcher.find()) { diff --git a/src/main/java/org/adrianvictor/lib/VersionedServiceFactory.java b/src/main/java/org/adrianvictor/lib/versioning/VersionedServiceFactory.java similarity index 84% rename from src/main/java/org/adrianvictor/lib/VersionedServiceFactory.java rename to src/main/java/org/adrianvictor/lib/versioning/VersionedServiceFactory.java index 749689d..850faea 100644 --- a/src/main/java/org/adrianvictor/lib/VersionedServiceFactory.java +++ b/src/main/java/org/adrianvictor/lib/versioning/VersionedServiceFactory.java @@ -1,4 +1,4 @@ -package org.adrianvictor.lib; +package org.adrianvictor.lib.versioning; import java.util.function.Supplier; diff --git a/src/main/java/org/adrianvictor/lib/VersionedServiceRegistrar.java b/src/main/java/org/adrianvictor/lib/versioning/VersionedServiceRegistrar.java similarity index 70% rename from src/main/java/org/adrianvictor/lib/VersionedServiceRegistrar.java rename to src/main/java/org/adrianvictor/lib/versioning/VersionedServiceRegistrar.java index ed78bc4..ee3fe52 100644 --- a/src/main/java/org/adrianvictor/lib/VersionedServiceRegistrar.java +++ b/src/main/java/org/adrianvictor/lib/versioning/VersionedServiceRegistrar.java @@ -1,4 +1,4 @@ -package org.adrianvictor.lib; +package org.adrianvictor.lib.versioning; public interface VersionedServiceRegistrar { void register(VersionedServiceFactory factory); diff --git a/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/R1_21Registrar.java b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/R1_21Registrar.java index d289886..d4b10b4 100644 --- a/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/R1_21Registrar.java +++ b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/R1_21Registrar.java @@ -1,7 +1,7 @@ package org.adrianvictor.lib.impl.r1_21; -import org.adrianvictor.lib.VersionedServiceFactory; -import org.adrianvictor.lib.VersionedServiceRegistrar; +import org.adrianvictor.lib.versioning.VersionedServiceFactory; +import org.adrianvictor.lib.versioning.VersionedServiceRegistrar; import org.adrianvictor.lib.configuration.provider.ConfigurationProvider; import org.adrianvictor.lib.impl.r1_21.configuration.Configuration; import org.adrianvictor.lib.text.provider.TextColorProvider; @@ -10,7 +10,7 @@ import org.adrianvictor.lib.impl.r1_21.text.TextColor; public class R1_21Registrar implements VersionedServiceRegistrar { @Override public void register(VersionedServiceFactory factory) { - factory.register(ConfigurationProvider.class, "r1_21", Configuration::new); - factory.register(TextColorProvider.class, "r1_21", TextColor::new); + factory.register(ConfigurationProvider.class, "r1_1", Configuration::new); + factory.register(TextColorProvider.class, "r1_16_5", TextColor::new); } } diff --git a/src/r1_21/resources/META-INF/services/org.adrianvictor.lib.VersionedServiceRegistrar b/src/r1_21/resources/META-INF/services/org.adrianvictor.lib.versioning.VersionedServiceRegistrar similarity index 100% rename from src/r1_21/resources/META-INF/services/org.adrianvictor.lib.VersionedServiceRegistrar rename to src/r1_21/resources/META-INF/services/org.adrianvictor.lib.versioning.VersionedServiceRegistrar From 2ef8b2d1be212dc11cafac1febe100d10a90d3f5 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Sat, 23 May 2026 21:49:38 -0300 Subject: [PATCH 10/22] Fix VersionMatcher not parsing version string correctly --- .../DefaultVersionedServiceFactoryTest.java | 42 ++++++------------- .../DefaultVersionedServiceFactory.java | 15 +++++-- .../lib/versioning/VersionMatcher.java | 42 ++++++++++--------- 3 files changed, 46 insertions(+), 53 deletions(-) diff --git a/app/src/test/java/org/adrianvictor/lib/DefaultVersionedServiceFactoryTest.java b/app/src/test/java/org/adrianvictor/lib/DefaultVersionedServiceFactoryTest.java index a624d68..8d512f1 100644 --- a/app/src/test/java/org/adrianvictor/lib/DefaultVersionedServiceFactoryTest.java +++ b/app/src/test/java/org/adrianvictor/lib/DefaultVersionedServiceFactoryTest.java @@ -7,6 +7,8 @@ import org.adrianvictor.lib.versioning.VersionedServiceRegistrar; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.util.List; + import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @@ -48,7 +50,7 @@ public class DefaultVersionedServiceFactoryTest { void testServiceRegistrationAndRetrievalForB1_7_3() { // Mock VersionMatcher to return b1_7_3 suffix when(mockVersionMatcher.getServerVersion()).thenReturn("1.7.3"); - when(mockVersionMatcher.getClassSuffix(eq("1.7.3"))).thenReturn("b1_7_3"); + when(mockVersionMatcher.getClassSuffixes(eq("1.7.3"))).thenReturn(List.of("b1_7_3")); // Register the dummy service for b1_7_3 new DummyRegistrarB1_7_3().register(factory); @@ -65,7 +67,7 @@ public class DefaultVersionedServiceFactoryTest { void testServiceRegistrationAndRetrievalForR1_1() { // Mock VersionMatcher to return r1_1 suffix for a 1.10.2 server when(mockVersionMatcher.getServerVersion()).thenReturn("1.10.2"); - when(mockVersionMatcher.getClassSuffix(eq("1.10.2"))).thenReturn("r1_1"); + when(mockVersionMatcher.getClassSuffixes(eq("1.10.2"))).thenReturn(List.of("r1_1")); // Register the dummy service for R1_21 (which covers r1_1 compat) new DummyRegistrarR1_21().register(factory); @@ -79,18 +81,17 @@ public class DefaultVersionedServiceFactoryTest { } @Test - void testServiceRegistrationAndRetrievalForR1_16_5() { - // Mock VersionMatcher to return r1_16_5 suffix for a 1.18.1 server - when(mockVersionMatcher.getServerVersion()).thenReturn("1.18.1"); - when(mockVersionMatcher.getClassSuffix(eq("1.18.1"))).thenReturn("r1_16_5"); + void testServiceFallbackCompatibility() { + // Mock a 1.21 server which supports r1_21, r1_16_5, and r1_1 + when(mockVersionMatcher.getServerVersion()).thenReturn("1.21.0"); + when(mockVersionMatcher.getClassSuffixes(eq("1.21.0"))).thenReturn(List.of("r1_21", "r1_16_5", "r1_1")); - // Register the dummy service for R1_21 (which covers r1_16_5 compat) - new DummyRegistrarR1_21().register(factory); + // Register ONLY for r1_1 + factory.register(TestService.class, "r1_1", TestServiceR1_21::new); - // Retrieve the service + // Retrieve the service - should fall back to r1_1 TestService service = factory.getService(TestService.class); - // Assert that the correct compatible version is returned assertNotNull(service); assertTrue(service instanceof TestServiceR1_21); } @@ -99,7 +100,7 @@ public class DefaultVersionedServiceFactoryTest { void testServiceRegistrationAndRetrievalForR1_21() { // Mock VersionMatcher to return r1_21 suffix when(mockVersionMatcher.getServerVersion()).thenReturn("1.21.0"); - when(mockVersionMatcher.getClassSuffix(eq("1.21.0"))).thenReturn("r1_21"); + when(mockVersionMatcher.getClassSuffixes(eq("1.21.0"))).thenReturn(List.of("r1_21", "r1_16_5", "r1_1")); // Register the dummy service for r1_21 new DummyRegistrarR1_21().register(factory); @@ -116,7 +117,7 @@ public class DefaultVersionedServiceFactoryTest { void testNoServiceRegisteredThrowsException() { // Mock VersionMatcher to return an unknown suffix when(mockVersionMatcher.getServerVersion()).thenReturn("unknown"); - when(mockVersionMatcher.getClassSuffix(anyString())).thenReturn("unknown_version"); + when(mockVersionMatcher.getClassSuffixes(anyString())).thenReturn(List.of("unknown_version")); // Attempt to retrieve a service without any registration IllegalStateException exception = assertThrows(IllegalStateException.class, () -> @@ -125,21 +126,4 @@ public class DefaultVersionedServiceFactoryTest { assertTrue(exception.getMessage().contains("No service registered for type")); } - - @Test - void testSpecificServiceNotRegisteredForVersionThrowsException() { - // Mock VersionMatcher to return b1_7_3 suffix - when(mockVersionMatcher.getServerVersion()).thenReturn("1.7.3"); - when(mockVersionMatcher.getClassSuffix(eq("1.7.3"))).thenReturn("b1_7_3"); - - // Register R1_21 service, but not B1_7_3 - new DummyRegistrarR1_21().register(factory); // R1_21 registrar doesn't register b1_7_3 - - // Attempt to retrieve b1_7_3 service - IllegalStateException exception = assertThrows(IllegalStateException.class, () -> - factory.getService(TestService.class) - ); - - assertTrue(exception.getMessage().contains("No service registered for type")); - } } diff --git a/src/main/java/org/adrianvictor/lib/versioning/DefaultVersionedServiceFactory.java b/src/main/java/org/adrianvictor/lib/versioning/DefaultVersionedServiceFactory.java index 9f528cf..a4d2313 100644 --- a/src/main/java/org/adrianvictor/lib/versioning/DefaultVersionedServiceFactory.java +++ b/src/main/java/org/adrianvictor/lib/versioning/DefaultVersionedServiceFactory.java @@ -23,12 +23,19 @@ public class DefaultVersionedServiceFactory implements VersionedServiceFactory { @SuppressWarnings("unchecked") public T getService(Class serviceType) { String serverVersion = versionMatcher.getServerVersion(); - String versionSuffix = versionMatcher.getClassSuffix(serverVersion); + java.util.List versionSuffixes = versionMatcher.getClassSuffixes(serverVersion); Map> versionSuppliers = serviceRegistrations.get(serviceType); - if (versionSuppliers == null || !versionSuppliers.containsKey(versionSuffix)) { - throw new IllegalStateException("No service registered for type " + serviceType.getName() + " and version " + versionSuffix + ". Current server version: " + serverVersion); + if (versionSuppliers == null) { + throw new IllegalStateException("No service registered for type " + serviceType.getName()); } - return (T) versionSuppliers.get(versionSuffix).get(); + + for (String suffix : versionSuffixes) { + if (versionSuppliers.containsKey(suffix)) { + return (T) versionSuppliers.get(suffix).get(); + } + } + + throw new IllegalStateException("No service registered for type " + serviceType.getName() + " and versions " + versionSuffixes + ". Current server version: " + serverVersion); } } diff --git a/src/main/java/org/adrianvictor/lib/versioning/VersionMatcher.java b/src/main/java/org/adrianvictor/lib/versioning/VersionMatcher.java index 91fee05..a2a6337 100644 --- a/src/main/java/org/adrianvictor/lib/versioning/VersionMatcher.java +++ b/src/main/java/org/adrianvictor/lib/versioning/VersionMatcher.java @@ -5,7 +5,7 @@ import org.bukkit.Bukkit; import java.util.Comparator; import java.util.List; import java.util.ArrayList; -import java.util.Optional; +import java.util.Collections; import java.util.regex.Pattern; import java.util.regex.Matcher; @@ -15,9 +15,9 @@ public class VersionMatcher { private record Entry(String pattern, String classSuffix, int minMajor, int minMinor, int maxMajor, int maxMinor) {} - public String getClassSuffix(String serverVersion) { + public List getClassSuffixes(String serverVersion) { if (serverVersion == null) { - return null; + return Collections.emptyList(); } List entries = populateEntries(); @@ -30,22 +30,24 @@ public class VersionMatcher { for (Entry entry : entries) { Pattern p = Pattern.compile(entry.pattern()); if (p.matcher(serverVersion).matches()) { - if (serverMajor >= entry.minMajor() && serverMinor >= entry.minMinor() && - serverMajor <= entry.maxMajor() && serverMinor <= entry.maxMinor()) { - applicableEntries.add(entry); + if (serverMajor > entry.minMajor() || (serverMajor == entry.minMajor() && serverMinor >= entry.minMinor())) { + if (serverMajor < entry.maxMajor() || (serverMajor == entry.maxMajor() && serverMinor <= entry.maxMinor())) { + applicableEntries.add(entry); + } } } } - applicableEntries.sort(Comparator - .comparing(Entry::maxMajor).reversed() - .thenComparing(Entry::maxMinor).reversed() - .thenComparing(Entry::minMajor).reversed() - .thenComparing(Entry::minMinor).reversed()); + applicableEntries.sort((a, b) -> { + if (a.minMajor() != b.minMajor()) { + return Integer.compare(b.minMajor(), a.minMajor()); + } + return Integer.compare(b.minMinor(), a.minMinor()); + }); - Optional bestMatch = applicableEntries.stream().findFirst(); - - return bestMatch.map(Entry::classSuffix).orElse(null); + return applicableEntries.stream() + .map(Entry::classSuffix) + .toList(); } public String getServerVersion() { @@ -70,12 +72,12 @@ public class VersionMatcher { private List populateEntries() { return List.of( new Entry("^1\\.7\\.3$", "b1_7_3", 1, 7, 1, 7), - // r1_1 covers versions from 1.1.x up to 1.16.4 - new Entry("^1\\.(1[0-5]|[1-9])(\\.\\d+)*$", "r1_1", 1, 1, 1, 16), - // r1_16_5 covers versions from 1.16.5 up to 1.20.x - new Entry("^1\\.(16\\.[5-9]|1[7-9]|20)(\\.\\d+)*$", "r1_16_5", 1, 16, 1, 20), - // r1_21 covers 1.21.x and above - new Entry("^1\\.21(\\.\\d+)*$", "r1_21", 1, 21, Integer.MAX_VALUE, Integer.MAX_VALUE) + // r1_1 covers versions 1.1 and above + new Entry("^1\\.\\d+(\\.\\d+)*$", "r1_1", 1, 1, Integer.MAX_VALUE, Integer.MAX_VALUE), + // r1_16_5 covers versions 1.16.5 and above + new Entry("^1\\.(16\\.[5-9]|1[7-9]|[2-9]\\d)(\\.\\d+)*$", "r1_16_5", 1, 16, Integer.MAX_VALUE, Integer.MAX_VALUE), + // r1_21 covers 1.21 and above + new Entry("^1\\.([2-9]\\d)(\\.\\d+)*$", "r1_21", 1, 21, Integer.MAX_VALUE, Integer.MAX_VALUE) ); } From 425599ff62560fb6ca887f3e3997a3fdaeb30c26 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Sat, 23 May 2026 21:49:38 -0300 Subject: [PATCH 11/22] Fix VersionMatcher not parsing version string correctly --- .../DefaultVersionedServiceFactoryTest.java | 42 ++++++------------- build.gradle.kts | 28 +++++++++++++ src/main/java/org/adrianvictor/lib/Main.java | 5 ++- .../DefaultVersionedServiceFactory.java | 15 +++++-- .../lib/versioning/VersionMatcher.java | 42 ++++++++++--------- 5 files changed, 78 insertions(+), 54 deletions(-) diff --git a/app/src/test/java/org/adrianvictor/lib/DefaultVersionedServiceFactoryTest.java b/app/src/test/java/org/adrianvictor/lib/DefaultVersionedServiceFactoryTest.java index a624d68..8d512f1 100644 --- a/app/src/test/java/org/adrianvictor/lib/DefaultVersionedServiceFactoryTest.java +++ b/app/src/test/java/org/adrianvictor/lib/DefaultVersionedServiceFactoryTest.java @@ -7,6 +7,8 @@ import org.adrianvictor.lib.versioning.VersionedServiceRegistrar; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.util.List; + import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @@ -48,7 +50,7 @@ public class DefaultVersionedServiceFactoryTest { void testServiceRegistrationAndRetrievalForB1_7_3() { // Mock VersionMatcher to return b1_7_3 suffix when(mockVersionMatcher.getServerVersion()).thenReturn("1.7.3"); - when(mockVersionMatcher.getClassSuffix(eq("1.7.3"))).thenReturn("b1_7_3"); + when(mockVersionMatcher.getClassSuffixes(eq("1.7.3"))).thenReturn(List.of("b1_7_3")); // Register the dummy service for b1_7_3 new DummyRegistrarB1_7_3().register(factory); @@ -65,7 +67,7 @@ public class DefaultVersionedServiceFactoryTest { void testServiceRegistrationAndRetrievalForR1_1() { // Mock VersionMatcher to return r1_1 suffix for a 1.10.2 server when(mockVersionMatcher.getServerVersion()).thenReturn("1.10.2"); - when(mockVersionMatcher.getClassSuffix(eq("1.10.2"))).thenReturn("r1_1"); + when(mockVersionMatcher.getClassSuffixes(eq("1.10.2"))).thenReturn(List.of("r1_1")); // Register the dummy service for R1_21 (which covers r1_1 compat) new DummyRegistrarR1_21().register(factory); @@ -79,18 +81,17 @@ public class DefaultVersionedServiceFactoryTest { } @Test - void testServiceRegistrationAndRetrievalForR1_16_5() { - // Mock VersionMatcher to return r1_16_5 suffix for a 1.18.1 server - when(mockVersionMatcher.getServerVersion()).thenReturn("1.18.1"); - when(mockVersionMatcher.getClassSuffix(eq("1.18.1"))).thenReturn("r1_16_5"); + void testServiceFallbackCompatibility() { + // Mock a 1.21 server which supports r1_21, r1_16_5, and r1_1 + when(mockVersionMatcher.getServerVersion()).thenReturn("1.21.0"); + when(mockVersionMatcher.getClassSuffixes(eq("1.21.0"))).thenReturn(List.of("r1_21", "r1_16_5", "r1_1")); - // Register the dummy service for R1_21 (which covers r1_16_5 compat) - new DummyRegistrarR1_21().register(factory); + // Register ONLY for r1_1 + factory.register(TestService.class, "r1_1", TestServiceR1_21::new); - // Retrieve the service + // Retrieve the service - should fall back to r1_1 TestService service = factory.getService(TestService.class); - // Assert that the correct compatible version is returned assertNotNull(service); assertTrue(service instanceof TestServiceR1_21); } @@ -99,7 +100,7 @@ public class DefaultVersionedServiceFactoryTest { void testServiceRegistrationAndRetrievalForR1_21() { // Mock VersionMatcher to return r1_21 suffix when(mockVersionMatcher.getServerVersion()).thenReturn("1.21.0"); - when(mockVersionMatcher.getClassSuffix(eq("1.21.0"))).thenReturn("r1_21"); + when(mockVersionMatcher.getClassSuffixes(eq("1.21.0"))).thenReturn(List.of("r1_21", "r1_16_5", "r1_1")); // Register the dummy service for r1_21 new DummyRegistrarR1_21().register(factory); @@ -116,7 +117,7 @@ public class DefaultVersionedServiceFactoryTest { void testNoServiceRegisteredThrowsException() { // Mock VersionMatcher to return an unknown suffix when(mockVersionMatcher.getServerVersion()).thenReturn("unknown"); - when(mockVersionMatcher.getClassSuffix(anyString())).thenReturn("unknown_version"); + when(mockVersionMatcher.getClassSuffixes(anyString())).thenReturn(List.of("unknown_version")); // Attempt to retrieve a service without any registration IllegalStateException exception = assertThrows(IllegalStateException.class, () -> @@ -125,21 +126,4 @@ public class DefaultVersionedServiceFactoryTest { assertTrue(exception.getMessage().contains("No service registered for type")); } - - @Test - void testSpecificServiceNotRegisteredForVersionThrowsException() { - // Mock VersionMatcher to return b1_7_3 suffix - when(mockVersionMatcher.getServerVersion()).thenReturn("1.7.3"); - when(mockVersionMatcher.getClassSuffix(eq("1.7.3"))).thenReturn("b1_7_3"); - - // Register R1_21 service, but not B1_7_3 - new DummyRegistrarR1_21().register(factory); // R1_21 registrar doesn't register b1_7_3 - - // Attempt to retrieve b1_7_3 service - IllegalStateException exception = assertThrows(IllegalStateException.class, () -> - factory.getService(TestService.class) - ); - - assertTrue(exception.getMessage().contains("No service registered for type")); - } } diff --git a/build.gradle.kts b/build.gradle.kts index 3e973c0..d71a0b9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -90,12 +90,40 @@ tasks.register("buildAll") { dependsOn(tasks.withType()) } +// Task to merge service files +val prepareServiceFiles = tasks.register("prepareServiceFiles") { + val outputDir = layout.buildDirectory.dir("generated/service-files") + val serviceFile = "META-INF/services/org.adrianvictor.lib.versioning.VersionedServiceRegistrar" + + // Define inputs + val inputFiles = mcVersions.map { ver -> file("src/$ver/resources/$serviceFile") }.filter { it.exists() } + inputs.files(inputFiles) + outputs.dir(outputDir) + + doLast { + val registrars = mutableSetOf() + inputFiles.forEach { file -> + registrars.addAll(file.readLines().filter { it.isNotBlank() }) + } + + val mergedFile = outputDir.get().file(serviceFile).asFile + mergedFile.parentFile.mkdirs() + mergedFile.writeText(registrars.joinToString("\n")) + } +} + tasks.register("bundleAll") { from(sourceSets["main"].output) mcVersions.forEach { ver -> from(sourceSets[ver].output) } + // Include the merged service file + from(prepareServiceFiles) { + into("META-INF/services") + include("org.adrianvictor.lib.versioning.VersionedServiceRegistrar") + } + duplicatesStrategy = DuplicatesStrategy.EXCLUDE archiveClassifier.set("all-implementations") diff --git a/src/main/java/org/adrianvictor/lib/Main.java b/src/main/java/org/adrianvictor/lib/Main.java index a16c99d..6190da1 100644 --- a/src/main/java/org/adrianvictor/lib/Main.java +++ b/src/main/java/org/adrianvictor/lib/Main.java @@ -19,10 +19,13 @@ public class Main extends JavaPlugin { versionedServiceFactory = new DefaultVersionedServiceFactory(versionMatcher); - ServiceLoader registrars = ServiceLoader.load(VersionedServiceRegistrar.class); + ServiceLoader registrars = ServiceLoader.load(VersionedServiceRegistrar.class, getClassLoader()); + int count = 0; for (VersionedServiceRegistrar registrar : registrars) { registrar.register(versionedServiceFactory); + count++; } + getLogger().info("Registered " + count + " version-specific registrars."); getLogger().info("tenkumaLib has been enabled!"); } diff --git a/src/main/java/org/adrianvictor/lib/versioning/DefaultVersionedServiceFactory.java b/src/main/java/org/adrianvictor/lib/versioning/DefaultVersionedServiceFactory.java index 9f528cf..a4d2313 100644 --- a/src/main/java/org/adrianvictor/lib/versioning/DefaultVersionedServiceFactory.java +++ b/src/main/java/org/adrianvictor/lib/versioning/DefaultVersionedServiceFactory.java @@ -23,12 +23,19 @@ public class DefaultVersionedServiceFactory implements VersionedServiceFactory { @SuppressWarnings("unchecked") public T getService(Class serviceType) { String serverVersion = versionMatcher.getServerVersion(); - String versionSuffix = versionMatcher.getClassSuffix(serverVersion); + java.util.List versionSuffixes = versionMatcher.getClassSuffixes(serverVersion); Map> versionSuppliers = serviceRegistrations.get(serviceType); - if (versionSuppliers == null || !versionSuppliers.containsKey(versionSuffix)) { - throw new IllegalStateException("No service registered for type " + serviceType.getName() + " and version " + versionSuffix + ". Current server version: " + serverVersion); + if (versionSuppliers == null) { + throw new IllegalStateException("No service registered for type " + serviceType.getName()); } - return (T) versionSuppliers.get(versionSuffix).get(); + + for (String suffix : versionSuffixes) { + if (versionSuppliers.containsKey(suffix)) { + return (T) versionSuppliers.get(suffix).get(); + } + } + + throw new IllegalStateException("No service registered for type " + serviceType.getName() + " and versions " + versionSuffixes + ". Current server version: " + serverVersion); } } diff --git a/src/main/java/org/adrianvictor/lib/versioning/VersionMatcher.java b/src/main/java/org/adrianvictor/lib/versioning/VersionMatcher.java index 91fee05..a2a6337 100644 --- a/src/main/java/org/adrianvictor/lib/versioning/VersionMatcher.java +++ b/src/main/java/org/adrianvictor/lib/versioning/VersionMatcher.java @@ -5,7 +5,7 @@ import org.bukkit.Bukkit; import java.util.Comparator; import java.util.List; import java.util.ArrayList; -import java.util.Optional; +import java.util.Collections; import java.util.regex.Pattern; import java.util.regex.Matcher; @@ -15,9 +15,9 @@ public class VersionMatcher { private record Entry(String pattern, String classSuffix, int minMajor, int minMinor, int maxMajor, int maxMinor) {} - public String getClassSuffix(String serverVersion) { + public List getClassSuffixes(String serverVersion) { if (serverVersion == null) { - return null; + return Collections.emptyList(); } List entries = populateEntries(); @@ -30,22 +30,24 @@ public class VersionMatcher { for (Entry entry : entries) { Pattern p = Pattern.compile(entry.pattern()); if (p.matcher(serverVersion).matches()) { - if (serverMajor >= entry.minMajor() && serverMinor >= entry.minMinor() && - serverMajor <= entry.maxMajor() && serverMinor <= entry.maxMinor()) { - applicableEntries.add(entry); + if (serverMajor > entry.minMajor() || (serverMajor == entry.minMajor() && serverMinor >= entry.minMinor())) { + if (serverMajor < entry.maxMajor() || (serverMajor == entry.maxMajor() && serverMinor <= entry.maxMinor())) { + applicableEntries.add(entry); + } } } } - applicableEntries.sort(Comparator - .comparing(Entry::maxMajor).reversed() - .thenComparing(Entry::maxMinor).reversed() - .thenComparing(Entry::minMajor).reversed() - .thenComparing(Entry::minMinor).reversed()); + applicableEntries.sort((a, b) -> { + if (a.minMajor() != b.minMajor()) { + return Integer.compare(b.minMajor(), a.minMajor()); + } + return Integer.compare(b.minMinor(), a.minMinor()); + }); - Optional bestMatch = applicableEntries.stream().findFirst(); - - return bestMatch.map(Entry::classSuffix).orElse(null); + return applicableEntries.stream() + .map(Entry::classSuffix) + .toList(); } public String getServerVersion() { @@ -70,12 +72,12 @@ public class VersionMatcher { private List populateEntries() { return List.of( new Entry("^1\\.7\\.3$", "b1_7_3", 1, 7, 1, 7), - // r1_1 covers versions from 1.1.x up to 1.16.4 - new Entry("^1\\.(1[0-5]|[1-9])(\\.\\d+)*$", "r1_1", 1, 1, 1, 16), - // r1_16_5 covers versions from 1.16.5 up to 1.20.x - new Entry("^1\\.(16\\.[5-9]|1[7-9]|20)(\\.\\d+)*$", "r1_16_5", 1, 16, 1, 20), - // r1_21 covers 1.21.x and above - new Entry("^1\\.21(\\.\\d+)*$", "r1_21", 1, 21, Integer.MAX_VALUE, Integer.MAX_VALUE) + // r1_1 covers versions 1.1 and above + new Entry("^1\\.\\d+(\\.\\d+)*$", "r1_1", 1, 1, Integer.MAX_VALUE, Integer.MAX_VALUE), + // r1_16_5 covers versions 1.16.5 and above + new Entry("^1\\.(16\\.[5-9]|1[7-9]|[2-9]\\d)(\\.\\d+)*$", "r1_16_5", 1, 16, Integer.MAX_VALUE, Integer.MAX_VALUE), + // r1_21 covers 1.21 and above + new Entry("^1\\.([2-9]\\d)(\\.\\d+)*$", "r1_21", 1, 21, Integer.MAX_VALUE, Integer.MAX_VALUE) ); } From b368118c3ba39ab102e0b6c5ccf37eec5f426f08 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Sun, 24 May 2026 14:02:19 -0300 Subject: [PATCH 12/22] Merge remote-tracking branch 'origin/main' --- build.gradle.kts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 879b2c3..7db6dbf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -127,10 +127,7 @@ tasks.register("bundleAll") { } // Include the merged service file - from(prepareServiceFiles) { - into("META-INF/services") - include("org.adrianvictor.lib.versioning.VersionedServiceRegistrar") - } + from(prepareServiceFiles.get().outputs.files.singleFile) duplicatesStrategy = DuplicatesStrategy.EXCLUDE From 8db4c560dbb75103a49524be4e85fd7e4604f7fc Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Sun, 24 May 2026 14:22:43 -0300 Subject: [PATCH 13/22] Merge remote-tracking branch 'origin/main' --- src/main/java/org/adrianvictor/lib/Main.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/org/adrianvictor/lib/Main.java b/src/main/java/org/adrianvictor/lib/Main.java index d10d7fd..edb5c75 100644 --- a/src/main/java/org/adrianvictor/lib/Main.java +++ b/src/main/java/org/adrianvictor/lib/Main.java @@ -36,6 +36,11 @@ public class Main extends JavaPlugin { count++; } getLogger().info("Registered " + count + " version-specific registrars."); + + String serverVersion = versionMatcher.getServerVersion(); + java.util.List suffixes = versionMatcher.getClassSuffixes(serverVersion); + getLogger().info("Server Version: " + serverVersion); + getLogger().info("Applicable suffixes: " + suffixes); getLogger().info("tenkumaLib has been enabled!"); } From 2956f2c04b40325385089fd9d2141c22ab3857b5 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Sun, 24 May 2026 14:22:43 -0300 Subject: [PATCH 14/22] Merge remote-tracking branch 'origin/main' --- ...org.adrianvictor.lib.versioning.VersionedServiceRegistrar | 1 + src/main/java/org/adrianvictor/lib/Main.java | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 META-INF/services/org.adrianvictor.lib.versioning.VersionedServiceRegistrar diff --git a/META-INF/services/org.adrianvictor.lib.versioning.VersionedServiceRegistrar b/META-INF/services/org.adrianvictor.lib.versioning.VersionedServiceRegistrar new file mode 100644 index 0000000..8f5e531 --- /dev/null +++ b/META-INF/services/org.adrianvictor.lib.versioning.VersionedServiceRegistrar @@ -0,0 +1 @@ +org.adrianvictor.lib.impl.r1_21.R1_21Registrar \ No newline at end of file diff --git a/src/main/java/org/adrianvictor/lib/Main.java b/src/main/java/org/adrianvictor/lib/Main.java index d10d7fd..edb5c75 100644 --- a/src/main/java/org/adrianvictor/lib/Main.java +++ b/src/main/java/org/adrianvictor/lib/Main.java @@ -36,6 +36,11 @@ public class Main extends JavaPlugin { count++; } getLogger().info("Registered " + count + " version-specific registrars."); + + String serverVersion = versionMatcher.getServerVersion(); + java.util.List suffixes = versionMatcher.getClassSuffixes(serverVersion); + getLogger().info("Server Version: " + serverVersion); + getLogger().info("Applicable suffixes: " + suffixes); getLogger().info("tenkumaLib has been enabled!"); } From df450e1f9b01c8c0650bd32848fe23fa12cc6f42 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Mon, 25 May 2026 18:27:54 -0300 Subject: [PATCH 15/22] Fix versioning not working on b1.7.3 --- .gitignore | 4 +- ...r.lib.versioning.VersionedServiceRegistrar | 1 - build.gradle.kts | 39 ++++-- .../lib/impl/b1_7_3/B1_7_3Registrar.java | 15 ++- .../lib/impl/b1_7_3/logging/Logger.java | 38 ++++++ src/main/java/org/adrianvictor/lib/Main.java | 121 +++++++++++++----- .../lib/loading/ModernRegistrarLoader.java | 8 ++ .../lib/loading/RegistrarBridge.java | 8 ++ .../org/adrianvictor/lib/logging/Logger.java | 10 ++ .../lib/logging/provider/LoggerProvider.java | 7 + .../lib/versioning/MinecraftVersion.java | 91 +++++++++++++ .../lib/versioning/VersionMatcher.java | 96 ++++---------- src/main/resources/plugin.yml | 2 +- .../lib/impl/r1_21/R1_21Registrar.java | 11 ++ .../lib/impl/r1_21/logging/Logger.java | 36 ++++++ 15 files changed, 363 insertions(+), 124 deletions(-) delete mode 100644 META-INF/services/org.adrianvictor.lib.versioning.VersionedServiceRegistrar create mode 100644 src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/logging/Logger.java create mode 100644 src/main/java/org/adrianvictor/lib/loading/ModernRegistrarLoader.java create mode 100644 src/main/java/org/adrianvictor/lib/loading/RegistrarBridge.java create mode 100644 src/main/java/org/adrianvictor/lib/logging/Logger.java create mode 100644 src/main/java/org/adrianvictor/lib/logging/provider/LoggerProvider.java create mode 100644 src/main/java/org/adrianvictor/lib/versioning/MinecraftVersion.java create mode 100644 src/r1_21/java/org/adrianvictor/lib/impl/r1_21/logging/Logger.java diff --git a/.gitignore b/.gitignore index df892ec..741d4ab 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,6 @@ out/ # Ignore Gradle build output directory build -run/ \ No newline at end of file +run/ + +*.log \ No newline at end of file diff --git a/META-INF/services/org.adrianvictor.lib.versioning.VersionedServiceRegistrar b/META-INF/services/org.adrianvictor.lib.versioning.VersionedServiceRegistrar deleted file mode 100644 index 8f5e531..0000000 --- a/META-INF/services/org.adrianvictor.lib.versioning.VersionedServiceRegistrar +++ /dev/null @@ -1 +0,0 @@ -org.adrianvictor.lib.impl.r1_21.R1_21Registrar \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 7db6dbf..711c583 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ plugins { } group = "org.adrianvictor" -version = System.getenv("VERSION") ?: "unknown" +version = "2.0" val buildEnv = System.getenv("BUILD_CHANNEL") ?: if (System.getenv("JITPACK") != null) "jitpack" else "local" @@ -60,8 +60,8 @@ mcVersions.forEach { ver -> /* ----------------------------------------- */ dependencies { - add("compileOnly", "io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT") - add("r1_21CompileOnly", "io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT") + add("compileOnly", "io.papermc.paper:paper-api:1.21.1-R0.1-SNAPSHOT") + add("r1_21CompileOnly", "io.papermc.paper:paper-api:1.21.1-R0.1-SNAPSHOT") add("b1_7_3CompileOnly", files("libs/craftbukkit-1060.jar")) } @@ -103,22 +103,43 @@ val prepareServiceFiles = tasks.register("prepareServiceFiles") { doLast { val registrars = mutableSetOf() inputFiles.forEach { file -> - println("Checking file: ${file.absolutePath}, exists: ${file.exists()}") if (file.exists()) { - val lines = file.readLines().filter { it.isNotBlank() } - println("Found lines: $lines") - registrars.addAll(lines) + file.readLines().forEach { line -> + val trimmed = line.trim() + if (trimmed.isNotEmpty()) { + registrars.add(trimmed) + } + } } } val mergedFile = outputDir.get().file(serviceFile).asFile mergedFile.parentFile.mkdirs() - mergedFile.writeText(registrars.joinToString("\n")) + mergedFile.writeText(registrars.joinToString("\n") + "\n") println("Merged service file content: \n${registrars.joinToString("\n")}") } } +tasks.named("jar") { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + from(sourceSets["main"].output) + mcVersions.forEach { ver -> + from(sourceSets[ver].output) { + exclude("META-INF/services/org.adrianvictor.lib.versioning.VersionedServiceRegistrar") + } + } + from(prepareServiceFiles) + + manifest { + attributes( + "Implemented-Versions" to mcVersions.joinToString(",") + ) + } +} + tasks.register("bundleAll") { + dependsOn("jar") + // This is now redundant but we keep it for compatibility with previous instructions from(sourceSets["main"].output) mcVersions.forEach { ver -> from(sourceSets[ver].output) { @@ -127,7 +148,7 @@ tasks.register("bundleAll") { } // Include the merged service file - from(prepareServiceFiles.get().outputs.files.singleFile) + from(prepareServiceFiles) duplicatesStrategy = DuplicatesStrategy.EXCLUDE diff --git a/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/B1_7_3Registrar.java b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/B1_7_3Registrar.java index 2475ca3..c9cd79b 100644 --- a/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/B1_7_3Registrar.java +++ b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/B1_7_3Registrar.java @@ -1,20 +1,27 @@ package org.adrianvictor.lib.impl.b1_7_3; +import org.adrianvictor.lib.impl.b1_7_3.logging.Logger; import org.adrianvictor.lib.versioning.VersionedServiceFactory; import org.adrianvictor.lib.versioning.VersionedServiceRegistrar; import org.adrianvictor.lib.configuration.provider.ConfigurationProvider; import org.adrianvictor.lib.impl.b1_7_3.configuration.Configuration; import org.adrianvictor.lib.text.provider.TextColorProvider; import org.adrianvictor.lib.impl.b1_7_3.text.TextColor; +import org.adrianvictor.lib.logging.provider.LoggerProvider; +import org.bukkit.plugin.java.JavaPlugin; public class B1_7_3Registrar implements VersionedServiceRegistrar { + private JavaPlugin plugin; + + public void setPlugin(JavaPlugin plugin) { + this.plugin = plugin; + } + @Override public void register(VersionedServiceFactory factory) { + Logger.setPlugin(this.plugin); factory.register(ConfigurationProvider.class, "b1_7_3", Configuration::new); factory.register(TextColorProvider.class, "b1_7_3", TextColor::new); - - // Register for b1_8_compat as well, assuming compatibility - factory.register(ConfigurationProvider.class, "b1_8_compat", Configuration::new); - factory.register(TextColorProvider.class, "b1_8_compat", TextColor::new); + factory.register(LoggerProvider.class, "b1_7_3", Logger::new); } } diff --git a/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/logging/Logger.java b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/logging/Logger.java new file mode 100644 index 0000000..183b2ab --- /dev/null +++ b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/logging/Logger.java @@ -0,0 +1,38 @@ +package org.adrianvictor.lib.impl.b1_7_3.logging; + +import org.adrianvictor.lib.logging.provider.LoggerProvider; +import org.bukkit.plugin.java.JavaPlugin; + +public class Logger implements LoggerProvider { + private static JavaPlugin plugin; + private java.util.logging.Logger logger; + private String prefix = "[tenkumaLib] "; + + public static void setPlugin(JavaPlugin p) { + plugin = p; + } + + public Logger() { + if (plugin != null) { + logger = plugin.getServer().getLogger(); + prefix = "[" + plugin.getDescription().getName() + "] "; + } else { + logger = java.util.logging.Logger.getLogger("Minecraft"); + } + } + + @Override + public void info(String text) { + logger.info(prefix + text); + } + + @Override + public void warning(String text) { + logger.warning(prefix + text); + } + + @Override + public void error(String text) { + logger.severe(prefix + text); + } +} diff --git a/src/main/java/org/adrianvictor/lib/Main.java b/src/main/java/org/adrianvictor/lib/Main.java index edb5c75..c37c85b 100644 --- a/src/main/java/org/adrianvictor/lib/Main.java +++ b/src/main/java/org/adrianvictor/lib/Main.java @@ -1,60 +1,113 @@ package org.adrianvictor.lib; +import org.adrianvictor.lib.logging.provider.LoggerProvider; import org.adrianvictor.lib.versioning.DefaultVersionedServiceFactory; +import org.adrianvictor.lib.versioning.MinecraftVersion; import org.adrianvictor.lib.versioning.VersionMatcher; import org.adrianvictor.lib.versioning.VersionedServiceFactory; import org.adrianvictor.lib.versioning.VersionedServiceRegistrar; +import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; -import java.util.ServiceLoader; +import java.util.logging.Logger; public class Main extends JavaPlugin { - static { - System.out.println("Main class loaded!"); - } + private static Logger globalLogger; + private static LoggerProvider pluginLogger; + private static DefaultVersionedServiceFactory versionedServiceFactory; - private static VersionedServiceFactory versionedServiceFactory; - private static VersionMatcher versionMatcher; + public static VersionedServiceFactory getServiceFactory() { + return versionedServiceFactory; + } @Override public void onEnable() { - versionMatcher = new VersionMatcher(); + globalLogger = Bukkit.getServer().getLogger(); + globalLogger.info("[tenkumaLib] Enabling tenkumaLib v2.0..."); + + VersionMatcher versionMatcher = new VersionMatcher(); versionedServiceFactory = new DefaultVersionedServiceFactory(versionMatcher); - ClassLoader cl = getClassLoader(); - getLogger().info("Plugin ClassLoader: " + cl.getClass().getName()); - String serviceFile = "META-INF/services/org.adrianvictor.lib.versioning.VersionedServiceRegistrar"; - java.net.URL res = cl.getResource(serviceFile); - getLogger().info("Service file resource: " + res); + MinecraftVersion version = MinecraftVersion.detect(); + globalLogger.info("[tenkumaLib] Detected Minecraft Version: " + version); - ServiceLoader registrars = ServiceLoader.load(VersionedServiceRegistrar.class, cl); - int count = 0; - for (VersionedServiceRegistrar registrar : registrars) { - registrar.register(versionedServiceFactory); - getLogger().info("Registered registrar: " + registrar.getClass().getName()); - count++; + boolean registered = false; + + // 1. Load core registrar based on version mapping + String coreRegistrar = version.getRegistrarClass(); + if (coreRegistrar != null) { + if (loadRegistrar(coreRegistrar)) { + registered = true; + } } - getLogger().info("Registered " + count + " version-specific registrars."); - - String serverVersion = versionMatcher.getServerVersion(); - java.util.List suffixes = versionMatcher.getClassSuffixes(serverVersion); - getLogger().info("Server Version: " + serverVersion); - getLogger().info("Applicable suffixes: " + suffixes); - getLogger().info("tenkumaLib has been enabled!"); + // 2. Load registrars via ServiceLoader for extensibility + try { + java.util.ServiceLoader loader = java.util.ServiceLoader.load(VersionedServiceRegistrar.class, getClassLoader()); + for (VersionedServiceRegistrar registrar : loader) { + String registrarName = registrar.getClass().getName(); + // Skip if already registered as core + if (registrarName.equals(coreRegistrar)) continue; + + if (registerRegistrar(registrar)) { + registered = true; + } + } + } catch (Throwable t) { + // ServiceLoader failure is expected on some legacy platforms + } + + if (!registered) { + globalLogger.warning("[tenkumaLib] No registrars were loaded! Services might not be available."); + } + + try { + org.adrianvictor.lib.logging.Logger.create(versionedServiceFactory); + pluginLogger = versionedServiceFactory.getService(LoggerProvider.class); + pluginLogger.info("tenkumaLib has been successfully enabled!"); + } catch (Exception e) { + globalLogger.severe("[tenkumaLib] Failed to initialize plugin logger: " + e.getMessage()); + e.printStackTrace(); + } + } + + private boolean loadRegistrar(String className) { + try { + Class clazz = Class.forName(className, true, getClassLoader()); + VersionedServiceRegistrar registrar = (VersionedServiceRegistrar) clazz.getDeclaredConstructor().newInstance(); + return registerRegistrar(registrar); + } catch (NoClassDefFoundError e) { + globalLogger.info("[tenkumaLib] Skipping registrar " + className + " (incompatible server version)"); + } catch (Throwable t) { + globalLogger.warning("[tenkumaLib] Failed to load registrar " + className + ": " + t.toString()); + } + return false; + } + + private boolean registerRegistrar(VersionedServiceRegistrar registrar) { + String registrarName = registrar.getClass().getName(); + try { + try { + java.lang.reflect.Method setPlugin = registrar.getClass().getMethod("setPlugin", JavaPlugin.class); + setPlugin.invoke(registrar, this); + } catch (NoSuchMethodException ignored) {} + + registrar.register(versionedServiceFactory); + globalLogger.info("[tenkumaLib] Registered " + registrarName); + return true; + } catch (NoClassDefFoundError e) { + globalLogger.info("[tenkumaLib] Skipping registrar " + registrarName + " (incompatible server version)"); + } catch (Throwable t) { + globalLogger.warning("[tenkumaLib] Failed to register " + registrarName + ": " + t.toString()); + } + return false; } @Override public void onDisable() { - getLogger().info("tenkumaLib has been disabled!"); - } - - public static VersionedServiceFactory getVersionedServiceFactory() { - return versionedServiceFactory; - } - - public static VersionMatcher getVersionMatcher() { - return versionMatcher; + if (pluginLogger != null) { + pluginLogger.info("tenkumaLib has been disabled!"); + } } } diff --git a/src/main/java/org/adrianvictor/lib/loading/ModernRegistrarLoader.java b/src/main/java/org/adrianvictor/lib/loading/ModernRegistrarLoader.java new file mode 100644 index 0000000..e8bb5dc --- /dev/null +++ b/src/main/java/org/adrianvictor/lib/loading/ModernRegistrarLoader.java @@ -0,0 +1,8 @@ +package org.adrianvictor.lib.loading; + +import org.adrianvictor.lib.versioning.VersionedServiceFactory; +import org.bukkit.plugin.java.JavaPlugin; + +public interface ModernRegistrarLoader { + void load(VersionedServiceFactory factory, JavaPlugin plugin); +} diff --git a/src/main/java/org/adrianvictor/lib/loading/RegistrarBridge.java b/src/main/java/org/adrianvictor/lib/loading/RegistrarBridge.java new file mode 100644 index 0000000..b0cd5c0 --- /dev/null +++ b/src/main/java/org/adrianvictor/lib/loading/RegistrarBridge.java @@ -0,0 +1,8 @@ +package org.adrianvictor.lib.loading; + +import org.adrianvictor.lib.versioning.VersionedServiceFactory; +import org.bukkit.plugin.java.JavaPlugin; + +public interface RegistrarBridge { + void load(VersionedServiceFactory factory, JavaPlugin plugin); +} diff --git a/src/main/java/org/adrianvictor/lib/logging/Logger.java b/src/main/java/org/adrianvictor/lib/logging/Logger.java new file mode 100644 index 0000000..a62717e --- /dev/null +++ b/src/main/java/org/adrianvictor/lib/logging/Logger.java @@ -0,0 +1,10 @@ +package org.adrianvictor.lib.logging; + +import org.adrianvictor.lib.logging.provider.LoggerProvider; +import org.adrianvictor.lib.versioning.VersionedServiceFactory; + +public class Logger { + public static LoggerProvider create(VersionedServiceFactory factory) { + return factory.getService(LoggerProvider.class); + } +} diff --git a/src/main/java/org/adrianvictor/lib/logging/provider/LoggerProvider.java b/src/main/java/org/adrianvictor/lib/logging/provider/LoggerProvider.java new file mode 100644 index 0000000..a3e3113 --- /dev/null +++ b/src/main/java/org/adrianvictor/lib/logging/provider/LoggerProvider.java @@ -0,0 +1,7 @@ +package org.adrianvictor.lib.logging.provider; + +public interface LoggerProvider { + void info(String text); + void warning(String text); + void error(String text); +} diff --git a/src/main/java/org/adrianvictor/lib/versioning/MinecraftVersion.java b/src/main/java/org/adrianvictor/lib/versioning/MinecraftVersion.java new file mode 100644 index 0000000..5ae57be --- /dev/null +++ b/src/main/java/org/adrianvictor/lib/versioning/MinecraftVersion.java @@ -0,0 +1,91 @@ +package org.adrianvictor.lib.versioning; + +import org.bukkit.Bukkit; + +public enum MinecraftVersion { + B1_7_3(0, "b1_7_3", "org.adrianvictor.lib.impl.b1_7_3.B1_7_3Registrar"), + V1_1(1, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), + V1_2(2, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), + V1_3(3, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), + V1_4(4, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), + V1_5(5, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), + V1_6(6, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), + V1_7(7, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), + V1_8(8, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), + V1_9(9, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), + V1_10(10, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), + V1_11(11, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), + V1_12(12, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), + V1_13(13, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), + V1_14(14, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), + V1_15(15, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), + V1_16(16, "r1_16_5", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), + V1_17(17, "r1_16_5", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), + V1_18(18, "r1_16_5", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), + V1_19(19, "r1_16_5", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), + V1_20(20, "r1_16_5", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), + V1_21(21, "r1_21", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), + UNKNOWN(-1, "unknown"); + + private final int versionId; + private final String suffix; + private final String registrarClass; + + MinecraftVersion(int versionId, String suffix) { + this(versionId, suffix, null); + } + + MinecraftVersion(int versionId, String suffix, String registrarClass) { + this.versionId = versionId; + this.suffix = suffix; + this.registrarClass = registrarClass; + } + + public int getVersionId() { + return versionId; + } + + public String getSuffix() { + return suffix; + } + + public String getRegistrarClass() { + return registrarClass; + } + + public boolean isAtLeast(MinecraftVersion other) { + return this.versionId >= other.versionId; + } + + public static MinecraftVersion detect() { + String version = Bukkit.getVersion(); + if (version.contains("1.7.3")) return B1_7_3; + + String bukkitVersion = Bukkit.getBukkitVersion(); + String mcVer = bukkitVersion.split("-")[0]; + + if (mcVer.startsWith("1.21")) return V1_21; + if (mcVer.startsWith("1.20")) return V1_20; + if (mcVer.startsWith("1.19")) return V1_19; + if (mcVer.startsWith("1.18")) return V1_18; + if (mcVer.startsWith("1.17")) return V1_17; + if (mcVer.startsWith("1.16")) return V1_16; + if (mcVer.startsWith("1.15")) return V1_15; + if (mcVer.startsWith("1.14")) return V1_14; + if (mcVer.startsWith("1.13")) return V1_13; + if (mcVer.startsWith("1.12")) return V1_12; + if (mcVer.startsWith("1.11")) return V1_11; + if (mcVer.startsWith("1.10")) return V1_10; + if (mcVer.startsWith("1.9")) return V1_9; + if (mcVer.startsWith("1.8")) return V1_8; + if (mcVer.startsWith("1.7")) return V1_7; + if (mcVer.startsWith("1.6")) return V1_6; + if (mcVer.startsWith("1.5")) return V1_5; + if (mcVer.startsWith("1.4")) return V1_4; + if (mcVer.startsWith("1.3")) return V1_3; + if (mcVer.startsWith("1.2")) return V1_2; + if (mcVer.startsWith("1.1")) return V1_1; + + return UNKNOWN; + } +} diff --git a/src/main/java/org/adrianvictor/lib/versioning/VersionMatcher.java b/src/main/java/org/adrianvictor/lib/versioning/VersionMatcher.java index a2a6337..1e82d04 100644 --- a/src/main/java/org/adrianvictor/lib/versioning/VersionMatcher.java +++ b/src/main/java/org/adrianvictor/lib/versioning/VersionMatcher.java @@ -1,91 +1,39 @@ package org.adrianvictor.lib.versioning; -import org.bukkit.Bukkit; - -import java.util.Comparator; -import java.util.List; import java.util.ArrayList; import java.util.Collections; -import java.util.regex.Pattern; -import java.util.regex.Matcher; +import java.util.List; public class VersionMatcher { public VersionMatcher() {} - private record Entry(String pattern, String classSuffix, int minMajor, int minMinor, int maxMajor, int maxMinor) {} - public List getClassSuffixes(String serverVersion) { - if (serverVersion == null) { + MinecraftVersion version = MinecraftVersion.detect(); + if (version == MinecraftVersion.UNKNOWN) { return Collections.emptyList(); } - - List entries = populateEntries(); - List applicableEntries = new ArrayList<>(); - - int[] parsedServerVersion = parseVersion(serverVersion); - int serverMajor = parsedServerVersion[0]; - int serverMinor = parsedServerVersion[1]; - - for (Entry entry : entries) { - Pattern p = Pattern.compile(entry.pattern()); - if (p.matcher(serverVersion).matches()) { - if (serverMajor > entry.minMajor() || (serverMajor == entry.minMajor() && serverMinor >= entry.minMinor())) { - if (serverMajor < entry.maxMajor() || (serverMajor == entry.maxMajor() && serverMinor <= entry.maxMinor())) { - applicableEntries.add(entry); - } - } - } + + List suffixes = new ArrayList<>(); + + // Add suffixes in order of specificity (newest/most specific first) + if (version.isAtLeast(MinecraftVersion.V1_21)) { + suffixes.add("r1_21"); } - - applicableEntries.sort((a, b) -> { - if (a.minMajor() != b.minMajor()) { - return Integer.compare(b.minMajor(), a.minMajor()); - } - return Integer.compare(b.minMinor(), a.minMinor()); - }); - - return applicableEntries.stream() - .map(Entry::classSuffix) - .toList(); + if (version.isAtLeast(MinecraftVersion.V1_16)) { + suffixes.add("r1_16_5"); + } + if (version.isAtLeast(MinecraftVersion.V1_1)) { + suffixes.add("r1_1"); + } + if (version == MinecraftVersion.B1_7_3) { + suffixes.add("b1_7_3"); + } + + return suffixes; } public String getServerVersion() { - String rawVersion = null; - try { - rawVersion = Bukkit.getMinecraftVersion(); - } catch (NoSuchMethodError ignored) {} - - if (rawVersion == null || rawVersion.isEmpty()) { - String v = Bukkit.getVersion(); - int start = v.lastIndexOf("MC: "); - if (start != -1) { - rawVersion = v.substring(start + 4, v.length() - 1); - } else { - rawVersion = "unknown"; - } - } - - return rawVersion; + return MinecraftVersion.detect().name(); } - - private List populateEntries() { - return List.of( - new Entry("^1\\.7\\.3$", "b1_7_3", 1, 7, 1, 7), - // r1_1 covers versions 1.1 and above - new Entry("^1\\.\\d+(\\.\\d+)*$", "r1_1", 1, 1, Integer.MAX_VALUE, Integer.MAX_VALUE), - // r1_16_5 covers versions 1.16.5 and above - new Entry("^1\\.(16\\.[5-9]|1[7-9]|[2-9]\\d)(\\.\\d+)*$", "r1_16_5", 1, 16, Integer.MAX_VALUE, Integer.MAX_VALUE), - // r1_21 covers 1.21 and above - new Entry("^1\\.([2-9]\\d)(\\.\\d+)*$", "r1_21", 1, 21, Integer.MAX_VALUE, Integer.MAX_VALUE) - ); - } - - private int[] parseVersion(String versionString) { - Matcher matcher = Pattern.compile("^(\\d+)\\.(\\d+).*").matcher(versionString); - if (matcher.find()) { - return new int[]{Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2))}; - } - return new int[]{0, 0}; // Default for unknown format - } -} \ No newline at end of file +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 5585d53..10e821b 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -5,4 +5,4 @@ main: org.adrianvictor.lib.Main startup: startup url: https://adrianvic.github.io version: '2.0' -api-version: '1.13' \ No newline at end of file +api-version: '1.21' \ No newline at end of file diff --git a/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/R1_21Registrar.java b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/R1_21Registrar.java index d4b10b4..1b7e752 100644 --- a/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/R1_21Registrar.java +++ b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/R1_21Registrar.java @@ -1,16 +1,27 @@ package org.adrianvictor.lib.impl.r1_21; +import org.adrianvictor.lib.impl.r1_21.logging.Logger; import org.adrianvictor.lib.versioning.VersionedServiceFactory; import org.adrianvictor.lib.versioning.VersionedServiceRegistrar; import org.adrianvictor.lib.configuration.provider.ConfigurationProvider; import org.adrianvictor.lib.impl.r1_21.configuration.Configuration; import org.adrianvictor.lib.text.provider.TextColorProvider; import org.adrianvictor.lib.impl.r1_21.text.TextColor; +import org.adrianvictor.lib.logging.provider.LoggerProvider; +import org.bukkit.plugin.java.JavaPlugin; public class R1_21Registrar implements VersionedServiceRegistrar { + private JavaPlugin plugin; + + public void setPlugin(JavaPlugin plugin) { + this.plugin = plugin; + } + @Override public void register(VersionedServiceFactory factory) { + Logger.setPlugin(this.plugin); factory.register(ConfigurationProvider.class, "r1_1", Configuration::new); factory.register(TextColorProvider.class, "r1_16_5", TextColor::new); + factory.register(LoggerProvider.class, "r1_21", Logger::new); } } diff --git a/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/logging/Logger.java b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/logging/Logger.java new file mode 100644 index 0000000..818cc5a --- /dev/null +++ b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/logging/Logger.java @@ -0,0 +1,36 @@ +package org.adrianvictor.lib.impl.r1_21.logging; + +import org.adrianvictor.lib.logging.provider.LoggerProvider; +import org.bukkit.plugin.java.JavaPlugin; + +public class Logger implements LoggerProvider { + private static JavaPlugin plugin; + private java.util.logging.Logger logger; + + public static void setPlugin(JavaPlugin p) { + plugin = p; + } + + public Logger() { + if (plugin != null) { + logger = plugin.getLogger(); + } else { + logger = java.util.logging.Logger.getLogger("Minecraft"); + } + } + + @Override + public void info(String text) { + logger.info(text); + } + + @Override + public void warning(String text) { + logger.warning(text); + } + + @Override + public void error(String text) { + logger.severe(text); + } +} From 26d18e255295d93017b7d4404e2bc204683dd5e7 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Mon, 25 May 2026 19:15:30 -0300 Subject: [PATCH 16/22] Add file provider with support for saving resource to disk --- .../lib/impl/b1_7_3/B1_7_3Registrar.java | 9 ++++ .../lib/impl/b1_7_3/file/File.java | 44 +++++++++++++++++++ src/main/java/org/adrianvictor/lib/Main.java | 8 +--- .../lib/configuration/Configuration.java | 10 ----- .../provider/ConfigurationProvider.java | 5 +++ .../lib/file/provider/FileProvider.java | 13 ++++++ .../org/adrianvictor/lib/logging/Logger.java | 10 ----- .../lib/logging/provider/LoggerProvider.java | 6 +++ .../org/adrianvictor/lib/text/TextColor.java | 10 ----- .../adrianvictor/lib/text/TextColorUtils.java | 6 ++- .../lib/text/provider/TextColorProvider.java | 6 +++ .../versioning/VersionedServiceRegistrar.java | 4 ++ .../lib/impl/r1_21/R1_21Registrar.java | 9 ++++ .../lib/impl/r1_21/file/File.java | 17 +++++++ 14 files changed, 120 insertions(+), 37 deletions(-) create mode 100644 src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/file/File.java delete mode 100644 src/main/java/org/adrianvictor/lib/configuration/Configuration.java create mode 100644 src/main/java/org/adrianvictor/lib/file/provider/FileProvider.java delete mode 100644 src/main/java/org/adrianvictor/lib/logging/Logger.java delete mode 100644 src/main/java/org/adrianvictor/lib/text/TextColor.java create mode 100644 src/r1_21/java/org/adrianvictor/lib/impl/r1_21/file/File.java diff --git a/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/B1_7_3Registrar.java b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/B1_7_3Registrar.java index c9cd79b..8c7c41d 100644 --- a/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/B1_7_3Registrar.java +++ b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/B1_7_3Registrar.java @@ -1,5 +1,7 @@ package org.adrianvictor.lib.impl.b1_7_3; +import org.adrianvictor.lib.file.provider.FileProvider; +import org.adrianvictor.lib.impl.b1_7_3.file.File; import org.adrianvictor.lib.impl.b1_7_3.logging.Logger; import org.adrianvictor.lib.versioning.VersionedServiceFactory; import org.adrianvictor.lib.versioning.VersionedServiceRegistrar; @@ -13,15 +15,22 @@ import org.bukkit.plugin.java.JavaPlugin; public class B1_7_3Registrar implements VersionedServiceRegistrar { private JavaPlugin plugin; + @Override public void setPlugin(JavaPlugin plugin) { this.plugin = plugin; } + @Override + public JavaPlugin getPlugin() { + return plugin; + } + @Override public void register(VersionedServiceFactory factory) { Logger.setPlugin(this.plugin); factory.register(ConfigurationProvider.class, "b1_7_3", Configuration::new); factory.register(TextColorProvider.class, "b1_7_3", TextColor::new); factory.register(LoggerProvider.class, "b1_7_3", Logger::new); + factory.register(FileProvider.class, "b1_7_3", () -> new File(plugin)); } } diff --git a/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/file/File.java b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/file/File.java new file mode 100644 index 0000000..731547d --- /dev/null +++ b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/file/File.java @@ -0,0 +1,44 @@ +package org.adrianvictor.lib.impl.b1_7_3.file; + +import org.adrianvictor.lib.file.provider.FileProvider; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +public class File implements FileProvider { + private final JavaPlugin plugin; + + public File(JavaPlugin plugin) { + this.plugin = plugin; + } + + @Override + public void saveResource(String resourcePath, boolean replace) throws IOException { + java.io.File file = new java.io.File(plugin.getDataFolder(), resourcePath); + + if (!file.exists() || replace) { + + file.getParentFile().mkdirs(); + + InputStream in = this.getClass().getResourceAsStream("/" + resourcePath); + + if (in == null) { + throw new IllegalArgumentException("Resource not found: " + resourcePath); + } + + FileOutputStream out = new FileOutputStream(file); + + byte[] buf = new byte[1024]; + int len; + + while ((len = in.read(buf)) != -1) { + out.write(buf, 0, len); + } + + in.close(); + out.close(); + } + } +} diff --git a/src/main/java/org/adrianvictor/lib/Main.java b/src/main/java/org/adrianvictor/lib/Main.java index c37c85b..e7c7760 100644 --- a/src/main/java/org/adrianvictor/lib/Main.java +++ b/src/main/java/org/adrianvictor/lib/Main.java @@ -63,8 +63,7 @@ public class Main extends JavaPlugin { } try { - org.adrianvictor.lib.logging.Logger.create(versionedServiceFactory); - pluginLogger = versionedServiceFactory.getService(LoggerProvider.class); + pluginLogger = LoggerProvider.get(); pluginLogger.info("tenkumaLib has been successfully enabled!"); } catch (Exception e) { globalLogger.severe("[tenkumaLib] Failed to initialize plugin logger: " + e.getMessage()); @@ -88,10 +87,7 @@ public class Main extends JavaPlugin { private boolean registerRegistrar(VersionedServiceRegistrar registrar) { String registrarName = registrar.getClass().getName(); try { - try { - java.lang.reflect.Method setPlugin = registrar.getClass().getMethod("setPlugin", JavaPlugin.class); - setPlugin.invoke(registrar, this); - } catch (NoSuchMethodException ignored) {} + registrar.setPlugin(this); registrar.register(versionedServiceFactory); globalLogger.info("[tenkumaLib] Registered " + registrarName); diff --git a/src/main/java/org/adrianvictor/lib/configuration/Configuration.java b/src/main/java/org/adrianvictor/lib/configuration/Configuration.java deleted file mode 100644 index 8cee615..0000000 --- a/src/main/java/org/adrianvictor/lib/configuration/Configuration.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.adrianvictor.lib.configuration; - -import org.adrianvictor.lib.versioning.VersionedServiceFactory; -import org.adrianvictor.lib.configuration.provider.ConfigurationProvider; - -public class Configuration { - public static ConfigurationProvider create(VersionedServiceFactory factory) { - return factory.getService(ConfigurationProvider.class); - } -} \ No newline at end of file diff --git a/src/main/java/org/adrianvictor/lib/configuration/provider/ConfigurationProvider.java b/src/main/java/org/adrianvictor/lib/configuration/provider/ConfigurationProvider.java index 718212e..1390133 100644 --- a/src/main/java/org/adrianvictor/lib/configuration/provider/ConfigurationProvider.java +++ b/src/main/java/org/adrianvictor/lib/configuration/provider/ConfigurationProvider.java @@ -1,5 +1,6 @@ package org.adrianvictor.lib.configuration.provider; +import org.adrianvictor.lib.Main; import org.adrianvictor.lib.configuration.exception.InvalidConfigurationException; import java.io.File; @@ -9,6 +10,10 @@ import java.util.List; import java.util.Map; public interface ConfigurationProvider { + static ConfigurationProvider get() { + return Main.getServiceFactory().getService(ConfigurationProvider.class); + } + void load(File file) throws IOException, InvalidConfigurationException; void load(String contents) throws IOException, InvalidConfigurationException; void load(Reader reader) throws IOException, InvalidConfigurationException; diff --git a/src/main/java/org/adrianvictor/lib/file/provider/FileProvider.java b/src/main/java/org/adrianvictor/lib/file/provider/FileProvider.java new file mode 100644 index 0000000..72eea80 --- /dev/null +++ b/src/main/java/org/adrianvictor/lib/file/provider/FileProvider.java @@ -0,0 +1,13 @@ +package org.adrianvictor.lib.file.provider; + +import org.adrianvictor.lib.Main; + +import java.io.IOException; + +public interface FileProvider { + static FileProvider get() { + return Main.getServiceFactory().getService(FileProvider.class); + } + + void saveResource(String resourcePath, boolean replace) throws IOException; +} diff --git a/src/main/java/org/adrianvictor/lib/logging/Logger.java b/src/main/java/org/adrianvictor/lib/logging/Logger.java deleted file mode 100644 index a62717e..0000000 --- a/src/main/java/org/adrianvictor/lib/logging/Logger.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.adrianvictor.lib.logging; - -import org.adrianvictor.lib.logging.provider.LoggerProvider; -import org.adrianvictor.lib.versioning.VersionedServiceFactory; - -public class Logger { - public static LoggerProvider create(VersionedServiceFactory factory) { - return factory.getService(LoggerProvider.class); - } -} diff --git a/src/main/java/org/adrianvictor/lib/logging/provider/LoggerProvider.java b/src/main/java/org/adrianvictor/lib/logging/provider/LoggerProvider.java index a3e3113..3747ad9 100644 --- a/src/main/java/org/adrianvictor/lib/logging/provider/LoggerProvider.java +++ b/src/main/java/org/adrianvictor/lib/logging/provider/LoggerProvider.java @@ -1,6 +1,12 @@ package org.adrianvictor.lib.logging.provider; +import org.adrianvictor.lib.Main; + public interface LoggerProvider { + static LoggerProvider get() { + return Main.getServiceFactory().getService(LoggerProvider.class); + } + void info(String text); void warning(String text); void error(String text); diff --git a/src/main/java/org/adrianvictor/lib/text/TextColor.java b/src/main/java/org/adrianvictor/lib/text/TextColor.java deleted file mode 100644 index c07ebdf..0000000 --- a/src/main/java/org/adrianvictor/lib/text/TextColor.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.adrianvictor.lib.text; - -import org.adrianvictor.lib.versioning.VersionedServiceFactory; -import org.adrianvictor.lib.text.provider.TextColorProvider; - -public class TextColor { - public static TextColorProvider create(VersionedServiceFactory factory) { - return factory.getService(TextColorProvider.class); - } -} diff --git a/src/main/java/org/adrianvictor/lib/text/TextColorUtils.java b/src/main/java/org/adrianvictor/lib/text/TextColorUtils.java index 81ba3cf..250da75 100644 --- a/src/main/java/org/adrianvictor/lib/text/TextColorUtils.java +++ b/src/main/java/org/adrianvictor/lib/text/TextColorUtils.java @@ -6,8 +6,12 @@ import org.adrianvictor.lib.text.provider.TextColorProvider; public class TextColorUtils { private final TextColorProvider provider; + public TextColorUtils() { + this.provider = TextColorProvider.get(); + } + public TextColorUtils(VersionedServiceFactory factory) { - provider = TextColor.create(factory); + this.provider = factory.getService(TextColorProvider.class); } public String formatColors(String message) { diff --git a/src/main/java/org/adrianvictor/lib/text/provider/TextColorProvider.java b/src/main/java/org/adrianvictor/lib/text/provider/TextColorProvider.java index a106e58..bd42b7f 100644 --- a/src/main/java/org/adrianvictor/lib/text/provider/TextColorProvider.java +++ b/src/main/java/org/adrianvictor/lib/text/provider/TextColorProvider.java @@ -1,5 +1,11 @@ package org.adrianvictor.lib.text.provider; +import org.adrianvictor.lib.Main; + public interface TextColorProvider { + static TextColorProvider get() { + return Main.getServiceFactory().getService(TextColorProvider.class); + } + String colorize(String colorCode); } diff --git a/src/main/java/org/adrianvictor/lib/versioning/VersionedServiceRegistrar.java b/src/main/java/org/adrianvictor/lib/versioning/VersionedServiceRegistrar.java index ee3fe52..7e2bf93 100644 --- a/src/main/java/org/adrianvictor/lib/versioning/VersionedServiceRegistrar.java +++ b/src/main/java/org/adrianvictor/lib/versioning/VersionedServiceRegistrar.java @@ -1,5 +1,9 @@ package org.adrianvictor.lib.versioning; +import org.bukkit.plugin.java.JavaPlugin; + public interface VersionedServiceRegistrar { + void setPlugin(JavaPlugin plugin); + JavaPlugin getPlugin(); void register(VersionedServiceFactory factory); } diff --git a/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/R1_21Registrar.java b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/R1_21Registrar.java index 1b7e752..4803ad6 100644 --- a/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/R1_21Registrar.java +++ b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/R1_21Registrar.java @@ -1,5 +1,7 @@ package org.adrianvictor.lib.impl.r1_21; +import org.adrianvictor.lib.file.provider.FileProvider; +import org.adrianvictor.lib.impl.r1_21.file.File; import org.adrianvictor.lib.impl.r1_21.logging.Logger; import org.adrianvictor.lib.versioning.VersionedServiceFactory; import org.adrianvictor.lib.versioning.VersionedServiceRegistrar; @@ -13,15 +15,22 @@ import org.bukkit.plugin.java.JavaPlugin; public class R1_21Registrar implements VersionedServiceRegistrar { private JavaPlugin plugin; + @Override public void setPlugin(JavaPlugin plugin) { this.plugin = plugin; } + @Override + public JavaPlugin getPlugin() { + return plugin; + } + @Override public void register(VersionedServiceFactory factory) { Logger.setPlugin(this.plugin); factory.register(ConfigurationProvider.class, "r1_1", Configuration::new); factory.register(TextColorProvider.class, "r1_16_5", TextColor::new); factory.register(LoggerProvider.class, "r1_21", Logger::new); + factory.register(FileProvider.class, "r1_3", () -> new File(plugin)); } } diff --git a/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/file/File.java b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/file/File.java new file mode 100644 index 0000000..26bf88c --- /dev/null +++ b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/file/File.java @@ -0,0 +1,17 @@ +package org.adrianvictor.lib.impl.r1_21.file; + +import org.adrianvictor.lib.file.provider.FileProvider; +import org.bukkit.plugin.java.JavaPlugin; + +public class File implements FileProvider { + private final JavaPlugin plugin; + + public File(JavaPlugin plugin) { + this.plugin = plugin; + } + + @Override + public void saveResource(String resourcePath, boolean replace) { + plugin.saveResource(resourcePath, replace); + } +} From 632993678ee86eefbf564900a5cf9c21a1cd6583 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Mon, 25 May 2026 21:45:33 -0300 Subject: [PATCH 17/22] Make file provider accept plugin to get correct resource file --- .../adrianvictor/lib/impl/b1_7_3/B1_7_3Registrar.java | 2 +- .../org/adrianvictor/lib/impl/b1_7_3/file/File.java | 10 ++-------- .../adrianvictor/lib/file/provider/FileProvider.java | 3 ++- .../adrianvictor/lib/impl/r1_21/R1_21Registrar.java | 2 +- .../org/adrianvictor/lib/impl/r1_21/file/File.java | 8 +------- 5 files changed, 7 insertions(+), 18 deletions(-) diff --git a/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/B1_7_3Registrar.java b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/B1_7_3Registrar.java index 8c7c41d..88934ee 100644 --- a/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/B1_7_3Registrar.java +++ b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/B1_7_3Registrar.java @@ -31,6 +31,6 @@ public class B1_7_3Registrar implements VersionedServiceRegistrar { factory.register(ConfigurationProvider.class, "b1_7_3", Configuration::new); factory.register(TextColorProvider.class, "b1_7_3", TextColor::new); factory.register(LoggerProvider.class, "b1_7_3", Logger::new); - factory.register(FileProvider.class, "b1_7_3", () -> new File(plugin)); + factory.register(FileProvider.class, "b1_7_3", File::new); } } diff --git a/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/file/File.java b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/file/File.java index 731547d..943c0b3 100644 --- a/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/file/File.java +++ b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/file/File.java @@ -8,21 +8,15 @@ import java.io.IOException; import java.io.InputStream; public class File implements FileProvider { - private final JavaPlugin plugin; - - public File(JavaPlugin plugin) { - this.plugin = plugin; - } - @Override - public void saveResource(String resourcePath, boolean replace) throws IOException { + public void saveResource(String resourcePath, boolean replace, JavaPlugin plugin) throws IOException { java.io.File file = new java.io.File(plugin.getDataFolder(), resourcePath); if (!file.exists() || replace) { file.getParentFile().mkdirs(); - InputStream in = this.getClass().getResourceAsStream("/" + resourcePath); + InputStream in = plugin.getClass().getResourceAsStream("/" + resourcePath); if (in == null) { throw new IllegalArgumentException("Resource not found: " + resourcePath); diff --git a/src/main/java/org/adrianvictor/lib/file/provider/FileProvider.java b/src/main/java/org/adrianvictor/lib/file/provider/FileProvider.java index 72eea80..5095370 100644 --- a/src/main/java/org/adrianvictor/lib/file/provider/FileProvider.java +++ b/src/main/java/org/adrianvictor/lib/file/provider/FileProvider.java @@ -1,6 +1,7 @@ package org.adrianvictor.lib.file.provider; import org.adrianvictor.lib.Main; +import org.bukkit.plugin.java.JavaPlugin; import java.io.IOException; @@ -9,5 +10,5 @@ public interface FileProvider { return Main.getServiceFactory().getService(FileProvider.class); } - void saveResource(String resourcePath, boolean replace) throws IOException; + void saveResource(String resourcePath, boolean replace, JavaPlugin plugin) throws IOException; } diff --git a/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/R1_21Registrar.java b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/R1_21Registrar.java index 4803ad6..dff4d46 100644 --- a/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/R1_21Registrar.java +++ b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/R1_21Registrar.java @@ -31,6 +31,6 @@ public class R1_21Registrar implements VersionedServiceRegistrar { factory.register(ConfigurationProvider.class, "r1_1", Configuration::new); factory.register(TextColorProvider.class, "r1_16_5", TextColor::new); factory.register(LoggerProvider.class, "r1_21", Logger::new); - factory.register(FileProvider.class, "r1_3", () -> new File(plugin)); + factory.register(FileProvider.class, "r1_21", File::new); } } diff --git a/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/file/File.java b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/file/File.java index 26bf88c..de313be 100644 --- a/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/file/File.java +++ b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/file/File.java @@ -4,14 +4,8 @@ import org.adrianvictor.lib.file.provider.FileProvider; import org.bukkit.plugin.java.JavaPlugin; public class File implements FileProvider { - private final JavaPlugin plugin; - - public File(JavaPlugin plugin) { - this.plugin = plugin; - } - @Override - public void saveResource(String resourcePath, boolean replace) { + public void saveResource(String resourcePath, boolean replace, JavaPlugin plugin) { plugin.saveResource(resourcePath, replace); } } From 6ef06b1c4dd481a3c57671dc182883995399e63c Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Tue, 26 May 2026 19:55:58 -0300 Subject: [PATCH 18/22] Fix bug in Configuration inialization and Configuration#load(String content) that was passing content as file path on 1.21 implementation. --- .../impl/b1_7_3/configuration/Configuration.java | 1 + .../adrianvictor/lib/impl/b1_7_3/file/File.java | 1 - .../impl/r1_21/configuration/Configuration.java | 15 ++++++++++----- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/configuration/Configuration.java b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/configuration/Configuration.java index 9e8c31b..5f3fd89 100644 --- a/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/configuration/Configuration.java +++ b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/configuration/Configuration.java @@ -17,6 +17,7 @@ public class Configuration implements org.adrianvictor.lib.configuration.provide @Override public void load(File file) { config = new org.bukkit.util.config.Configuration(file); + config.load(); } @Override diff --git a/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/file/File.java b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/file/File.java index 943c0b3..5629c8b 100644 --- a/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/file/File.java +++ b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/file/File.java @@ -13,7 +13,6 @@ public class File implements FileProvider { java.io.File file = new java.io.File(plugin.getDataFolder(), resourcePath); if (!file.exists() || replace) { - file.getParentFile().mkdirs(); InputStream in = plugin.getClass().getResourceAsStream("/" + resourcePath); diff --git a/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/configuration/Configuration.java b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/configuration/Configuration.java index 0fe9c3d..57c324f 100644 --- a/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/configuration/Configuration.java +++ b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/configuration/Configuration.java @@ -6,6 +6,10 @@ import org.bukkit.configuration.file.YamlConfiguration; import java.io.File; import java.io.IOException; import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.*; public class Configuration implements org.adrianvictor.lib.configuration.provider.ConfigurationProvider { @@ -24,11 +28,12 @@ public class Configuration implements org.adrianvictor.lib.configuration.provide @Override public void load(String contents) throws IOException, org.adrianvictor.lib.configuration.exception.InvalidConfigurationException { - try { - config.load(contents); - } catch (InvalidConfigurationException e) { - throw new org.adrianvictor.lib.configuration.exception.InvalidConfigurationException(e.getMessage()); - } + String timestamp = LocalDateTime.now() + .format(DateTimeFormatter.ofPattern("HH-mm-dd-MM-yyyy")); + Path temp = Files.createTempFile("tlib-configprovider-tmp-%s".formatted(timestamp), ".yml"); + Files.writeString(temp, contents); + File file = temp.toFile(); + load(file); } @Override From 7a03172d57793cd2035459efe8cf96dad50c52b1 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Tue, 26 May 2026 19:56:47 -0300 Subject: [PATCH 19/22] Bump to 2.1 --- src/main/resources/plugin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 10e821b..a5d7a42 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -4,5 +4,5 @@ database: false main: org.adrianvictor.lib.Main startup: startup url: https://adrianvic.github.io -version: '2.0' +version: '2.1' api-version: '1.21' \ No newline at end of file From f0e9682a9f2bbb6c9d44c03cf214ae20a101d9ba Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Tue, 26 May 2026 19:56:47 -0300 Subject: [PATCH 20/22] Bump to 2.1 --- src/main/resources/plugin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 10e821b..a5d7a42 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -4,5 +4,5 @@ database: false main: org.adrianvictor.lib.Main startup: startup url: https://adrianvic.github.io -version: '2.0' +version: '2.1' api-version: '1.21' \ No newline at end of file From e19d1694250b179bd02ff7f8fc0da27f3c0c9085 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Sat, 30 May 2026 11:06:14 -0300 Subject: [PATCH 21/22] Bump to 2.1, deprecated LoggerProvider#error, change version mathcing to return 1.21 on any version that is not b1.7.3, remove leftover classes. --- build.gradle.kts | 14 ++---- .../lib/impl/b1_7_3/logging/Logger.java | 2 +- src/main/java/org/adrianvictor/lib/Main.java | 4 +- .../lib/loading/ModernRegistrarLoader.java | 8 ---- .../lib/loading/RegistrarBridge.java | 8 ---- .../lib/logging/provider/LoggerProvider.java | 13 +++++- .../lib/versioning/MinecraftVersion.java | 45 +------------------ .../lib/versioning/VersionMatcher.java | 7 --- .../lib/impl/r1_21/R1_21Registrar.java | 4 +- .../lib/impl/r1_21/logging/Logger.java | 2 +- 10 files changed, 22 insertions(+), 85 deletions(-) delete mode 100644 src/main/java/org/adrianvictor/lib/loading/ModernRegistrarLoader.java delete mode 100644 src/main/java/org/adrianvictor/lib/loading/RegistrarBridge.java diff --git a/build.gradle.kts b/build.gradle.kts index 711c583..d929735 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ plugins { } group = "org.adrianvictor" -version = "2.0" +version = "2.1.1" val buildEnv = System.getenv("BUILD_CHANNEL") ?: if (System.getenv("JITPACK") != null) "jitpack" else "local" @@ -86,16 +86,10 @@ mcVersions.forEach { ver -> } } -tasks.register("buildAll") { - dependsOn(tasks.withType()) -} - -// Task to merge service files val prepareServiceFiles = tasks.register("prepareServiceFiles") { val outputDir = layout.buildDirectory.dir("generated/service-files") val serviceFile = "META-INF/services/org.adrianvictor.lib.versioning.VersionedServiceRegistrar" - // Define inputs val inputFiles = mcVersions.map { ver -> file("src/$ver/resources/$serviceFile") }.filter { it.exists() } inputs.files(inputFiles) outputs.dir(outputDir) @@ -139,7 +133,7 @@ tasks.named("jar") { tasks.register("bundleAll") { dependsOn("jar") - // This is now redundant but we keep it for compatibility with previous instructions + from(sourceSets["main"].output) mcVersions.forEach { ver -> from(sourceSets[ver].output) { @@ -147,7 +141,6 @@ tasks.register("bundleAll") { } } - // Include the merged service file from(prepareServiceFiles) duplicatesStrategy = DuplicatesStrategy.EXCLUDE @@ -187,7 +180,6 @@ tasks.withType { /* ----------------------------------------- */ tasks.runServer { - minecraftVersion("1.21.1") - // Include ONLY the all-implementations jar as the plugin + minecraftVersion("1.21.11") pluginJars.setFrom(tasks.named("bundleAll")) } \ No newline at end of file diff --git a/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/logging/Logger.java b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/logging/Logger.java index 183b2ab..41d2f72 100644 --- a/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/logging/Logger.java +++ b/src/b1_7_3/java/org/adrianvictor/lib/impl/b1_7_3/logging/Logger.java @@ -32,7 +32,7 @@ public class Logger implements LoggerProvider { } @Override - public void error(String text) { + public void severe(String text) { logger.severe(prefix + text); } } diff --git a/src/main/java/org/adrianvictor/lib/Main.java b/src/main/java/org/adrianvictor/lib/Main.java index e7c7760..e13bdb5 100644 --- a/src/main/java/org/adrianvictor/lib/Main.java +++ b/src/main/java/org/adrianvictor/lib/Main.java @@ -34,7 +34,6 @@ public class Main extends JavaPlugin { boolean registered = false; - // 1. Load core registrar based on version mapping String coreRegistrar = version.getRegistrarClass(); if (coreRegistrar != null) { if (loadRegistrar(coreRegistrar)) { @@ -42,12 +41,11 @@ public class Main extends JavaPlugin { } } - // 2. Load registrars via ServiceLoader for extensibility try { java.util.ServiceLoader loader = java.util.ServiceLoader.load(VersionedServiceRegistrar.class, getClassLoader()); for (VersionedServiceRegistrar registrar : loader) { String registrarName = registrar.getClass().getName(); - // Skip if already registered as core + if (registrarName.equals(coreRegistrar)) continue; if (registerRegistrar(registrar)) { diff --git a/src/main/java/org/adrianvictor/lib/loading/ModernRegistrarLoader.java b/src/main/java/org/adrianvictor/lib/loading/ModernRegistrarLoader.java deleted file mode 100644 index e8bb5dc..0000000 --- a/src/main/java/org/adrianvictor/lib/loading/ModernRegistrarLoader.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.adrianvictor.lib.loading; - -import org.adrianvictor.lib.versioning.VersionedServiceFactory; -import org.bukkit.plugin.java.JavaPlugin; - -public interface ModernRegistrarLoader { - void load(VersionedServiceFactory factory, JavaPlugin plugin); -} diff --git a/src/main/java/org/adrianvictor/lib/loading/RegistrarBridge.java b/src/main/java/org/adrianvictor/lib/loading/RegistrarBridge.java deleted file mode 100644 index b0cd5c0..0000000 --- a/src/main/java/org/adrianvictor/lib/loading/RegistrarBridge.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.adrianvictor.lib.loading; - -import org.adrianvictor.lib.versioning.VersionedServiceFactory; -import org.bukkit.plugin.java.JavaPlugin; - -public interface RegistrarBridge { - void load(VersionedServiceFactory factory, JavaPlugin plugin); -} diff --git a/src/main/java/org/adrianvictor/lib/logging/provider/LoggerProvider.java b/src/main/java/org/adrianvictor/lib/logging/provider/LoggerProvider.java index 3747ad9..ad5b342 100644 --- a/src/main/java/org/adrianvictor/lib/logging/provider/LoggerProvider.java +++ b/src/main/java/org/adrianvictor/lib/logging/provider/LoggerProvider.java @@ -9,5 +9,16 @@ public interface LoggerProvider { void info(String text); void warning(String text); - void error(String text); + + /** + * @deprecated + * Does not throw an error. + *

Use {@link LoggerProvider#severe(String)} instead. + */ + @Deprecated + default void error(String text) { + severe(text); + } + + void severe(String text); } diff --git a/src/main/java/org/adrianvictor/lib/versioning/MinecraftVersion.java b/src/main/java/org/adrianvictor/lib/versioning/MinecraftVersion.java index 5ae57be..25539fa 100644 --- a/src/main/java/org/adrianvictor/lib/versioning/MinecraftVersion.java +++ b/src/main/java/org/adrianvictor/lib/versioning/MinecraftVersion.java @@ -4,26 +4,6 @@ import org.bukkit.Bukkit; public enum MinecraftVersion { B1_7_3(0, "b1_7_3", "org.adrianvictor.lib.impl.b1_7_3.B1_7_3Registrar"), - V1_1(1, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), - V1_2(2, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), - V1_3(3, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), - V1_4(4, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), - V1_5(5, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), - V1_6(6, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), - V1_7(7, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), - V1_8(8, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), - V1_9(9, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), - V1_10(10, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), - V1_11(11, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), - V1_12(12, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), - V1_13(13, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), - V1_14(14, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), - V1_15(15, "r1_1", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), - V1_16(16, "r1_16_5", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), - V1_17(17, "r1_16_5", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), - V1_18(18, "r1_16_5", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), - V1_19(19, "r1_16_5", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), - V1_20(20, "r1_16_5", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), V1_21(21, "r1_21", "org.adrianvictor.lib.impl.r1_21.R1_21Registrar"), UNKNOWN(-1, "unknown"); @@ -64,28 +44,7 @@ public enum MinecraftVersion { String bukkitVersion = Bukkit.getBukkitVersion(); String mcVer = bukkitVersion.split("-")[0]; - if (mcVer.startsWith("1.21")) return V1_21; - if (mcVer.startsWith("1.20")) return V1_20; - if (mcVer.startsWith("1.19")) return V1_19; - if (mcVer.startsWith("1.18")) return V1_18; - if (mcVer.startsWith("1.17")) return V1_17; - if (mcVer.startsWith("1.16")) return V1_16; - if (mcVer.startsWith("1.15")) return V1_15; - if (mcVer.startsWith("1.14")) return V1_14; - if (mcVer.startsWith("1.13")) return V1_13; - if (mcVer.startsWith("1.12")) return V1_12; - if (mcVer.startsWith("1.11")) return V1_11; - if (mcVer.startsWith("1.10")) return V1_10; - if (mcVer.startsWith("1.9")) return V1_9; - if (mcVer.startsWith("1.8")) return V1_8; - if (mcVer.startsWith("1.7")) return V1_7; - if (mcVer.startsWith("1.6")) return V1_6; - if (mcVer.startsWith("1.5")) return V1_5; - if (mcVer.startsWith("1.4")) return V1_4; - if (mcVer.startsWith("1.3")) return V1_3; - if (mcVer.startsWith("1.2")) return V1_2; - if (mcVer.startsWith("1.1")) return V1_1; - - return UNKNOWN; + //if (mcVer.startsWith("1.21")) return V1_21; + return V1_21; } } diff --git a/src/main/java/org/adrianvictor/lib/versioning/VersionMatcher.java b/src/main/java/org/adrianvictor/lib/versioning/VersionMatcher.java index 1e82d04..cfc2e53 100644 --- a/src/main/java/org/adrianvictor/lib/versioning/VersionMatcher.java +++ b/src/main/java/org/adrianvictor/lib/versioning/VersionMatcher.java @@ -16,16 +16,9 @@ public class VersionMatcher { List suffixes = new ArrayList<>(); - // Add suffixes in order of specificity (newest/most specific first) if (version.isAtLeast(MinecraftVersion.V1_21)) { suffixes.add("r1_21"); } - if (version.isAtLeast(MinecraftVersion.V1_16)) { - suffixes.add("r1_16_5"); - } - if (version.isAtLeast(MinecraftVersion.V1_1)) { - suffixes.add("r1_1"); - } if (version == MinecraftVersion.B1_7_3) { suffixes.add("b1_7_3"); } diff --git a/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/R1_21Registrar.java b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/R1_21Registrar.java index dff4d46..5047d4a 100644 --- a/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/R1_21Registrar.java +++ b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/R1_21Registrar.java @@ -28,8 +28,8 @@ public class R1_21Registrar implements VersionedServiceRegistrar { @Override public void register(VersionedServiceFactory factory) { Logger.setPlugin(this.plugin); - factory.register(ConfigurationProvider.class, "r1_1", Configuration::new); - factory.register(TextColorProvider.class, "r1_16_5", TextColor::new); + factory.register(ConfigurationProvider.class, "r1_21", Configuration::new); // min 1.1 + factory.register(TextColorProvider.class, "r1_21", TextColor::new); // min 1.16.5 factory.register(LoggerProvider.class, "r1_21", Logger::new); factory.register(FileProvider.class, "r1_21", File::new); } diff --git a/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/logging/Logger.java b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/logging/Logger.java index 818cc5a..16ffc6b 100644 --- a/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/logging/Logger.java +++ b/src/r1_21/java/org/adrianvictor/lib/impl/r1_21/logging/Logger.java @@ -30,7 +30,7 @@ public class Logger implements LoggerProvider { } @Override - public void error(String text) { + public void severe(String text) { logger.severe(text); } } From daca492c5e356bcaa551b17a8920ebbcc4d6a1ff Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Sat, 30 May 2026 11:06:58 -0300 Subject: [PATCH 22/22] Bump to 2.1 on plugin.yml. --- src/main/resources/plugin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index a5d7a42..1eccf37 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -4,5 +4,5 @@ database: false main: org.adrianvictor.lib.Main startup: startup url: https://adrianvic.github.io -version: '2.1' +version: '2.1.1' api-version: '1.21' \ No newline at end of file