diff --git a/.github/dependabot.yml b/.github/dependabot.yml index dc5f0b6d6b..78ea8f5ebb 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,3 +12,7 @@ updates: - dependency-name: "org.mockito:mockito-core" versions: ">= 5" - dependency-name: ch.jalu:configme +- package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly diff --git a/.gitignore b/.gitignore index 9a8a9ebf97..8422aa3e42 100644 --- a/.gitignore +++ b/.gitignore @@ -88,6 +88,7 @@ local.properties ### Maven ### target/ +.flattened-pom.xml pom.xml.tag pom.xml.releaseBackup pom.xml.versionsBackup diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e380fd20ae..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,31 +0,0 @@ -dist: focal - -language: java - -matrix: - include: - - env: - - JDK_VERSION=11 - -before_install: - - "[[ -d $HOME/.sdkman/ ]] && [[ -d $HOME/.sdkman/bin/ ]] || rm -rf $HOME/.sdkman/" - - curl -s "https://get.sdkman.io" | bash - - mkdir -p "$HOME/.sdkman/etc/" - - echo sdkman_auto_answer=true > "$HOME/.sdkman/etc/config" - - echo sdkman_auto_selfupdate=true >> "$HOME/.sdkman/etc/config" - - source "$HOME/.sdkman/bin/sdkman-init.sh" - -install: - - sdk install java $(sdk list java | grep -o "$JDK_VERSION\.[0-9]*\.[0-9]*\-open" | head -1) - - sdk install maven - - export JAVA_HOME="$HOME/.sdkman/candidates/java/current" - - export PATH=${JAVA_HOME}/bin:${PATH} - - export MAVEN_HOME="$HOME/.sdkman/candidates/maven/current" - - export M2_HOME="$MAVEN_HOME" - - export PATH=${M2_HOME}/bin:${PATH} - - env - - mvn -v - -cache: - directories: - - $HOME/.m2/repository diff --git a/README.md b/README.md index a8baddd4f8..44019bacef 100644 --- a/README.md +++ b/README.md @@ -115,17 +115,25 @@ You can also create your own translation file and, if you want, you can share it ## Requirements ##### Compiling requirements: ->- JDK 17+ +>- JDK 17+ for `authme-core` and `authme-spigot-legacy` +>- JDK 21+ for the full multi-module build (`authme-spigot-1.21`, `authme-paper-common`, `authme-paper`, `authme-folia`) >- Maven (3.8.8+) >- Git/GitHub (Optional) ##### How to compile the project: >- Clone the project with Git/GitHub ->- Execute command "mvn clean package" +>- Execute command `mvn clean package` +>- With JDK 17, Maven builds only the Java 17-compatible modules +>- With JDK 21+, Maven builds the full reactor +>- Build and tooling command reference: [docs/build.md](docs/build.md) ##### Running requirements: ->- Java 17+ ->- Paper or Spigot (1.16.5 and up) +>- Use the jar matching your server platform/version +>- Java 17+ for `AuthMe-*-Spigot-Legacy.jar` (Spigot 1.16.x – 1.19.x) +>- Java 21+ for: +> - `AuthMe-*-Spigot-1.21.jar` (Spigot 1.20.x – 1.21.x) +> - `AuthMe-*-Paper.jar` (Paper 1.21+) +> - `AuthMe-*-Folia.jar` (Folia 1.21+) >- ProtocolLib (optional, required by some features) ## Credits diff --git a/authme-core/pom.xml b/authme-core/pom.xml new file mode 100644 index 0000000000..80d3217728 --- /dev/null +++ b/authme-core/pom.xml @@ -0,0 +1,272 @@ + + + 4.0.0 + + + fr.xephi + authme-parent + ${revision} + + + authme-core + jar + AuthMe Core + AuthMe core business logic (not a standalone plugin) + + + + + ch.jalu + injector + + + net.ricecode + string-similarity + + + com.maxmind.geoip2 + geoip2 + + + javatar + javatar + + + org.apache.commons + commons-email + + + com.zaxxer + HikariCP + + + org.slf4j + slf4j-simple + + + ch.jalu + datasourcecolumns + + + com.mysql + mysql-connector-j + + + org.mariadb.jdbc + mariadb-java-client + + + org.postgresql + postgresql + + + de.rtner + PBKDF2 + + + de.mkammerer + argon2-jvm-nolibs + + + at.favre.lib + bcrypt + + + com.warrenstrange + googleauth + + + ch.jalu + configme + + + + + org.spigotmc + spigot-api + ${dependencies.spigot.version} + provided + + + org.joml + joml + + + net.md-5 + bungeecord-chat + + + + + + + org.bstats + bstats-bukkit + + + com.comphenix.protocol + ProtocolLib + + + net.luckperms + api + + + ru.tehkode + PermissionsEx + + + org.tyrannyofheaven.bukkit + zPermissions + + + net.milkbowl.vault + VaultAPI + + + com.onarandombox.multiversecore + multiverse-core + + + net.essentialsx + EssentialsX + + + org.apache.logging.log4j + log4j-core + + + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + + + org.mockito + mockito-junit-jupiter + test + + + org.hamcrest + hamcrest + test + + + org.xerial + sqlite-jdbc + test + + + com.h2database + h2 + test + + + + + + + . + false + + LICENSE + + + + src/main/resources/ + true + + + plugin.yml + + + + src/main/resources/messages/ + ./messages/ + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.jacoco + jacoco-maven-plugin + + + fr/xephi/authme/* + fr/xephi/authme/**/* + + + + + pre-unit-test + + prepare-agent + + + + post-unit-test + + report + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + -Xshare:off -javaagent:${dependencies.mockito.javaagent} --add-opens=java.base/java.lang=ALL-UNNAMED -Dfile.encoding=${project.build.sourceEncoding} -Duser.language=en @{argLine} + + ${project.skipExtendedHashTests} + + false + + + + org.apache.maven.plugins + maven-source-plugin + + ${project.finalNameBase} + + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + ${project.finalNameBase} + ${module.java.version} + + + + attach-javadoc + + jar + + + + + + + diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/authme-core/src/main/java/fr/xephi/authme/AuthMe.java similarity index 83% rename from src/main/java/fr/xephi/authme/AuthMe.java rename to authme-core/src/main/java/fr/xephi/authme/AuthMe.java index 045d1ee974..4c36ff77f4 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/authme-core/src/main/java/fr/xephi/authme/AuthMe.java @@ -5,6 +5,7 @@ import com.google.common.annotations.VisibleForTesting; import fr.xephi.authme.api.v3.AuthMeApi; import fr.xephi.authme.command.CommandHandler; +import fr.xephi.authme.command.CommandInitializer; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.initialization.DataFolder; import fr.xephi.authme.initialization.DataSourceProvider; @@ -15,11 +16,9 @@ import fr.xephi.authme.listener.BlockListener; import fr.xephi.authme.listener.EntityListener; import fr.xephi.authme.listener.PlayerListener; -import fr.xephi.authme.listener.PlayerListener111; -import fr.xephi.authme.listener.PlayerListener19; -import fr.xephi.authme.listener.PlayerListener19Spigot; import fr.xephi.authme.listener.ServerListener; import fr.xephi.authme.output.ConsoleLoggerFactory; +import fr.xephi.authme.platform.SchedulingAdapter; import fr.xephi.authme.security.crypts.Sha256; import fr.xephi.authme.service.BackupService; import fr.xephi.authme.service.BukkitService; @@ -41,12 +40,18 @@ import org.bukkit.plugin.java.JavaPluginLoader; import org.bukkit.scheduler.BukkitScheduler; +import fr.xephi.authme.platform.ChatAdapter; +import fr.xephi.authme.platform.CommandRegistrationAdapter; +import fr.xephi.authme.platform.DialogAdapter; +import fr.xephi.authme.platform.EventRegistrationAdapter; +import fr.xephi.authme.platform.PlatformAdapter; +import fr.xephi.authme.platform.TeleportAdapter; + import java.io.File; +import java.util.ServiceLoader; import java.util.function.Consumer; import static fr.xephi.authme.service.BukkitService.TICKS_PER_MINUTE; -import static fr.xephi.authme.util.Utils.isClassLoaded; - /** * The AuthMe main class. */ @@ -123,11 +128,11 @@ public void onEnable() { ConsoleLogger.initialize(getLogger(), new File(getDataFolder(), LOG_FILENAME)); logger = ConsoleLoggerFactory.get(AuthMe.class); - // Check server version - if (!isClassLoaded("org.spigotmc.event.player.PlayerSpawnLocationEvent") - || !isClassLoaded("org.bukkit.event.player.PlayerInteractAtEntityEvent")) { + PlatformAdapter platformAdapter = loadPlatformAdapter(); + String compatibilityError = platformAdapter.getCompatibilityError(); + if (compatibilityError != null) { logger.warning("You are running an unsupported server version (" + getServerNameVersionSafe() + "). " - + "AuthMe requires Spigot 1.8.X or later!"); + + compatibilityError); stopOrUnload(); return; } @@ -142,7 +147,7 @@ public void onEnable() { // Initialize the plugin try { - initialize(); + initialize(platformAdapter); } catch (Throwable th) { YamlParseException yamlParseException = ExceptionUtils.findThrowableInCause(YamlParseException.class, th); if (yamlParseException == null) { @@ -196,7 +201,7 @@ private static void loadPluginInfo(String versionRaw) { /** * Initialize the plugin and all the services. */ - private void initialize() { + private void initialize(PlatformAdapter platformAdapter) { // Create plugin folder getDataFolder().mkdir(); @@ -212,6 +217,14 @@ private void initialize() { injector.registerProvider(Settings.class, SettingsProvider.class); injector.registerProvider(DataSource.class, DataSourceProvider.class); + injector.register(PlatformAdapter.class, platformAdapter); + injector.register(TeleportAdapter.class, platformAdapter); + injector.register(ChatAdapter.class, platformAdapter); + injector.register(EventRegistrationAdapter.class, platformAdapter); + injector.register(SchedulingAdapter.class, platformAdapter); + injector.register(DialogAdapter.class, platformAdapter); + injector.register(CommandRegistrationAdapter.class, platformAdapter); + // Get settings and set up logger settings = injector.getSingleton(Settings.class); ConsoleLoggerFactory.reloadSettings(settings); @@ -220,6 +233,8 @@ private void initialize() { // Set all service fields on the AuthMe class instantiateServices(injector); + registerPlatformCommands(platformAdapter, injector); + // Convert deprecated PLAINTEXT hash entries MigrationService.changePlainTextToSha256(settings, database, new Sha256()); @@ -236,6 +251,13 @@ private void initialize() { onStartupTasks.scheduleRecallEmailTask(); } + private PlatformAdapter loadPlatformAdapter() { + return ServiceLoader.load(PlatformAdapter.class, getClass().getClassLoader()) + .findFirst() + .orElseThrow(() -> new IllegalStateException( + "No PlatformAdapter found. Ensure you are using a version-specific AuthMe jar.")); + } + /** * Instantiates all services. * @@ -254,6 +276,11 @@ void instantiateServices(Injector injector) { injector.getSingleton(AuthMeApi.class); } + private void registerPlatformCommands(PlatformAdapter platformAdapter, Injector injector) { + CommandInitializer commandInitializer = injector.getSingleton(CommandInitializer.class); + platformAdapter.registerCommands(this, commandHandler, commandInitializer.getCommands()); + } + /** * Registers all event listeners. * @@ -269,19 +296,10 @@ void registerEventListeners(Injector injector) { pluginManager.registerEvents(injector.getSingleton(EntityListener.class), this); pluginManager.registerEvents(injector.getSingleton(ServerListener.class), this); - // Try to register 1.9 player listeners - if (isClassLoaded("org.bukkit.event.player.PlayerSwapHandItemsEvent")) { - pluginManager.registerEvents(injector.getSingleton(PlayerListener19.class), this); - } - - // Try to register 1.9 spigot player listeners - if (isClassLoaded("org.spigotmc.event.player.PlayerSpawnLocationEvent")) { - pluginManager.registerEvents(injector.getSingleton(PlayerListener19Spigot.class), this); - } - - // Register listener for 1.11 events if available - if (isClassLoaded("org.bukkit.event.entity.EntityAirChangeEvent")) { - pluginManager.registerEvents(injector.getSingleton(PlayerListener111.class), this); + // Register platform-specific listeners provided by the version module + EventRegistrationAdapter eventRegistration = injector.getSingleton(EventRegistrationAdapter.class); + for (Class listenerClass : eventRegistration.getAdditionalListeners()) { + pluginManager.registerEvents(injector.getSingleton(listenerClass), this); } } diff --git a/src/main/java/fr/xephi/authme/ConsoleLogger.java b/authme-core/src/main/java/fr/xephi/authme/ConsoleLogger.java similarity index 100% rename from src/main/java/fr/xephi/authme/ConsoleLogger.java rename to authme-core/src/main/java/fr/xephi/authme/ConsoleLogger.java diff --git a/src/main/java/fr/xephi/authme/api/v3/AuthMeApi.java b/authme-core/src/main/java/fr/xephi/authme/api/v3/AuthMeApi.java similarity index 100% rename from src/main/java/fr/xephi/authme/api/v3/AuthMeApi.java rename to authme-core/src/main/java/fr/xephi/authme/api/v3/AuthMeApi.java diff --git a/src/main/java/fr/xephi/authme/api/v3/AuthMePlayer.java b/authme-core/src/main/java/fr/xephi/authme/api/v3/AuthMePlayer.java similarity index 100% rename from src/main/java/fr/xephi/authme/api/v3/AuthMePlayer.java rename to authme-core/src/main/java/fr/xephi/authme/api/v3/AuthMePlayer.java diff --git a/src/main/java/fr/xephi/authme/api/v3/AuthMePlayerImpl.java b/authme-core/src/main/java/fr/xephi/authme/api/v3/AuthMePlayerImpl.java similarity index 100% rename from src/main/java/fr/xephi/authme/api/v3/AuthMePlayerImpl.java rename to authme-core/src/main/java/fr/xephi/authme/api/v3/AuthMePlayerImpl.java diff --git a/src/main/java/fr/xephi/authme/command/CommandArgumentDescription.java b/authme-core/src/main/java/fr/xephi/authme/command/CommandArgumentDescription.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/CommandArgumentDescription.java rename to authme-core/src/main/java/fr/xephi/authme/command/CommandArgumentDescription.java diff --git a/src/main/java/fr/xephi/authme/command/CommandDescription.java b/authme-core/src/main/java/fr/xephi/authme/command/CommandDescription.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/CommandDescription.java rename to authme-core/src/main/java/fr/xephi/authme/command/CommandDescription.java diff --git a/src/main/java/fr/xephi/authme/command/CommandHandler.java b/authme-core/src/main/java/fr/xephi/authme/command/CommandHandler.java similarity index 95% rename from src/main/java/fr/xephi/authme/command/CommandHandler.java rename to authme-core/src/main/java/fr/xephi/authme/command/CommandHandler.java index 69420c1bff..e32297e481 100644 --- a/src/main/java/fr/xephi/authme/command/CommandHandler.java +++ b/authme-core/src/main/java/fr/xephi/authme/command/CommandHandler.java @@ -62,6 +62,17 @@ public boolean processCommand(CommandSender sender, String bukkitCommandLabel, S List parts = skipEmptyArguments(bukkitArgs); parts.add(0, bukkitCommandLabel); + return processCommand(sender, parts); + } + + /** + * Processes a command represented as pre-split parts. + * + * @param sender the command sender + * @param parts the command input split into label and arguments + * @return true if the command was executed, false otherwise + */ + public boolean processCommand(CommandSender sender, List parts) { FoundCommandResult result = commandMapper.mapPartsToCommand(sender, parts); handleCommandResult(sender, result); return !FoundResultStatus.MISSING_BASE_COMMAND.equals(result.getResultStatus()); diff --git a/src/main/java/fr/xephi/authme/command/CommandInitializer.java b/authme-core/src/main/java/fr/xephi/authme/command/CommandInitializer.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/CommandInitializer.java rename to authme-core/src/main/java/fr/xephi/authme/command/CommandInitializer.java diff --git a/src/main/java/fr/xephi/authme/command/CommandMapper.java b/authme-core/src/main/java/fr/xephi/authme/command/CommandMapper.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/CommandMapper.java rename to authme-core/src/main/java/fr/xephi/authme/command/CommandMapper.java diff --git a/src/main/java/fr/xephi/authme/command/CommandUtils.java b/authme-core/src/main/java/fr/xephi/authme/command/CommandUtils.java similarity index 83% rename from src/main/java/fr/xephi/authme/command/CommandUtils.java rename to authme-core/src/main/java/fr/xephi/authme/command/CommandUtils.java index 391a146311..01a574a143 100644 --- a/src/main/java/fr/xephi/authme/command/CommandUtils.java +++ b/authme-core/src/main/java/fr/xephi/authme/command/CommandUtils.java @@ -4,7 +4,9 @@ import org.bukkit.ChatColor; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; /** * Utility functions for {@link CommandDescription} objects. @@ -106,4 +108,26 @@ public static String formatArgument(CommandArgumentDescription argument) { } return "<" + argument.getName() + ">"; } + + /** + * Splits raw command input into non-empty parts. + * + * @param input the raw command input, with or without a leading slash + * @return command parts without empty entries + */ + public static List splitInput(String input) { + if (input == null) { + return new ArrayList<>(); + } + String trimmedInput = input.trim(); + if (trimmedInput.startsWith("/")) { + trimmedInput = trimmedInput.substring(1); + } + if (trimmedInput.isEmpty()) { + return new ArrayList<>(); + } + return Arrays.stream(trimmedInput.split("\\s+")) + .filter(part -> !part.isEmpty()) + .collect(Collectors.toList()); + } } diff --git a/src/main/java/fr/xephi/authme/command/ExecutableCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/ExecutableCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/ExecutableCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/ExecutableCommand.java diff --git a/src/main/java/fr/xephi/authme/command/FoundCommandResult.java b/authme-core/src/main/java/fr/xephi/authme/command/FoundCommandResult.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/FoundCommandResult.java rename to authme-core/src/main/java/fr/xephi/authme/command/FoundCommandResult.java diff --git a/src/main/java/fr/xephi/authme/command/FoundResultStatus.java b/authme-core/src/main/java/fr/xephi/authme/command/FoundResultStatus.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/FoundResultStatus.java rename to authme-core/src/main/java/fr/xephi/authme/command/FoundResultStatus.java diff --git a/src/main/java/fr/xephi/authme/command/PlayerCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/PlayerCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/PlayerCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/PlayerCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/HelpCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/HelpCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/HelpCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/HelpCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/AccountsCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/AccountsCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/AccountsCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/AccountsCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/AuthMeCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/AuthMeCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/AuthMeCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/AuthMeCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/BackupCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/BackupCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/BackupCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/BackupCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java similarity index 96% rename from src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java index 7f77d5cfd6..152a204169 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java +++ b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java @@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableSortedMap; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.datasource.converter.AuthPlusConverter; import fr.xephi.authme.datasource.converter.Converter; import fr.xephi.authme.datasource.converter.CrazyLoginConverter; import fr.xephi.authme.datasource.converter.LoginSecurityConverter; @@ -81,6 +82,7 @@ private static Class getConverterClassFromArgs(List */ private static Map> getConverters() { return ImmutableSortedMap.>naturalOrder() + .put("authplus", AuthPlusConverter.class) .put("crazylogin", CrazyLoginConverter.class) .put("rakamak", RakamakConverter.class) .put("royalauth", RoyalAuthConverter.class) diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/ForceLoginCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/ForceLoginCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/ForceLoginCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/ForceLoginCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/GetEmailCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/GetEmailCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/GetEmailCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/GetEmailCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/GetIpCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/GetIpCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/GetIpCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/GetIpCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/LastLoginCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/LastLoginCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/LastLoginCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/LastLoginCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/PurgeCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/PurgeCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/PurgeCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/PurgeCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/PurgePlayerCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/PurgePlayerCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/PurgePlayerCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/PurgePlayerCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/RecentPlayersCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/RecentPlayersCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/RecentPlayersCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/RecentPlayersCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java similarity index 95% rename from src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java index eaa56a41ec..ed108014d7 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java +++ b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java @@ -77,8 +77,8 @@ public void executeCommand(final CommandSender sender, List arguments) { logger.info(sender.getName() + " registered " + playerName); final Player player = bukkitService.getPlayerExact(playerName); if (player != null) { - bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> - player.kickPlayer(commonService.retrieveSingleMessage(player, MessageKey.KICK_FOR_ADMIN_REGISTER))); + bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(player, + () -> player.kickPlayer(commonService.retrieveSingleMessage(player, MessageKey.KICK_FOR_ADMIN_REGISTER))); } }); } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/ReloadCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/ReloadCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/ReloadCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/ReloadCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/SetEmailCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/SetEmailCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/SetEmailCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/SetEmailCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/SetFirstSpawnCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/SetFirstSpawnCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/SetFirstSpawnCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/SetFirstSpawnCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/SetSpawnCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/SetSpawnCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/SetSpawnCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/SetSpawnCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/SpawnCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/SpawnCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/SpawnCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/SpawnCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/TotpDisableAdminCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/TotpDisableAdminCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/TotpDisableAdminCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/TotpDisableAdminCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/TotpViewStatusCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/TotpViewStatusCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/TotpViewStatusCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/TotpViewStatusCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/UpdateHelpMessagesCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/UpdateHelpMessagesCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/UpdateHelpMessagesCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/UpdateHelpMessagesCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/VersionCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/VersionCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/VersionCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/VersionCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/CountryLookup.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/CountryLookup.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/debug/CountryLookup.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/CountryLookup.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/DataStatistics.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/DataStatistics.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/debug/DataStatistics.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/DataStatistics.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/DebugCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/DebugCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/debug/DebugCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/DebugCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/DebugSection.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/DebugSection.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/debug/DebugSection.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/DebugSection.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/DebugSectionUtils.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/DebugSectionUtils.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/debug/DebugSectionUtils.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/DebugSectionUtils.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/HasPermissionChecker.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/HasPermissionChecker.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/debug/HasPermissionChecker.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/HasPermissionChecker.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/InputValidator.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/InputValidator.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/debug/InputValidator.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/InputValidator.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/LimboPlayerViewer.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/LimboPlayerViewer.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/debug/LimboPlayerViewer.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/LimboPlayerViewer.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/MySqlDefaultChanger.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/MySqlDefaultChanger.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/debug/MySqlDefaultChanger.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/MySqlDefaultChanger.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/PermissionGroups.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/PermissionGroups.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/debug/PermissionGroups.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/PermissionGroups.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/PlayerAuthViewer.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/PlayerAuthViewer.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/debug/PlayerAuthViewer.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/PlayerAuthViewer.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/SpawnLocationViewer.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/SpawnLocationViewer.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/debug/SpawnLocationViewer.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/SpawnLocationViewer.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/TestEmailSender.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/TestEmailSender.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/authme/debug/TestEmailSender.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/authme/debug/TestEmailSender.java diff --git a/src/main/java/fr/xephi/authme/command/executable/captcha/CaptchaCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/captcha/CaptchaCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/captcha/CaptchaCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/captcha/CaptchaCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/email/AddEmailCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/email/AddEmailCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/email/AddEmailCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/email/AddEmailCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/email/ChangeEmailCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/email/ChangeEmailCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/email/ChangeEmailCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/email/ChangeEmailCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/email/EmailBaseCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/email/EmailBaseCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/email/EmailBaseCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/email/EmailBaseCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/email/EmailSetPasswordCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/email/EmailSetPasswordCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/email/EmailSetPasswordCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/email/EmailSetPasswordCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/email/ProcessCodeCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/email/ProcessCodeCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/email/ProcessCodeCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/email/ProcessCodeCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/email/RecoverEmailCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/email/RecoverEmailCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/email/RecoverEmailCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/email/RecoverEmailCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/email/ShowEmailCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/email/ShowEmailCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/email/ShowEmailCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/email/ShowEmailCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/login/LoginCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/login/LoginCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/login/LoginCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/login/LoginCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/logout/LogoutCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/logout/LogoutCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/logout/LogoutCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/logout/LogoutCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/register/RegisterCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/register/RegisterCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/register/RegisterCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/register/RegisterCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/AddTotpCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/totp/AddTotpCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/totp/AddTotpCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/totp/AddTotpCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/TotpBaseCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/totp/TotpBaseCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/totp/TotpBaseCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/totp/TotpBaseCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/totp/TotpCodeCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/totp/TotpCodeCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/totp/TotpCodeCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/totp/TotpCodeCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/unregister/UnregisterCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/unregister/UnregisterCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/unregister/UnregisterCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/unregister/UnregisterCommand.java diff --git a/src/main/java/fr/xephi/authme/command/executable/verification/VerificationCommand.java b/authme-core/src/main/java/fr/xephi/authme/command/executable/verification/VerificationCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/executable/verification/VerificationCommand.java rename to authme-core/src/main/java/fr/xephi/authme/command/executable/verification/VerificationCommand.java diff --git a/src/main/java/fr/xephi/authme/command/help/HelpMessage.java b/authme-core/src/main/java/fr/xephi/authme/command/help/HelpMessage.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/help/HelpMessage.java rename to authme-core/src/main/java/fr/xephi/authme/command/help/HelpMessage.java diff --git a/src/main/java/fr/xephi/authme/command/help/HelpMessagesService.java b/authme-core/src/main/java/fr/xephi/authme/command/help/HelpMessagesService.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/help/HelpMessagesService.java rename to authme-core/src/main/java/fr/xephi/authme/command/help/HelpMessagesService.java diff --git a/src/main/java/fr/xephi/authme/command/help/HelpProvider.java b/authme-core/src/main/java/fr/xephi/authme/command/help/HelpProvider.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/help/HelpProvider.java rename to authme-core/src/main/java/fr/xephi/authme/command/help/HelpProvider.java diff --git a/src/main/java/fr/xephi/authme/command/help/HelpSection.java b/authme-core/src/main/java/fr/xephi/authme/command/help/HelpSection.java similarity index 100% rename from src/main/java/fr/xephi/authme/command/help/HelpSection.java rename to authme-core/src/main/java/fr/xephi/authme/command/help/HelpSection.java diff --git a/src/main/java/fr/xephi/authme/data/ProxySessionManager.java b/authme-core/src/main/java/fr/xephi/authme/data/ProxySessionManager.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/ProxySessionManager.java rename to authme-core/src/main/java/fr/xephi/authme/data/ProxySessionManager.java diff --git a/src/main/java/fr/xephi/authme/data/QuickCommandsProtectionManager.java b/authme-core/src/main/java/fr/xephi/authme/data/QuickCommandsProtectionManager.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/QuickCommandsProtectionManager.java rename to authme-core/src/main/java/fr/xephi/authme/data/QuickCommandsProtectionManager.java diff --git a/src/main/java/fr/xephi/authme/data/TempbanManager.java b/authme-core/src/main/java/fr/xephi/authme/data/TempbanManager.java similarity index 89% rename from src/main/java/fr/xephi/authme/data/TempbanManager.java rename to authme-core/src/main/java/fr/xephi/authme/data/TempbanManager.java index fd2a0a3eda..9c22193087 100644 --- a/src/main/java/fr/xephi/authme/data/TempbanManager.java +++ b/authme-core/src/main/java/fr/xephi/authme/data/TempbanManager.java @@ -103,17 +103,15 @@ public void tempbanPlayer(final Player player) { long newTime = expires.getTime() + (length * MILLIS_PER_MINUTE); expires.setTime(newTime); - bukkitService.scheduleSyncDelayedTask(() -> { - if (customCommand.isEmpty()) { - bukkitService.banIp(ip, reason, expires, "AuthMe"); - player.kickPlayer(reason); - } else { - String command = customCommand - .replace("%player%", name) - .replace("%ip%", ip); - bukkitService.dispatchConsoleCommand(command); - } - }); + if (customCommand.isEmpty()) { + bukkitService.runOnGlobalRegion(() -> bukkitService.banIp(ip, reason, expires, "AuthMe")); + bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(player, () -> player.kickPlayer(reason)); + } else { + String command = customCommand + .replace("%player%", name) + .replace("%ip%", ip); + bukkitService.runOnGlobalRegion(() -> bukkitService.dispatchConsoleCommand(command)); + } ipLoginFailureCounts.remove(ip); } diff --git a/src/main/java/fr/xephi/authme/data/VerificationCodeManager.java b/authme-core/src/main/java/fr/xephi/authme/data/VerificationCodeManager.java similarity index 83% rename from src/main/java/fr/xephi/authme/data/VerificationCodeManager.java rename to authme-core/src/main/java/fr/xephi/authme/data/VerificationCodeManager.java index fb3cedd185..416cbe43ac 100644 --- a/src/main/java/fr/xephi/authme/data/VerificationCodeManager.java +++ b/authme-core/src/main/java/fr/xephi/authme/data/VerificationCodeManager.java @@ -29,6 +29,7 @@ public class VerificationCodeManager implements SettingsDependent, HasCleanup { private final BukkitService bukkitService; private final ExpiringMap verificationCodes; + private final Set pendingCodeGenerations; private final Set verifiedPlayers; private boolean canSendMail; @@ -40,6 +41,7 @@ public class VerificationCodeManager implements SettingsDependent, HasCleanup { this.dataSource = dataSource; this.permissionsManager = permissionsManager; this.bukkitService = bukkitService; + pendingCodeGenerations = ConcurrentHashMap.newKeySet(); verifiedPlayers = ConcurrentHashMap.newKeySet(); long countTimeout = settings.getProperty(SecuritySettings.VERIFICATION_CODE_EXPIRATION_MINUTES); verificationCodes = new ExpiringMap<>(countTimeout, TimeUnit.MINUTES); @@ -123,8 +125,17 @@ public boolean hasEmail(String name) { * @param name the player's name */ public void codeExistOrGenerateNew(String name) { - if (!hasCode(name)) { - generateCode(name); + String lowerName = name.toLowerCase(Locale.ROOT); + if (verificationCodes.get(lowerName) == null && pendingCodeGenerations.add(lowerName)) { + boolean scheduled = false; + try { + bukkitService.runTaskAsynchronously(() -> generateCode(name, lowerName)); + scheduled = true; + } finally { + if (!scheduled) { + pendingCodeGenerations.remove(lowerName); + } + } } } @@ -132,17 +143,21 @@ public void codeExistOrGenerateNew(String name) { * Generates a code for the player and returns it. * * @param name the name of the player to generate a code for + * @param lowerName the lower-case player name */ - private void generateCode(String name) { - DataSourceValue emailResult = dataSource.getEmail(name); - if (emailResult.rowExists()) { - final String email = emailResult.getValue(); - if (!Utils.isEmailEmpty(email)) { - String code = RandomStringUtils.generateNum(6); // 6 digits code - verificationCodes.put(name.toLowerCase(Locale.ROOT), code); - bukkitService.runTaskAsynchronously(() -> - emailService.sendVerificationMail(name, email, code)); + private void generateCode(String name, String lowerName) { + try { + DataSourceValue emailResult = dataSource.getEmail(name); + if (emailResult.rowExists()) { + final String email = emailResult.getValue(); + if (!Utils.isEmailEmpty(email)) { + String code = RandomStringUtils.generateNum(6); // 6 digits code + verificationCodes.put(lowerName, code); + emailService.sendVerificationMail(name, email, code); + } } + } finally { + pendingCodeGenerations.remove(lowerName); } } diff --git a/src/main/java/fr/xephi/authme/data/auth/PlayerAuth.java b/authme-core/src/main/java/fr/xephi/authme/data/auth/PlayerAuth.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/auth/PlayerAuth.java rename to authme-core/src/main/java/fr/xephi/authme/data/auth/PlayerAuth.java diff --git a/src/main/java/fr/xephi/authme/data/auth/PlayerCache.java b/authme-core/src/main/java/fr/xephi/authme/data/auth/PlayerCache.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/auth/PlayerCache.java rename to authme-core/src/main/java/fr/xephi/authme/data/auth/PlayerCache.java diff --git a/src/main/java/fr/xephi/authme/data/captcha/CaptchaCodeStorage.java b/authme-core/src/main/java/fr/xephi/authme/data/captcha/CaptchaCodeStorage.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/captcha/CaptchaCodeStorage.java rename to authme-core/src/main/java/fr/xephi/authme/data/captcha/CaptchaCodeStorage.java diff --git a/src/main/java/fr/xephi/authme/data/captcha/CaptchaManager.java b/authme-core/src/main/java/fr/xephi/authme/data/captcha/CaptchaManager.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/captcha/CaptchaManager.java rename to authme-core/src/main/java/fr/xephi/authme/data/captcha/CaptchaManager.java diff --git a/src/main/java/fr/xephi/authme/data/captcha/LoginCaptchaManager.java b/authme-core/src/main/java/fr/xephi/authme/data/captcha/LoginCaptchaManager.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/captcha/LoginCaptchaManager.java rename to authme-core/src/main/java/fr/xephi/authme/data/captcha/LoginCaptchaManager.java diff --git a/src/main/java/fr/xephi/authme/data/captcha/RegistrationCaptchaManager.java b/authme-core/src/main/java/fr/xephi/authme/data/captcha/RegistrationCaptchaManager.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/captcha/RegistrationCaptchaManager.java rename to authme-core/src/main/java/fr/xephi/authme/data/captcha/RegistrationCaptchaManager.java diff --git a/src/main/java/fr/xephi/authme/data/limbo/AllowFlightRestoreType.java b/authme-core/src/main/java/fr/xephi/authme/data/limbo/AllowFlightRestoreType.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/limbo/AllowFlightRestoreType.java rename to authme-core/src/main/java/fr/xephi/authme/data/limbo/AllowFlightRestoreType.java diff --git a/src/main/java/fr/xephi/authme/data/limbo/AuthGroupHandler.java b/authme-core/src/main/java/fr/xephi/authme/data/limbo/AuthGroupHandler.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/limbo/AuthGroupHandler.java rename to authme-core/src/main/java/fr/xephi/authme/data/limbo/AuthGroupHandler.java diff --git a/src/main/java/fr/xephi/authme/data/limbo/AuthGroupType.java b/authme-core/src/main/java/fr/xephi/authme/data/limbo/AuthGroupType.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/limbo/AuthGroupType.java rename to authme-core/src/main/java/fr/xephi/authme/data/limbo/AuthGroupType.java diff --git a/src/main/java/fr/xephi/authme/data/limbo/LimboMessageType.java b/authme-core/src/main/java/fr/xephi/authme/data/limbo/LimboMessageType.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/limbo/LimboMessageType.java rename to authme-core/src/main/java/fr/xephi/authme/data/limbo/LimboMessageType.java diff --git a/src/main/java/fr/xephi/authme/data/limbo/LimboPlayer.java b/authme-core/src/main/java/fr/xephi/authme/data/limbo/LimboPlayer.java similarity index 87% rename from src/main/java/fr/xephi/authme/data/limbo/LimboPlayer.java rename to authme-core/src/main/java/fr/xephi/authme/data/limbo/LimboPlayer.java index b1681ebc3c..3f27c577a7 100644 --- a/src/main/java/fr/xephi/authme/data/limbo/LimboPlayer.java +++ b/authme-core/src/main/java/fr/xephi/authme/data/limbo/LimboPlayer.java @@ -1,6 +1,7 @@ package fr.xephi.authme.data.limbo; import fr.xephi.authme.task.MessageTask; +import fr.xephi.authme.service.CancellableTask; import org.bukkit.Location; import org.bukkit.entity.EntityType; import org.bukkit.scheduler.BukkitTask; @@ -26,8 +27,9 @@ public class LimboPlayer { private final Location loc; private final float walkSpeed; private final float flySpeed; - private BukkitTask timeoutTask = null; + private CancellableTask timeoutTask = null; private MessageTask messageTask = null; + private CancellableTask messageTaskHandle = null; private LimboPlayerState state = LimboPlayerState.PASSWORD_REQUIRED; private Set enderPearlUuids = new HashSet<>(); private UUID vehicleUuid; @@ -88,7 +90,7 @@ public float getFlySpeed() { * * @return The timeout task associated to the player */ - public BukkitTask getTimeoutTask() { + public CancellableTask getTimeoutTask() { return timeoutTask; } @@ -98,13 +100,17 @@ public BukkitTask getTimeoutTask() { * * @param timeoutTask The task to set */ - public void setTimeoutTask(BukkitTask timeoutTask) { + public void setTimeoutTask(CancellableTask timeoutTask) { if (this.timeoutTask != null) { this.timeoutTask.cancel(); } this.timeoutTask = timeoutTask; } + public void setTimeoutTask(BukkitTask timeoutTask) { + setTimeoutTask(timeoutTask == null ? null : timeoutTask::cancel); + } + /** * Return the message task reminding the player to log in or register. * @@ -119,19 +125,24 @@ public MessageTask getMessageTask() { * * @param messageTask The message task to set */ - public void setMessageTask(MessageTask messageTask) { - if (this.messageTask != null) { - this.messageTask.cancel(); + public void setMessageTask(MessageTask messageTask, CancellableTask messageTaskHandle) { + if (this.messageTaskHandle != null) { + this.messageTaskHandle.cancel(); } this.messageTask = messageTask; + this.messageTaskHandle = messageTaskHandle; + } + + public void setMessageTask(MessageTask messageTask) { + setMessageTask(messageTask, messageTask == null ? null : messageTask::cancel); } /** * Clears all tasks associated to the player. */ public void clearTasks() { - setMessageTask(null); - setTimeoutTask(null); + setMessageTask(null, null); + setTimeoutTask((CancellableTask) null); } public LimboPlayerState getState() { diff --git a/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerState.java b/authme-core/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerState.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/limbo/LimboPlayerState.java rename to authme-core/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerState.java diff --git a/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManager.java b/authme-core/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManager.java similarity index 80% rename from src/main/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManager.java rename to authme-core/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManager.java index 3612e67975..33abdfadd4 100644 --- a/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManager.java +++ b/authme-core/src/main/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManager.java @@ -1,17 +1,18 @@ package fr.xephi.authme.data.limbo; +import ch.jalu.configme.properties.Property; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.data.captcha.RegistrationCaptchaManager; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.CancellableTask; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.task.MessageTask; import fr.xephi.authme.task.TimeoutTask; import org.bukkit.entity.Player; -import org.bukkit.scheduler.BukkitTask; import javax.inject.Inject; @@ -53,8 +54,9 @@ void registerMessageTask(Player player, LimboPlayer limbo, LimboMessageType mess if (interval > 0) { String[] joinMessage = messages.retrieveSingle(player, result.messageKey, result.args).split("\n"); MessageTask messageTask = new MessageTask(player, joinMessage); - bukkitService.runTaskTimer(messageTask, 2 * TICKS_PER_SECOND, interval * TICKS_PER_SECOND); - limbo.setMessageTask(messageTask); + CancellableTask taskHandle = + bukkitService.runTaskTimer(player, messageTask, 2 * TICKS_PER_SECOND, interval * TICKS_PER_SECOND); + limbo.setMessageTask(messageTask, taskHandle); } } @@ -63,12 +65,16 @@ void registerMessageTask(Player player, LimboPlayer limbo, LimboMessageType mess * * @param player the player to register a timeout task for * @param limbo the associated limbo player + * @param messageType the limbo message type, used to select login vs. register timeout */ - void registerTimeoutTask(Player player, LimboPlayer limbo) { - final int timeout = settings.getProperty(RestrictionSettings.TIMEOUT) * TICKS_PER_SECOND; + void registerTimeoutTask(Player player, LimboPlayer limbo, LimboMessageType messageType) { + Property timeoutProperty = messageType == LimboMessageType.REGISTER + ? RestrictionSettings.REGISTER_TIMEOUT + : RestrictionSettings.LOGIN_TIMEOUT; + final int timeout = settings.getProperty(timeoutProperty) * TICKS_PER_SECOND; if (timeout > 0) { String message = messages.retrieveSingle(player, MessageKey.LOGIN_TIMEOUT_ERROR); - BukkitTask task = bukkitService.runTaskLater(new TimeoutTask(player, message, playerCache), timeout); + CancellableTask task = bukkitService.runTaskLater(player, new TimeoutTask(player, message, playerCache), timeout); limbo.setTimeoutTask(task); } } diff --git a/src/main/java/fr/xephi/authme/data/limbo/LimboService.java b/authme-core/src/main/java/fr/xephi/authme/data/limbo/LimboService.java similarity index 91% rename from src/main/java/fr/xephi/authme/data/limbo/LimboService.java rename to authme-core/src/main/java/fr/xephi/authme/data/limbo/LimboService.java index 7e2fb2b09c..e6efeb7050 100644 --- a/src/main/java/fr/xephi/authme/data/limbo/LimboService.java +++ b/authme-core/src/main/java/fr/xephi/authme/data/limbo/LimboService.java @@ -3,6 +3,7 @@ import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.data.limbo.persistence.LimboPersistence; import fr.xephi.authme.output.ConsoleLoggerFactory; +import fr.xephi.authme.service.TeleportationService; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.SpawnLoader; import org.bukkit.Location; @@ -53,6 +54,9 @@ public class LimboService { @Inject private SpawnLoader spawnLoader; + @Inject + private TeleportationService teleportationService; + LimboService() { } @@ -76,13 +80,13 @@ public void createLimboPlayer(Player player, boolean isRegistered) { logger.debug("LimboPlayer for `{0}` already present in memory", name); } - Location location = spawnLoader.getPlayerLocationOrSpawn(player); + Location location = teleportationService.consumeOriginalJoinLocation(name, spawnLoader.getPlayerLocationOrSpawn(player)); LimboPlayer limboPlayer = helper.merge(existingLimbo, limboFromDisk); limboPlayer = helper.merge(helper.createLimboPlayer(player, isRegistered, location), limboPlayer); - taskManager.registerMessageTask(player, limboPlayer, - isRegistered ? LimboMessageType.LOG_IN : LimboMessageType.REGISTER); - taskManager.registerTimeoutTask(player, limboPlayer); + LimboMessageType messageType = isRegistered ? LimboMessageType.LOG_IN : LimboMessageType.REGISTER; + taskManager.registerMessageTask(player, limboPlayer, messageType); + taskManager.registerTimeoutTask(player, limboPlayer, messageType); helper.revokeLimboStates(player); authGroupHandler.setGroup(player, limboPlayer, isRegistered ? AuthGroupType.REGISTERED_UNAUTHENTICATED : AuthGroupType.UNREGISTERED); @@ -140,6 +144,19 @@ public void restoreData(Player player) { authGroupHandler.setGroup(player, limbo, AuthGroupType.LOGGED_IN); } + /** + * Re-applies limbo speed and flight restrictions after a player respawns. + * Bukkit sends a fresh PlayerAbilities packet on respawn that resets walk/fly speed, + * so the restrictions set during join must be re-applied one tick later. + * + * @param player the player to re-apply restrictions for + */ + public void reapplyLimboRestrictions(Player player) { + if (hasLimboPlayer(player.getName())) { + helper.revokeLimboStates(player); + } + } + /** * Creates new tasks for the given player and cancels the old ones for a newly registered player. * This resets his time to log in (TimeoutTask) and updates the message he is shown (MessageTask). @@ -149,7 +166,7 @@ public void restoreData(Player player) { public void replaceTasksAfterRegistration(Player player) { Optional limboPlayer = getLimboOrLogError(player, "reset tasks"); limboPlayer.ifPresent(limbo -> { - taskManager.registerTimeoutTask(player, limbo); + taskManager.registerTimeoutTask(player, limbo, LimboMessageType.LOG_IN); taskManager.registerMessageTask(player, limbo, LimboMessageType.LOG_IN); }); authGroupHandler.setGroup(player, limboPlayer.orElse(null), AuthGroupType.REGISTERED_UNAUTHENTICATED); @@ -294,4 +311,4 @@ public void restoreEntities(Player player) { } } } -} \ No newline at end of file +} diff --git a/src/main/java/fr/xephi/authme/data/limbo/LimboServiceHelper.java b/authme-core/src/main/java/fr/xephi/authme/data/limbo/LimboServiceHelper.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/limbo/LimboServiceHelper.java rename to authme-core/src/main/java/fr/xephi/authme/data/limbo/LimboServiceHelper.java diff --git a/src/main/java/fr/xephi/authme/data/limbo/UserGroup.java b/authme-core/src/main/java/fr/xephi/authme/data/limbo/UserGroup.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/limbo/UserGroup.java rename to authme-core/src/main/java/fr/xephi/authme/data/limbo/UserGroup.java diff --git a/src/main/java/fr/xephi/authme/data/limbo/WalkFlySpeedRestoreType.java b/authme-core/src/main/java/fr/xephi/authme/data/limbo/WalkFlySpeedRestoreType.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/limbo/WalkFlySpeedRestoreType.java rename to authme-core/src/main/java/fr/xephi/authme/data/limbo/WalkFlySpeedRestoreType.java diff --git a/src/main/java/fr/xephi/authme/data/limbo/persistence/DistributedFilesPersistenceHandler.java b/authme-core/src/main/java/fr/xephi/authme/data/limbo/persistence/DistributedFilesPersistenceHandler.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/limbo/persistence/DistributedFilesPersistenceHandler.java rename to authme-core/src/main/java/fr/xephi/authme/data/limbo/persistence/DistributedFilesPersistenceHandler.java diff --git a/src/main/java/fr/xephi/authme/data/limbo/persistence/IndividualFilesPersistenceHandler.java b/authme-core/src/main/java/fr/xephi/authme/data/limbo/persistence/IndividualFilesPersistenceHandler.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/limbo/persistence/IndividualFilesPersistenceHandler.java rename to authme-core/src/main/java/fr/xephi/authme/data/limbo/persistence/IndividualFilesPersistenceHandler.java diff --git a/src/main/java/fr/xephi/authme/data/limbo/persistence/LimboPersistence.java b/authme-core/src/main/java/fr/xephi/authme/data/limbo/persistence/LimboPersistence.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/limbo/persistence/LimboPersistence.java rename to authme-core/src/main/java/fr/xephi/authme/data/limbo/persistence/LimboPersistence.java diff --git a/src/main/java/fr/xephi/authme/data/limbo/persistence/LimboPersistenceHandler.java b/authme-core/src/main/java/fr/xephi/authme/data/limbo/persistence/LimboPersistenceHandler.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/limbo/persistence/LimboPersistenceHandler.java rename to authme-core/src/main/java/fr/xephi/authme/data/limbo/persistence/LimboPersistenceHandler.java diff --git a/src/main/java/fr/xephi/authme/data/limbo/persistence/LimboPersistenceType.java b/authme-core/src/main/java/fr/xephi/authme/data/limbo/persistence/LimboPersistenceType.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/limbo/persistence/LimboPersistenceType.java rename to authme-core/src/main/java/fr/xephi/authme/data/limbo/persistence/LimboPersistenceType.java diff --git a/src/main/java/fr/xephi/authme/data/limbo/persistence/LimboPlayerDeserializer.java b/authme-core/src/main/java/fr/xephi/authme/data/limbo/persistence/LimboPlayerDeserializer.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/limbo/persistence/LimboPlayerDeserializer.java rename to authme-core/src/main/java/fr/xephi/authme/data/limbo/persistence/LimboPlayerDeserializer.java diff --git a/src/main/java/fr/xephi/authme/data/limbo/persistence/LimboPlayerSerializer.java b/authme-core/src/main/java/fr/xephi/authme/data/limbo/persistence/LimboPlayerSerializer.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/limbo/persistence/LimboPlayerSerializer.java rename to authme-core/src/main/java/fr/xephi/authme/data/limbo/persistence/LimboPlayerSerializer.java diff --git a/src/main/java/fr/xephi/authme/data/limbo/persistence/NoOpPersistenceHandler.java b/authme-core/src/main/java/fr/xephi/authme/data/limbo/persistence/NoOpPersistenceHandler.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/limbo/persistence/NoOpPersistenceHandler.java rename to authme-core/src/main/java/fr/xephi/authme/data/limbo/persistence/NoOpPersistenceHandler.java diff --git a/src/main/java/fr/xephi/authme/data/limbo/persistence/SegmentNameBuilder.java b/authme-core/src/main/java/fr/xephi/authme/data/limbo/persistence/SegmentNameBuilder.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/limbo/persistence/SegmentNameBuilder.java rename to authme-core/src/main/java/fr/xephi/authme/data/limbo/persistence/SegmentNameBuilder.java diff --git a/src/main/java/fr/xephi/authme/data/limbo/persistence/SegmentSize.java b/authme-core/src/main/java/fr/xephi/authme/data/limbo/persistence/SegmentSize.java similarity index 100% rename from src/main/java/fr/xephi/authme/data/limbo/persistence/SegmentSize.java rename to authme-core/src/main/java/fr/xephi/authme/data/limbo/persistence/SegmentSize.java diff --git a/src/main/java/fr/xephi/authme/datasource/AbstractSqlDataSource.java b/authme-core/src/main/java/fr/xephi/authme/datasource/AbstractSqlDataSource.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/AbstractSqlDataSource.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/AbstractSqlDataSource.java diff --git a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java b/authme-core/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/CacheDataSource.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java diff --git a/src/main/java/fr/xephi/authme/datasource/Columns.java b/authme-core/src/main/java/fr/xephi/authme/datasource/Columns.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/Columns.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/Columns.java diff --git a/src/main/java/fr/xephi/authme/datasource/DataSource.java b/authme-core/src/main/java/fr/xephi/authme/datasource/DataSource.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/DataSource.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/DataSource.java diff --git a/src/main/java/fr/xephi/authme/datasource/DataSourceType.java b/authme-core/src/main/java/fr/xephi/authme/datasource/DataSourceType.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/DataSourceType.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/DataSourceType.java diff --git a/src/main/java/fr/xephi/authme/datasource/MariaDB.java b/authme-core/src/main/java/fr/xephi/authme/datasource/MariaDB.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/MariaDB.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/MariaDB.java diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/authme-core/src/main/java/fr/xephi/authme/datasource/MySQL.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/MySQL.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/MySQL.java diff --git a/src/main/java/fr/xephi/authme/datasource/MySqlMigrater.java b/authme-core/src/main/java/fr/xephi/authme/datasource/MySqlMigrater.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/MySqlMigrater.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/MySqlMigrater.java diff --git a/src/main/java/fr/xephi/authme/datasource/PostgreSqlDataSource.java b/authme-core/src/main/java/fr/xephi/authme/datasource/PostgreSqlDataSource.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/PostgreSqlDataSource.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/PostgreSqlDataSource.java diff --git a/src/main/java/fr/xephi/authme/datasource/SQLite.java b/authme-core/src/main/java/fr/xephi/authme/datasource/SQLite.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/SQLite.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/SQLite.java diff --git a/src/main/java/fr/xephi/authme/datasource/SqLiteMigrater.java b/authme-core/src/main/java/fr/xephi/authme/datasource/SqLiteMigrater.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/SqLiteMigrater.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/SqLiteMigrater.java diff --git a/src/main/java/fr/xephi/authme/datasource/SqlDataSourceUtils.java b/authme-core/src/main/java/fr/xephi/authme/datasource/SqlDataSourceUtils.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/SqlDataSourceUtils.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/SqlDataSourceUtils.java diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumns.java b/authme-core/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumns.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumns.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumns.java diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsFactory.java b/authme-core/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsFactory.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsFactory.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsFactory.java diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java b/authme-core/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/columnshandler/AuthMeColumnsHandler.java diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/ColumnContext.java b/authme-core/src/main/java/fr/xephi/authme/datasource/columnshandler/ColumnContext.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/columnshandler/ColumnContext.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/columnshandler/ColumnContext.java diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/DataSourceColumn.java b/authme-core/src/main/java/fr/xephi/authme/datasource/columnshandler/DataSourceColumn.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/columnshandler/DataSourceColumn.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/columnshandler/DataSourceColumn.java diff --git a/src/main/java/fr/xephi/authme/datasource/columnshandler/PlayerAuthColumn.java b/authme-core/src/main/java/fr/xephi/authme/datasource/columnshandler/PlayerAuthColumn.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/columnshandler/PlayerAuthColumn.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/columnshandler/PlayerAuthColumn.java diff --git a/src/main/java/fr/xephi/authme/datasource/converter/AbstractDataSourceConverter.java b/authme-core/src/main/java/fr/xephi/authme/datasource/converter/AbstractDataSourceConverter.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/converter/AbstractDataSourceConverter.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/converter/AbstractDataSourceConverter.java diff --git a/authme-core/src/main/java/fr/xephi/authme/datasource/converter/AuthPlusConverter.java b/authme-core/src/main/java/fr/xephi/authme/datasource/converter/AuthPlusConverter.java new file mode 100644 index 0000000000..e2dcd31843 --- /dev/null +++ b/authme-core/src/main/java/fr/xephi/authme/datasource/converter/AuthPlusConverter.java @@ -0,0 +1,105 @@ +package fr.xephi.authme.datasource.converter; + +import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.initialization.DataFolder; +import fr.xephi.authme.output.ConsoleLoggerFactory; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.YamlConfiguration; + +import javax.inject.Inject; +import java.io.File; +import java.util.Locale; +import java.util.Set; +import java.util.UUID; + +import static fr.xephi.authme.util.Utils.logAndSendMessage; + +/** + * Converts data from Auth+ to AuthMe. + *

+ * Auth+ stores accounts in {@code plugins/Auth/players.yml} as UUID keys with a nested {@code hash} field + * using PBKDF2-HmacSHA256 (Base64-encoded salt and hash). Set {@code passwordHash: PBKDF2BASE64} in + * AuthMe's config before running this converter. + */ +public class AuthPlusConverter implements Converter { + + private final ConsoleLogger logger = ConsoleLoggerFactory.get(AuthPlusConverter.class); + private final DataSource dataSource; + private final File authPlusFile; + + @Inject + AuthPlusConverter(@DataFolder File dataFolder, DataSource dataSource) { + this.dataSource = dataSource; + this.authPlusFile = new File(dataFolder.getParentFile(), "Auth/players.yml"); + } + + @Override + public void execute(CommandSender sender) { + if (!authPlusFile.exists()) { + sender.sendMessage("The file '" + authPlusFile.getPath() + "' does not exist"); + return; + } + + YamlConfiguration config = YamlConfiguration.loadConfiguration(authPlusFile); + Set keys = config.getKeys(false); + + long successCount = 0; + long skippedCount = 0; + for (String uuidStr : keys) { + String hash = config.getString(uuidStr + ".hash"); + if (hash == null || hash.isEmpty()) { + logger.warning("No hash found for UUID '" + uuidStr + "', skipping"); + continue; + } + + UUID uuid; + try { + uuid = UUID.fromString(uuidStr); + } catch (IllegalArgumentException e) { + logger.warning("Invalid UUID '" + uuidStr + "', skipping"); + continue; + } + + String name = resolveName(uuid); + if (name == null) { + logger.warning("Could not resolve name for UUID '" + uuidStr + "', skipping"); + continue; + } + + String lowercaseName = name.toLowerCase(Locale.ROOT); + if (dataSource.isAuthAvailable(lowercaseName)) { + ++skippedCount; + continue; + } + + PlayerAuth auth = PlayerAuth.builder() + .name(lowercaseName) + .realName(name) + .uuid(uuid) + .password(hash, null) + .build(); + dataSource.saveAuth(auth); + ++successCount; + } + + logAndSendMessage(sender, "Auth+ conversion: " + successCount + " account(s) imported, " + + skippedCount + " skipped (already exist)"); + } + + private String resolveName(UUID uuid) { + try { + return Bukkit.getOfflinePlayer(uuid).getName(); + } catch (Exception | NoSuchMethodError e) { + for (OfflinePlayer op : Bukkit.getOfflinePlayers()) { + if (uuid.equals(op.getUniqueId())) { + return op.getName(); + } + } + return null; + } + } +} diff --git a/src/main/java/fr/xephi/authme/datasource/converter/Converter.java b/authme-core/src/main/java/fr/xephi/authme/datasource/converter/Converter.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/converter/Converter.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/converter/Converter.java diff --git a/src/main/java/fr/xephi/authme/datasource/converter/CrazyLoginConverter.java b/authme-core/src/main/java/fr/xephi/authme/datasource/converter/CrazyLoginConverter.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/converter/CrazyLoginConverter.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/converter/CrazyLoginConverter.java diff --git a/src/main/java/fr/xephi/authme/datasource/converter/LoginSecurityConverter.java b/authme-core/src/main/java/fr/xephi/authme/datasource/converter/LoginSecurityConverter.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/converter/LoginSecurityConverter.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/converter/LoginSecurityConverter.java diff --git a/src/main/java/fr/xephi/authme/datasource/converter/MySqlToSqlite.java b/authme-core/src/main/java/fr/xephi/authme/datasource/converter/MySqlToSqlite.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/converter/MySqlToSqlite.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/converter/MySqlToSqlite.java diff --git a/src/main/java/fr/xephi/authme/datasource/converter/RakamakConverter.java b/authme-core/src/main/java/fr/xephi/authme/datasource/converter/RakamakConverter.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/converter/RakamakConverter.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/converter/RakamakConverter.java diff --git a/src/main/java/fr/xephi/authme/datasource/converter/RoyalAuthConverter.java b/authme-core/src/main/java/fr/xephi/authme/datasource/converter/RoyalAuthConverter.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/converter/RoyalAuthConverter.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/converter/RoyalAuthConverter.java diff --git a/src/main/java/fr/xephi/authme/datasource/converter/SqliteToSql.java b/authme-core/src/main/java/fr/xephi/authme/datasource/converter/SqliteToSql.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/converter/SqliteToSql.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/converter/SqliteToSql.java diff --git a/src/main/java/fr/xephi/authme/datasource/converter/VAuthConverter.java b/authme-core/src/main/java/fr/xephi/authme/datasource/converter/VAuthConverter.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/converter/VAuthConverter.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/converter/VAuthConverter.java diff --git a/src/main/java/fr/xephi/authme/datasource/mysqlextensions/Ipb4Extension.java b/authme-core/src/main/java/fr/xephi/authme/datasource/mysqlextensions/Ipb4Extension.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/mysqlextensions/Ipb4Extension.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/mysqlextensions/Ipb4Extension.java diff --git a/src/main/java/fr/xephi/authme/datasource/mysqlextensions/MySqlExtension.java b/authme-core/src/main/java/fr/xephi/authme/datasource/mysqlextensions/MySqlExtension.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/mysqlextensions/MySqlExtension.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/mysqlextensions/MySqlExtension.java diff --git a/src/main/java/fr/xephi/authme/datasource/mysqlextensions/MySqlExtensionsFactory.java b/authme-core/src/main/java/fr/xephi/authme/datasource/mysqlextensions/MySqlExtensionsFactory.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/mysqlextensions/MySqlExtensionsFactory.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/mysqlextensions/MySqlExtensionsFactory.java diff --git a/src/main/java/fr/xephi/authme/datasource/mysqlextensions/NoOpExtension.java b/authme-core/src/main/java/fr/xephi/authme/datasource/mysqlextensions/NoOpExtension.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/mysqlextensions/NoOpExtension.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/mysqlextensions/NoOpExtension.java diff --git a/src/main/java/fr/xephi/authme/datasource/mysqlextensions/PhpBbExtension.java b/authme-core/src/main/java/fr/xephi/authme/datasource/mysqlextensions/PhpBbExtension.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/mysqlextensions/PhpBbExtension.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/mysqlextensions/PhpBbExtension.java diff --git a/src/main/java/fr/xephi/authme/datasource/mysqlextensions/WordpressExtension.java b/authme-core/src/main/java/fr/xephi/authme/datasource/mysqlextensions/WordpressExtension.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/mysqlextensions/WordpressExtension.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/mysqlextensions/WordpressExtension.java diff --git a/src/main/java/fr/xephi/authme/datasource/mysqlextensions/XfBcryptExtension.java b/authme-core/src/main/java/fr/xephi/authme/datasource/mysqlextensions/XfBcryptExtension.java similarity index 100% rename from src/main/java/fr/xephi/authme/datasource/mysqlextensions/XfBcryptExtension.java rename to authme-core/src/main/java/fr/xephi/authme/datasource/mysqlextensions/XfBcryptExtension.java diff --git a/src/main/java/fr/xephi/authme/events/AbstractTeleportEvent.java b/authme-core/src/main/java/fr/xephi/authme/events/AbstractTeleportEvent.java similarity index 100% rename from src/main/java/fr/xephi/authme/events/AbstractTeleportEvent.java rename to authme-core/src/main/java/fr/xephi/authme/events/AbstractTeleportEvent.java diff --git a/src/main/java/fr/xephi/authme/events/AbstractUnregisterEvent.java b/authme-core/src/main/java/fr/xephi/authme/events/AbstractUnregisterEvent.java similarity index 100% rename from src/main/java/fr/xephi/authme/events/AbstractUnregisterEvent.java rename to authme-core/src/main/java/fr/xephi/authme/events/AbstractUnregisterEvent.java diff --git a/src/main/java/fr/xephi/authme/events/AuthMeAsyncPreLoginEvent.java b/authme-core/src/main/java/fr/xephi/authme/events/AuthMeAsyncPreLoginEvent.java similarity index 100% rename from src/main/java/fr/xephi/authme/events/AuthMeAsyncPreLoginEvent.java rename to authme-core/src/main/java/fr/xephi/authme/events/AuthMeAsyncPreLoginEvent.java diff --git a/src/main/java/fr/xephi/authme/events/AuthMeAsyncPreRegisterEvent.java b/authme-core/src/main/java/fr/xephi/authme/events/AuthMeAsyncPreRegisterEvent.java similarity index 100% rename from src/main/java/fr/xephi/authme/events/AuthMeAsyncPreRegisterEvent.java rename to authme-core/src/main/java/fr/xephi/authme/events/AuthMeAsyncPreRegisterEvent.java diff --git a/src/main/java/fr/xephi/authme/events/AuthMeTeleportEvent.java b/authme-core/src/main/java/fr/xephi/authme/events/AuthMeTeleportEvent.java similarity index 100% rename from src/main/java/fr/xephi/authme/events/AuthMeTeleportEvent.java rename to authme-core/src/main/java/fr/xephi/authme/events/AuthMeTeleportEvent.java diff --git a/src/main/java/fr/xephi/authme/events/CustomEvent.java b/authme-core/src/main/java/fr/xephi/authme/events/CustomEvent.java similarity index 100% rename from src/main/java/fr/xephi/authme/events/CustomEvent.java rename to authme-core/src/main/java/fr/xephi/authme/events/CustomEvent.java diff --git a/src/main/java/fr/xephi/authme/events/EmailChangedEvent.java b/authme-core/src/main/java/fr/xephi/authme/events/EmailChangedEvent.java similarity index 100% rename from src/main/java/fr/xephi/authme/events/EmailChangedEvent.java rename to authme-core/src/main/java/fr/xephi/authme/events/EmailChangedEvent.java diff --git a/src/main/java/fr/xephi/authme/events/FailedLoginEvent.java b/authme-core/src/main/java/fr/xephi/authme/events/FailedLoginEvent.java similarity index 100% rename from src/main/java/fr/xephi/authme/events/FailedLoginEvent.java rename to authme-core/src/main/java/fr/xephi/authme/events/FailedLoginEvent.java diff --git a/src/main/java/fr/xephi/authme/events/FirstSpawnTeleportEvent.java b/authme-core/src/main/java/fr/xephi/authme/events/FirstSpawnTeleportEvent.java similarity index 100% rename from src/main/java/fr/xephi/authme/events/FirstSpawnTeleportEvent.java rename to authme-core/src/main/java/fr/xephi/authme/events/FirstSpawnTeleportEvent.java diff --git a/src/main/java/fr/xephi/authme/events/LoginEvent.java b/authme-core/src/main/java/fr/xephi/authme/events/LoginEvent.java similarity index 100% rename from src/main/java/fr/xephi/authme/events/LoginEvent.java rename to authme-core/src/main/java/fr/xephi/authme/events/LoginEvent.java diff --git a/src/main/java/fr/xephi/authme/events/LogoutEvent.java b/authme-core/src/main/java/fr/xephi/authme/events/LogoutEvent.java similarity index 100% rename from src/main/java/fr/xephi/authme/events/LogoutEvent.java rename to authme-core/src/main/java/fr/xephi/authme/events/LogoutEvent.java diff --git a/src/main/java/fr/xephi/authme/events/PasswordEncryptionEvent.java b/authme-core/src/main/java/fr/xephi/authme/events/PasswordEncryptionEvent.java similarity index 100% rename from src/main/java/fr/xephi/authme/events/PasswordEncryptionEvent.java rename to authme-core/src/main/java/fr/xephi/authme/events/PasswordEncryptionEvent.java diff --git a/src/main/java/fr/xephi/authme/events/ProtectInventoryEvent.java b/authme-core/src/main/java/fr/xephi/authme/events/ProtectInventoryEvent.java similarity index 100% rename from src/main/java/fr/xephi/authme/events/ProtectInventoryEvent.java rename to authme-core/src/main/java/fr/xephi/authme/events/ProtectInventoryEvent.java diff --git a/src/main/java/fr/xephi/authme/events/RegisterEvent.java b/authme-core/src/main/java/fr/xephi/authme/events/RegisterEvent.java similarity index 100% rename from src/main/java/fr/xephi/authme/events/RegisterEvent.java rename to authme-core/src/main/java/fr/xephi/authme/events/RegisterEvent.java diff --git a/src/main/java/fr/xephi/authme/events/RestoreInventoryEvent.java b/authme-core/src/main/java/fr/xephi/authme/events/RestoreInventoryEvent.java similarity index 100% rename from src/main/java/fr/xephi/authme/events/RestoreInventoryEvent.java rename to authme-core/src/main/java/fr/xephi/authme/events/RestoreInventoryEvent.java diff --git a/src/main/java/fr/xephi/authme/events/RestoreSessionEvent.java b/authme-core/src/main/java/fr/xephi/authme/events/RestoreSessionEvent.java similarity index 100% rename from src/main/java/fr/xephi/authme/events/RestoreSessionEvent.java rename to authme-core/src/main/java/fr/xephi/authme/events/RestoreSessionEvent.java diff --git a/src/main/java/fr/xephi/authme/events/SpawnTeleportEvent.java b/authme-core/src/main/java/fr/xephi/authme/events/SpawnTeleportEvent.java similarity index 100% rename from src/main/java/fr/xephi/authme/events/SpawnTeleportEvent.java rename to authme-core/src/main/java/fr/xephi/authme/events/SpawnTeleportEvent.java diff --git a/src/main/java/fr/xephi/authme/events/UnregisterByAdminEvent.java b/authme-core/src/main/java/fr/xephi/authme/events/UnregisterByAdminEvent.java similarity index 100% rename from src/main/java/fr/xephi/authme/events/UnregisterByAdminEvent.java rename to authme-core/src/main/java/fr/xephi/authme/events/UnregisterByAdminEvent.java diff --git a/src/main/java/fr/xephi/authme/events/UnregisterByPlayerEvent.java b/authme-core/src/main/java/fr/xephi/authme/events/UnregisterByPlayerEvent.java similarity index 100% rename from src/main/java/fr/xephi/authme/events/UnregisterByPlayerEvent.java rename to authme-core/src/main/java/fr/xephi/authme/events/UnregisterByPlayerEvent.java diff --git a/src/main/java/fr/xephi/authme/initialization/DataFolder.java b/authme-core/src/main/java/fr/xephi/authme/initialization/DataFolder.java similarity index 100% rename from src/main/java/fr/xephi/authme/initialization/DataFolder.java rename to authme-core/src/main/java/fr/xephi/authme/initialization/DataFolder.java diff --git a/src/main/java/fr/xephi/authme/initialization/DataSourceProvider.java b/authme-core/src/main/java/fr/xephi/authme/initialization/DataSourceProvider.java similarity index 100% rename from src/main/java/fr/xephi/authme/initialization/DataSourceProvider.java rename to authme-core/src/main/java/fr/xephi/authme/initialization/DataSourceProvider.java diff --git a/src/main/java/fr/xephi/authme/initialization/HasCleanup.java b/authme-core/src/main/java/fr/xephi/authme/initialization/HasCleanup.java similarity index 100% rename from src/main/java/fr/xephi/authme/initialization/HasCleanup.java rename to authme-core/src/main/java/fr/xephi/authme/initialization/HasCleanup.java diff --git a/src/main/java/fr/xephi/authme/initialization/OnShutdownPlayerSaver.java b/authme-core/src/main/java/fr/xephi/authme/initialization/OnShutdownPlayerSaver.java similarity index 100% rename from src/main/java/fr/xephi/authme/initialization/OnShutdownPlayerSaver.java rename to authme-core/src/main/java/fr/xephi/authme/initialization/OnShutdownPlayerSaver.java diff --git a/src/main/java/fr/xephi/authme/initialization/OnStartupTasks.java b/authme-core/src/main/java/fr/xephi/authme/initialization/OnStartupTasks.java similarity index 100% rename from src/main/java/fr/xephi/authme/initialization/OnStartupTasks.java rename to authme-core/src/main/java/fr/xephi/authme/initialization/OnStartupTasks.java diff --git a/src/main/java/fr/xephi/authme/initialization/Reloadable.java b/authme-core/src/main/java/fr/xephi/authme/initialization/Reloadable.java similarity index 100% rename from src/main/java/fr/xephi/authme/initialization/Reloadable.java rename to authme-core/src/main/java/fr/xephi/authme/initialization/Reloadable.java diff --git a/src/main/java/fr/xephi/authme/initialization/SettingsDependent.java b/authme-core/src/main/java/fr/xephi/authme/initialization/SettingsDependent.java similarity index 100% rename from src/main/java/fr/xephi/authme/initialization/SettingsDependent.java rename to authme-core/src/main/java/fr/xephi/authme/initialization/SettingsDependent.java diff --git a/src/main/java/fr/xephi/authme/initialization/SettingsProvider.java b/authme-core/src/main/java/fr/xephi/authme/initialization/SettingsProvider.java similarity index 100% rename from src/main/java/fr/xephi/authme/initialization/SettingsProvider.java rename to authme-core/src/main/java/fr/xephi/authme/initialization/SettingsProvider.java diff --git a/src/main/java/fr/xephi/authme/initialization/TaskCloser.java b/authme-core/src/main/java/fr/xephi/authme/initialization/TaskCloser.java similarity index 100% rename from src/main/java/fr/xephi/authme/initialization/TaskCloser.java rename to authme-core/src/main/java/fr/xephi/authme/initialization/TaskCloser.java diff --git a/src/main/java/fr/xephi/authme/listener/BlockListener.java b/authme-core/src/main/java/fr/xephi/authme/listener/BlockListener.java similarity index 100% rename from src/main/java/fr/xephi/authme/listener/BlockListener.java rename to authme-core/src/main/java/fr/xephi/authme/listener/BlockListener.java diff --git a/src/main/java/fr/xephi/authme/listener/EntityListener.java b/authme-core/src/main/java/fr/xephi/authme/listener/EntityListener.java similarity index 100% rename from src/main/java/fr/xephi/authme/listener/EntityListener.java rename to authme-core/src/main/java/fr/xephi/authme/listener/EntityListener.java diff --git a/src/main/java/fr/xephi/authme/listener/FailedVerificationException.java b/authme-core/src/main/java/fr/xephi/authme/listener/FailedVerificationException.java similarity index 100% rename from src/main/java/fr/xephi/authme/listener/FailedVerificationException.java rename to authme-core/src/main/java/fr/xephi/authme/listener/FailedVerificationException.java diff --git a/src/main/java/fr/xephi/authme/listener/ListenerService.java b/authme-core/src/main/java/fr/xephi/authme/listener/ListenerService.java similarity index 98% rename from src/main/java/fr/xephi/authme/listener/ListenerService.java rename to authme-core/src/main/java/fr/xephi/authme/listener/ListenerService.java index d283f3e4e5..0ace8ebd78 100644 --- a/src/main/java/fr/xephi/authme/listener/ListenerService.java +++ b/authme-core/src/main/java/fr/xephi/authme/listener/ListenerService.java @@ -17,7 +17,7 @@ /** * Service class for the AuthMe listeners to determine whether an event should be canceled. */ -class ListenerService implements SettingsDependent { +public class ListenerService implements SettingsDependent { private final DataSource dataSource; private final PlayerCache playerCache; diff --git a/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java b/authme-core/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java similarity index 88% rename from src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java rename to authme-core/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java index 7ed12f31e8..bec01cde82 100644 --- a/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java +++ b/authme-core/src/main/java/fr/xephi/authme/listener/OnJoinVerifier.java @@ -80,7 +80,7 @@ public void checkNameRestrictions(String name, InetAddress address) throws Faile if (!validationService.fulfillsNameRestrictions(name, address)) { if (settings.getProperty(RestrictionSettings.BAN_UNKNOWN_IP)) { String ip = address.getHostAddress(); - bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> server.banIP(ip)); + bukkitService.runOnGlobalRegion(() -> server.banIP(ip)); } throw new FailedVerificationException(MessageKey.NOT_OWNER_ERROR); } @@ -142,32 +142,47 @@ public void checkIsValidName(String name) throws FailedVerificationException { * further), false if the player is not refused */ public boolean refusePlayerForFullServer(PlayerLoginEvent event) { - final Player player = event.getPlayer(); if (event.getResult() != PlayerLoginEvent.Result.KICK_FULL) { // Server is not full, no need to do anything return false; - } else if (!permissionsManager.hasPermission(player, PlayerStatePermission.IS_VIP)) { - // Server is full and player is NOT VIP; set kick message and proceed with kick - event.setKickMessage(messages.retrieveSingle(player, MessageKey.KICK_FULL_SERVER)); - return true; } - // Server is full and player is VIP; attempt to kick a non-VIP player to make room - Collection onlinePlayers = bukkitService.getOnlinePlayers(); - if (onlinePlayers.size() < server.getMaxPlayers()) { + String kickMessage = getServerFullKickMessageIfDenied(event.getPlayer().getName()); + if (kickMessage == null) { event.allow(); return false; } + + event.setKickMessage(kickMessage); + return true; + } + + /** + * Handles the case of a full server for the given player name. If the user may join, {@code null} + * is returned and the caller should allow the connection. Otherwise the returned message should be + * used as the kick reason. + * + * @param playerName the joining player name + * @return the kick message if access should be denied, or null if the player may join + */ + public String getServerFullKickMessageIfDenied(String playerName) { + if (!permissionsManager.hasPermissionOffline(playerName, PlayerStatePermission.IS_VIP)) { + return messages.retrieveSingle(playerName, MessageKey.KICK_FULL_SERVER); + } + + Collection onlinePlayers = bukkitService.getOnlinePlayers(); + if (onlinePlayers.size() < server.getMaxPlayers()) { + return null; + } + Player nonVipPlayer = generateKickPlayer(onlinePlayers); if (nonVipPlayer != null) { - nonVipPlayer.kickPlayer(messages.retrieveSingle(player, MessageKey.KICK_FOR_VIP)); - event.allow(); - return false; - } else { - logger.info("VIP player " + player.getName() + " tried to join, but the server was full"); - event.setKickMessage(messages.retrieveSingle(player, MessageKey.KICK_FULL_SERVER)); - return true; + nonVipPlayer.kickPlayer(messages.retrieveSingle(playerName, MessageKey.KICK_FOR_VIP)); + return null; } + + logger.info("VIP player " + playerName + " tried to join, but the server was full"); + return messages.retrieveSingle(playerName, MessageKey.KICK_FULL_SERVER); } /** diff --git a/src/main/java/fr/xephi/authme/listener/PlayerListener.java b/authme-core/src/main/java/fr/xephi/authme/listener/PlayerListener.java similarity index 87% rename from src/main/java/fr/xephi/authme/listener/PlayerListener.java rename to authme-core/src/main/java/fr/xephi/authme/listener/PlayerListener.java index d9b1c70a0d..4a461ad847 100644 --- a/src/main/java/fr/xephi/authme/listener/PlayerListener.java +++ b/authme-core/src/main/java/fr/xephi/authme/listener/PlayerListener.java @@ -15,6 +15,9 @@ import fr.xephi.authme.service.TeleportationService; import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.platform.ChatAdapter; +import fr.xephi.authme.platform.PlatformAdapter; +import fr.xephi.authme.platform.TeleportAdapter; import fr.xephi.authme.settings.SpawnLoader; import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.RegistrationSettings; @@ -29,6 +32,7 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.SignChangeEvent; +import org.bukkit.event.entity.EntityAirChangeEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryOpenEvent; @@ -48,11 +52,13 @@ import org.bukkit.event.player.PlayerKickEvent; import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.event.player.PlayerMoveEvent; -import org.bukkit.event.player.PlayerPickupItemEvent; +import org.bukkit.event.entity.EntityPickupItemEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.event.player.PlayerShearEntityEvent; +import org.bukkit.event.player.PlayerSwapHandItemsEvent; import org.bukkit.inventory.InventoryView; +import org.spigotmc.event.player.PlayerSpawnLocationEvent; import javax.inject.Inject; import java.util.Locale; @@ -98,6 +104,12 @@ public class PlayerListener implements Listener { private QuickCommandsProtectionManager quickCommandsProtectionManager; @Inject private LimboService limboService; + @Inject + private ChatAdapter chatAdapter; + @Inject + private TeleportAdapter teleportAdapter; + @Inject + private PlatformAdapter platformAdapter; // Lowest priority to apply fast protection checks @EventHandler(priority = EventPriority.LOWEST) @@ -167,6 +179,10 @@ public void onAsyncPlayerPreLoginEventHighest(AsyncPlayerPreLoginEvent event) { @EventHandler(priority = EventPriority.LOW) public void onPlayerLogin(PlayerLoginEvent event) { + if (!platformAdapter.shouldHandlePlayerLoginEvent()) { + return; + } + final Player player = event.getPlayer(); final String name = player.getName(); @@ -189,7 +205,7 @@ public void onPlayerLogin(PlayerLoginEvent event) { public void onPlayerJoin(PlayerJoinEvent event) { final Player player = event.getPlayer(); - if (!PlayerListener19Spigot.isPlayerSpawnLocationEventCalled()) { + if (!SpawnLocationTracker.isEventCalled()) { teleportationService.teleportOnJoin(player); } @@ -202,6 +218,21 @@ public void onPlayerJoin(PlayerJoinEvent event) { teleportationService.teleportNewPlayerToFirstSpawn(player); } + @EventHandler(priority = EventPriority.HIGH) + public void onPlayerSpawn(PlayerSpawnLocationEvent event) { + if (!platformAdapter.shouldHandlePlayerSpawnLocationEvent()) { + return; + } + + SpawnLocationTracker.markEventCalled(); + final Player player = event.getPlayer(); + + Location customSpawnLocation = teleportationService.prepareOnJoinSpawnLocation(player, event.getSpawnLocation()); + if (customSpawnLocation != null) { + event.setSpawnLocation(customSpawnLocation); + } + } + @EventHandler(priority = EventPriority.HIGH) // HIGH as EssentialsX listens at HIGHEST public void onJoinMessage(PlayerJoinEvent event) { final Player player = event.getPlayer(); @@ -285,7 +316,7 @@ public void onPlayerKick(PlayerKickEvent event) { // Note #831: Especially for offline CraftBukkit, we need to catch players being kicked because of // "logged in from another location" and to cancel their kick if (settings.getProperty(RestrictionSettings.FORCE_SINGLE_SESSION) - && event.getReason().contains("You logged in from another location")) { + && chatAdapter.getKickReason(event).contains("You logged in from another location")) { event.setCancelled(true); return; } @@ -392,25 +423,28 @@ public void onPlayerMove(PlayerMoveEvent event) { Location spawn = spawnLoader.getSpawnLocation(player); if (spawn != null && spawn.getWorld() != null) { if (!player.getWorld().equals(spawn.getWorld())) { - player.teleport(spawn); + teleportAdapter.teleportPlayer(player, spawn); } else if (spawn.distance(player.getLocation()) > settings.getProperty(ALLOWED_MOVEMENT_RADIUS)) { - player.teleport(spawn); + teleportAdapter.teleportPlayer(player, spawn); } } } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onPlayerRespawn(PlayerRespawnEvent event) { - if (settings.getProperty(RestrictionSettings.NO_TELEPORT)) { - return; - } + Player player = event.getPlayer(); if (!listenerService.shouldCancelEvent(event)) { return; } - Location spawn = spawnLoader.getSpawnLocation(event.getPlayer()); - if (spawn != null && spawn.getWorld() != null) { - event.setRespawnLocation(spawn); + if (!settings.getProperty(RestrictionSettings.NO_TELEPORT)) { + Location spawn = spawnLoader.getPlayerRespawnLocationOrSpawn(player); + if (spawn != null && spawn.getWorld() != null) { + event.setRespawnLocation(spawn); + } } + // Bukkit sends a fresh PlayerAbilities packet on respawn that resets walk/fly speed, + // overriding the restriction set during join. Re-apply one tick later. + bukkitService.runTaskLater(player, () -> limboService.reapplyLimboRestrictions(player), 1L); } /* @@ -459,6 +493,13 @@ public void onPlayerFish(PlayerFishEvent event) { } } + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) + public void onPlayerSwapHandItems(PlayerSwapHandItemsEvent event) { + if (listenerService.shouldCancelEvent(event)) { + event.setCancelled(true); + } + } + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) public void onPlayerBedEnter(PlayerBedEnterEvent event) { if (listenerService.shouldCancelEvent(event)) { @@ -486,8 +527,11 @@ public void onSignChange(SignChangeEvent event) { */ @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) - public void onPlayerPickupItem(PlayerPickupItemEvent event) { - if (listenerService.shouldCancelEvent(event)) { + public void onPlayerPickupItem(EntityPickupItemEvent event) { + if (!(event.getEntity() instanceof Player)) { + return; + } + if (listenerService.shouldCancelEvent((Player) event.getEntity())) { event.setCancelled(true); } } @@ -499,6 +543,13 @@ public void onPlayerDropItem(PlayerDropItemEvent event) { } } + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) + public void onPlayerAirChange(EntityAirChangeEvent event) { + if (listenerService.shouldCancelEvent(event)) { + event.setCancelled(true); + } + } + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) public void onPlayerHeldItem(PlayerItemHeldEvent event) { if (listenerService.shouldCancelEvent(event)) { @@ -533,7 +584,7 @@ public void onPlayerInventoryOpen(InventoryOpenEvent event) { * @note little hack cause InventoryOpenEvent cannot be cancelled for * real, cause no packet is sent to server by client for the main inv */ - bukkitService.scheduleSyncDelayedTask(player::closeInventory, 1); + bukkitService.scheduleSyncDelayedTask((Player) player, player::closeInventory, 1); } } diff --git a/src/main/java/fr/xephi/authme/listener/ServerListener.java b/authme-core/src/main/java/fr/xephi/authme/listener/ServerListener.java similarity index 100% rename from src/main/java/fr/xephi/authme/listener/ServerListener.java rename to authme-core/src/main/java/fr/xephi/authme/listener/ServerListener.java diff --git a/authme-core/src/main/java/fr/xephi/authme/listener/SpawnLocationTracker.java b/authme-core/src/main/java/fr/xephi/authme/listener/SpawnLocationTracker.java new file mode 100644 index 0000000000..640c3887ac --- /dev/null +++ b/authme-core/src/main/java/fr/xephi/authme/listener/SpawnLocationTracker.java @@ -0,0 +1,22 @@ +package fr.xephi.authme.listener; + +/** + * Tracks whether a platform-specific spawn location event has been fired for the current + * server session. Used to determine whether the spawn teleport fallback in + * {@link PlayerListener} is needed. + */ +public final class SpawnLocationTracker { + + private static volatile boolean eventCalled = false; + + private SpawnLocationTracker() { + } + + public static void markEventCalled() { + eventCalled = true; + } + + public static boolean isEventCalled() { + return eventCalled; + } +} diff --git a/src/main/java/fr/xephi/authme/listener/protocollib/InventoryPacketAdapter.java b/authme-core/src/main/java/fr/xephi/authme/listener/protocollib/InventoryPacketAdapter.java similarity index 90% rename from src/main/java/fr/xephi/authme/listener/protocollib/InventoryPacketAdapter.java rename to authme-core/src/main/java/fr/xephi/authme/listener/protocollib/InventoryPacketAdapter.java index fb8234092d..384a055433 100644 --- a/src/main/java/fr/xephi/authme/listener/protocollib/InventoryPacketAdapter.java +++ b/authme-core/src/main/java/fr/xephi/authme/listener/protocollib/InventoryPacketAdapter.java @@ -103,15 +103,8 @@ public void sendBlankInventoryPacket(Player player) { ItemStack[] blankInventory = new ItemStack[inventorySize]; Arrays.fill(blankInventory, new ItemStack(Material.AIR)); - //old minecraft versions - StructureModifier itemArrayModifier = inventoryPacket.getItemArrayModifier(); - if (itemArrayModifier.size() > 0) { - itemArrayModifier.write(0, blankInventory); - } else { - //minecraft versions above 1.11 - StructureModifier> itemListModifier = inventoryPacket.getItemListModifier(); - itemListModifier.write(0, Arrays.asList(blankInventory)); - } + StructureModifier> itemListModifier = inventoryPacket.getItemListModifier(); + itemListModifier.write(0, Arrays.asList(blankInventory)); try { protocolManager.sendServerPacket(player, inventoryPacket, false); diff --git a/src/main/java/fr/xephi/authme/listener/protocollib/ProtocolLibService.java b/authme-core/src/main/java/fr/xephi/authme/listener/protocollib/ProtocolLibService.java similarity index 100% rename from src/main/java/fr/xephi/authme/listener/protocollib/ProtocolLibService.java rename to authme-core/src/main/java/fr/xephi/authme/listener/protocollib/ProtocolLibService.java diff --git a/src/main/java/fr/xephi/authme/listener/protocollib/TabCompletePacketAdapter.java b/authme-core/src/main/java/fr/xephi/authme/listener/protocollib/TabCompletePacketAdapter.java similarity index 100% rename from src/main/java/fr/xephi/authme/listener/protocollib/TabCompletePacketAdapter.java rename to authme-core/src/main/java/fr/xephi/authme/listener/protocollib/TabCompletePacketAdapter.java diff --git a/src/main/java/fr/xephi/authme/mail/EmailService.java b/authme-core/src/main/java/fr/xephi/authme/mail/EmailService.java similarity index 100% rename from src/main/java/fr/xephi/authme/mail/EmailService.java rename to authme-core/src/main/java/fr/xephi/authme/mail/EmailService.java diff --git a/src/main/java/fr/xephi/authme/mail/ImageGenerator.java b/authme-core/src/main/java/fr/xephi/authme/mail/ImageGenerator.java similarity index 100% rename from src/main/java/fr/xephi/authme/mail/ImageGenerator.java rename to authme-core/src/main/java/fr/xephi/authme/mail/ImageGenerator.java diff --git a/src/main/java/fr/xephi/authme/mail/OAuth2Provider.java b/authme-core/src/main/java/fr/xephi/authme/mail/OAuth2Provider.java similarity index 100% rename from src/main/java/fr/xephi/authme/mail/OAuth2Provider.java rename to authme-core/src/main/java/fr/xephi/authme/mail/OAuth2Provider.java diff --git a/src/main/java/fr/xephi/authme/mail/OAuth2SaslClient.java b/authme-core/src/main/java/fr/xephi/authme/mail/OAuth2SaslClient.java similarity index 100% rename from src/main/java/fr/xephi/authme/mail/OAuth2SaslClient.java rename to authme-core/src/main/java/fr/xephi/authme/mail/OAuth2SaslClient.java diff --git a/src/main/java/fr/xephi/authme/mail/OAuth2SaslClientFactory.java b/authme-core/src/main/java/fr/xephi/authme/mail/OAuth2SaslClientFactory.java similarity index 100% rename from src/main/java/fr/xephi/authme/mail/OAuth2SaslClientFactory.java rename to authme-core/src/main/java/fr/xephi/authme/mail/OAuth2SaslClientFactory.java diff --git a/src/main/java/fr/xephi/authme/mail/SendMailSsl.java b/authme-core/src/main/java/fr/xephi/authme/mail/SendMailSsl.java similarity index 100% rename from src/main/java/fr/xephi/authme/mail/SendMailSsl.java rename to authme-core/src/main/java/fr/xephi/authme/mail/SendMailSsl.java diff --git a/src/main/java/fr/xephi/authme/message/AbstractMessageFileHandler.java b/authme-core/src/main/java/fr/xephi/authme/message/AbstractMessageFileHandler.java similarity index 71% rename from src/main/java/fr/xephi/authme/message/AbstractMessageFileHandler.java rename to authme-core/src/main/java/fr/xephi/authme/message/AbstractMessageFileHandler.java index 5e6fc2210d..6f797e0f08 100644 --- a/src/main/java/fr/xephi/authme/message/AbstractMessageFileHandler.java +++ b/authme-core/src/main/java/fr/xephi/authme/message/AbstractMessageFileHandler.java @@ -14,6 +14,7 @@ import javax.annotation.PostConstruct; import javax.inject.Inject; import java.io.File; +import java.util.concurrent.ConcurrentHashMap; import static fr.xephi.authme.message.MessagePathHelper.DEFAULT_LANGUAGE; @@ -22,6 +23,8 @@ */ public abstract class AbstractMessageFileHandler implements Reloadable { + private static final FileConfiguration UNAVAILABLE = new YamlConfiguration(); + private final ConsoleLogger logger = ConsoleLoggerFactory.get(AbstractMessageFileHandler.class); @DataFolder @@ -34,6 +37,7 @@ public abstract class AbstractMessageFileHandler implements Reloadable { private String filename; private FileConfiguration configuration; private final String defaultFile; + private final ConcurrentHashMap languageCache = new ConcurrentHashMap<>(); protected AbstractMessageFileHandler() { this.defaultFile = createFilePath(DEFAULT_LANGUAGE); @@ -46,6 +50,7 @@ public void reload() { filename = createFilePath(language); File messagesFile = initializeFile(filename); configuration = YamlConfiguration.loadConfiguration(messagesFile); + languageCache.clear(); } protected String getLanguage() { @@ -94,6 +99,35 @@ public String getMessageIfExists(String key) { return configuration.getString(key); } + /** + * Returns the message for the given key in the requested language, falling back to the + * server-configured language if the requested language is unavailable or missing the key. + * + * @param key the key to retrieve the message for + * @param language the AuthMe language code (e.g. {@code "fr"}), or {@code null} to use the server default + * @return the message + */ + public String getMessage(String key, String language) { + if (language == null || language.equals(getLanguage())) { + return getMessage(key); + } + FileConfiguration config = languageCache.computeIfAbsent(language, this::loadLanguageConfiguration); + if (config == UNAVAILABLE) { + return getMessage(key); + } + String message = config.getString(key); + return message != null ? message : getMessage(key); + } + + private FileConfiguration loadLanguageConfiguration(String language) { + String filePath = createFilePath(language); + File file = new File(dataFolder, filePath); + if (!file.exists() && FileUtils.getResourceFromJar(filePath) != null) { + FileUtils.copyFileFromResource(file, filePath); + } + return file.exists() ? YamlConfiguration.loadConfiguration(file) : UNAVAILABLE; + } + /** * Creates the path to the messages file for the given language code. * diff --git a/src/main/java/fr/xephi/authme/message/HelpMessagesFileHandler.java b/authme-core/src/main/java/fr/xephi/authme/message/HelpMessagesFileHandler.java similarity index 100% rename from src/main/java/fr/xephi/authme/message/HelpMessagesFileHandler.java rename to authme-core/src/main/java/fr/xephi/authme/message/HelpMessagesFileHandler.java diff --git a/src/main/java/fr/xephi/authme/message/MessageKey.java b/authme-core/src/main/java/fr/xephi/authme/message/MessageKey.java similarity index 100% rename from src/main/java/fr/xephi/authme/message/MessageKey.java rename to authme-core/src/main/java/fr/xephi/authme/message/MessageKey.java diff --git a/src/main/java/fr/xephi/authme/message/MessagePathHelper.java b/authme-core/src/main/java/fr/xephi/authme/message/MessagePathHelper.java similarity index 100% rename from src/main/java/fr/xephi/authme/message/MessagePathHelper.java rename to authme-core/src/main/java/fr/xephi/authme/message/MessagePathHelper.java diff --git a/src/main/java/fr/xephi/authme/message/Messages.java b/authme-core/src/main/java/fr/xephi/authme/message/Messages.java similarity index 78% rename from src/main/java/fr/xephi/authme/message/Messages.java rename to authme-core/src/main/java/fr/xephi/authme/message/Messages.java index 2305de332a..82d25772d2 100644 --- a/src/main/java/fr/xephi/authme/message/Messages.java +++ b/authme-core/src/main/java/fr/xephi/authme/message/Messages.java @@ -4,6 +4,9 @@ import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.output.ConsoleLoggerFactory; import fr.xephi.authme.mail.EmailService; +import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.util.expiring.Duration; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; @@ -41,14 +44,22 @@ public class Messages { private final ConsoleLogger logger = ConsoleLoggerFactory.get(EmailService.class); - private MessagesFileHandler messagesFileHandler; + private final BukkitService bukkitService; + private final MessagesFileHandler messagesFileHandler; + private final Settings settings; /* * Constructor. */ @Inject - Messages(MessagesFileHandler messagesFileHandler) { + Messages(MessagesFileHandler messagesFileHandler, BukkitService bukkitService, Settings settings) { this.messagesFileHandler = messagesFileHandler; + this.bukkitService = bukkitService; + this.settings = settings; + } + + Messages(MessagesFileHandler messagesFileHandler) { + this(messagesFileHandler, null, null); } /** @@ -58,10 +69,12 @@ public class Messages { * @param key The key of the message to send */ public void send(CommandSender sender, MessageKey key) { - String[] lines = retrieve(key, sender); - for (String line : lines) { - sender.sendMessage(line); - } + runOnSenderThread(sender, () -> { + String[] lines = retrieve(key, sender); + for (String line : lines) { + sender.sendMessage(line); + } + }); } /** @@ -74,10 +87,12 @@ public void send(CommandSender sender, MessageKey key) { * @param replacements The replacements to apply for the tags */ public void send(CommandSender sender, MessageKey key, String... replacements) { - String message = retrieveSingle(sender, key, replacements); - for (String line : message.split("\n")) { - sender.sendMessage(line); - } + runOnSenderThread(sender, () -> { + String message = retrieveSingle(sender, key, replacements); + for (String line : message.split("\n")) { + sender.sendMessage(line); + } + }); } /** @@ -120,12 +135,21 @@ public String formatDuration(Duration duration) { * @return The message from the file */ private String retrieveMessage(MessageKey key, CommandSender sender) { - String message = messagesFileHandler.getMessage(key.getKey()); - String displayName = sender.getName(); - if (sender instanceof Player) { - displayName = ((Player) sender).getDisplayName(); + String message; + String displayName; + if (sender instanceof Player player) { + displayName = player.getDisplayName(); + if (settings != null && settings.getProperty(PluginSettings.PER_PLAYER_LOCALE)) { + String language = PlayerLocaleResolver.toLanguageCode(player.getLocale()); + message = messagesFileHandler.getMessage(key.getKey(), language); + } else { + message = messagesFileHandler.getMessage(key.getKey()); + } + } else { + message = messagesFileHandler.getMessage(key.getKey()); + displayName = sender.getName(); } - + return ChatColor.translateAlternateColorCodes('&', message) .replace(NEWLINE_TAG, "\n") .replace(USERNAME_TAG, sender.getName()) @@ -193,4 +217,12 @@ public String retrieveSingle(String name, MessageKey key, String... replacements } return message; } + + private void runOnSenderThread(CommandSender sender, Runnable action) { + if (sender instanceof Player player && bukkitService != null) { + bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(player, action); + } else { + action.run(); + } + } } diff --git a/src/main/java/fr/xephi/authme/message/MessagesFileHandler.java b/authme-core/src/main/java/fr/xephi/authme/message/MessagesFileHandler.java similarity index 100% rename from src/main/java/fr/xephi/authme/message/MessagesFileHandler.java rename to authme-core/src/main/java/fr/xephi/authme/message/MessagesFileHandler.java diff --git a/authme-core/src/main/java/fr/xephi/authme/message/PlayerLocaleResolver.java b/authme-core/src/main/java/fr/xephi/authme/message/PlayerLocaleResolver.java new file mode 100644 index 0000000000..91fc2f031b --- /dev/null +++ b/authme-core/src/main/java/fr/xephi/authme/message/PlayerLocaleResolver.java @@ -0,0 +1,42 @@ +package fr.xephi.authme.message; + +import java.util.Map; + +/** + * Maps Minecraft client locale strings (e.g. {@code "fr_fr"}) to AuthMe language codes (e.g. {@code "fr"}). + */ +public final class PlayerLocaleResolver { + + // Minecraft locales whose language code alone is not enough to pick the right AuthMe file + private static final Map LOCALE_OVERRIDES = Map.of( + "pt_br", "br", + "zh_cn", "zhcn", + "zh_tw", "zhtw", + "zh_hk", "zhhk" + ); + + private PlayerLocaleResolver() { + } + + /** + * Converts a Minecraft client locale string to an AuthMe language code. + *

+ * Returns {@code null} for blank input; the caller is expected to fall back to the + * server-configured language in that case. + * + * @param minecraftLocale the locale reported by {@link org.bukkit.entity.Player#getLocale()} (e.g. {@code "fr_fr"}) + * @return the matching AuthMe language code, or {@code null} if the input is blank + */ + public static String toLanguageCode(String minecraftLocale) { + if (minecraftLocale == null || minecraftLocale.isBlank()) { + return null; + } + String locale = minecraftLocale.toLowerCase(); + String override = LOCALE_OVERRIDES.get(locale); + if (override != null) { + return override; + } + int underscore = locale.indexOf('_'); + return underscore > 0 ? locale.substring(0, underscore) : locale; + } +} diff --git a/src/main/java/fr/xephi/authme/message/updater/JarMessageSource.java b/authme-core/src/main/java/fr/xephi/authme/message/updater/JarMessageSource.java similarity index 100% rename from src/main/java/fr/xephi/authme/message/updater/JarMessageSource.java rename to authme-core/src/main/java/fr/xephi/authme/message/updater/JarMessageSource.java diff --git a/src/main/java/fr/xephi/authme/message/updater/MessageKeyConfigurationData.java b/authme-core/src/main/java/fr/xephi/authme/message/updater/MessageKeyConfigurationData.java similarity index 100% rename from src/main/java/fr/xephi/authme/message/updater/MessageKeyConfigurationData.java rename to authme-core/src/main/java/fr/xephi/authme/message/updater/MessageKeyConfigurationData.java diff --git a/src/main/java/fr/xephi/authme/message/updater/MessageMigraterPropertyReader.java b/authme-core/src/main/java/fr/xephi/authme/message/updater/MessageMigraterPropertyReader.java similarity index 100% rename from src/main/java/fr/xephi/authme/message/updater/MessageMigraterPropertyReader.java rename to authme-core/src/main/java/fr/xephi/authme/message/updater/MessageMigraterPropertyReader.java diff --git a/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java b/authme-core/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java similarity index 100% rename from src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java rename to authme-core/src/main/java/fr/xephi/authme/message/updater/MessageUpdater.java diff --git a/src/main/java/fr/xephi/authme/message/updater/MigraterYamlFileResource.java b/authme-core/src/main/java/fr/xephi/authme/message/updater/MigraterYamlFileResource.java similarity index 100% rename from src/main/java/fr/xephi/authme/message/updater/MigraterYamlFileResource.java rename to authme-core/src/main/java/fr/xephi/authme/message/updater/MigraterYamlFileResource.java diff --git a/src/main/java/fr/xephi/authme/message/updater/OldMessageKeysMigrater.java b/authme-core/src/main/java/fr/xephi/authme/message/updater/OldMessageKeysMigrater.java similarity index 100% rename from src/main/java/fr/xephi/authme/message/updater/OldMessageKeysMigrater.java rename to authme-core/src/main/java/fr/xephi/authme/message/updater/OldMessageKeysMigrater.java diff --git a/src/main/java/fr/xephi/authme/output/ConsoleFilter.java b/authme-core/src/main/java/fr/xephi/authme/output/ConsoleFilter.java similarity index 100% rename from src/main/java/fr/xephi/authme/output/ConsoleFilter.java rename to authme-core/src/main/java/fr/xephi/authme/output/ConsoleFilter.java diff --git a/src/main/java/fr/xephi/authme/output/ConsoleLoggerFactory.java b/authme-core/src/main/java/fr/xephi/authme/output/ConsoleLoggerFactory.java similarity index 100% rename from src/main/java/fr/xephi/authme/output/ConsoleLoggerFactory.java rename to authme-core/src/main/java/fr/xephi/authme/output/ConsoleLoggerFactory.java diff --git a/src/main/java/fr/xephi/authme/output/Log4JFilter.java b/authme-core/src/main/java/fr/xephi/authme/output/Log4JFilter.java similarity index 100% rename from src/main/java/fr/xephi/authme/output/Log4JFilter.java rename to authme-core/src/main/java/fr/xephi/authme/output/Log4JFilter.java diff --git a/src/main/java/fr/xephi/authme/output/LogFilterHelper.java b/authme-core/src/main/java/fr/xephi/authme/output/LogFilterHelper.java similarity index 100% rename from src/main/java/fr/xephi/authme/output/LogFilterHelper.java rename to authme-core/src/main/java/fr/xephi/authme/output/LogFilterHelper.java diff --git a/src/main/java/fr/xephi/authme/output/LogLevel.java b/authme-core/src/main/java/fr/xephi/authme/output/LogLevel.java similarity index 100% rename from src/main/java/fr/xephi/authme/output/LogLevel.java rename to authme-core/src/main/java/fr/xephi/authme/output/LogLevel.java diff --git a/src/main/java/fr/xephi/authme/permission/AdminPermission.java b/authme-core/src/main/java/fr/xephi/authme/permission/AdminPermission.java similarity index 100% rename from src/main/java/fr/xephi/authme/permission/AdminPermission.java rename to authme-core/src/main/java/fr/xephi/authme/permission/AdminPermission.java diff --git a/src/main/java/fr/xephi/authme/permission/DebugSectionPermissions.java b/authme-core/src/main/java/fr/xephi/authme/permission/DebugSectionPermissions.java similarity index 100% rename from src/main/java/fr/xephi/authme/permission/DebugSectionPermissions.java rename to authme-core/src/main/java/fr/xephi/authme/permission/DebugSectionPermissions.java diff --git a/src/main/java/fr/xephi/authme/permission/DefaultPermission.java b/authme-core/src/main/java/fr/xephi/authme/permission/DefaultPermission.java similarity index 100% rename from src/main/java/fr/xephi/authme/permission/DefaultPermission.java rename to authme-core/src/main/java/fr/xephi/authme/permission/DefaultPermission.java diff --git a/src/main/java/fr/xephi/authme/permission/PermissionNode.java b/authme-core/src/main/java/fr/xephi/authme/permission/PermissionNode.java similarity index 100% rename from src/main/java/fr/xephi/authme/permission/PermissionNode.java rename to authme-core/src/main/java/fr/xephi/authme/permission/PermissionNode.java diff --git a/src/main/java/fr/xephi/authme/permission/PermissionsManager.java b/authme-core/src/main/java/fr/xephi/authme/permission/PermissionsManager.java similarity index 100% rename from src/main/java/fr/xephi/authme/permission/PermissionsManager.java rename to authme-core/src/main/java/fr/xephi/authme/permission/PermissionsManager.java diff --git a/src/main/java/fr/xephi/authme/permission/PermissionsSystemType.java b/authme-core/src/main/java/fr/xephi/authme/permission/PermissionsSystemType.java similarity index 100% rename from src/main/java/fr/xephi/authme/permission/PermissionsSystemType.java rename to authme-core/src/main/java/fr/xephi/authme/permission/PermissionsSystemType.java diff --git a/src/main/java/fr/xephi/authme/permission/PlayerPermission.java b/authme-core/src/main/java/fr/xephi/authme/permission/PlayerPermission.java similarity index 100% rename from src/main/java/fr/xephi/authme/permission/PlayerPermission.java rename to authme-core/src/main/java/fr/xephi/authme/permission/PlayerPermission.java diff --git a/src/main/java/fr/xephi/authme/permission/PlayerStatePermission.java b/authme-core/src/main/java/fr/xephi/authme/permission/PlayerStatePermission.java similarity index 100% rename from src/main/java/fr/xephi/authme/permission/PlayerStatePermission.java rename to authme-core/src/main/java/fr/xephi/authme/permission/PlayerStatePermission.java diff --git a/src/main/java/fr/xephi/authme/permission/handlers/LuckPermGroup.java b/authme-core/src/main/java/fr/xephi/authme/permission/handlers/LuckPermGroup.java similarity index 100% rename from src/main/java/fr/xephi/authme/permission/handlers/LuckPermGroup.java rename to authme-core/src/main/java/fr/xephi/authme/permission/handlers/LuckPermGroup.java diff --git a/src/main/java/fr/xephi/authme/permission/handlers/LuckPermsHandler.java b/authme-core/src/main/java/fr/xephi/authme/permission/handlers/LuckPermsHandler.java similarity index 100% rename from src/main/java/fr/xephi/authme/permission/handlers/LuckPermsHandler.java rename to authme-core/src/main/java/fr/xephi/authme/permission/handlers/LuckPermsHandler.java diff --git a/src/main/java/fr/xephi/authme/permission/handlers/PermissionHandler.java b/authme-core/src/main/java/fr/xephi/authme/permission/handlers/PermissionHandler.java similarity index 100% rename from src/main/java/fr/xephi/authme/permission/handlers/PermissionHandler.java rename to authme-core/src/main/java/fr/xephi/authme/permission/handlers/PermissionHandler.java diff --git a/src/main/java/fr/xephi/authme/permission/handlers/PermissionHandlerException.java b/authme-core/src/main/java/fr/xephi/authme/permission/handlers/PermissionHandlerException.java similarity index 100% rename from src/main/java/fr/xephi/authme/permission/handlers/PermissionHandlerException.java rename to authme-core/src/main/java/fr/xephi/authme/permission/handlers/PermissionHandlerException.java diff --git a/src/main/java/fr/xephi/authme/permission/handlers/PermissionLoadUserException.java b/authme-core/src/main/java/fr/xephi/authme/permission/handlers/PermissionLoadUserException.java similarity index 100% rename from src/main/java/fr/xephi/authme/permission/handlers/PermissionLoadUserException.java rename to authme-core/src/main/java/fr/xephi/authme/permission/handlers/PermissionLoadUserException.java diff --git a/src/main/java/fr/xephi/authme/permission/handlers/PermissionsExHandler.java b/authme-core/src/main/java/fr/xephi/authme/permission/handlers/PermissionsExHandler.java similarity index 100% rename from src/main/java/fr/xephi/authme/permission/handlers/PermissionsExHandler.java rename to authme-core/src/main/java/fr/xephi/authme/permission/handlers/PermissionsExHandler.java diff --git a/src/main/java/fr/xephi/authme/permission/handlers/VaultHandler.java b/authme-core/src/main/java/fr/xephi/authme/permission/handlers/VaultHandler.java similarity index 100% rename from src/main/java/fr/xephi/authme/permission/handlers/VaultHandler.java rename to authme-core/src/main/java/fr/xephi/authme/permission/handlers/VaultHandler.java diff --git a/src/main/java/fr/xephi/authme/permission/handlers/ZPermissionsHandler.java b/authme-core/src/main/java/fr/xephi/authme/permission/handlers/ZPermissionsHandler.java similarity index 100% rename from src/main/java/fr/xephi/authme/permission/handlers/ZPermissionsHandler.java rename to authme-core/src/main/java/fr/xephi/authme/permission/handlers/ZPermissionsHandler.java diff --git a/authme-core/src/main/java/fr/xephi/authme/platform/AbstractSpigotPlatformAdapter.java b/authme-core/src/main/java/fr/xephi/authme/platform/AbstractSpigotPlatformAdapter.java new file mode 100644 index 0000000000..209411871a --- /dev/null +++ b/authme-core/src/main/java/fr/xephi/authme/platform/AbstractSpigotPlatformAdapter.java @@ -0,0 +1,83 @@ +package fr.xephi.authme.platform; + +import fr.xephi.authme.AuthMe; +import fr.xephi.authme.service.CancellableTask; +import fr.xephi.authme.util.Utils; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; + +/** + * Base implementation of {@link PlatformAdapter} for all Spigot versions. + * Uses synchronous (blocking) teleport via the Bukkit API. + */ +public abstract class AbstractSpigotPlatformAdapter implements PlatformAdapter { + + @Override + public void teleportPlayer(Player player, Location location) { + player.teleport(location); + } + + @Override + public Location getPlayerRespawnLocation(Player player) { + return player.getBedSpawnLocation(); + } + + @Override + public boolean isOwnedByCurrentThread(Entity entity) { + return Bukkit.isPrimaryThread(); + } + + @Override + public boolean isGlobalThread() { + return Bukkit.isPrimaryThread(); + } + + @Override + public void runOnEntityThread(AuthMe plugin, Entity entity, Runnable task) { + Bukkit.getScheduler().runTask(plugin, task); + } + + @Override + public CancellableTask runDelayedOnEntityThread(AuthMe plugin, Entity entity, Runnable task, long delay) { + return wrapTask(Bukkit.getScheduler().runTaskLater(plugin, task, delay)); + } + + @Override + public CancellableTask runAtFixedRateOnEntityThread(AuthMe plugin, Entity entity, Runnable task, + long delay, long period) { + BukkitRunnable bukkitRunnable = new BukkitRunnable() { + @Override + public void run() { + task.run(); + } + }; + return wrapTask(bukkitRunnable.runTaskTimer(plugin, delay, period)); + } + + @Override + public void runOnGlobalThread(AuthMe plugin, Runnable task) { + Bukkit.getScheduler().runTask(plugin, task); + } + + @Override + public CancellableTask runDelayedOnGlobalThread(AuthMe plugin, Runnable task, long delay) { + return wrapTask(Bukkit.getScheduler().runTaskLater(plugin, task, delay)); + } + + protected final String getCompatibilityError(String errorMessage, String... requiredClasses) { + for (String className : requiredClasses) { + if (!Utils.isClassLoaded(className)) { + return errorMessage; + } + } + return null; + } + + private static CancellableTask wrapTask(BukkitTask task) { + return task::cancel; + } +} diff --git a/authme-core/src/main/java/fr/xephi/authme/platform/ChatAdapter.java b/authme-core/src/main/java/fr/xephi/authme/platform/ChatAdapter.java new file mode 100644 index 0000000000..5510afaf43 --- /dev/null +++ b/authme-core/src/main/java/fr/xephi/authme/platform/ChatAdapter.java @@ -0,0 +1,14 @@ +package fr.xephi.authme.platform; + +import org.bukkit.event.player.PlayerKickEvent; + +/** + * Platform-specific chat and kick behavior. + * Paper 1.21+ overrides {@link #getKickReason} to use the Adventure API. + */ +public interface ChatAdapter { + + default String getKickReason(PlayerKickEvent event) { + return event.getReason(); + } +} diff --git a/authme-core/src/main/java/fr/xephi/authme/platform/CommandRegistrationAdapter.java b/authme-core/src/main/java/fr/xephi/authme/platform/CommandRegistrationAdapter.java new file mode 100644 index 0000000000..49d80d251a --- /dev/null +++ b/authme-core/src/main/java/fr/xephi/authme/platform/CommandRegistrationAdapter.java @@ -0,0 +1,25 @@ +package fr.xephi.authme.platform; + +import fr.xephi.authme.AuthMe; +import fr.xephi.authme.command.CommandDescription; +import fr.xephi.authme.command.CommandHandler; + +import java.util.Collection; + +/** + * Allows a platform module to register commands using platform-specific APIs. + */ +public interface CommandRegistrationAdapter { + + /** + * Registers commands for the current platform. + * + * @param plugin the plugin instance + * @param commandHandler the shared AuthMe command handler + * @param commands the base command descriptions + */ + default void registerCommands(AuthMe plugin, CommandHandler commandHandler, + Collection commands) { + // Default no-op: platforms without a dedicated command API continue using plugin.yml + onCommand. + } +} diff --git a/authme-core/src/main/java/fr/xephi/authme/platform/DialogAdapter.java b/authme-core/src/main/java/fr/xephi/authme/platform/DialogAdapter.java new file mode 100644 index 0000000000..f673d8f5eb --- /dev/null +++ b/authme-core/src/main/java/fr/xephi/authme/platform/DialogAdapter.java @@ -0,0 +1,39 @@ +package fr.xephi.authme.platform; + +import fr.xephi.authme.process.register.RegisterSecondaryArgument; +import fr.xephi.authme.process.register.RegistrationType; +import org.bukkit.entity.Player; + +/** + * Platform adapter for showing graphical dialog UIs to players on supported platforms (1.21.6+). + * The default implementation is a no-op (dialog not supported). + */ +public interface DialogAdapter { + + /** + * Returns whether this platform supports showing dialog UIs. + * + * @return true if dialog UI is supported, false otherwise + */ + default boolean isDialogSupported() { + return false; + } + + /** + * Shows the login dialog to the given player. + * + * @param player the player to show the dialog to + */ + default void showLoginDialog(Player player) { + } + + /** + * Shows the register dialog to the given player. + * + * @param player the player to show the dialog to + * @param type the registration type (password or email) + * @param secondArg the secondary argument type for registration + */ + default void showRegisterDialog(Player player, RegistrationType type, RegisterSecondaryArgument secondArg) { + } +} diff --git a/authme-core/src/main/java/fr/xephi/authme/platform/EventRegistrationAdapter.java b/authme-core/src/main/java/fr/xephi/authme/platform/EventRegistrationAdapter.java new file mode 100644 index 0000000000..782bfa6b6d --- /dev/null +++ b/authme-core/src/main/java/fr/xephi/authme/platform/EventRegistrationAdapter.java @@ -0,0 +1,16 @@ +package fr.xephi.authme.platform; + +import org.bukkit.event.Listener; + +import java.util.Collections; +import java.util.List; + +/** + * Supplies version-specific listeners to register at startup, in addition to the core set. + */ +public interface EventRegistrationAdapter { + + default List> getAdditionalListeners() { + return Collections.emptyList(); + } +} diff --git a/authme-core/src/main/java/fr/xephi/authme/platform/PlatformAdapter.java b/authme-core/src/main/java/fr/xephi/authme/platform/PlatformAdapter.java new file mode 100644 index 0000000000..4b44810063 --- /dev/null +++ b/authme-core/src/main/java/fr/xephi/authme/platform/PlatformAdapter.java @@ -0,0 +1,47 @@ +package fr.xephi.authme.platform; + +/** + * Combines all platform-specific behavior. Implementations are provided by each version + * module via ServiceLoader and registered as {@link TeleportAdapter}, {@link ChatAdapter}, + * {@link EventRegistrationAdapter}, {@link SchedulingAdapter}, and + * {@link CommandRegistrationAdapter} in the DI container. + */ +public interface PlatformAdapter extends TeleportAdapter, ChatAdapter, EventRegistrationAdapter, + DialogAdapter, CommandRegistrationAdapter, SchedulingAdapter { + + /** + * Returns a short identifier for logging, e.g. "spigot-legacy", "spigot-1.20", "paper-1.21". + */ + String getPlatformName(); + + /** + * Returns an error message if this platform adapter does not support the current server, + * or null if the server is compatible. + * + * @return the compatibility error message, or null if compatible + */ + default String getCompatibilityError() { + return null; + } + + /** + * Returns whether the core {@link org.bukkit.event.player.PlayerLoginEvent} listener should be active. + * Paper-derived platforms disable it in favor of newer, split login validation events. + * + * @return true if the legacy PlayerLoginEvent listener should run + */ + default boolean shouldHandlePlayerLoginEvent() { + return true; + } + + /** + * Returns whether the core Spigot {@link org.spigotmc.event.player.PlayerSpawnLocationEvent} + * listener should be active. Paper-derived platforms disable it in favor of their async + * spawn-location event. + * + * @return true if the legacy PlayerSpawnLocationEvent listener should run + */ + default boolean shouldHandlePlayerSpawnLocationEvent() { + return true; + } +} diff --git a/authme-core/src/main/java/fr/xephi/authme/platform/SchedulingAdapter.java b/authme-core/src/main/java/fr/xephi/authme/platform/SchedulingAdapter.java new file mode 100644 index 0000000000..8bf54a5a5c --- /dev/null +++ b/authme-core/src/main/java/fr/xephi/authme/platform/SchedulingAdapter.java @@ -0,0 +1,74 @@ +package fr.xephi.authme.platform; + +import fr.xephi.authme.AuthMe; +import fr.xephi.authme.service.CancellableTask; +import org.bukkit.entity.Entity; + +/** + * Platform-specific task scheduling bridge used by {@link fr.xephi.authme.service.BukkitService}. + * Implementations map AuthMe's global and entity-owned task concepts to the appropriate + * scheduler model of the running server platform. + */ +public interface SchedulingAdapter { + + /** + * @param entity the entity that would own the task + * @return true if the current thread may already safely interact with the entity + */ + boolean isOwnedByCurrentThread(Entity entity); + + /** + * @return true if the current thread may already safely perform global server work + */ + boolean isGlobalThread(); + + /** + * Runs a task on the owning thread of the given entity. + * + * @param plugin the plugin scheduling the task + * @param entity the entity that owns the execution context + * @param task the task to run + */ + void runOnEntityThread(AuthMe plugin, Entity entity, Runnable task); + + /** + * Runs a delayed task on the owning thread of the given entity. + * + * @param plugin the plugin scheduling the task + * @param entity the entity that owns the execution context + * @param task the task to run + * @param delay the delay in ticks + * @return the scheduled task handle + */ + CancellableTask runDelayedOnEntityThread(AuthMe plugin, Entity entity, Runnable task, long delay); + + /** + * Runs a repeating task on the owning thread of the given entity. + * + * @param plugin the plugin scheduling the task + * @param entity the entity that owns the execution context + * @param task the task to run + * @param delay the initial delay in ticks + * @param period the repeat period in ticks + * @return the scheduled task handle + */ + CancellableTask runAtFixedRateOnEntityThread(AuthMe plugin, Entity entity, Runnable task, long delay, long period); + + /** + * Runs a task on the platform's global execution context. + * + * @param plugin the plugin scheduling the task + * @param task the task to run + */ + void runOnGlobalThread(AuthMe plugin, Runnable task); + + /** + * Runs a delayed task on the platform's global execution context. + * + * @param plugin the plugin scheduling the task + * @param task the task to run + * @param delay the delay in ticks + * @return the scheduled task handle + */ + CancellableTask runDelayedOnGlobalThread(AuthMe plugin, Runnable task, long delay); +} diff --git a/authme-core/src/main/java/fr/xephi/authme/platform/TeleportAdapter.java b/authme-core/src/main/java/fr/xephi/authme/platform/TeleportAdapter.java new file mode 100644 index 0000000000..b72f9046f2 --- /dev/null +++ b/authme-core/src/main/java/fr/xephi/authme/platform/TeleportAdapter.java @@ -0,0 +1,24 @@ +package fr.xephi.authme.platform; + +import org.bukkit.Location; +import org.bukkit.entity.Player; + +/** + * Platform-specific teleportation behavior. + * May be asynchronous on platforms that support it (e.g. Paper). + */ +public interface TeleportAdapter { + + void teleportPlayer(Player player, Location location); + + /** + * Returns the platform-appropriate respawn location for the player, or null if there is no valid respawn point. + *

+ * Legacy platforms only expose bed spawn directly, while newer APIs can provide the server's full respawn target + * (bed, respawn anchor, world spawn logic, etc.). + * + * @param player the player + * @return the respawn location, or null if none is available + */ + Location getPlayerRespawnLocation(Player player); +} diff --git a/src/main/java/fr/xephi/authme/process/AsynchronousProcess.java b/authme-core/src/main/java/fr/xephi/authme/process/AsynchronousProcess.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/AsynchronousProcess.java rename to authme-core/src/main/java/fr/xephi/authme/process/AsynchronousProcess.java diff --git a/src/main/java/fr/xephi/authme/process/Management.java b/authme-core/src/main/java/fr/xephi/authme/process/Management.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/Management.java rename to authme-core/src/main/java/fr/xephi/authme/process/Management.java diff --git a/src/main/java/fr/xephi/authme/process/SyncProcessManager.java b/authme-core/src/main/java/fr/xephi/authme/process/SyncProcessManager.java similarity index 74% rename from src/main/java/fr/xephi/authme/process/SyncProcessManager.java rename to authme-core/src/main/java/fr/xephi/authme/process/SyncProcessManager.java index 0fdfdde3de..47a1cbf502 100644 --- a/src/main/java/fr/xephi/authme/process/SyncProcessManager.java +++ b/authme-core/src/main/java/fr/xephi/authme/process/SyncProcessManager.java @@ -37,26 +37,26 @@ public class SyncProcessManager { public void processSyncEmailRegister(Player player) { - runTask(() -> processSyncEmailRegister.processEmailRegister(player)); + runTask(player, () -> processSyncEmailRegister.processEmailRegister(player)); } public void processSyncPasswordRegister(Player player) { - runTask(() -> processSyncPasswordRegister.processPasswordRegister(player)); + runTask(player, () -> processSyncPasswordRegister.processPasswordRegister(player)); } public void processSyncPlayerLogout(Player player) { - runTask(() -> processSyncPlayerLogout.processSyncLogout(player)); + runTask(player, () -> processSyncPlayerLogout.processSyncLogout(player)); } public void processSyncPlayerLogin(Player player, boolean isFirstLogin, List authsWithSameIp) { - runTask(() -> processSyncPlayerLogin.processPlayerLogin(player, isFirstLogin, authsWithSameIp)); + runTask(player, () -> processSyncPlayerLogin.processPlayerLogin(player, isFirstLogin, authsWithSameIp)); } public void processSyncPlayerQuit(Player player, boolean wasLoggedIn) { - runTask(() -> processSyncPlayerQuit.processSyncQuit(player, wasLoggedIn)); + runTask(player, () -> processSyncPlayerQuit.processSyncQuit(player, wasLoggedIn)); } - private void runTask(Runnable runnable) { - bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(runnable); + private void runTask(Player player, Runnable runnable) { + bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(player, runnable); } } diff --git a/src/main/java/fr/xephi/authme/process/SynchronousProcess.java b/authme-core/src/main/java/fr/xephi/authme/process/SynchronousProcess.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/SynchronousProcess.java rename to authme-core/src/main/java/fr/xephi/authme/process/SynchronousProcess.java diff --git a/src/main/java/fr/xephi/authme/process/changepassword/AsyncChangePassword.java b/authme-core/src/main/java/fr/xephi/authme/process/changepassword/AsyncChangePassword.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/changepassword/AsyncChangePassword.java rename to authme-core/src/main/java/fr/xephi/authme/process/changepassword/AsyncChangePassword.java diff --git a/src/main/java/fr/xephi/authme/process/email/AsyncAddEmail.java b/authme-core/src/main/java/fr/xephi/authme/process/email/AsyncAddEmail.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/email/AsyncAddEmail.java rename to authme-core/src/main/java/fr/xephi/authme/process/email/AsyncAddEmail.java diff --git a/src/main/java/fr/xephi/authme/process/email/AsyncChangeEmail.java b/authme-core/src/main/java/fr/xephi/authme/process/email/AsyncChangeEmail.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/email/AsyncChangeEmail.java rename to authme-core/src/main/java/fr/xephi/authme/process/email/AsyncChangeEmail.java diff --git a/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java b/authme-core/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java similarity index 84% rename from src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java rename to authme-core/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java index d60ce4e831..ef45bdb122 100644 --- a/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java +++ b/authme-core/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java @@ -8,8 +8,11 @@ import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.output.ConsoleLoggerFactory; import fr.xephi.authme.permission.PlayerStatePermission; +import fr.xephi.authme.platform.DialogAdapter; import fr.xephi.authme.process.AsynchronousProcess; import fr.xephi.authme.process.login.AsynchronousLogin; +import fr.xephi.authme.process.register.RegisterSecondaryArgument; +import fr.xephi.authme.process.register.RegistrationType; import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.PluginHookService; @@ -80,6 +83,9 @@ public class AsynchronousJoin implements AsynchronousProcess { @Inject private ProxySessionManager proxySessionManager; + @Inject + private DialogAdapter dialogAdapter; + AsynchronousJoin() { } @@ -104,7 +110,7 @@ public void processJoin(Player player) { if (service.getProperty(RestrictionSettings.FORCE_SURVIVAL_MODE) && player.getGameMode() != GameMode.SURVIVAL && !service.hasPermission(player, PlayerStatePermission.BYPASS_FORCE_SURVIVAL)) { - bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> player.setGameMode(GameMode.SURVIVAL)); + bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(player, () -> player.setGameMode(GameMode.SURVIVAL)); } if (service.getProperty(HooksSettings.DISABLE_SOCIAL_SPY)) { @@ -123,7 +129,7 @@ public void processJoin(Player player) { ProtectInventoryEvent ev = bukkitService.createAndCallEvent( isAsync -> new ProtectInventoryEvent(player, isAsync)); if (ev.isCancelled()) { - player.updateInventory(); + bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(player, player::updateInventory); logger.fine("ProtectInventoryEvent has been cancelled for " + player.getName() + "..."); } } @@ -132,14 +138,14 @@ public void processJoin(Player player) { if (sessionService.canResumeSession(player)) { service.send(player, MessageKey.SESSION_RECONNECTION); // Run commands - bukkitService.scheduleSyncTaskFromOptionallyAsyncTask( + bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(player, () -> commandManager.runCommandsOnSessionLogin(player)); bukkitService.runTaskOptionallyAsync(() -> asynchronousLogin.forceLogin(player)); return; } else if (proxySessionManager.shouldResumeSession(name)) { service.send(player, MessageKey.SESSION_RECONNECTION); // Run commands - bukkitService.scheduleSyncTaskFromOptionallyAsyncTask( + bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(player, () -> commandManager.runCommandsOnSessionLogin(player)); bukkitService.runTaskOptionallyAsync(() -> asynchronousLogin.forceLogin(player)); logger.info("The user " + player.getName() + " has been automatically logged in, " @@ -147,7 +153,7 @@ public void processJoin(Player player) { return; } } else if (!service.getProperty(RegistrationSettings.FORCE)) { - bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> { + bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(player, () -> { welcomeMessageConfiguration.sendWelcomeMessage(player); }); @@ -156,7 +162,7 @@ public void processJoin(Player player) { if (bungeeSender.isEnabled()) { // As described at https://www.spigotmc.org/wiki/bukkit-bungee-plugin-messaging-channel/ // "Keep in mind that you can't send plugin messages directly after a player joins." - bukkitService.scheduleSyncDelayedTask(() -> + bukkitService.scheduleSyncDelayedTask(player, () -> bungeeSender.sendAuthMeBungeecordMessage(player, MessageType.LOGIN), 5L); } return; @@ -166,12 +172,11 @@ public void processJoin(Player player) { } private void handlePlayerWithUnmetNameRestriction(Player player, String ip) { - bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> { - player.kickPlayer(service.retrieveSingleMessage(player, MessageKey.NOT_OWNER_ERROR)); - if (service.getProperty(RestrictionSettings.BAN_UNKNOWN_IP)) { - server.banIP(ip); - } - }); + bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(player, + () -> player.kickPlayer(service.retrieveSingleMessage(player, MessageKey.NOT_OWNER_ERROR))); + if (service.getProperty(RestrictionSettings.BAN_UNKNOWN_IP)) { + bukkitService.runOnGlobalRegion(() -> server.banIP(ip)); + } } /** @@ -182,9 +187,11 @@ private void handlePlayerWithUnmetNameRestriction(Player player, String ip) { * @param isAuthAvailable true if the player is registered, false otherwise */ private void processJoinSync(Player player, boolean isAuthAvailable) { - int registrationTimeout = service.getProperty(RestrictionSettings.TIMEOUT) * TICKS_PER_SECOND; + int registrationTimeout = service.getProperty( + isAuthAvailable ? RestrictionSettings.LOGIN_TIMEOUT : RestrictionSettings.REGISTER_TIMEOUT + ) * TICKS_PER_SECOND; - bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> { + bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(player, () -> { limboService.createLimboPlayer(player, isAuthAvailable); player.setNoDamageTicks(registrationTimeout); @@ -197,6 +204,15 @@ private void processJoinSync(Player player, boolean isAuthAvailable) { player.addPotionEffect(bukkitService.createBlindnessEffect(blindTimeOut)); } commandManager.runCommandsOnJoin(player); + if (service.getProperty(RegistrationSettings.USE_DIALOG_UI) && dialogAdapter.isDialogSupported()) { + if (isAuthAvailable) { + dialogAdapter.showLoginDialog(player); + } else { + dialogAdapter.showRegisterDialog(player, + service.getProperty(RegistrationSettings.REGISTRATION_TYPE), + service.getProperty(RegistrationSettings.REGISTER_SECOND_ARGUMENT)); + } + } }); } @@ -215,7 +231,7 @@ private boolean validatePlayerCountForIp(Player player, String ip) { && !InternetProtocolUtils.isLoopbackAddress(ip) && countOnlinePlayersByIp(ip) > service.getProperty(RestrictionSettings.MAX_JOIN_PER_IP)) { - bukkitService.scheduleSyncTaskFromOptionallyAsyncTask( + bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(player, () -> player.kickPlayer(service.retrieveSingleMessage(player, MessageKey.SAME_IP_ONLINE))); return false; } diff --git a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java b/authme-core/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java similarity index 99% rename from src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java rename to authme-core/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java index 3e9f5fd74c..8fca24a505 100644 --- a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java +++ b/authme-core/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java @@ -243,7 +243,7 @@ private void handleWrongPassword(Player player, PlayerAuth auth, String ip) { if (tempbanManager.shouldTempban(ip)) { tempbanManager.tempbanPlayer(player); } else if (service.getProperty(RestrictionSettings.KICK_ON_WRONG_PASSWORD)) { - bukkitService.scheduleSyncTaskFromOptionallyAsyncTask( + bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(player, () -> player.kickPlayer(service.retrieveSingleMessage(player, MessageKey.WRONG_PASSWORD))); } else { service.send(player, MessageKey.WRONG_PASSWORD); @@ -304,7 +304,7 @@ public void performLogin(Player player, PlayerAuth auth) { if (bungeeSender.isEnabled()) { // As described at https://www.spigotmc.org/wiki/bukkit-bungee-plugin-messaging-channel/ // "Keep in mind that you can't send plugin messages directly after a player joins." - bukkitService.scheduleSyncDelayedTask(() -> + bukkitService.scheduleSyncDelayedTask(player, () -> bungeeSender.sendAuthMeBungeecordMessage(player, MessageType.LOGIN), 5L); } diff --git a/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java b/authme-core/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java similarity index 98% rename from src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java rename to authme-core/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java index 73b142be92..a7bd09d1c7 100644 --- a/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java +++ b/authme-core/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java @@ -99,6 +99,7 @@ public void processPlayerLogin(Player player, boolean isFirstLogin, List } teleportationService.teleportOnLogin(player, auth, limbo); + teleportationService.clearOriginalJoinLocation(name); // We can now display the join message (if delayed) joinMessageService.sendMessage(name); diff --git a/src/main/java/fr/xephi/authme/process/logout/AsynchronousLogout.java b/authme-core/src/main/java/fr/xephi/authme/process/logout/AsynchronousLogout.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/logout/AsynchronousLogout.java rename to authme-core/src/main/java/fr/xephi/authme/process/logout/AsynchronousLogout.java diff --git a/src/main/java/fr/xephi/authme/process/logout/ProcessSyncPlayerLogout.java b/authme-core/src/main/java/fr/xephi/authme/process/logout/ProcessSyncPlayerLogout.java similarity index 98% rename from src/main/java/fr/xephi/authme/process/logout/ProcessSyncPlayerLogout.java rename to authme-core/src/main/java/fr/xephi/authme/process/logout/ProcessSyncPlayerLogout.java index 96865e21c3..680be22021 100644 --- a/src/main/java/fr/xephi/authme/process/logout/ProcessSyncPlayerLogout.java +++ b/authme-core/src/main/java/fr/xephi/authme/process/logout/ProcessSyncPlayerLogout.java @@ -72,7 +72,7 @@ private void applyLogoutEffect(Player player) { // Apply Blindness effect if (service.getProperty(RegistrationSettings.APPLY_BLIND_EFFECT)) { - int timeout = service.getProperty(RestrictionSettings.TIMEOUT) * TICKS_PER_SECOND; + int timeout = service.getProperty(RestrictionSettings.LOGIN_TIMEOUT) * TICKS_PER_SECOND; player.addPotionEffect(bukkitService.createBlindnessEffect(timeout)); } diff --git a/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java b/authme-core/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java similarity index 94% rename from src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java rename to authme-core/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java index f4b1d3250d..8022ed0318 100644 --- a/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java +++ b/authme-core/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java @@ -9,6 +9,7 @@ import fr.xephi.authme.process.SyncProcessManager; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.SessionService; +import fr.xephi.authme.service.TeleportationService; import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.settings.SpawnLoader; import fr.xephi.authme.settings.properties.PluginSettings; @@ -52,6 +53,9 @@ public class AsynchronousQuit implements AsynchronousProcess { @Inject private SessionService sessionService; + @Inject + private TeleportationService teleportationService; + AsynchronousQuit() { } @@ -65,6 +69,7 @@ public void processQuit(Player player) { return; } String name = player.getName().toLowerCase(Locale.ROOT); + teleportationService.clearOriginalJoinLocation(name); boolean wasLoggedIn = playerCache.isAuthenticated(name); if (wasLoggedIn) { diff --git a/src/main/java/fr/xephi/authme/process/quit/ProcessSyncPlayerQuit.java b/authme-core/src/main/java/fr/xephi/authme/process/quit/ProcessSyncPlayerQuit.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/quit/ProcessSyncPlayerQuit.java rename to authme-core/src/main/java/fr/xephi/authme/process/quit/ProcessSyncPlayerQuit.java diff --git a/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java b/authme-core/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/register/AsyncRegister.java rename to authme-core/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java diff --git a/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java b/authme-core/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java rename to authme-core/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java diff --git a/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java b/authme-core/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java rename to authme-core/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java diff --git a/src/main/java/fr/xephi/authme/process/register/RegisterSecondaryArgument.java b/authme-core/src/main/java/fr/xephi/authme/process/register/RegisterSecondaryArgument.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/register/RegisterSecondaryArgument.java rename to authme-core/src/main/java/fr/xephi/authme/process/register/RegisterSecondaryArgument.java diff --git a/src/main/java/fr/xephi/authme/process/register/RegistrationType.java b/authme-core/src/main/java/fr/xephi/authme/process/register/RegistrationType.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/register/RegistrationType.java rename to authme-core/src/main/java/fr/xephi/authme/process/register/RegistrationType.java diff --git a/src/main/java/fr/xephi/authme/process/register/executors/AbstractPasswordRegisterExecutor.java b/authme-core/src/main/java/fr/xephi/authme/process/register/executors/AbstractPasswordRegisterExecutor.java similarity index 96% rename from src/main/java/fr/xephi/authme/process/register/executors/AbstractPasswordRegisterExecutor.java rename to authme-core/src/main/java/fr/xephi/authme/process/register/executors/AbstractPasswordRegisterExecutor.java index 179aa59df8..0913558fb0 100644 --- a/src/main/java/fr/xephi/authme/process/register/executors/AbstractPasswordRegisterExecutor.java +++ b/authme-core/src/main/java/fr/xephi/authme/process/register/executors/AbstractPasswordRegisterExecutor.java @@ -91,7 +91,8 @@ public void executePostPersistAction(P params) { if (commonService.getProperty(PluginSettings.USE_ASYNC_TASKS)) { bukkitService.runTaskAsynchronously(() -> asynchronousLogin.forceLogin(player)); } else { - bukkitService.scheduleSyncDelayedTask(() -> asynchronousLogin.forceLogin(player), SYNC_LOGIN_DELAY); + bukkitService.scheduleSyncDelayedTask(player, + () -> asynchronousLogin.forceLogin(player), SYNC_LOGIN_DELAY); } } syncProcessManager.processSyncPasswordRegister(player); diff --git a/src/main/java/fr/xephi/authme/process/register/executors/AbstractPasswordRegisterParams.java b/authme-core/src/main/java/fr/xephi/authme/process/register/executors/AbstractPasswordRegisterParams.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/register/executors/AbstractPasswordRegisterParams.java rename to authme-core/src/main/java/fr/xephi/authme/process/register/executors/AbstractPasswordRegisterParams.java diff --git a/src/main/java/fr/xephi/authme/process/register/executors/ApiPasswordRegisterExecutor.java b/authme-core/src/main/java/fr/xephi/authme/process/register/executors/ApiPasswordRegisterExecutor.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/register/executors/ApiPasswordRegisterExecutor.java rename to authme-core/src/main/java/fr/xephi/authme/process/register/executors/ApiPasswordRegisterExecutor.java diff --git a/src/main/java/fr/xephi/authme/process/register/executors/ApiPasswordRegisterParams.java b/authme-core/src/main/java/fr/xephi/authme/process/register/executors/ApiPasswordRegisterParams.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/register/executors/ApiPasswordRegisterParams.java rename to authme-core/src/main/java/fr/xephi/authme/process/register/executors/ApiPasswordRegisterParams.java diff --git a/src/main/java/fr/xephi/authme/process/register/executors/EmailRegisterExecutor.java b/authme-core/src/main/java/fr/xephi/authme/process/register/executors/EmailRegisterExecutor.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/register/executors/EmailRegisterExecutor.java rename to authme-core/src/main/java/fr/xephi/authme/process/register/executors/EmailRegisterExecutor.java diff --git a/src/main/java/fr/xephi/authme/process/register/executors/EmailRegisterParams.java b/authme-core/src/main/java/fr/xephi/authme/process/register/executors/EmailRegisterParams.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/register/executors/EmailRegisterParams.java rename to authme-core/src/main/java/fr/xephi/authme/process/register/executors/EmailRegisterParams.java diff --git a/src/main/java/fr/xephi/authme/process/register/executors/PasswordRegisterExecutor.java b/authme-core/src/main/java/fr/xephi/authme/process/register/executors/PasswordRegisterExecutor.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/register/executors/PasswordRegisterExecutor.java rename to authme-core/src/main/java/fr/xephi/authme/process/register/executors/PasswordRegisterExecutor.java diff --git a/src/main/java/fr/xephi/authme/process/register/executors/PasswordRegisterParams.java b/authme-core/src/main/java/fr/xephi/authme/process/register/executors/PasswordRegisterParams.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/register/executors/PasswordRegisterParams.java rename to authme-core/src/main/java/fr/xephi/authme/process/register/executors/PasswordRegisterParams.java diff --git a/src/main/java/fr/xephi/authme/process/register/executors/PlayerAuthBuilderHelper.java b/authme-core/src/main/java/fr/xephi/authme/process/register/executors/PlayerAuthBuilderHelper.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/register/executors/PlayerAuthBuilderHelper.java rename to authme-core/src/main/java/fr/xephi/authme/process/register/executors/PlayerAuthBuilderHelper.java diff --git a/src/main/java/fr/xephi/authme/process/register/executors/RegistrationExecutor.java b/authme-core/src/main/java/fr/xephi/authme/process/register/executors/RegistrationExecutor.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/register/executors/RegistrationExecutor.java rename to authme-core/src/main/java/fr/xephi/authme/process/register/executors/RegistrationExecutor.java diff --git a/src/main/java/fr/xephi/authme/process/register/executors/RegistrationMethod.java b/authme-core/src/main/java/fr/xephi/authme/process/register/executors/RegistrationMethod.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/register/executors/RegistrationMethod.java rename to authme-core/src/main/java/fr/xephi/authme/process/register/executors/RegistrationMethod.java diff --git a/src/main/java/fr/xephi/authme/process/register/executors/RegistrationParameters.java b/authme-core/src/main/java/fr/xephi/authme/process/register/executors/RegistrationParameters.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/register/executors/RegistrationParameters.java rename to authme-core/src/main/java/fr/xephi/authme/process/register/executors/RegistrationParameters.java diff --git a/src/main/java/fr/xephi/authme/process/register/executors/TwoFactorRegisterExecutor.java b/authme-core/src/main/java/fr/xephi/authme/process/register/executors/TwoFactorRegisterExecutor.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/register/executors/TwoFactorRegisterExecutor.java rename to authme-core/src/main/java/fr/xephi/authme/process/register/executors/TwoFactorRegisterExecutor.java diff --git a/src/main/java/fr/xephi/authme/process/register/executors/TwoFactorRegisterParams.java b/authme-core/src/main/java/fr/xephi/authme/process/register/executors/TwoFactorRegisterParams.java similarity index 100% rename from src/main/java/fr/xephi/authme/process/register/executors/TwoFactorRegisterParams.java rename to authme-core/src/main/java/fr/xephi/authme/process/register/executors/TwoFactorRegisterParams.java diff --git a/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java b/authme-core/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java similarity index 93% rename from src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java rename to authme-core/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java index 5d37c8ad6c..9da45221cd 100644 --- a/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java +++ b/authme-core/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java @@ -116,7 +116,8 @@ public void adminUnregister(CommandSender initiator, String name, Player player) */ private void performPostUnregisterActions(String name, Player player) { if (player != null && playerCache.isAuthenticated(name)) { - bungeeSender.sendAuthMeBungeecordMessage(player, MessageType.LOGOUT); + bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(player, + () -> bungeeSender.sendAuthMeBungeecordMessage(player, MessageType.LOGOUT)); } playerCache.removePlayer(name); @@ -125,13 +126,13 @@ private void performPostUnregisterActions(String name, Player player) { if (player == null || !player.isOnline()) { return; } - bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> - commandManager.runCommandsOnUnregister(player)); + bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(player, + () -> commandManager.runCommandsOnUnregister(player)); if (service.getProperty(RegistrationSettings.FORCE)) { teleportationService.teleportOnJoin(player); - bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> { + bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(player, () -> { limboService.createLimboPlayer(player, false); applyBlindEffect(player); }); @@ -141,7 +142,7 @@ private void performPostUnregisterActions(String name, Player player) { private void applyBlindEffect(Player player) { if (service.getProperty(RegistrationSettings.APPLY_BLIND_EFFECT)) { - int timeout = service.getProperty(RestrictionSettings.TIMEOUT) * TICKS_PER_SECOND; + int timeout = service.getProperty(RestrictionSettings.REGISTER_TIMEOUT) * TICKS_PER_SECOND; player.addPotionEffect(bukkitService.createBlindnessEffect(timeout)); } } diff --git a/src/main/java/fr/xephi/authme/security/HashAlgorithm.java b/authme-core/src/main/java/fr/xephi/authme/security/HashAlgorithm.java similarity index 97% rename from src/main/java/fr/xephi/authme/security/HashAlgorithm.java rename to authme-core/src/main/java/fr/xephi/authme/security/HashAlgorithm.java index 8f75e0b75c..6725404c37 100644 --- a/src/main/java/fr/xephi/authme/security/HashAlgorithm.java +++ b/authme-core/src/main/java/fr/xephi/authme/security/HashAlgorithm.java @@ -18,6 +18,7 @@ public enum HashAlgorithm { MD5VB(fr.xephi.authme.security.crypts.Md5vB.class), MYBB(fr.xephi.authme.security.crypts.MyBB.class), PBKDF2(fr.xephi.authme.security.crypts.Pbkdf2.class), + PBKDF2BASE64(fr.xephi.authme.security.crypts.Pbkdf2Base64.class), PBKDF2DJANGO(fr.xephi.authme.security.crypts.Pbkdf2Django.class), PHPBB(fr.xephi.authme.security.crypts.PhpBB.class), PHPFUSION(fr.xephi.authme.security.crypts.PhpFusion.class), diff --git a/src/main/java/fr/xephi/authme/security/HashUtils.java b/authme-core/src/main/java/fr/xephi/authme/security/HashUtils.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/HashUtils.java rename to authme-core/src/main/java/fr/xephi/authme/security/HashUtils.java diff --git a/src/main/java/fr/xephi/authme/security/MessageDigestAlgorithm.java b/authme-core/src/main/java/fr/xephi/authme/security/MessageDigestAlgorithm.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/MessageDigestAlgorithm.java rename to authme-core/src/main/java/fr/xephi/authme/security/MessageDigestAlgorithm.java diff --git a/src/main/java/fr/xephi/authme/security/PasswordSecurity.java b/authme-core/src/main/java/fr/xephi/authme/security/PasswordSecurity.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/PasswordSecurity.java rename to authme-core/src/main/java/fr/xephi/authme/security/PasswordSecurity.java diff --git a/authme-core/src/main/java/fr/xephi/authme/security/crypts/AbstractPbkdf2.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/AbstractPbkdf2.java new file mode 100644 index 0000000000..4f8217b5a2 --- /dev/null +++ b/authme-core/src/main/java/fr/xephi/authme/security/crypts/AbstractPbkdf2.java @@ -0,0 +1,26 @@ +package fr.xephi.authme.security.crypts; + +import de.rtner.security.auth.spi.PBKDF2Engine; +import de.rtner.security.auth.spi.PBKDF2Parameters; +import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.SecuritySettings; + +public abstract class AbstractPbkdf2 extends HexSaltedMethod { + + protected final int numberOfRounds; + + protected AbstractPbkdf2(Settings settings, int defaultRounds) { + int configured = settings.getProperty(SecuritySettings.PBKDF2_NUMBER_OF_ROUNDS); + this.numberOfRounds = configured > 0 ? configured : defaultRounds; + } + + protected byte[] deriveKey(String password, byte[] saltBytes, int iterations, int keyLength) { + PBKDF2Parameters params = new PBKDF2Parameters("HmacSHA256", "UTF-8", saltBytes, iterations); + return new PBKDF2Engine(params).deriveKey(password, keyLength); + } + + protected boolean verifyKey(String password, byte[] saltBytes, int iterations, byte[] expectedKey) { + PBKDF2Parameters params = new PBKDF2Parameters("HmacSHA256", "UTF-8", saltBytes, iterations, expectedKey); + return new PBKDF2Engine(params).verifyKey(password); + } +} diff --git a/src/main/java/fr/xephi/authme/security/crypts/Argon2.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/Argon2.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/Argon2.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/Argon2.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/BCrypt.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/BCrypt.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/BCrypt.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/BCrypt.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/BCrypt2y.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/BCrypt2y.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/BCrypt2y.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/BCrypt2y.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/BCryptBasedHash.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/BCryptBasedHash.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/BCryptBasedHash.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/BCryptBasedHash.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/BCryptHasher.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/BCryptHasher.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/BCryptHasher.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/BCryptHasher.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/CmwCrypt.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/CmwCrypt.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/CmwCrypt.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/CmwCrypt.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/CrazyCrypt1.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/CrazyCrypt1.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/CrazyCrypt1.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/CrazyCrypt1.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/DoubleMd5.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/DoubleMd5.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/DoubleMd5.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/DoubleMd5.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/EncryptionMethod.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/EncryptionMethod.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/EncryptionMethod.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/EncryptionMethod.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/HashedPassword.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/HashedPassword.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/HashedPassword.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/HashedPassword.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/HexSaltedMethod.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/HexSaltedMethod.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/HexSaltedMethod.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/HexSaltedMethod.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/Ipb3.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/Ipb3.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/Ipb3.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/Ipb3.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/Ipb4.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/Ipb4.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/Ipb4.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/Ipb4.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/Joomla.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/Joomla.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/Joomla.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/Joomla.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/Md5.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/Md5.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/Md5.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/Md5.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/Md5vB.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/Md5vB.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/Md5vB.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/Md5vB.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/MyBB.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/MyBB.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/MyBB.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/MyBB.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/Pbkdf2.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/Pbkdf2.java similarity index 54% rename from src/main/java/fr/xephi/authme/security/crypts/Pbkdf2.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/Pbkdf2.java index c79647ab79..2be6ad1560 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/Pbkdf2.java +++ b/authme-core/src/main/java/fr/xephi/authme/security/crypts/Pbkdf2.java @@ -2,37 +2,29 @@ import com.google.common.primitives.Ints; import de.rtner.misc.BinTools; -import de.rtner.security.auth.spi.PBKDF2Engine; -import de.rtner.security.auth.spi.PBKDF2Parameters; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.output.ConsoleLoggerFactory; import fr.xephi.authme.security.crypts.description.Recommendation; import fr.xephi.authme.security.crypts.description.Usage; import fr.xephi.authme.settings.Settings; -import fr.xephi.authme.settings.properties.SecuritySettings; import javax.inject.Inject; @Recommendation(Usage.RECOMMENDED) -public class Pbkdf2 extends HexSaltedMethod { +public class Pbkdf2 extends AbstractPbkdf2 { private static final int DEFAULT_ROUNDS = 10_000; private final ConsoleLogger logger = ConsoleLoggerFactory.get(Pbkdf2.class); - private int numberOfRounds; @Inject Pbkdf2(Settings settings) { - int configuredRounds = settings.getProperty(SecuritySettings.PBKDF2_NUMBER_OF_ROUNDS); - this.numberOfRounds = configuredRounds > 0 ? configuredRounds : DEFAULT_ROUNDS; + super(settings, DEFAULT_ROUNDS); } @Override public String computeHash(String password, String salt, String name) { - String result = "pbkdf2_sha256$" + numberOfRounds + "$" + salt + "$"; - PBKDF2Parameters params = new PBKDF2Parameters("HmacSHA256", "UTF-8", salt.getBytes(), numberOfRounds); - PBKDF2Engine engine = new PBKDF2Engine(params); - - return result + BinTools.bin2hex(engine.deriveKey(password, 64)); + return "pbkdf2_sha256$" + numberOfRounds + "$" + salt + "$" + + BinTools.bin2hex(deriveKey(password, salt.getBytes(), numberOfRounds, 64)); } @Override @@ -46,12 +38,7 @@ public boolean comparePassword(String password, HashedPassword hashedPassword, S logger.warning("Cannot read number of rounds for Pbkdf2: '" + line[1] + "'"); return false; } - - String salt = line[2]; - byte[] derivedKey = BinTools.hex2bin(line[3]); - PBKDF2Parameters params = new PBKDF2Parameters("HmacSHA256", "UTF-8", salt.getBytes(), iterations, derivedKey); - PBKDF2Engine engine = new PBKDF2Engine(params); - return engine.verifyKey(password); + return verifyKey(password, line[2].getBytes(), iterations, BinTools.hex2bin(line[3])); } @Override diff --git a/authme-core/src/main/java/fr/xephi/authme/security/crypts/Pbkdf2Base64.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/Pbkdf2Base64.java new file mode 100644 index 0000000000..d865566493 --- /dev/null +++ b/authme-core/src/main/java/fr/xephi/authme/security/crypts/Pbkdf2Base64.java @@ -0,0 +1,66 @@ +package fr.xephi.authme.security.crypts; + +import com.google.common.primitives.Ints; +import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.output.ConsoleLoggerFactory; +import fr.xephi.authme.security.crypts.description.Recommendation; +import fr.xephi.authme.security.crypts.description.Usage; +import fr.xephi.authme.settings.Settings; + +import javax.inject.Inject; +import java.security.SecureRandom; +import java.util.Base64; + +/** + * PBKDF2-HmacSHA256 with Base64-encoded salt and hash. + * Format: {@code pbkdf2$$$} + */ +@Recommendation(Usage.RECOMMENDED) +public class Pbkdf2Base64 extends AbstractPbkdf2 { + + private static final int DEFAULT_ROUNDS = 120_000; + private static final int SALT_BYTES = 16; + private static final int HASH_BYTES = 32; + + private final ConsoleLogger logger = ConsoleLoggerFactory.get(Pbkdf2Base64.class); + + @Inject + Pbkdf2Base64(Settings settings) { + super(settings, DEFAULT_ROUNDS); + } + + @Override + public String computeHash(String password, String salt, String name) { + byte[] saltBytes = Base64.getDecoder().decode(salt); + byte[] hash = deriveKey(password, saltBytes, numberOfRounds, HASH_BYTES); + return "pbkdf2$" + numberOfRounds + "$" + salt + "$" + Base64.getEncoder().encodeToString(hash); + } + + @Override + public boolean comparePassword(String password, HashedPassword hashedPassword, String unusedName) { + String[] parts = hashedPassword.getHash().split("\\$"); + if (parts.length != 4) { + return false; + } + Integer iterations = Ints.tryParse(parts[1]); + if (iterations == null) { + logger.warning("Cannot read number of rounds for Pbkdf2Base64: '" + parts[1] + "'"); + return false; + } + byte[] saltBytes = Base64.getDecoder().decode(parts[2]); + byte[] expectedKey = Base64.getDecoder().decode(parts[3]); + return verifyKey(password, saltBytes, iterations, expectedKey); + } + + @Override + public String generateSalt() { + byte[] salt = new byte[SALT_BYTES]; + new SecureRandom().nextBytes(salt); + return Base64.getEncoder().encodeToString(salt); + } + + @Override + public int getSaltLength() { + return SALT_BYTES; + } +} diff --git a/src/main/java/fr/xephi/authme/security/crypts/Pbkdf2Django.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/Pbkdf2Django.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/Pbkdf2Django.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/Pbkdf2Django.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/PhpBB.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/PhpBB.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/PhpBB.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/PhpBB.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/PhpFusion.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/PhpFusion.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/PhpFusion.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/PhpFusion.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/RoyalAuth.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/RoyalAuth.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/RoyalAuth.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/RoyalAuth.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/Salted2Md5.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/Salted2Md5.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/Salted2Md5.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/Salted2Md5.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/SaltedSha512.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/SaltedSha512.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/SaltedSha512.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/SaltedSha512.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/SeparateSaltMethod.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/SeparateSaltMethod.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/SeparateSaltMethod.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/SeparateSaltMethod.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/Sha1.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/Sha1.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/Sha1.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/Sha1.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/Sha256.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/Sha256.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/Sha256.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/Sha256.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/Sha512.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/Sha512.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/Sha512.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/Sha512.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/Smf.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/Smf.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/Smf.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/Smf.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/TwoFactor.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/TwoFactor.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/TwoFactor.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/TwoFactor.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/UnsaltedMethod.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/UnsaltedMethod.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/UnsaltedMethod.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/UnsaltedMethod.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/UsernameSaltMethod.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/UsernameSaltMethod.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/UsernameSaltMethod.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/UsernameSaltMethod.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/Wbb3.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/Wbb3.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/Wbb3.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/Wbb3.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/Wbb4.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/Wbb4.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/Wbb4.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/Wbb4.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/Wordpress.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/Wordpress.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/Wordpress.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/Wordpress.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/XfBCrypt.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/XfBCrypt.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/XfBCrypt.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/XfBCrypt.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/description/AsciiRestricted.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/description/AsciiRestricted.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/description/AsciiRestricted.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/description/AsciiRestricted.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/description/HasSalt.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/description/HasSalt.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/description/HasSalt.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/description/HasSalt.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/description/Recommendation.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/description/Recommendation.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/description/Recommendation.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/description/Recommendation.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/description/SaltType.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/description/SaltType.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/description/SaltType.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/description/SaltType.java diff --git a/src/main/java/fr/xephi/authme/security/crypts/description/Usage.java b/authme-core/src/main/java/fr/xephi/authme/security/crypts/description/Usage.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/crypts/description/Usage.java rename to authme-core/src/main/java/fr/xephi/authme/security/crypts/description/Usage.java diff --git a/src/main/java/fr/xephi/authme/security/totp/GenerateTotpService.java b/authme-core/src/main/java/fr/xephi/authme/security/totp/GenerateTotpService.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/totp/GenerateTotpService.java rename to authme-core/src/main/java/fr/xephi/authme/security/totp/GenerateTotpService.java diff --git a/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java b/authme-core/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java similarity index 100% rename from src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java rename to authme-core/src/main/java/fr/xephi/authme/security/totp/TotpAuthenticator.java diff --git a/src/main/java/fr/xephi/authme/service/AntiBotService.java b/authme-core/src/main/java/fr/xephi/authme/service/AntiBotService.java similarity index 83% rename from src/main/java/fr/xephi/authme/service/AntiBotService.java rename to authme-core/src/main/java/fr/xephi/authme/service/AntiBotService.java index abe987b701..6bcf536603 100644 --- a/src/main/java/fr/xephi/authme/service/AntiBotService.java +++ b/authme-core/src/main/java/fr/xephi/authme/service/AntiBotService.java @@ -8,7 +8,7 @@ import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.ProtectionSettings; import fr.xephi.authme.util.AtomicIntervalCounter; -import org.bukkit.scheduler.BukkitTask; +import org.bukkit.entity.Player; import javax.inject.Inject; import java.util.Locale; @@ -32,7 +32,7 @@ public class AntiBotService implements SettingsDependent { // Service status private AntiBotStatus antiBotStatus; private boolean startup; - private BukkitTask disableTask; + private CancellableTask disableTask; private AtomicIntervalCounter flaggedCounter; @Inject @@ -73,7 +73,7 @@ public void reload(Settings settings) { // Delay the schedule on first start if (startup) { int delay = settings.getProperty(ProtectionSettings.ANTIBOT_DELAY); - bukkitService.scheduleSyncDelayedTask(enableTask, delay * TICKS_PER_SECOND); + bukkitService.runTaskLaterOnGlobalRegion(enableTask, delay * TICKS_PER_SECOND); startup = false; } else { enableTask.run(); @@ -91,14 +91,9 @@ private void startProtection() { disableTask.cancel(); } // Schedule auto-disable - disableTask = bukkitService.runTaskLater(this::stopProtection, duration * TICKS_PER_MINUTE); + disableTask = bukkitService.runTaskLaterOnGlobalRegion(this::stopProtection, duration * TICKS_PER_MINUTE); antiBotStatus = AntiBotStatus.ACTIVE; - bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> { - // Inform admins - bukkitService.getOnlinePlayers().stream() - .filter(player -> permissionsManager.hasPermission(player, AdminPermission.ANTIBOT_MESSAGES)) - .forEach(player -> messages.send(player, MessageKey.ANTIBOT_AUTO_ENABLED_MESSAGE)); - }); + notifyAdmins(MessageKey.ANTIBOT_AUTO_ENABLED_MESSAGE); } /** @@ -119,10 +114,7 @@ private void stopProtection() { disableTask = null; // Inform admins - String durationString = Integer.toString(duration); - bukkitService.getOnlinePlayers().stream() - .filter(player -> permissionsManager.hasPermission(player, AdminPermission.ANTIBOT_MESSAGES)) - .forEach(player -> messages.send(player, MessageKey.ANTIBOT_AUTO_DISABLED_MESSAGE, durationString)); + notifyAdmins(MessageKey.ANTIBOT_AUTO_DISABLED_MESSAGE, Integer.toString(duration)); } /** @@ -190,6 +182,21 @@ public void addPlayerKick(String name) { antibotKicked.addIfAbsent(name.toLowerCase(Locale.ROOT)); } + private void notifyAdmins(MessageKey key, String... args) { + bukkitService.runOnGlobalRegion(() -> { + Object[] players = bukkitService.getOnlinePlayers().toArray(); + for (Object onlinePlayer : players) { + if (onlinePlayer instanceof Player player) { + bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(player, () -> { + if (permissionsManager.hasPermission(player, AdminPermission.ANTIBOT_MESSAGES)) { + messages.send(player, key, args); + } + }); + } + } + }); + } + public enum AntiBotStatus { LISTENING, DISABLED, diff --git a/src/main/java/fr/xephi/authme/service/BackupService.java b/authme-core/src/main/java/fr/xephi/authme/service/BackupService.java similarity index 100% rename from src/main/java/fr/xephi/authme/service/BackupService.java rename to authme-core/src/main/java/fr/xephi/authme/service/BackupService.java diff --git a/src/main/java/fr/xephi/authme/service/BukkitService.java b/authme-core/src/main/java/fr/xephi/authme/service/BukkitService.java similarity index 73% rename from src/main/java/fr/xephi/authme/service/BukkitService.java rename to authme-core/src/main/java/fr/xephi/authme/service/BukkitService.java index 8864a902ef..8690ddd302 100644 --- a/src/main/java/fr/xephi/authme/service/BukkitService.java +++ b/authme-core/src/main/java/fr/xephi/authme/service/BukkitService.java @@ -2,15 +2,18 @@ import fr.xephi.authme.AuthMe; import fr.xephi.authme.initialization.SettingsDependent; +import fr.xephi.authme.platform.SchedulingAdapter; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.PluginSettings; import org.bukkit.BanEntry; import org.bukkit.BanList; import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.OfflinePlayer; import org.bukkit.World; import org.bukkit.command.CommandSender; import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.potion.PotionEffect; @@ -20,10 +23,13 @@ import javax.inject.Inject; import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.Date; import java.util.Optional; import java.util.Set; import java.util.function.Function; +import java.util.function.Supplier; /** * Service for operations requiring the Bukkit API, such as for scheduling. @@ -36,11 +42,13 @@ public class BukkitService implements SettingsDependent { public static final int TICKS_PER_MINUTE = 60 * TICKS_PER_SECOND; private final AuthMe authMe; + private final SchedulingAdapter schedulingAdapter; private boolean useAsyncTasks; @Inject - BukkitService(AuthMe authMe, Settings settings) { + BukkitService(AuthMe authMe, Settings settings, SchedulingAdapter schedulingAdapter) { this.authMe = authMe; + this.schedulingAdapter = schedulingAdapter; reload(settings); } @@ -69,6 +77,31 @@ public int scheduleSyncDelayedTask(Runnable task, long delay) { return Bukkit.getScheduler().scheduleSyncDelayedTask(authMe, task, delay); } + /** + * Schedules a task on the owning thread of the given entity. + * + * @param entity the entity whose scheduler should own the task + * @param task the task to run + */ + public void scheduleSyncTaskFromOptionallyAsyncTask(Entity entity, Runnable task) { + if (schedulingAdapter.isOwnedByCurrentThread(entity)) { + task.run(); + return; + } + schedulingAdapter.runOnEntityThread(authMe, entity, task); + } + + /** + * Schedules a delayed task on the owning thread of the given entity. + * + * @param entity the entity whose scheduler should own the task + * @param task the task to run + * @param delay the delay in ticks + */ + public void scheduleSyncDelayedTask(Entity entity, Runnable task, long delay) { + runTaskLater(entity, task, delay); + } + /** * Schedules a synchronous task if we are currently on a async thread; if not, it runs the task immediately. * Use this when {@link #runTaskOptionallyAsync(Runnable) optionally asynchronous tasks} have to @@ -77,10 +110,50 @@ public int scheduleSyncDelayedTask(Runnable task, long delay) { * @param task the task to be run */ public void scheduleSyncTaskFromOptionallyAsyncTask(Runnable task) { - if (Bukkit.isPrimaryThread()) { + if (schedulingAdapter.isGlobalThread()) { task.run(); } else { - scheduleSyncDelayedTask(task); + runOnGlobalRegion(task); + } + } + + /** + * Runs the given supplier on the main thread if necessary and returns its result. + * + * @param supplier the supplier to run + * @param the return type + * @return the supplier's result + */ + public T callSyncMethodFromOptionallyAsyncTask(Supplier supplier) { + if (schedulingAdapter.isGlobalThread()) { + return supplier.get(); + } + + CompletableFuture future = new CompletableFuture<>(); + runOnGlobalRegion(() -> { + try { + future.complete(supplier.get()); + } catch (RuntimeException e) { + future.completeExceptionally(e); + } catch (Error e) { + future.completeExceptionally(e); + } + }); + + try { + return future.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IllegalStateException("Interrupted while waiting for sync task", e); + } catch (ExecutionException e) { + Throwable cause = e.getCause(); + if (cause instanceof RuntimeException) { + throw (RuntimeException) cause; + } + if (cause instanceof Error) { + throw (Error) cause; + } + throw new IllegalStateException("Unexpected exception while waiting for sync task", cause); } } @@ -110,6 +183,18 @@ public BukkitTask runTaskLater(Runnable task, long delay) { return Bukkit.getScheduler().runTaskLater(authMe, task, delay); } + /** + * Schedules a delayed task on the owning thread of the given entity. + * + * @param entity the entity whose scheduler should own the task + * @param task the task to run + * @param delay the delay in ticks + * @return the scheduled task handle + */ + public CancellableTask runTaskLater(Entity entity, Runnable task, long delay) { + return schedulingAdapter.runDelayedOnEntityThread(authMe, entity, task, delay); + } + /** * Schedules this task to run asynchronously or immediately executes it based on * AuthMe's configuration. @@ -173,6 +258,43 @@ public BukkitTask runTaskTimer(BukkitRunnable task, long delay, long period) { return task.runTaskTimer(authMe, delay, period); } + /** + * Schedules a repeating task on the owning thread of the given entity. + * + * @param entity the entity whose scheduler should own the task + * @param task the task to schedule + * @param delay the initial delay in ticks + * @param period the repeat period in ticks + * @return the scheduled task handle + */ + public CancellableTask runTaskTimer(Entity entity, Runnable task, long delay, long period) { + return schedulingAdapter.runAtFixedRateOnEntityThread(authMe, entity, task, delay, period); + } + + /** + * Schedules a task on the global region. + * + * @param task the task to run + */ + public void runOnGlobalRegion(Runnable task) { + if (schedulingAdapter.isGlobalThread()) { + task.run(); + return; + } + schedulingAdapter.runOnGlobalThread(authMe, task); + } + + /** + * Schedules a delayed task on the global region. + * + * @param task the task to run + * @param delay the delay in ticks + * @return the scheduled task handle + */ + public CancellableTask runTaskLaterOnGlobalRegion(Runnable task, long delay) { + return schedulingAdapter.runDelayedOnGlobalThread(authMe, task, delay); + } + /** * Broadcast a message to all players. * diff --git a/authme-core/src/main/java/fr/xephi/authme/service/CancellableTask.java b/authme-core/src/main/java/fr/xephi/authme/service/CancellableTask.java new file mode 100644 index 0000000000..46a6342fab --- /dev/null +++ b/authme-core/src/main/java/fr/xephi/authme/service/CancellableTask.java @@ -0,0 +1,7 @@ +package fr.xephi.authme.service; + +@FunctionalInterface +public interface CancellableTask { + + void cancel(); +} diff --git a/src/main/java/fr/xephi/authme/service/CommonService.java b/authme-core/src/main/java/fr/xephi/authme/service/CommonService.java similarity index 100% rename from src/main/java/fr/xephi/authme/service/CommonService.java rename to authme-core/src/main/java/fr/xephi/authme/service/CommonService.java diff --git a/src/main/java/fr/xephi/authme/service/GeoIpService.java b/authme-core/src/main/java/fr/xephi/authme/service/GeoIpService.java similarity index 100% rename from src/main/java/fr/xephi/authme/service/GeoIpService.java rename to authme-core/src/main/java/fr/xephi/authme/service/GeoIpService.java diff --git a/src/main/java/fr/xephi/authme/service/HelpTranslationGenerator.java b/authme-core/src/main/java/fr/xephi/authme/service/HelpTranslationGenerator.java similarity index 100% rename from src/main/java/fr/xephi/authme/service/HelpTranslationGenerator.java rename to authme-core/src/main/java/fr/xephi/authme/service/HelpTranslationGenerator.java diff --git a/src/main/java/fr/xephi/authme/service/JoinMessageService.java b/authme-core/src/main/java/fr/xephi/authme/service/JoinMessageService.java similarity index 100% rename from src/main/java/fr/xephi/authme/service/JoinMessageService.java rename to authme-core/src/main/java/fr/xephi/authme/service/JoinMessageService.java diff --git a/src/main/java/fr/xephi/authme/service/MigrationService.java b/authme-core/src/main/java/fr/xephi/authme/service/MigrationService.java similarity index 100% rename from src/main/java/fr/xephi/authme/service/MigrationService.java rename to authme-core/src/main/java/fr/xephi/authme/service/MigrationService.java diff --git a/src/main/java/fr/xephi/authme/service/PasswordRecoveryService.java b/authme-core/src/main/java/fr/xephi/authme/service/PasswordRecoveryService.java similarity index 100% rename from src/main/java/fr/xephi/authme/service/PasswordRecoveryService.java rename to authme-core/src/main/java/fr/xephi/authme/service/PasswordRecoveryService.java diff --git a/src/main/java/fr/xephi/authme/service/PluginHookService.java b/authme-core/src/main/java/fr/xephi/authme/service/PluginHookService.java similarity index 100% rename from src/main/java/fr/xephi/authme/service/PluginHookService.java rename to authme-core/src/main/java/fr/xephi/authme/service/PluginHookService.java diff --git a/src/main/java/fr/xephi/authme/service/RecoveryCodeService.java b/authme-core/src/main/java/fr/xephi/authme/service/RecoveryCodeService.java similarity index 100% rename from src/main/java/fr/xephi/authme/service/RecoveryCodeService.java rename to authme-core/src/main/java/fr/xephi/authme/service/RecoveryCodeService.java diff --git a/src/main/java/fr/xephi/authme/service/SessionService.java b/authme-core/src/main/java/fr/xephi/authme/service/SessionService.java similarity index 100% rename from src/main/java/fr/xephi/authme/service/SessionService.java rename to authme-core/src/main/java/fr/xephi/authme/service/SessionService.java diff --git a/src/main/java/fr/xephi/authme/service/SessionState.java b/authme-core/src/main/java/fr/xephi/authme/service/SessionState.java similarity index 100% rename from src/main/java/fr/xephi/authme/service/SessionState.java rename to authme-core/src/main/java/fr/xephi/authme/service/SessionState.java diff --git a/authme-core/src/main/java/fr/xephi/authme/service/TeleportationService.java b/authme-core/src/main/java/fr/xephi/authme/service/TeleportationService.java new file mode 100644 index 0000000000..84c3837787 --- /dev/null +++ b/authme-core/src/main/java/fr/xephi/authme/service/TeleportationService.java @@ -0,0 +1,402 @@ +package fr.xephi.authme.service; + +import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.data.auth.PlayerCache; +import fr.xephi.authme.data.limbo.LimboPlayer; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.events.AbstractTeleportEvent; +import fr.xephi.authme.events.AuthMeTeleportEvent; +import fr.xephi.authme.events.FirstSpawnTeleportEvent; +import fr.xephi.authme.events.SpawnTeleportEvent; +import fr.xephi.authme.initialization.Reloadable; +import fr.xephi.authme.output.ConsoleLoggerFactory; +import fr.xephi.authme.platform.TeleportAdapter; +import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.SpawnLoader; +import fr.xephi.authme.settings.properties.RegistrationSettings; +import fr.xephi.authme.settings.properties.RestrictionSettings; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import static fr.xephi.authme.settings.properties.RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN; + +/** + * Handles teleportation (placement of player to spawn). + */ +public class TeleportationService implements Reloadable { + + private final ConsoleLogger logger = ConsoleLoggerFactory.get(TeleportationService.class); + + @Inject + private Settings settings; + + @Inject + private BukkitService bukkitService; + + @Inject + private SpawnLoader spawnLoader; + + @Inject + private PlayerCache playerCache; + + @Inject + private DataSource dataSource; + + @Inject + private TeleportAdapter teleportAdapter; + + private Set spawnOnLoginWorlds; + private final ConcurrentHashMap preloadedAuthStatus = new ConcurrentHashMap<>(); + private final ConcurrentHashMap originalJoinLocations = new ConcurrentHashMap<>(); + + TeleportationService() { + } + + @PostConstruct + @Override + public void reload() { + // Use a Set for better performance with #contains() + spawnOnLoginWorlds = new HashSet<>(settings.getProperty(RestrictionSettings.FORCE_SPAWN_ON_WORLDS)); + } + + /** + * Teleports the player according to the settings when he joins. + * + * @param player the player to process + */ + public void teleportOnJoin(final Player player) { + if (!settings.getProperty(RestrictionSettings.NO_TELEPORT) + && settings.getProperty(TELEPORT_UNAUTHED_TO_SPAWN) + && !isUnregisteredWithOptionalAuth(player.getName())) { + logger.debug("Teleport on join for player `{0}`", player.getName()); + teleportToSpawn(player, playerCache.isAuthenticated(player.getName())); + } + } + + /** + * Returns the player's custom on join location. + * + * @param player the player to process + * + * @return the custom spawn location, null if the player should spawn at the original location + */ + public Location prepareOnJoinSpawnLocation(final Player player) { + return prepareOnJoinSpawnLocation(player, null); + } + + /** + * Returns the player's custom on join location and stores the original spawn location for later restoration. + * + * @param player the player to process + * @param originalLocation the server-selected spawn location before AuthMe modifies it + * @return the custom spawn location, null if the player should spawn at the original location + */ + public Location prepareOnJoinSpawnLocation(final Player player, final Location originalLocation) { + rememberOriginalJoinLocation(player.getName(), originalLocation); + if (shouldApplyOnJoinSpawnLocation(player.getName())) { + final Location location = spawnLoader.getSpawnLocation(player); + + SpawnTeleportEvent event = new SpawnTeleportEvent(player, location, + playerCache.isAuthenticated(player.getName())); + bukkitService.callEvent(event); + if (!isEventValid(event)) { + return null; + } + + logger.debug("Returning custom location for join event for player `{0}`", player.getName()); + return event.getTo(); + } + return null; + } + + /** + * Returns the player's custom on join location without requiring a {@link Player} instance. + * Intended for platform hooks that run before the player object is available. + * + * @param playerName the player's name + * @param world the world the player is about to join in + * @return the custom spawn location, null if the player should spawn at the original location + */ + public Location prepareOnJoinSpawnLocation(String playerName, World world) { + return prepareOnJoinSpawnLocationInternal(playerName, world); + } + + /** + * Returns the player's custom on join location without requiring a {@link Player} instance and stores the + * original spawn location for later restoration. + * + * @param playerName the player's name + * @param originalLocation the server-selected spawn location before AuthMe modifies it + * @return the custom spawn location, null if the player should spawn at the original location + */ + public Location prepareOnJoinSpawnLocation(String playerName, Location originalLocation) { + rememberOriginalJoinLocation(playerName, originalLocation); + return prepareOnJoinSpawnLocationInternal(playerName, + originalLocation == null ? null : originalLocation.getWorld()); + } + + private Location prepareOnJoinSpawnLocationInternal(String playerName, World world) { + if (!shouldApplyOnJoinSpawnLocation(playerName) || world == null) { + return null; + } + + logger.debug("Returning custom location for async join event for player `{0}`", playerName); + return spawnLoader.getSpawnLocation(world); + } + + /** + * Teleports the player to the first spawn if he is new and the first spawn is configured. + * + * @param player the player to process + */ + public void teleportNewPlayerToFirstSpawn(final Player player) { + if (settings.getProperty(RestrictionSettings.NO_TELEPORT)) { + return; + } + + Location firstSpawn = spawnLoader.getFirstSpawn(); + if (firstSpawn == null) { + return; + } + + if (!player.hasPlayedBefore() || !dataSource.isAuthAvailable(player.getName())) { + logger.debug("Attempting to teleport player `{0}` to first spawn", player.getName()); + performTeleportation(player, new FirstSpawnTeleportEvent(player, firstSpawn)); + } + } + + /** + * Teleports the player according to the settings after having successfully logged in. + * + * @param player the player + * @param auth corresponding PlayerAuth object + * @param limbo corresponding LimboPlayer object + */ + public void teleportOnLogin(final Player player, PlayerAuth auth, LimboPlayer limbo) { + if (settings.getProperty(RestrictionSettings.NO_TELEPORT)) { + return; + } + + // #856: If LimboPlayer comes from a persisted file, the Location might be null + Location joinLocation = limbo != null && limbo.getLocation() != null + ? limbo.getLocation() + : peekOriginalJoinLocation(player.getName()); + String worldName = (joinLocation != null && joinLocation.getWorld() != null) + ? joinLocation.getWorld().getName() + : null; + + // The world in LimboPlayer is from where the player comes, before any teleportation by AuthMe + if (mustForceSpawnAfterLogin(worldName)) { + logger.debug("Teleporting `{0}` to spawn because of 'force-spawn after login'", player.getName()); + teleportToSpawn(player, true); + } else if (settings.getProperty(TELEPORT_UNAUTHED_TO_SPAWN)) { + if (settings.getProperty(RestrictionSettings.SAVE_QUIT_LOCATION)) { + Location location = buildLocationFromAuth(player, auth); + if (shouldUseJoinLocationAsFallback(auth, joinLocation)) { + logger.debug("Teleporting `{0}` after login, based on the remembered join location", + player.getName()); + location = joinLocation; + } else { + logger.debug("Teleporting `{0}` after login, based on the player auth", player.getName()); + } + if (hasSamePositionAndWorld(location, player.getLocation())) { + logger.debug("Skipping teleport of `{0}` after login because the player is already there", + player.getName()); + return; + } + teleportBackFromSpawn(player, location); + } else if (joinLocation != null) { + logger.debug("Teleporting `{0}` after login, based on the remembered join location", player.getName()); + teleportBackFromSpawn(player, joinLocation); + } + } + } + + /** + * Caches the player's registration status from the async pre-login event to avoid a blocking + * DB call on the main thread later. Must be cleared with {@link #clearPreloadedAuthStatus}. + * + * @param name the player name (case-insensitive) + * @param isRegistered whether the player has an account + */ + public void preloadAuthStatus(String name, boolean isRegistered) { + String normalizedName = normalizePlayerName(name); + if (normalizedName != null) { + preloadedAuthStatus.put(normalizedName, isRegistered); + } + } + + /** + * Clears the pre-loaded registration status for the given player after it has been consumed. + * + * @param name the player name + */ + public void clearPreloadedAuthStatus(String name) { + String normalizedName = normalizePlayerName(name); + if (normalizedName != null) { + preloadedAuthStatus.remove(normalizedName); + } + } + + /** + * Stores the server-selected join location before AuthMe redirects the player to its configured spawn. + * + * @param name the player name + * @param location the original join location + */ + public void rememberOriginalJoinLocation(String name, Location location) { + String normalizedName = normalizePlayerName(name); + if (normalizedName == null) { + return; + } + if (location == null || location.getWorld() == null) { + clearOriginalJoinLocation(normalizedName); + return; + } + originalJoinLocations.put(normalizedName, location.clone()); + } + + /** + * Returns the remembered original join location without consuming it. + * + * @param name the player name + * @return the remembered location, or null if none exists + */ + public Location peekOriginalJoinLocation(String name) { + String normalizedName = normalizePlayerName(name); + return normalizedName == null ? null : originalJoinLocations.get(normalizedName); + } + + /** + * Returns and clears the remembered original join location for the player. + * + * @param name the player name + * @param fallback the location to return if no join location was remembered + * @return the remembered location, or the fallback if absent + */ + public Location consumeOriginalJoinLocation(String name, Location fallback) { + String normalizedName = normalizePlayerName(name); + if (normalizedName == null) { + return fallback; + } + Location location = originalJoinLocations.remove(normalizedName); + return location != null ? location : fallback; + } + + /** + * Clears the remembered original join location for the given player. + * + * @param name the player name + */ + public void clearOriginalJoinLocation(String name) { + String normalizedName = normalizePlayerName(name); + if (normalizedName != null) { + originalJoinLocations.remove(normalizedName); + } + } + + private boolean shouldApplyOnJoinSpawnLocation(String playerName) { + return !settings.getProperty(RestrictionSettings.NO_TELEPORT) + && settings.getProperty(TELEPORT_UNAUTHED_TO_SPAWN) + && !isUnregisteredWithOptionalAuth(playerName); + } + + private boolean isUnregisteredWithOptionalAuth(String playerName) { + if (settings.getProperty(RegistrationSettings.FORCE)) { + return false; + } + String key = normalizePlayerName(playerName); + if (key == null) { + return false; + } + Boolean cached = preloadedAuthStatus.get(key); + boolean isRegistered = cached != null ? cached : dataSource.isAuthAvailable(playerName); + return !isRegistered; + } + + private boolean mustForceSpawnAfterLogin(String worldName) { + return worldName != null && settings.getProperty(RestrictionSettings.FORCE_SPAWN_LOCATION_AFTER_LOGIN) + && spawnOnLoginWorlds.contains(worldName); + } + + private static boolean shouldUseJoinLocationAsFallback(PlayerAuth auth, Location joinLocation) { + return joinLocation != null + && hasDefaultStoredQuitLocation(auth) + && !matchesStoredQuitLocation(auth, joinLocation); + } + + private Location buildLocationFromAuth(Player player, PlayerAuth auth) { + World world = bukkitService.getWorld(auth.getWorld()); + if (world == null) { + world = player.getWorld(); + } + return new Location(world, auth.getQuitLocX(), auth.getQuitLocY(), auth.getQuitLocZ(), + auth.getYaw(), auth.getPitch()); + } + + private static boolean hasDefaultStoredQuitLocation(PlayerAuth auth) { + return auth != null + && Double.compare(auth.getQuitLocX(), 0.0) == 0 + && Double.compare(auth.getQuitLocY(), 0.0) == 0 + && Double.compare(auth.getQuitLocZ(), 0.0) == 0 + && "world".equals(auth.getWorld()); + } + + private static boolean matchesStoredQuitLocation(PlayerAuth auth, Location location) { + return location != null && location.getWorld() != null + && Double.compare(auth.getQuitLocX(), location.getX()) == 0 + && Double.compare(auth.getQuitLocY(), location.getY()) == 0 + && Double.compare(auth.getQuitLocZ(), location.getZ()) == 0 + && auth.getWorld().equals(location.getWorld().getName()); + } + + private static String normalizePlayerName(String name) { + return name == null ? null : name.toLowerCase(Locale.ROOT); + } + + private static boolean hasSamePositionAndWorld(Location first, Location second) { + return first != null && second != null + && Double.compare(first.getX(), second.getX()) == 0 + && Double.compare(first.getY(), second.getY()) == 0 + && Double.compare(first.getZ(), second.getZ()) == 0 + && first.getWorld() == second.getWorld(); + } + + private void teleportBackFromSpawn(final Player player, final Location location) { + performTeleportation(player, new AuthMeTeleportEvent(player, location)); + } + + private void teleportToSpawn(final Player player, final boolean isAuthenticated) { + final Location spawnLoc = spawnLoader.getSpawnLocation(player); + performTeleportation(player, new SpawnTeleportEvent(player, spawnLoc, isAuthenticated)); + } + + /** + * Emits the teleportation event and performs teleportation according to it (potentially modified + * by external listeners). Note that no teleportation is performed if the event's location is empty. + * + * @param player the player to teleport + * @param event the event to emit and according to which to teleport + */ + private void performTeleportation(final Player player, final AbstractTeleportEvent event) { + bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(player, () -> { + bukkitService.callEvent(event); + if (player.isOnline() && isEventValid(event)) { + teleportAdapter.teleportPlayer(player, event.getTo()); + } + }); + } + + private static boolean isEventValid(AbstractTeleportEvent event) { + return !event.isCancelled() && event.getTo() != null && event.getTo().getWorld() != null; + } +} diff --git a/src/main/java/fr/xephi/authme/service/ValidationService.java b/authme-core/src/main/java/fr/xephi/authme/service/ValidationService.java similarity index 100% rename from src/main/java/fr/xephi/authme/service/ValidationService.java rename to authme-core/src/main/java/fr/xephi/authme/service/ValidationService.java diff --git a/src/main/java/fr/xephi/authme/service/bungeecord/BungeeReceiver.java b/authme-core/src/main/java/fr/xephi/authme/service/bungeecord/BungeeReceiver.java similarity index 100% rename from src/main/java/fr/xephi/authme/service/bungeecord/BungeeReceiver.java rename to authme-core/src/main/java/fr/xephi/authme/service/bungeecord/BungeeReceiver.java diff --git a/src/main/java/fr/xephi/authme/service/bungeecord/BungeeSender.java b/authme-core/src/main/java/fr/xephi/authme/service/bungeecord/BungeeSender.java similarity index 98% rename from src/main/java/fr/xephi/authme/service/bungeecord/BungeeSender.java rename to authme-core/src/main/java/fr/xephi/authme/service/bungeecord/BungeeSender.java index 3d43605bba..b57c946332 100644 --- a/src/main/java/fr/xephi/authme/service/bungeecord/BungeeSender.java +++ b/authme-core/src/main/java/fr/xephi/authme/service/bungeecord/BungeeSender.java @@ -85,7 +85,7 @@ public void connectPlayerOnLogin(Player player) { return; } // Add a small delay, just in case... - bukkitService.scheduleSyncDelayedTask(() -> + bukkitService.scheduleSyncDelayedTask(player, () -> sendBungeecordMessage(player, "Connect", destinationServerOnLogin), 10L); } diff --git a/src/main/java/fr/xephi/authme/service/bungeecord/MessageType.java b/authme-core/src/main/java/fr/xephi/authme/service/bungeecord/MessageType.java similarity index 100% rename from src/main/java/fr/xephi/authme/service/bungeecord/MessageType.java rename to authme-core/src/main/java/fr/xephi/authme/service/bungeecord/MessageType.java diff --git a/src/main/java/fr/xephi/authme/service/yaml/YamlFileResourceProvider.java b/authme-core/src/main/java/fr/xephi/authme/service/yaml/YamlFileResourceProvider.java similarity index 100% rename from src/main/java/fr/xephi/authme/service/yaml/YamlFileResourceProvider.java rename to authme-core/src/main/java/fr/xephi/authme/service/yaml/YamlFileResourceProvider.java diff --git a/src/main/java/fr/xephi/authme/service/yaml/YamlParseException.java b/authme-core/src/main/java/fr/xephi/authme/service/yaml/YamlParseException.java similarity index 100% rename from src/main/java/fr/xephi/authme/service/yaml/YamlParseException.java rename to authme-core/src/main/java/fr/xephi/authme/service/yaml/YamlParseException.java diff --git a/src/main/java/fr/xephi/authme/settings/EnumSetProperty.java b/authme-core/src/main/java/fr/xephi/authme/settings/EnumSetProperty.java similarity index 100% rename from src/main/java/fr/xephi/authme/settings/EnumSetProperty.java rename to authme-core/src/main/java/fr/xephi/authme/settings/EnumSetProperty.java diff --git a/src/main/java/fr/xephi/authme/settings/Settings.java b/authme-core/src/main/java/fr/xephi/authme/settings/Settings.java similarity index 100% rename from src/main/java/fr/xephi/authme/settings/Settings.java rename to authme-core/src/main/java/fr/xephi/authme/settings/Settings.java diff --git a/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java b/authme-core/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java similarity index 91% rename from src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java rename to authme-core/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java index fe035a8b20..34e5b5c5ff 100644 --- a/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java +++ b/authme-core/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java @@ -36,6 +36,8 @@ import static fr.xephi.authme.settings.properties.RestrictionSettings.ALLOWED_NICKNAME_CHARACTERS; import static fr.xephi.authme.settings.properties.RestrictionSettings.FORCE_SPAWN_LOCATION_AFTER_LOGIN; import static fr.xephi.authme.settings.properties.RestrictionSettings.FORCE_SPAWN_ON_WORLDS; +import static fr.xephi.authme.settings.properties.RestrictionSettings.LOGIN_TIMEOUT; +import static fr.xephi.authme.settings.properties.RestrictionSettings.REGISTER_TIMEOUT; /** * Service for verifying that the configuration is up-to-date. @@ -89,9 +91,39 @@ protected boolean performMigrations(PropertyReader reader, ConfigurationData con | mergeAndMovePermissionGroupSettings(reader, configurationData) | moveDeprecatedHashAlgorithmIntoLegacySection(reader, configurationData) | moveSaltColumnConfigWithOtherColumnConfigs(reader, configurationData) + | migrateTimeoutToLoginAndRegisterTimeout(reader, configurationData) || hasDeprecatedProperties(reader); } + /** + * Migrates the old combined {@code settings.restrictions.timeout} to the separate + * {@code loginTimeout} and {@code registerTimeout} settings, preserving the user's value. + * + * @param reader The property reader + * @param configData Configuration data + * @return True if a migration has been performed, false otherwise + */ + private static boolean migrateTimeoutToLoginAndRegisterTimeout(PropertyReader reader, + ConfigurationData configData) { + Property oldTimeout = newProperty("settings.restrictions.timeout", 30); + PropertyValue oldValue = oldTimeout.determineValue(reader); + if (!oldValue.isValidInResource()) { + return false; + } + boolean changed = false; + if (!reader.contains(LOGIN_TIMEOUT.getPath())) { + logger.info("Migrating 'settings.restrictions.timeout' to '" + LOGIN_TIMEOUT.getPath() + "'"); + configData.setValue(LOGIN_TIMEOUT, oldValue.getValue()); + changed = true; + } + if (!reader.contains(REGISTER_TIMEOUT.getPath())) { + logger.info("Migrating 'settings.restrictions.timeout' to '" + REGISTER_TIMEOUT.getPath() + "'"); + configData.setValue(REGISTER_TIMEOUT, oldValue.getValue()); + changed = true; + } + return changed; + } + private static boolean hasDeprecatedProperties(PropertyReader reader) { String[] deprecatedProperties = { "Converter.Rakamak.newPasswordHash", "Hooks.chestshop", "Hooks.legacyChestshop", "Hooks.notifications", @@ -101,7 +133,8 @@ private static boolean hasDeprecatedProperties(PropertyReader reader) { "settings.restrictions.keepCollisionsDisabled", "settings.forceCommands", "settings.forceCommandsAsConsole", "settings.forceRegisterCommands", "settings.forceRegisterCommandsAsConsole", "settings.sessions.sessionExpireOnIpChange", "settings.restrictions.otherAccountsCmd", - "settings.restrictions.otherAccountsCmdThreshold, DataSource.mySQLDriverClassName"}; + "settings.restrictions.otherAccountsCmdThreshold, DataSource.mySQLDriverClassName", + "settings.restrictions.timeout"}; for (String deprecatedPath : deprecatedProperties) { if (reader.contains(deprecatedPath)) { return true; diff --git a/src/main/java/fr/xephi/authme/settings/SettingsWarner.java b/authme-core/src/main/java/fr/xephi/authme/settings/SettingsWarner.java similarity index 100% rename from src/main/java/fr/xephi/authme/settings/SettingsWarner.java rename to authme-core/src/main/java/fr/xephi/authme/settings/SettingsWarner.java diff --git a/src/main/java/fr/xephi/authme/settings/SpawnLoader.java b/authme-core/src/main/java/fr/xephi/authme/settings/SpawnLoader.java similarity index 86% rename from src/main/java/fr/xephi/authme/settings/SpawnLoader.java rename to authme-core/src/main/java/fr/xephi/authme/settings/SpawnLoader.java index 47e9711de2..4fe2d0d8af 100644 --- a/src/main/java/fr/xephi/authme/settings/SpawnLoader.java +++ b/authme-core/src/main/java/fr/xephi/authme/settings/SpawnLoader.java @@ -4,6 +4,7 @@ import fr.xephi.authme.initialization.DataFolder; import fr.xephi.authme.initialization.Reloadable; import fr.xephi.authme.output.ConsoleLoggerFactory; +import fr.xephi.authme.platform.TeleportAdapter; import fr.xephi.authme.service.PluginHookService; import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; @@ -36,6 +37,7 @@ public class SpawnLoader implements Reloadable { private final File authMeConfigurationFile; private final Settings settings; private final PluginHookService pluginHookService; + private final TeleportAdapter teleportAdapter; private FileConfiguration authMeConfiguration; private String[] spawnPriority; private Location essentialsSpawn; @@ -47,14 +49,17 @@ public class SpawnLoader implements Reloadable { * @param pluginFolder The AuthMe data folder * @param settings The setting instance * @param pluginHookService The plugin hooks instance + * @param teleportAdapter The platform-specific teleport adapter */ @Inject - SpawnLoader(@DataFolder File pluginFolder, Settings settings, PluginHookService pluginHookService) { + SpawnLoader(@DataFolder File pluginFolder, Settings settings, PluginHookService pluginHookService, + TeleportAdapter teleportAdapter) { File spawnFile = new File(pluginFolder, "spawn.yml"); FileUtils.copyFileFromResource(spawnFile, "spawn.yml"); this.authMeConfigurationFile = spawnFile; this.settings = settings; this.pluginHookService = pluginHookService; + this.teleportAdapter = teleportAdapter; reload(); } @@ -171,11 +176,25 @@ public void unloadCmiSpawn() { * @see RestrictionSettings#SPAWN_PRIORITY */ public Location getSpawnLocation(Player player) { - if (player == null || player.getWorld() == null) { + if (player == null) { + return null; + } + return getSpawnLocation(player.getWorld()); + } + + /** + * Return the spawn location for the given world. The source of the spawn location varies + * depending on the spawn priority setting. + * + * @param world The world to retrieve the spawn point for + * + * @return The spawn location, or the default spawn location upon failure + */ + public Location getSpawnLocation(World world) { + if (world == null) { return null; } - World world = player.getWorld(); Location spawnLoc = null; for (String priority : spawnPriority) { switch (priority.toLowerCase(Locale.ROOT).trim()) { @@ -222,13 +241,16 @@ public Location getSpawnLocation(Player player) { } /** - * Checks if a given location is a valid spawn point [!= (0,0,0)]. + * Checks if a given location is a valid spawn point [!= null && != (0,0,0)]. * * @param location The location to check * * @return True upon success, false otherwise */ private boolean isValidSpawnPoint(Location location) { + if (location == null) { + return false; + } if (location.getX() == 0 && location.getY() == 0 && location.getZ() == 0) { return false; } @@ -275,11 +297,30 @@ private boolean saveAuthMeConfig() { */ public Location getPlayerLocationOrSpawn(Player player) { if (player.getHealth() <= 0.0) { - return getSpawnLocation(player); + return getPlayerRespawnLocationOrSpawn(player); } return player.getLocation(); } + /** + * Return the player's effective respawn location if available, or the configured spawn location as fallback. + *

+ * We need this extra check for players who die and disconnect before actually respawning: in that case we want + * to preserve the server's real respawn target instead of collapsing everything back to AuthMe's configured spawn. + * The version-specific API call lives in {@link TeleportAdapter}: legacy builds use bed spawn, newer builds can + * use {@code Player#getRespawnLocation()} directly. + * + * @param player player to retrieve + * @return the player's respawn location if available, otherwise the configured spawn + */ + public Location getPlayerRespawnLocationOrSpawn(Player player) { + Location respawnLocation = teleportAdapter.getPlayerRespawnLocation(player); + if (respawnLocation != null && respawnLocation.getWorld() != null) { + return respawnLocation; + } + return getSpawnLocation(player); + } + /** * Build a {@link Location} object from the given path in the file configuration. * diff --git a/src/main/java/fr/xephi/authme/settings/WelcomeMessageConfiguration.java b/authme-core/src/main/java/fr/xephi/authme/settings/WelcomeMessageConfiguration.java similarity index 100% rename from src/main/java/fr/xephi/authme/settings/WelcomeMessageConfiguration.java rename to authme-core/src/main/java/fr/xephi/authme/settings/WelcomeMessageConfiguration.java diff --git a/src/main/java/fr/xephi/authme/settings/commandconfig/Command.java b/authme-core/src/main/java/fr/xephi/authme/settings/commandconfig/Command.java similarity index 100% rename from src/main/java/fr/xephi/authme/settings/commandconfig/Command.java rename to authme-core/src/main/java/fr/xephi/authme/settings/commandconfig/Command.java diff --git a/src/main/java/fr/xephi/authme/settings/commandconfig/CommandConfig.java b/authme-core/src/main/java/fr/xephi/authme/settings/commandconfig/CommandConfig.java similarity index 100% rename from src/main/java/fr/xephi/authme/settings/commandconfig/CommandConfig.java rename to authme-core/src/main/java/fr/xephi/authme/settings/commandconfig/CommandConfig.java diff --git a/src/main/java/fr/xephi/authme/settings/commandconfig/CommandManager.java b/authme-core/src/main/java/fr/xephi/authme/settings/commandconfig/CommandManager.java similarity index 98% rename from src/main/java/fr/xephi/authme/settings/commandconfig/CommandManager.java rename to authme-core/src/main/java/fr/xephi/authme/settings/commandconfig/CommandManager.java index 1018da0a8e..2aa3203122 100644 --- a/src/main/java/fr/xephi/authme/settings/commandconfig/CommandManager.java +++ b/authme-core/src/main/java/fr/xephi/authme/settings/commandconfig/CommandManager.java @@ -129,7 +129,7 @@ private void executeCommands(Player player, List commands if (predicate.test(cmd)) { long delay = cmd.getDelay(); if (delay > 0) { - bukkitService.scheduleSyncDelayedTask(() -> dispatchCommand(player, cmd), delay); + bukkitService.scheduleSyncDelayedTask(player, () -> dispatchCommand(player, cmd), delay); } else { dispatchCommand(player, cmd); } diff --git a/src/main/java/fr/xephi/authme/settings/commandconfig/CommandMigrationService.java b/authme-core/src/main/java/fr/xephi/authme/settings/commandconfig/CommandMigrationService.java similarity index 100% rename from src/main/java/fr/xephi/authme/settings/commandconfig/CommandMigrationService.java rename to authme-core/src/main/java/fr/xephi/authme/settings/commandconfig/CommandMigrationService.java diff --git a/src/main/java/fr/xephi/authme/settings/commandconfig/CommandSettingsHolder.java b/authme-core/src/main/java/fr/xephi/authme/settings/commandconfig/CommandSettingsHolder.java similarity index 100% rename from src/main/java/fr/xephi/authme/settings/commandconfig/CommandSettingsHolder.java rename to authme-core/src/main/java/fr/xephi/authme/settings/commandconfig/CommandSettingsHolder.java diff --git a/src/main/java/fr/xephi/authme/settings/commandconfig/Executor.java b/authme-core/src/main/java/fr/xephi/authme/settings/commandconfig/Executor.java similarity index 100% rename from src/main/java/fr/xephi/authme/settings/commandconfig/Executor.java rename to authme-core/src/main/java/fr/xephi/authme/settings/commandconfig/Executor.java diff --git a/src/main/java/fr/xephi/authme/settings/commandconfig/OnLoginCommand.java b/authme-core/src/main/java/fr/xephi/authme/settings/commandconfig/OnLoginCommand.java similarity index 100% rename from src/main/java/fr/xephi/authme/settings/commandconfig/OnLoginCommand.java rename to authme-core/src/main/java/fr/xephi/authme/settings/commandconfig/OnLoginCommand.java diff --git a/src/main/java/fr/xephi/authme/settings/properties/AuthMeSettingsRetriever.java b/authme-core/src/main/java/fr/xephi/authme/settings/properties/AuthMeSettingsRetriever.java similarity index 100% rename from src/main/java/fr/xephi/authme/settings/properties/AuthMeSettingsRetriever.java rename to authme-core/src/main/java/fr/xephi/authme/settings/properties/AuthMeSettingsRetriever.java diff --git a/src/main/java/fr/xephi/authme/settings/properties/BackupSettings.java b/authme-core/src/main/java/fr/xephi/authme/settings/properties/BackupSettings.java similarity index 100% rename from src/main/java/fr/xephi/authme/settings/properties/BackupSettings.java rename to authme-core/src/main/java/fr/xephi/authme/settings/properties/BackupSettings.java diff --git a/src/main/java/fr/xephi/authme/settings/properties/ConverterSettings.java b/authme-core/src/main/java/fr/xephi/authme/settings/properties/ConverterSettings.java similarity index 100% rename from src/main/java/fr/xephi/authme/settings/properties/ConverterSettings.java rename to authme-core/src/main/java/fr/xephi/authme/settings/properties/ConverterSettings.java diff --git a/src/main/java/fr/xephi/authme/settings/properties/DatabaseSettings.java b/authme-core/src/main/java/fr/xephi/authme/settings/properties/DatabaseSettings.java similarity index 100% rename from src/main/java/fr/xephi/authme/settings/properties/DatabaseSettings.java rename to authme-core/src/main/java/fr/xephi/authme/settings/properties/DatabaseSettings.java diff --git a/src/main/java/fr/xephi/authme/settings/properties/EmailSettings.java b/authme-core/src/main/java/fr/xephi/authme/settings/properties/EmailSettings.java similarity index 100% rename from src/main/java/fr/xephi/authme/settings/properties/EmailSettings.java rename to authme-core/src/main/java/fr/xephi/authme/settings/properties/EmailSettings.java diff --git a/src/main/java/fr/xephi/authme/settings/properties/HooksSettings.java b/authme-core/src/main/java/fr/xephi/authme/settings/properties/HooksSettings.java similarity index 100% rename from src/main/java/fr/xephi/authme/settings/properties/HooksSettings.java rename to authme-core/src/main/java/fr/xephi/authme/settings/properties/HooksSettings.java diff --git a/src/main/java/fr/xephi/authme/settings/properties/LimboSettings.java b/authme-core/src/main/java/fr/xephi/authme/settings/properties/LimboSettings.java similarity index 100% rename from src/main/java/fr/xephi/authme/settings/properties/LimboSettings.java rename to authme-core/src/main/java/fr/xephi/authme/settings/properties/LimboSettings.java diff --git a/src/main/java/fr/xephi/authme/settings/properties/PluginSettings.java b/authme-core/src/main/java/fr/xephi/authme/settings/properties/PluginSettings.java similarity index 77% rename from src/main/java/fr/xephi/authme/settings/properties/PluginSettings.java rename to authme-core/src/main/java/fr/xephi/authme/settings/properties/PluginSettings.java index 99e31757d9..884021ca48 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/PluginSettings.java +++ b/authme-core/src/main/java/fr/xephi/authme/settings/properties/PluginSettings.java @@ -34,6 +34,14 @@ public final class PluginSettings implements SettingsHolder { public static final Property MESSAGES_LANGUAGE = newProperty("settings.messagesLanguage", "en"); + @Comment({ + "When enabled, messages are sent to each player in their client language,", + "falling back to messagesLanguage if their locale is unavailable.", + "Disable this if you want all players to receive messages in the same language." + }) + public static final Property PER_PLAYER_LOCALE = + newProperty("settings.perPlayerLocale", true); + @Comment({ "Enables switching a player to defined permission groups before they log in.", "See below for a detailed explanation." @@ -76,8 +84,14 @@ public final class PluginSettings implements SettingsHolder { newProperty(LogLevel.class, "settings.logLevel", LogLevel.FINE); @Comment({ - "By default we schedule async tasks when talking to the database. If you want", - "typical communication with the database to happen synchronously, set this to false" + "Whether to run authentication work (password hashing, database queries) on async threads.", + "When true (strongly recommended), all CPU-intensive and I/O-bound operations — including", + "password hashing and database access — are offloaded from the main server thread, keeping", + "the server responsive during login and registration.", + "Setting this to false forces all of that work onto the main thread, which will cause", + "noticeable lag spikes on every login or registration attempt, especially with slow", + "algorithms (Argon2, BCrypt) or a remote database. Only set this to false if you have", + "a specific technical reason — it is not recommended under any normal circumstances." }) public static final Property USE_ASYNC_TASKS = newProperty("settings.useAsyncTasks", true); diff --git a/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java b/authme-core/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java similarity index 100% rename from src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java rename to authme-core/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java diff --git a/src/main/java/fr/xephi/authme/settings/properties/PurgeSettings.java b/authme-core/src/main/java/fr/xephi/authme/settings/properties/PurgeSettings.java similarity index 100% rename from src/main/java/fr/xephi/authme/settings/properties/PurgeSettings.java rename to authme-core/src/main/java/fr/xephi/authme/settings/properties/PurgeSettings.java diff --git a/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java b/authme-core/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java similarity index 93% rename from src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java rename to authme-core/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java index daefe92cf6..3c74b5c27b 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java +++ b/authme-core/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java @@ -107,6 +107,13 @@ public final class RegistrationSettings implements SettingsHolder { public static final Property PREVENT_OTHER_CASE = newProperty("settings.preventOtherCase", true); + @Comment({ + "Show a graphical dialog UI for login/register instead of chat messages.", + "Requires Minecraft 1.21.6+ on Spigot, or 1.21.11+ on Paper.", + "On older server versions, this option is automatically ignored."}) + public static final Property USE_DIALOG_UI = + newProperty("settings.registration.useDialogUi", true); + private RegistrationSettings() { } diff --git a/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java b/authme-core/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java similarity index 95% rename from src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java rename to authme-core/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java index 8794ba4473..96cf8fda7e 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java +++ b/authme-core/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java @@ -111,10 +111,16 @@ public final class RestrictionSettings implements SettingsHolder { newProperty("settings.restrictions.allowMovement", false); @Comment({ - "After how many seconds should players who fail to login or register", - "be kicked? Set to 0 to disable."}) - public static final Property TIMEOUT = - newProperty("settings.restrictions.timeout", 30); + "After how many seconds should players who fail to login be kicked?", + "Set to 0 to disable."}) + public static final Property LOGIN_TIMEOUT = + newProperty("settings.restrictions.loginTimeout", 30); + + @Comment({ + "After how many seconds should players who fail to register be kicked?", + "Set to 0 to disable."}) + public static final Property REGISTER_TIMEOUT = + newProperty("settings.restrictions.registerTimeout", 30); @Comment("Regex pattern of allowed characters in the player name.") public static final Property ALLOWED_NICKNAME_CHARACTERS = diff --git a/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java b/authme-core/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java similarity index 100% rename from src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java rename to authme-core/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java diff --git a/src/main/java/fr/xephi/authme/task/CleanupTask.java b/authme-core/src/main/java/fr/xephi/authme/task/CleanupTask.java similarity index 100% rename from src/main/java/fr/xephi/authme/task/CleanupTask.java rename to authme-core/src/main/java/fr/xephi/authme/task/CleanupTask.java diff --git a/src/main/java/fr/xephi/authme/task/MessageTask.java b/authme-core/src/main/java/fr/xephi/authme/task/MessageTask.java similarity index 100% rename from src/main/java/fr/xephi/authme/task/MessageTask.java rename to authme-core/src/main/java/fr/xephi/authme/task/MessageTask.java diff --git a/src/main/java/fr/xephi/authme/task/TimeoutTask.java b/authme-core/src/main/java/fr/xephi/authme/task/TimeoutTask.java similarity index 100% rename from src/main/java/fr/xephi/authme/task/TimeoutTask.java rename to authme-core/src/main/java/fr/xephi/authme/task/TimeoutTask.java diff --git a/src/main/java/fr/xephi/authme/task/purge/PurgeExecutor.java b/authme-core/src/main/java/fr/xephi/authme/task/purge/PurgeExecutor.java similarity index 100% rename from src/main/java/fr/xephi/authme/task/purge/PurgeExecutor.java rename to authme-core/src/main/java/fr/xephi/authme/task/purge/PurgeExecutor.java diff --git a/src/main/java/fr/xephi/authme/task/purge/PurgeService.java b/authme-core/src/main/java/fr/xephi/authme/task/purge/PurgeService.java similarity index 100% rename from src/main/java/fr/xephi/authme/task/purge/PurgeService.java rename to authme-core/src/main/java/fr/xephi/authme/task/purge/PurgeService.java diff --git a/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java b/authme-core/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java similarity index 100% rename from src/main/java/fr/xephi/authme/task/purge/PurgeTask.java rename to authme-core/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java diff --git a/src/main/java/fr/xephi/authme/util/AtomicIntervalCounter.java b/authme-core/src/main/java/fr/xephi/authme/util/AtomicIntervalCounter.java similarity index 100% rename from src/main/java/fr/xephi/authme/util/AtomicIntervalCounter.java rename to authme-core/src/main/java/fr/xephi/authme/util/AtomicIntervalCounter.java diff --git a/src/main/java/fr/xephi/authme/util/ExceptionUtils.java b/authme-core/src/main/java/fr/xephi/authme/util/ExceptionUtils.java similarity index 100% rename from src/main/java/fr/xephi/authme/util/ExceptionUtils.java rename to authme-core/src/main/java/fr/xephi/authme/util/ExceptionUtils.java diff --git a/src/main/java/fr/xephi/authme/util/FileUtils.java b/authme-core/src/main/java/fr/xephi/authme/util/FileUtils.java similarity index 100% rename from src/main/java/fr/xephi/authme/util/FileUtils.java rename to authme-core/src/main/java/fr/xephi/authme/util/FileUtils.java diff --git a/src/main/java/fr/xephi/authme/util/InternetProtocolUtils.java b/authme-core/src/main/java/fr/xephi/authme/util/InternetProtocolUtils.java similarity index 100% rename from src/main/java/fr/xephi/authme/util/InternetProtocolUtils.java rename to authme-core/src/main/java/fr/xephi/authme/util/InternetProtocolUtils.java diff --git a/src/main/java/fr/xephi/authme/util/PlayerUtils.java b/authme-core/src/main/java/fr/xephi/authme/util/PlayerUtils.java similarity index 100% rename from src/main/java/fr/xephi/authme/util/PlayerUtils.java rename to authme-core/src/main/java/fr/xephi/authme/util/PlayerUtils.java diff --git a/src/main/java/fr/xephi/authme/util/RandomStringUtils.java b/authme-core/src/main/java/fr/xephi/authme/util/RandomStringUtils.java similarity index 100% rename from src/main/java/fr/xephi/authme/util/RandomStringUtils.java rename to authme-core/src/main/java/fr/xephi/authme/util/RandomStringUtils.java diff --git a/src/main/java/fr/xephi/authme/util/StringUtils.java b/authme-core/src/main/java/fr/xephi/authme/util/StringUtils.java similarity index 100% rename from src/main/java/fr/xephi/authme/util/StringUtils.java rename to authme-core/src/main/java/fr/xephi/authme/util/StringUtils.java diff --git a/src/main/java/fr/xephi/authme/util/Utils.java b/authme-core/src/main/java/fr/xephi/authme/util/Utils.java similarity index 100% rename from src/main/java/fr/xephi/authme/util/Utils.java rename to authme-core/src/main/java/fr/xephi/authme/util/Utils.java diff --git a/src/main/java/fr/xephi/authme/util/UuidUtils.java b/authme-core/src/main/java/fr/xephi/authme/util/UuidUtils.java similarity index 100% rename from src/main/java/fr/xephi/authme/util/UuidUtils.java rename to authme-core/src/main/java/fr/xephi/authme/util/UuidUtils.java diff --git a/src/main/java/fr/xephi/authme/util/expiring/Duration.java b/authme-core/src/main/java/fr/xephi/authme/util/expiring/Duration.java similarity index 100% rename from src/main/java/fr/xephi/authme/util/expiring/Duration.java rename to authme-core/src/main/java/fr/xephi/authme/util/expiring/Duration.java diff --git a/src/main/java/fr/xephi/authme/util/expiring/ExpiringMap.java b/authme-core/src/main/java/fr/xephi/authme/util/expiring/ExpiringMap.java similarity index 100% rename from src/main/java/fr/xephi/authme/util/expiring/ExpiringMap.java rename to authme-core/src/main/java/fr/xephi/authme/util/expiring/ExpiringMap.java diff --git a/src/main/java/fr/xephi/authme/util/expiring/ExpiringSet.java b/authme-core/src/main/java/fr/xephi/authme/util/expiring/ExpiringSet.java similarity index 100% rename from src/main/java/fr/xephi/authme/util/expiring/ExpiringSet.java rename to authme-core/src/main/java/fr/xephi/authme/util/expiring/ExpiringSet.java diff --git a/src/main/java/fr/xephi/authme/util/expiring/TimedCounter.java b/authme-core/src/main/java/fr/xephi/authme/util/expiring/TimedCounter.java similarity index 100% rename from src/main/java/fr/xephi/authme/util/expiring/TimedCounter.java rename to authme-core/src/main/java/fr/xephi/authme/util/expiring/TimedCounter.java diff --git a/src/main/java/fr/xephi/authme/util/lazytags/DependentTag.java b/authme-core/src/main/java/fr/xephi/authme/util/lazytags/DependentTag.java similarity index 100% rename from src/main/java/fr/xephi/authme/util/lazytags/DependentTag.java rename to authme-core/src/main/java/fr/xephi/authme/util/lazytags/DependentTag.java diff --git a/src/main/java/fr/xephi/authme/util/lazytags/SimpleTag.java b/authme-core/src/main/java/fr/xephi/authme/util/lazytags/SimpleTag.java similarity index 100% rename from src/main/java/fr/xephi/authme/util/lazytags/SimpleTag.java rename to authme-core/src/main/java/fr/xephi/authme/util/lazytags/SimpleTag.java diff --git a/src/main/java/fr/xephi/authme/util/lazytags/Tag.java b/authme-core/src/main/java/fr/xephi/authme/util/lazytags/Tag.java similarity index 100% rename from src/main/java/fr/xephi/authme/util/lazytags/Tag.java rename to authme-core/src/main/java/fr/xephi/authme/util/lazytags/Tag.java diff --git a/src/main/java/fr/xephi/authme/util/lazytags/TagBuilder.java b/authme-core/src/main/java/fr/xephi/authme/util/lazytags/TagBuilder.java similarity index 100% rename from src/main/java/fr/xephi/authme/util/lazytags/TagBuilder.java rename to authme-core/src/main/java/fr/xephi/authme/util/lazytags/TagBuilder.java diff --git a/src/main/java/fr/xephi/authme/util/lazytags/TagReplacer.java b/authme-core/src/main/java/fr/xephi/authme/util/lazytags/TagReplacer.java similarity index 100% rename from src/main/java/fr/xephi/authme/util/lazytags/TagReplacer.java rename to authme-core/src/main/java/fr/xephi/authme/util/lazytags/TagReplacer.java diff --git a/src/main/java/fr/xephi/authme/util/lazytags/WrappedTagReplacer.java b/authme-core/src/main/java/fr/xephi/authme/util/lazytags/WrappedTagReplacer.java similarity index 100% rename from src/main/java/fr/xephi/authme/util/lazytags/WrappedTagReplacer.java rename to authme-core/src/main/java/fr/xephi/authme/util/lazytags/WrappedTagReplacer.java diff --git a/src/main/resources/commands.yml b/authme-core/src/main/resources/commands.yml similarity index 99% rename from src/main/resources/commands.yml rename to authme-core/src/main/resources/commands.yml index 6afd5a6005..63ae7c2a19 100644 --- a/src/main/resources/commands.yml +++ b/authme-core/src/main/resources/commands.yml @@ -1,4 +1,3 @@ - # This configuration file allows you to execute commands on various events. # Supported placeholders in commands: # %p is replaced with the player name. @@ -40,16 +39,16 @@ # command: 'say Uh oh! %p has many alt accounts!' # executor: CONSOLE # ifNumberOfAccountsAtLeast: 5 -# Commands to run for players logging in whose 'last login date' was empty -onFirstLogin: {} onJoin: {} onLogin: {} +onSessionLogin: {} +# Commands to run for players logging in whose 'last login date' was empty +onFirstLogin: {} +onRegister: {} +# Commands to run whenever a player is unregistered (by himself, or by an admin) +onUnregister: {} # These commands are called whenever a logged in player uses /logout or quits. # The commands are not run if a player that was not logged in quits the server. # Note: if your server crashes, these commands won't be run, so don't rely on them to undo # 'onLogin' commands that would be dangerous for non-logged in players to have! onLogout: {} -onRegister: {} -onSessionLogin: {} -# Commands to run whenever a player is unregistered (by himself, or by an admin) -onUnregister: {} \ No newline at end of file diff --git a/src/main/resources/email.html b/authme-core/src/main/resources/email.html similarity index 100% rename from src/main/resources/email.html rename to authme-core/src/main/resources/email.html diff --git a/src/main/resources/messages/help_bg.yml b/authme-core/src/main/resources/messages/help_bg.yml similarity index 100% rename from src/main/resources/messages/help_bg.yml rename to authme-core/src/main/resources/messages/help_bg.yml diff --git a/src/main/resources/messages/help_br.yml b/authme-core/src/main/resources/messages/help_br.yml similarity index 100% rename from src/main/resources/messages/help_br.yml rename to authme-core/src/main/resources/messages/help_br.yml diff --git a/src/main/resources/messages/help_cz.yml b/authme-core/src/main/resources/messages/help_cz.yml similarity index 100% rename from src/main/resources/messages/help_cz.yml rename to authme-core/src/main/resources/messages/help_cz.yml diff --git a/src/main/resources/messages/help_de.yml b/authme-core/src/main/resources/messages/help_de.yml similarity index 100% rename from src/main/resources/messages/help_de.yml rename to authme-core/src/main/resources/messages/help_de.yml diff --git a/src/main/resources/messages/help_en.yml b/authme-core/src/main/resources/messages/help_en.yml similarity index 100% rename from src/main/resources/messages/help_en.yml rename to authme-core/src/main/resources/messages/help_en.yml diff --git a/src/main/resources/messages/help_eo.yml b/authme-core/src/main/resources/messages/help_eo.yml similarity index 100% rename from src/main/resources/messages/help_eo.yml rename to authme-core/src/main/resources/messages/help_eo.yml diff --git a/src/main/resources/messages/help_es.yml b/authme-core/src/main/resources/messages/help_es.yml similarity index 100% rename from src/main/resources/messages/help_es.yml rename to authme-core/src/main/resources/messages/help_es.yml diff --git a/src/main/resources/messages/help_et.yml b/authme-core/src/main/resources/messages/help_et.yml similarity index 100% rename from src/main/resources/messages/help_et.yml rename to authme-core/src/main/resources/messages/help_et.yml diff --git a/src/main/resources/messages/help_eu.yml b/authme-core/src/main/resources/messages/help_eu.yml similarity index 100% rename from src/main/resources/messages/help_eu.yml rename to authme-core/src/main/resources/messages/help_eu.yml diff --git a/src/main/resources/messages/help_fr.yml b/authme-core/src/main/resources/messages/help_fr.yml similarity index 100% rename from src/main/resources/messages/help_fr.yml rename to authme-core/src/main/resources/messages/help_fr.yml diff --git a/src/main/resources/messages/help_hu.yml b/authme-core/src/main/resources/messages/help_hu.yml similarity index 100% rename from src/main/resources/messages/help_hu.yml rename to authme-core/src/main/resources/messages/help_hu.yml diff --git a/src/main/resources/messages/help_it.yml b/authme-core/src/main/resources/messages/help_it.yml similarity index 100% rename from src/main/resources/messages/help_it.yml rename to authme-core/src/main/resources/messages/help_it.yml diff --git a/src/main/resources/messages/help_ja.yml b/authme-core/src/main/resources/messages/help_ja.yml similarity index 100% rename from src/main/resources/messages/help_ja.yml rename to authme-core/src/main/resources/messages/help_ja.yml diff --git a/src/main/resources/messages/help_ko.yml b/authme-core/src/main/resources/messages/help_ko.yml similarity index 100% rename from src/main/resources/messages/help_ko.yml rename to authme-core/src/main/resources/messages/help_ko.yml diff --git a/src/main/resources/messages/help_lt.yml b/authme-core/src/main/resources/messages/help_lt.yml similarity index 100% rename from src/main/resources/messages/help_lt.yml rename to authme-core/src/main/resources/messages/help_lt.yml diff --git a/src/main/resources/messages/help_nl.yml b/authme-core/src/main/resources/messages/help_nl.yml similarity index 100% rename from src/main/resources/messages/help_nl.yml rename to authme-core/src/main/resources/messages/help_nl.yml diff --git a/src/main/resources/messages/help_pl.yml b/authme-core/src/main/resources/messages/help_pl.yml similarity index 100% rename from src/main/resources/messages/help_pl.yml rename to authme-core/src/main/resources/messages/help_pl.yml diff --git a/src/main/resources/messages/help_pt.yml b/authme-core/src/main/resources/messages/help_pt.yml similarity index 100% rename from src/main/resources/messages/help_pt.yml rename to authme-core/src/main/resources/messages/help_pt.yml diff --git a/src/main/resources/messages/help_ro.yml b/authme-core/src/main/resources/messages/help_ro.yml similarity index 100% rename from src/main/resources/messages/help_ro.yml rename to authme-core/src/main/resources/messages/help_ro.yml diff --git a/src/main/resources/messages/help_ru.yml b/authme-core/src/main/resources/messages/help_ru.yml similarity index 100% rename from src/main/resources/messages/help_ru.yml rename to authme-core/src/main/resources/messages/help_ru.yml diff --git a/src/main/resources/messages/help_si.yml b/authme-core/src/main/resources/messages/help_si.yml similarity index 100% rename from src/main/resources/messages/help_si.yml rename to authme-core/src/main/resources/messages/help_si.yml diff --git a/src/main/resources/messages/help_sr.yml b/authme-core/src/main/resources/messages/help_sr.yml similarity index 100% rename from src/main/resources/messages/help_sr.yml rename to authme-core/src/main/resources/messages/help_sr.yml diff --git a/src/main/resources/messages/help_vn.yml b/authme-core/src/main/resources/messages/help_vn.yml similarity index 100% rename from src/main/resources/messages/help_vn.yml rename to authme-core/src/main/resources/messages/help_vn.yml diff --git a/src/main/resources/messages/help_zhcn.yml b/authme-core/src/main/resources/messages/help_zhcn.yml similarity index 100% rename from src/main/resources/messages/help_zhcn.yml rename to authme-core/src/main/resources/messages/help_zhcn.yml diff --git a/src/main/resources/messages/help_zhhk.yml b/authme-core/src/main/resources/messages/help_zhhk.yml similarity index 100% rename from src/main/resources/messages/help_zhhk.yml rename to authme-core/src/main/resources/messages/help_zhhk.yml diff --git a/src/main/resources/messages/help_zhtw.yml b/authme-core/src/main/resources/messages/help_zhtw.yml similarity index 100% rename from src/main/resources/messages/help_zhtw.yml rename to authme-core/src/main/resources/messages/help_zhtw.yml diff --git a/src/main/resources/messages/messages_bg.yml b/authme-core/src/main/resources/messages/messages_bg.yml similarity index 100% rename from src/main/resources/messages/messages_bg.yml rename to authme-core/src/main/resources/messages/messages_bg.yml diff --git a/src/main/resources/messages/messages_br.yml b/authme-core/src/main/resources/messages/messages_br.yml similarity index 100% rename from src/main/resources/messages/messages_br.yml rename to authme-core/src/main/resources/messages/messages_br.yml diff --git a/src/main/resources/messages/messages_cz.yml b/authme-core/src/main/resources/messages/messages_cz.yml similarity index 100% rename from src/main/resources/messages/messages_cz.yml rename to authme-core/src/main/resources/messages/messages_cz.yml diff --git a/src/main/resources/messages/messages_de.yml b/authme-core/src/main/resources/messages/messages_de.yml similarity index 100% rename from src/main/resources/messages/messages_de.yml rename to authme-core/src/main/resources/messages/messages_de.yml diff --git a/src/main/resources/messages/messages_en.yml b/authme-core/src/main/resources/messages/messages_en.yml similarity index 100% rename from src/main/resources/messages/messages_en.yml rename to authme-core/src/main/resources/messages/messages_en.yml diff --git a/src/main/resources/messages/messages_eo.yml b/authme-core/src/main/resources/messages/messages_eo.yml similarity index 100% rename from src/main/resources/messages/messages_eo.yml rename to authme-core/src/main/resources/messages/messages_eo.yml diff --git a/src/main/resources/messages/messages_es.yml b/authme-core/src/main/resources/messages/messages_es.yml similarity index 100% rename from src/main/resources/messages/messages_es.yml rename to authme-core/src/main/resources/messages/messages_es.yml diff --git a/src/main/resources/messages/messages_et.yml b/authme-core/src/main/resources/messages/messages_et.yml similarity index 100% rename from src/main/resources/messages/messages_et.yml rename to authme-core/src/main/resources/messages/messages_et.yml diff --git a/src/main/resources/messages/messages_eu.yml b/authme-core/src/main/resources/messages/messages_eu.yml similarity index 100% rename from src/main/resources/messages/messages_eu.yml rename to authme-core/src/main/resources/messages/messages_eu.yml diff --git a/src/main/resources/messages/messages_fi.yml b/authme-core/src/main/resources/messages/messages_fi.yml similarity index 100% rename from src/main/resources/messages/messages_fi.yml rename to authme-core/src/main/resources/messages/messages_fi.yml diff --git a/src/main/resources/messages/messages_fr.yml b/authme-core/src/main/resources/messages/messages_fr.yml similarity index 100% rename from src/main/resources/messages/messages_fr.yml rename to authme-core/src/main/resources/messages/messages_fr.yml diff --git a/src/main/resources/messages/messages_gl.yml b/authme-core/src/main/resources/messages/messages_gl.yml similarity index 100% rename from src/main/resources/messages/messages_gl.yml rename to authme-core/src/main/resources/messages/messages_gl.yml diff --git a/src/main/resources/messages/messages_hu.yml b/authme-core/src/main/resources/messages/messages_hu.yml similarity index 100% rename from src/main/resources/messages/messages_hu.yml rename to authme-core/src/main/resources/messages/messages_hu.yml diff --git a/src/main/resources/messages/messages_id.yml b/authme-core/src/main/resources/messages/messages_id.yml similarity index 100% rename from src/main/resources/messages/messages_id.yml rename to authme-core/src/main/resources/messages/messages_id.yml diff --git a/src/main/resources/messages/messages_it.yml b/authme-core/src/main/resources/messages/messages_it.yml similarity index 100% rename from src/main/resources/messages/messages_it.yml rename to authme-core/src/main/resources/messages/messages_it.yml diff --git a/src/main/resources/messages/messages_ja.yml b/authme-core/src/main/resources/messages/messages_ja.yml similarity index 100% rename from src/main/resources/messages/messages_ja.yml rename to authme-core/src/main/resources/messages/messages_ja.yml diff --git a/src/main/resources/messages/messages_ko.yml b/authme-core/src/main/resources/messages/messages_ko.yml similarity index 100% rename from src/main/resources/messages/messages_ko.yml rename to authme-core/src/main/resources/messages/messages_ko.yml diff --git a/src/main/resources/messages/messages_lt.yml b/authme-core/src/main/resources/messages/messages_lt.yml similarity index 100% rename from src/main/resources/messages/messages_lt.yml rename to authme-core/src/main/resources/messages/messages_lt.yml diff --git a/src/main/resources/messages/messages_nl.yml b/authme-core/src/main/resources/messages/messages_nl.yml similarity index 100% rename from src/main/resources/messages/messages_nl.yml rename to authme-core/src/main/resources/messages/messages_nl.yml diff --git a/src/main/resources/messages/messages_pl.yml b/authme-core/src/main/resources/messages/messages_pl.yml similarity index 100% rename from src/main/resources/messages/messages_pl.yml rename to authme-core/src/main/resources/messages/messages_pl.yml diff --git a/src/main/resources/messages/messages_pt.yml b/authme-core/src/main/resources/messages/messages_pt.yml similarity index 100% rename from src/main/resources/messages/messages_pt.yml rename to authme-core/src/main/resources/messages/messages_pt.yml diff --git a/src/main/resources/messages/messages_ro.yml b/authme-core/src/main/resources/messages/messages_ro.yml similarity index 100% rename from src/main/resources/messages/messages_ro.yml rename to authme-core/src/main/resources/messages/messages_ro.yml diff --git a/src/main/resources/messages/messages_ru.yml b/authme-core/src/main/resources/messages/messages_ru.yml similarity index 100% rename from src/main/resources/messages/messages_ru.yml rename to authme-core/src/main/resources/messages/messages_ru.yml diff --git a/src/main/resources/messages/messages_si.yml b/authme-core/src/main/resources/messages/messages_si.yml similarity index 100% rename from src/main/resources/messages/messages_si.yml rename to authme-core/src/main/resources/messages/messages_si.yml diff --git a/src/main/resources/messages/messages_sk.yml b/authme-core/src/main/resources/messages/messages_sk.yml similarity index 100% rename from src/main/resources/messages/messages_sk.yml rename to authme-core/src/main/resources/messages/messages_sk.yml diff --git a/src/main/resources/messages/messages_sr.yml b/authme-core/src/main/resources/messages/messages_sr.yml similarity index 100% rename from src/main/resources/messages/messages_sr.yml rename to authme-core/src/main/resources/messages/messages_sr.yml diff --git a/src/main/resources/messages/messages_tr.yml b/authme-core/src/main/resources/messages/messages_tr.yml similarity index 100% rename from src/main/resources/messages/messages_tr.yml rename to authme-core/src/main/resources/messages/messages_tr.yml diff --git a/src/main/resources/messages/messages_uk.yml b/authme-core/src/main/resources/messages/messages_uk.yml similarity index 100% rename from src/main/resources/messages/messages_uk.yml rename to authme-core/src/main/resources/messages/messages_uk.yml diff --git a/src/main/resources/messages/messages_vn.yml b/authme-core/src/main/resources/messages/messages_vn.yml similarity index 100% rename from src/main/resources/messages/messages_vn.yml rename to authme-core/src/main/resources/messages/messages_vn.yml diff --git a/src/main/resources/messages/messages_zhcn.yml b/authme-core/src/main/resources/messages/messages_zhcn.yml similarity index 100% rename from src/main/resources/messages/messages_zhcn.yml rename to authme-core/src/main/resources/messages/messages_zhcn.yml diff --git a/src/main/resources/messages/messages_zhhk.yml b/authme-core/src/main/resources/messages/messages_zhhk.yml similarity index 100% rename from src/main/resources/messages/messages_zhhk.yml rename to authme-core/src/main/resources/messages/messages_zhhk.yml diff --git a/src/main/resources/messages/messages_zhmc.yml b/authme-core/src/main/resources/messages/messages_zhmc.yml similarity index 100% rename from src/main/resources/messages/messages_zhmc.yml rename to authme-core/src/main/resources/messages/messages_zhmc.yml diff --git a/src/main/resources/messages/messages_zhtw.yml b/authme-core/src/main/resources/messages/messages_zhtw.yml similarity index 100% rename from src/main/resources/messages/messages_zhtw.yml rename to authme-core/src/main/resources/messages/messages_zhtw.yml diff --git a/src/main/resources/otheraccounts.yml b/authme-core/src/main/resources/otheraccounts.yml similarity index 100% rename from src/main/resources/otheraccounts.yml rename to authme-core/src/main/resources/otheraccounts.yml diff --git a/src/main/resources/players.yml b/authme-core/src/main/resources/players.yml similarity index 100% rename from src/main/resources/players.yml rename to authme-core/src/main/resources/players.yml diff --git a/src/main/resources/plugin.yml b/authme-core/src/main/resources/plugin.yml similarity index 100% rename from src/main/resources/plugin.yml rename to authme-core/src/main/resources/plugin.yml index 59ee6a764c..2e027f3400 100644 --- a/src/main/resources/plugin.yml +++ b/authme-core/src/main/resources/plugin.yml @@ -198,12 +198,12 @@ permissions: authme.allowmultipleaccounts: description: Permission to be able to register multiple accounts. default: op - authme.bypassbungeesend: - description: Permission node to bypass BungeeCord server teleportation. - default: false authme.bypassantibot: description: Permission node to bypass AntiBot protection. default: op + authme.bypassbungeesend: + description: Permission node to bypass BungeeCord server teleportation. + default: false authme.bypasscountrycheck: description: Permission to bypass the GeoIp country code check. default: false diff --git a/src/main/resources/recovery_code_email.html b/authme-core/src/main/resources/recovery_code_email.html similarity index 100% rename from src/main/resources/recovery_code_email.html rename to authme-core/src/main/resources/recovery_code_email.html diff --git a/src/main/resources/spawn.yml b/authme-core/src/main/resources/spawn.yml similarity index 100% rename from src/main/resources/spawn.yml rename to authme-core/src/main/resources/spawn.yml diff --git a/src/main/resources/verification_code_email.html b/authme-core/src/main/resources/verification_code_email.html similarity index 100% rename from src/main/resources/verification_code_email.html rename to authme-core/src/main/resources/verification_code_email.html diff --git a/src/main/resources/welcome.txt b/authme-core/src/main/resources/welcome.txt similarity index 100% rename from src/main/resources/welcome.txt rename to authme-core/src/main/resources/welcome.txt diff --git a/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java b/authme-core/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java similarity index 80% rename from src/test/java/fr/xephi/authme/AuthMeInitializationTest.java rename to authme-core/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java index f1fd62597a..0e2aaaea3f 100644 --- a/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import ch.jalu.configme.resource.PropertyReader; import ch.jalu.configme.resource.PropertyResource; import ch.jalu.injector.Injector; @@ -10,6 +14,12 @@ import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.initialization.DataFolder; import fr.xephi.authme.listener.BlockListener; +import fr.xephi.authme.platform.ChatAdapter; +import fr.xephi.authme.platform.DialogAdapter; +import fr.xephi.authme.platform.EventRegistrationAdapter; +import fr.xephi.authme.platform.PlatformAdapter; +import fr.xephi.authme.platform.SchedulingAdapter; +import fr.xephi.authme.platform.TeleportAdapter; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.process.Management; import fr.xephi.authme.process.login.ProcessSyncPlayerLogin; @@ -25,13 +35,10 @@ import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPluginLoader; import org.bukkit.scheduler.BukkitScheduler; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import fr.xephi.authme.TempFolder; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.io.File; import java.io.IOException; @@ -52,7 +59,8 @@ * Integration test verifying that all services can be initialized in {@link AuthMe} * with the {@link Injector}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class AuthMeInitializationTest { @Mock @@ -63,11 +71,9 @@ public class AuthMeInitializationTest { private AuthMe authMe; private File dataFolder; + public TempFolder temporaryFolder = new TempFolder(); - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); - - @Before + @BeforeEach public void initAuthMe() throws IOException { dataFolder = temporaryFolder.newFolder(); File settingsFile = new File(dataFolder, "config.yml"); @@ -77,7 +83,6 @@ public void initAuthMe() throws IOException { // Mock / wire various Bukkit components ReflectionTestUtils.setField(Bukkit.class, null, "server", server); - given(server.getPluginManager()).willReturn(pluginManager); // PluginDescriptionFile is final: need to create a sample one PluginDescriptionFile descriptionFile = new PluginDescriptionFile( @@ -100,6 +105,7 @@ public void shouldInitializeAllServices() { Injector injector = new InjectorBuilder() .addDefaultHandlers("fr.xephi.authme") .create(); + given(server.getPluginManager()).willReturn(pluginManager); injector.provide(DataFolder.class, dataFolder); injector.register(Server.class, server); injector.register(PluginManager.class, pluginManager); @@ -108,6 +114,14 @@ public void shouldInitializeAllServices() { injector.register(Settings.class, settings); injector.register(DataSource.class, mock(DataSource.class)); injector.register(BukkitService.class, mock(BukkitService.class)); + PlatformAdapter platformAdapter = mock(PlatformAdapter.class); + given(platformAdapter.getAdditionalListeners()).willReturn(Collections.emptyList()); + injector.register(PlatformAdapter.class, platformAdapter); + injector.register(TeleportAdapter.class, platformAdapter); + injector.register(ChatAdapter.class, platformAdapter); + injector.register(EventRegistrationAdapter.class, platformAdapter); + injector.register(SchedulingAdapter.class, platformAdapter); + injector.register(DialogAdapter.class, platformAdapter); // when authMe.instantiateServices(injector); @@ -143,3 +157,5 @@ public void shouldHandlePrematureShutdownGracefully() { verify(scheduler).getActiveWorkers(); // via TaskCloser } } + + diff --git a/src/test/java/fr/xephi/authme/AuthMeMatchers.java b/authme-core/src/test/java/fr/xephi/authme/AuthMeMatchers.java similarity index 100% rename from src/test/java/fr/xephi/authme/AuthMeMatchers.java rename to authme-core/src/test/java/fr/xephi/authme/AuthMeMatchers.java diff --git a/src/test/java/fr/xephi/authme/ClassCollector.java b/authme-core/src/test/java/fr/xephi/authme/ClassCollector.java similarity index 100% rename from src/test/java/fr/xephi/authme/ClassCollector.java rename to authme-core/src/test/java/fr/xephi/authme/ClassCollector.java diff --git a/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java b/authme-core/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java similarity index 96% rename from src/test/java/fr/xephi/authme/ClassesConsistencyTest.java rename to authme-core/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java index cbabf8f4c5..4ac728d406 100644 --- a/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/ClassesConsistencyTest.java @@ -11,13 +11,14 @@ import fr.xephi.authme.datasource.columnshandler.DataSourceColumn; import fr.xephi.authme.datasource.columnshandler.PlayerAuthColumn; import fr.xephi.authme.datasource.mysqlextensions.MySqlExtension; +import fr.xephi.authme.security.crypts.AbstractPbkdf2; import fr.xephi.authme.initialization.HasCleanup; import fr.xephi.authme.process.register.executors.RegistrationMethod; import fr.xephi.authme.util.expiring.ExpiringMap; import fr.xephi.authme.util.expiring.ExpiringSet; import fr.xephi.authme.util.expiring.TimedCounter; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.File; import java.lang.reflect.Field; @@ -35,7 +36,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; /** * Contains consistency tests across all AuthMe classes. @@ -62,6 +63,7 @@ int.class, long.class, float.class, String.class, File.class, Enum.class, collec /** Classes excluded from the field visibility test. */ private static final Set> CLASSES_EXCLUDED_FROM_VISIBILITY_TEST = ImmutableSet.of( + AbstractPbkdf2.class, // has immutable protected fields used by all children MySqlExtension.class, // has immutable protected fields used by all children AbstractSqlDataSource.class, // protected members for inheritance Columns.class, // uses non-static String constants, which is safe @@ -172,3 +174,4 @@ private static Class collectionsUnmodifiableList() { return Collections.unmodifiableList(new ArrayList<>()).getClass(); } } + diff --git a/src/test/java/fr/xephi/authme/CodeClimateConfigTest.java b/authme-core/src/test/java/fr/xephi/authme/CodeClimateConfigTest.java similarity index 89% rename from src/test/java/fr/xephi/authme/CodeClimateConfigTest.java rename to authme-core/src/test/java/fr/xephi/authme/CodeClimateConfigTest.java index 669d10b306..da556005a6 100644 --- a/src/test/java/fr/xephi/authme/CodeClimateConfigTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/CodeClimateConfigTest.java @@ -2,7 +2,7 @@ import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.File; import java.util.List; @@ -11,14 +11,14 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; /** * Consistency test for the CodeClimate configuration file. */ public class CodeClimateConfigTest { - private static final String CONFIG_FILE = ".codeclimate.yml"; + private static final String CONFIG_FILE = "../.codeclimate.yml"; @Test public void shouldHaveExistingClassesInExclusions() { @@ -42,3 +42,4 @@ private static void removeTestsExclusionOrThrow(List excludePaths) { wasRemoved, equalTo(true)); } } + diff --git a/src/test/java/fr/xephi/authme/ConsoleLoggerTest.java b/authme-core/src/test/java/fr/xephi/authme/ConsoleLoggerTest.java similarity index 90% rename from src/test/java/fr/xephi/authme/ConsoleLoggerTest.java rename to authme-core/src/test/java/fr/xephi/authme/ConsoleLoggerTest.java index 19a64df2d5..80c2052436 100644 --- a/src/test/java/fr/xephi/authme/ConsoleLoggerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/ConsoleLoggerTest.java @@ -1,18 +1,19 @@ package fr.xephi.authme; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.output.LogLevel; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.SecuritySettings; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import fr.xephi.authme.TempFolder; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.io.File; import java.io.FileWriter; @@ -32,6 +33,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -40,20 +42,19 @@ /** * Test for {@link ConsoleLogger}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class ConsoleLoggerTest { private ConsoleLogger consoleLogger; @Mock private Logger logger; - - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); + public TempFolder temporaryFolder = new TempFolder(); private File logFile; - @Before + @BeforeEach public void setMockLogger() throws IOException { File folder = temporaryFolder.newFolder(); File logFile = new File(folder, "authme.log"); @@ -65,7 +66,7 @@ public void setMockLogger() throws IOException { this.consoleLogger = new ConsoleLogger("test"); } - @After + @AfterEach public void closeFileHandlers() { ConsoleLogger.closeFileWriter(); } @@ -75,7 +76,7 @@ public void closeFileHandlers() { * is that we no longer enable logging to a file as the log file we've supplied will no longer * be around after this test class has finished. */ - @AfterClass + @AfterAll public static void resetConsoleToDefault() { ConsoleLogger.initializeSharedSettings(newSettings(false, LogLevel.INFO)); } @@ -207,8 +208,8 @@ public void shouldHandleExceptionOnFileWriterClose() throws IOException { private static Settings newSettings(boolean logToFile, LogLevel logLevel) { Settings settings = mock(Settings.class); - given(settings.getProperty(SecuritySettings.USE_LOGGING)).willReturn(logToFile); - given(settings.getProperty(PluginSettings.LOG_LEVEL)).willReturn(logLevel); + lenient().when(settings.getProperty(SecuritySettings.USE_LOGGING)).thenReturn(logToFile); + lenient().when(settings.getProperty(PluginSettings.LOG_LEVEL)).thenReturn(logLevel); return settings; } @@ -225,3 +226,5 @@ public String toString() { } } } + + diff --git a/authme-core/src/test/java/fr/xephi/authme/DelayedInjectionExtension.java b/authme-core/src/test/java/fr/xephi/authme/DelayedInjectionExtension.java new file mode 100644 index 0000000000..7793fd87cd --- /dev/null +++ b/authme-core/src/test/java/fr/xephi/authme/DelayedInjectionExtension.java @@ -0,0 +1,205 @@ +package fr.xephi.authme; + +import ch.jalu.injector.Injector; +import ch.jalu.injector.InjectorBuilder; +import ch.jalu.injector.context.ResolutionContext; +import ch.jalu.injector.exceptions.InjectorException; +import ch.jalu.injector.handlers.Handler; +import ch.jalu.injector.handlers.instantiation.Resolution; +import ch.jalu.injector.handlers.instantiation.SimpleResolution; +import ch.jalu.injector.handlers.postconstruct.PostConstructMethodInvoker; +import ch.jalu.injector.testing.BeforeInjecting; +import ch.jalu.injector.testing.InjectDelayed; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.Spy; + +import javax.inject.Inject; +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * JUnit 5 equivalent of Jalu Injector's DelayedInjectionRunner. + */ +public class DelayedInjectionExtension implements BeforeEachCallback, AfterEachCallback { + + private static final ExtensionContext.Namespace NAMESPACE = + ExtensionContext.Namespace.create(DelayedInjectionExtension.class); + + @Override + public void beforeEach(ExtensionContext context) throws Exception { + Object testInstance = context.getRequiredTestInstance(); + AutoCloseable mocks = MockitoAnnotations.openMocks(testInstance); + context.getStore(NAMESPACE).put(testInstance, mocks); + + invokeBeforeInjectingMethods(testInstance); + injectDelayedFields(testInstance); + } + + @Override + public void afterEach(ExtensionContext context) throws Exception { + Object testInstance = context.getRequiredTestInstance(); + AutoCloseable mocks = context.getStore(NAMESPACE).remove(testInstance, AutoCloseable.class); + if (mocks != null) { + mocks.close(); + } + } + + private static void invokeBeforeInjectingMethods(Object testInstance) throws ReflectiveOperationException { + for (Method method : getAnnotatedMethods(testInstance.getClass(), BeforeInjecting.class)) { + method.setAccessible(true); + method.invoke(testInstance); + } + } + + private static void injectDelayedFields(Object testInstance) throws ReflectiveOperationException { + List delayedFields = getAnnotatedFields(testInstance.getClass(), InjectDelayed.class); + if (delayedFields.isEmpty()) { + return; + } + + Injector injector = new InjectorBuilder() + .addHandlers( + new TestAnnotationResolver(testInstance.getClass(), testInstance), + new MockDependencyHandler(testInstance.getClass(), testInstance, delayedFields), + new PostConstructMethodInvoker()) + .addHandlers(InjectorBuilder.createInstantiationProviders("")) + .create(); + + for (Field field : delayedFields) { + field.setAccessible(true); + if (field.get(testInstance) != null) { + throw new IllegalStateException("Field with @InjectDelayed must be null on startup. Field '" + + field.getName() + "' is not null"); + } + field.set(testInstance, injector.getSingleton(field.getType())); + } + } + + private static List getAnnotatedMethods(Class type, Class annotationType) { + List methods = new ArrayList<>(); + Class current = type; + while (current != null && current != Object.class) { + for (Method method : current.getDeclaredMethods()) { + if (method.isAnnotationPresent(annotationType)) { + methods.add(0, method); + } + } + current = current.getSuperclass(); + } + return methods; + } + + private static List getAnnotatedFields(Class type, Class annotationType) { + List fields = new ArrayList<>(); + Class current = type; + while (current != null && current != Object.class) { + for (Field field : current.getDeclaredFields()) { + if (field.isAnnotationPresent(annotationType)) { + fields.add(field); + } + } + current = current.getSuperclass(); + } + return fields; + } + + private static final class MockDependencyHandler implements Handler { + + private final Class testClass; + private final Object target; + private final Set> fieldsToInject; + + private boolean areMocksRegistered; + + private MockDependencyHandler(Class testClass, Object target, List delayedFields) { + this.testClass = testClass; + this.target = target; + this.fieldsToInject = new HashSet<>(); + for (Field field : delayedFields) { + fieldsToInject.add(field.getType()); + } + } + + @Override + public Resolution resolve(ResolutionContext context) throws IllegalAccessException { + Injector injector = context.getInjector(); + if (!areMocksRegistered) { + registerAllMocks(injector); + areMocksRegistered = true; + } + + Class requestedType = context.getIdentifier().getTypeAsClass(); + Object existingObject = injector.getIfAvailable(requestedType); + if (existingObject != null) { + return new SimpleResolution<>(existingObject); + } + if (fieldsToInject.contains(requestedType)) { + return null; + } + throw new InjectorException("No mock found for '" + requestedType + + "'. All dependencies of @InjectDelayed must be provided as @Mock or @InjectDelayed fields"); + } + + private void registerAllMocks(Injector injector) throws IllegalAccessException { + for (Field field : getAnnotatedFields(testClass, Mock.class)) { + field.setAccessible(true); + registerMock(injector, field, field.get(target)); + } + } + + @SuppressWarnings("unchecked") + private static void registerMock(Injector injector, Field field, Object mock) { + injector.register((Class) field.getType(), (T) mock); + } + } + + private static final class TestAnnotationResolver implements Handler { + + private static final Set> IGNORED_ANNOTATIONS = Set.of( + Inject.class, org.mockito.InjectMocks.class, Mock.class, Spy.class, InjectDelayed.class); + + private final Class testClass; + private final Object target; + + private TestAnnotationResolver(Class testClass, Object target) { + this.testClass = testClass; + this.target = target; + } + + @Override + public Resolution resolve(ResolutionContext context) throws IllegalAccessException { + Class requestedType = context.getIdentifier().getTypeAsClass(); + for (Annotation annotation : context.getIdentifier().getAnnotations()) { + Object resolvedValue = resolveByAnnotation(annotation.annotationType(), requestedType); + if (resolvedValue != null) { + return new SimpleResolution<>(resolvedValue); + } + } + return null; + } + + private Object resolveByAnnotation(Class annotationType, Class requestedType) + throws IllegalAccessException { + if (IGNORED_ANNOTATIONS.contains(annotationType)) { + return null; + } + + for (Field field : getAnnotatedFields(testClass, annotationType)) { + if (requestedType.isAssignableFrom(field.getType())) { + field.setAccessible(true); + return field.get(target); + } + } + return null; + } + } +} diff --git a/src/test/java/fr/xephi/authme/IsEqualByReflectionMatcher.java b/authme-core/src/test/java/fr/xephi/authme/IsEqualByReflectionMatcher.java similarity index 98% rename from src/test/java/fr/xephi/authme/IsEqualByReflectionMatcher.java rename to authme-core/src/test/java/fr/xephi/authme/IsEqualByReflectionMatcher.java index 2acf5ee708..b45f0e9982 100644 --- a/src/test/java/fr/xephi/authme/IsEqualByReflectionMatcher.java +++ b/authme-core/src/test/java/fr/xephi/authme/IsEqualByReflectionMatcher.java @@ -10,7 +10,7 @@ import java.util.List; import java.util.Objects; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; /** * Matcher which checks with reflection that all fields have the same value. diff --git a/src/test/java/fr/xephi/authme/ReflectionTestUtils.java b/authme-core/src/test/java/fr/xephi/authme/ReflectionTestUtils.java similarity index 100% rename from src/test/java/fr/xephi/authme/ReflectionTestUtils.java rename to authme-core/src/test/java/fr/xephi/authme/ReflectionTestUtils.java diff --git a/authme-core/src/test/java/fr/xephi/authme/TempFolder.java b/authme-core/src/test/java/fr/xephi/authme/TempFolder.java new file mode 100644 index 0000000000..9ff91a1546 --- /dev/null +++ b/authme-core/src/test/java/fr/xephi/authme/TempFolder.java @@ -0,0 +1,38 @@ +package fr.xephi.authme; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +/** + * Minimal test helper replacing JUnit 4's TemporaryFolder rule. + */ +public class TempFolder { + + private final File root; + + public TempFolder() { + try { + root = Files.createTempDirectory("authme-tests-").toFile(); + root.deleteOnExit(); + } catch (IOException e) { + throw new IllegalStateException("Could not create temporary folder", e); + } + } + + public File newFolder() throws IOException { + File folder = Files.createTempDirectory(root.toPath(), "folder-").toFile(); + folder.deleteOnExit(); + return folder; + } + + public File newFile() throws IOException { + File file = Files.createTempFile(root.toPath(), "file-", ".tmp").toFile(); + file.deleteOnExit(); + return file; + } + + public File getRoot() { + return root; + } +} diff --git a/src/test/java/fr/xephi/authme/TestHelper.java b/authme-core/src/test/java/fr/xephi/authme/TestHelper.java similarity index 96% rename from src/test/java/fr/xephi/authme/TestHelper.java rename to authme-core/src/test/java/fr/xephi/authme/TestHelper.java index ad50c5119b..11fd5ab35c 100644 --- a/src/test/java/fr/xephi/authme/TestHelper.java +++ b/authme-core/src/test/java/fr/xephi/authme/TestHelper.java @@ -18,6 +18,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.lenient; /** * AuthMe test utilities. @@ -102,7 +103,7 @@ public static void mockIpAddressToPlayer(Player player, String ip) { try { InetAddress inetAddress = InetAddress.getByName(ip); InetSocketAddress inetSocketAddress = new InetSocketAddress(inetAddress, 8093); - given(player.getAddress()).willReturn(inetSocketAddress); + lenient().when(player.getAddress()).thenReturn(inetSocketAddress); } catch (UnknownHostException e) { throw new IllegalStateException("Invalid IP address: " + ip, e); } diff --git a/src/test/java/fr/xephi/authme/api/v3/AuthMeApiTest.java b/authme-core/src/test/java/fr/xephi/authme/api/v3/AuthMeApiTest.java similarity index 97% rename from src/test/java/fr/xephi/authme/api/v3/AuthMeApiTest.java rename to authme-core/src/test/java/fr/xephi/authme/api/v3/AuthMeApiTest.java index b52f7908b1..e276559ea1 100644 --- a/src/test/java/fr/xephi/authme/api/v3/AuthMeApiTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/api/v3/AuthMeApiTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.api.v3; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.AuthMe; import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.data.auth.PlayerAuth; @@ -17,12 +21,11 @@ import org.bukkit.Server; import org.bukkit.World; import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.time.Instant; import java.util.Arrays; @@ -51,7 +54,8 @@ /** * Test for {@link AuthMeApi}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class AuthMeApiTest { @InjectMocks @@ -71,6 +75,8 @@ public class AuthMeApiTest { private AuthMe authMe; @Mock private GeoIpService geoIpService; + @Captor + private ArgumentCaptor authCaptor; @Test public void shouldReturnInstanceOrNull() { @@ -467,7 +473,6 @@ public void shouldRegisterPlayer() { // then assertThat(result, equalTo(true)); verify(passwordSecurity).computeHash(password, name.toLowerCase(Locale.ROOT)); - ArgumentCaptor authCaptor = ArgumentCaptor.forClass(PlayerAuth.class); verify(dataSource).saveAuth(authCaptor.capture()); assertThat(authCaptor.getValue().getNickname(), equalTo(name.toLowerCase(Locale.ROOT))); assertThat(authCaptor.getValue().getRealName(), equalTo(name)); @@ -556,3 +561,5 @@ private static Player mockPlayerWithName(String name) { return player; } } + + diff --git a/src/test/java/fr/xephi/authme/api/v3/AuthMePlayerImplTest.java b/authme-core/src/test/java/fr/xephi/authme/api/v3/AuthMePlayerImplTest.java similarity index 98% rename from src/test/java/fr/xephi/authme/api/v3/AuthMePlayerImplTest.java rename to authme-core/src/test/java/fr/xephi/authme/api/v3/AuthMePlayerImplTest.java index aa8b50c953..50e98e5a7a 100644 --- a/src/test/java/fr/xephi/authme/api/v3/AuthMePlayerImplTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/api/v3/AuthMePlayerImplTest.java @@ -4,7 +4,7 @@ import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Instant; import java.util.Optional; @@ -93,3 +93,4 @@ protected boolean matchesSafely(Optional item) { }; } } + diff --git a/src/test/java/fr/xephi/authme/command/CommandConsistencyTest.java b/authme-core/src/test/java/fr/xephi/authme/command/CommandConsistencyTest.java similarity index 98% rename from src/test/java/fr/xephi/authme/command/CommandConsistencyTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/CommandConsistencyTest.java index fc3b3aeee7..4233c38748 100644 --- a/src/test/java/fr/xephi/authme/command/CommandConsistencyTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/CommandConsistencyTest.java @@ -3,7 +3,7 @@ import org.bukkit.configuration.MemorySection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Collection; @@ -82,3 +82,4 @@ private static Map> getLabelsFromPluginFile() { } } + diff --git a/src/test/java/fr/xephi/authme/command/CommandHandlerTest.java b/authme-core/src/test/java/fr/xephi/authme/command/CommandHandlerTest.java similarity index 90% rename from src/test/java/fr/xephi/authme/command/CommandHandlerTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/CommandHandlerTest.java index 81b0733af3..60abe4e35d 100644 --- a/src/test/java/fr/xephi/authme/command/CommandHandlerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/CommandHandlerTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.command; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import ch.jalu.injector.factory.Factory; import com.google.common.collect.Sets; import fr.xephi.authme.command.TestCommandsUtil.TestLoginCommand; @@ -10,13 +14,12 @@ import fr.xephi.authme.message.Messages; import fr.xephi.authme.permission.PermissionsManager; import org.bukkit.command.CommandSender; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; -import org.mockito.junit.MockitoJUnitRunner; import org.mockito.stubbing.Answer; import java.util.Collections; @@ -47,7 +50,8 @@ // Justification: It's more readable to use asList() everywhere in the test when we often generated two lists where one // often consists of only one element, e.g. myMethod(asList("authme"), asList("my", "args"), ...) @SuppressWarnings("ArraysAsListWithZeroOrOneArgument") -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class CommandHandlerTest { private CommandHandler handler; @@ -62,10 +66,12 @@ public class CommandHandlerTest { private Messages messages; @Mock private HelpProvider helpProvider; + @Captor + private ArgumentCaptor cmdCaptor; private Map, ExecutableCommand> mockedCommands = new HashMap<>(); - @Before + @BeforeEach @SuppressWarnings("unchecked") public void initializeCommandMapper() { given(commandMapper.getCommandClasses()).willReturn(Sets.newHashSet( @@ -123,6 +129,25 @@ public void shouldCallMappedCommandWithArgs() { verify(sender, never()).sendMessage(anyString()); } + @Test + public void shouldCallMappedCommandWithPreSplitParts() { + // given + CommandSender sender = mock(CommandSender.class); + CommandDescription command = mock(CommandDescription.class); + doReturn(TestLoginCommand.class).when(command).getExecutableCommand(); + given(commandMapper.mapPartsToCommand(any(CommandSender.class), anyList())) + .willReturn(new FoundCommandResult(command, asList("Authme", "Login"), asList("myPass"), 0.0, SUCCESS)); + + // when + handler.processCommand(sender, asList("Authme", "Login", "myPass")); + + // then + ExecutableCommand executableCommand = mockedCommands.get(TestLoginCommand.class); + verify(commandMapper).mapPartsToCommand(sender, asList("Authme", "Login", "myPass")); + verify(executableCommand).executeCommand(sender, asList("myPass")); + verify(sender, never()).sendMessage(anyString()); + } + @Test public void shouldNotCallExecutableCommandIfNoPermission() { // given @@ -246,7 +271,6 @@ public void shouldNotCallExecutableForUnknownLabelAndHaveSuggestion() { verify(commandMapper).mapPartsToCommand(sender, asList("unreg", "testPlayer")); verify(command, never()).getExecutableCommand(); verify(messages).send(sender, MessageKey.UNKNOWN_COMMAND); - ArgumentCaptor cmdCaptor = ArgumentCaptor.forClass(String.class); verify(messages).send(eq(sender), eq(MessageKey.COMMAND_DID_YOU_MEAN), cmdCaptor.capture()); assertThat(cmdCaptor.getValue(), containsString("test_cmd")); verify(messages).send(eq(sender), eq(MessageKey.COMMAND_SEE_HELP), anyString()); @@ -297,3 +321,5 @@ public void shouldStripWhitespace() { } } + + diff --git a/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java b/authme-core/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java similarity index 98% rename from src/test/java/fr/xephi/authme/command/CommandInitializerTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java index 2110e2d660..b388949b8c 100644 --- a/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java @@ -1,8 +1,8 @@ package fr.xephi.authme.command; import fr.xephi.authme.util.StringUtils; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Collection; @@ -18,7 +18,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; /** * Test for {@link CommandInitializer} to guarantee the integrity of the defined commands. @@ -33,7 +33,7 @@ public class CommandInitializerTest { private static Collection commands; - @BeforeClass + @BeforeAll public static void initializeCommandCollection() { CommandInitializer commandInitializer = new CommandInitializer(); commands = commandInitializer.getCommands(); @@ -281,3 +281,4 @@ private static List getAbsoluteLabels(CommandDescription command) { } } + diff --git a/src/test/java/fr/xephi/authme/command/CommandMapperTest.java b/authme-core/src/test/java/fr/xephi/authme/command/CommandMapperTest.java similarity index 98% rename from src/test/java/fr/xephi/authme/command/CommandMapperTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/CommandMapperTest.java index 4311d709cf..610a29618f 100644 --- a/src/test/java/fr/xephi/authme/command/CommandMapperTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/CommandMapperTest.java @@ -1,7 +1,8 @@ package fr.xephi.authme.command; +import org.junit.jupiter.api.extension.ExtendWith; +import fr.xephi.authme.DelayedInjectionExtension; import ch.jalu.injector.testing.BeforeInjecting; -import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.InjectDelayed; import fr.xephi.authme.command.TestCommandsUtil.TestLoginCommand; import fr.xephi.authme.command.TestCommandsUtil.TestRegisterCommand; @@ -10,9 +11,8 @@ import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.permission.PermissionsManager; import org.bukkit.command.CommandSender; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.Mock; import java.util.List; @@ -37,7 +37,7 @@ /** * Test for {@link CommandMapper}. */ -@RunWith(DelayedInjectionRunner.class) +@ExtendWith(DelayedInjectionExtension.class) public class CommandMapperTest { private static List commands; @@ -51,7 +51,7 @@ public class CommandMapperTest { @Mock private CommandInitializer commandInitializer; - @BeforeClass + @BeforeAll public static void setUpCommandHandler() { commands = TestCommandsUtil.generateCommands(); } @@ -317,3 +317,5 @@ public void shouldReturnExecutableCommandClasses() { } } + + diff --git a/src/test/java/fr/xephi/authme/command/CommandUtilsTest.java b/authme-core/src/test/java/fr/xephi/authme/command/CommandUtilsTest.java similarity index 68% rename from src/test/java/fr/xephi/authme/command/CommandUtilsTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/CommandUtilsTest.java index bd5a18d8a7..684b35e901 100644 --- a/src/test/java/fr/xephi/authme/command/CommandUtilsTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/CommandUtilsTest.java @@ -1,8 +1,8 @@ package fr.xephi.authme.command; import org.bukkit.ChatColor; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collection; @@ -19,7 +19,7 @@ public class CommandUtilsTest { private static Collection commands; - @BeforeClass + @BeforeAll public static void setUpTestCommands() { commands = Collections.unmodifiableCollection(TestCommandsUtil.generateCommands()); } @@ -122,12 +122,78 @@ public void shouldFormatCommandWithOptionalArgument() { assertThat(result, equalTo(ChatColor.WHITE + "/email" + ChatColor.YELLOW + " [player]")); } + @Test + public void shouldReturnEmptyListForNullInput() { + assertSplitInput(null); + } + + @Test + public void shouldReturnEmptyListForBlankInput() { + assertSplitInput(" \t \n "); + } + + @Test + public void shouldReturnEmptyListForSlashOnlyInput() { + assertSplitInput("/"); + } + + @Test + public void shouldReturnEmptyListForSlashWithOnlyWhitespace() { + assertSplitInput("/ \t "); + } + + @Test + public void shouldSplitSingleCommandWithoutLeadingSlash() { + assertSplitInput("authme", "authme"); + } + + @Test + public void shouldSplitSingleCommandWithLeadingSlash() { + assertSplitInput("/authme", "authme"); + } + + @Test + public void shouldTrimInputBeforeRemovingLeadingSlash() { + assertSplitInput(" /authme register ", "authme", "register"); + } + + @Test + public void shouldIgnoreRepeatedWhitespaceBetweenParts() { + assertSplitInput("/authme register player password", + "authme", "register", "player", "password"); + } + + @Test + public void shouldSplitTabsAndNewlinesAsWhitespace() { + assertSplitInput("/authme\tregister\nplayer\r\npassword", + "authme", "register", "player", "password"); + } + + @Test + public void shouldSupportSlashSeparatedByWhitespaceFromCommand() { + assertSplitInput("/ authme register", "authme", "register"); + } + + @Test + public void shouldPreserveAdditionalLeadingSlashInFirstPart() { + assertSplitInput("//authme register", "/authme", "register"); + } + + @Test + public void shouldPreserveNonEmptySpecialCharactersInsideParts() { + assertSplitInput("/login p@ss-word_123", "login", "p@ss-word_123"); + } + private static void checkArgumentCount(CommandDescription command, int expectedMin, int expectedMax) { assertThat(CommandUtils.getMinNumberOfArguments(command), equalTo(expectedMin)); assertThat(CommandUtils.getMaxNumberOfArguments(command), equalTo(expectedMax)); } + private static void assertSplitInput(String input, String... expectedParts) { + assertThat(CommandUtils.splitInput(input), equalTo(Arrays.asList(expectedParts))); + } + private static CommandDescription.CommandBuilder getBuilderForArgsTest() { return CommandDescription.builder() .labels("authme", "auth") @@ -136,3 +202,4 @@ private static CommandDescription.CommandBuilder getBuilderForArgsTest() { .executableCommand(ExecutableCommand.class); } } + diff --git a/src/test/java/fr/xephi/authme/command/PlayerCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/PlayerCommandTest.java similarity index 89% rename from src/test/java/fr/xephi/authme/command/PlayerCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/PlayerCommandTest.java index 4b0aff5ab8..01e01e0565 100644 --- a/src/test/java/fr/xephi/authme/command/PlayerCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/PlayerCommandTest.java @@ -1,14 +1,16 @@ package fr.xephi.authme.command; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; import org.bukkit.command.BlockCommandSender; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import java.util.Collections; @@ -22,7 +24,8 @@ /** * Test for {@link PlayerCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class PlayerCommandTest { @Mock @@ -90,3 +93,5 @@ public String getAlternativeCommand() { } } } + + diff --git a/src/test/java/fr/xephi/authme/command/TestCommandsUtil.java b/authme-core/src/test/java/fr/xephi/authme/command/TestCommandsUtil.java similarity index 100% rename from src/test/java/fr/xephi/authme/command/TestCommandsUtil.java rename to authme-core/src/test/java/fr/xephi/authme/command/TestCommandsUtil.java diff --git a/src/test/java/fr/xephi/authme/command/executable/HelpCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/HelpCommandTest.java similarity index 94% rename from src/test/java/fr/xephi/authme/command/executable/HelpCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/HelpCommandTest.java index 9e40a4c62b..1ebf8d5d0d 100644 --- a/src/test/java/fr/xephi/authme/command/executable/HelpCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/HelpCommandTest.java @@ -1,17 +1,20 @@ package fr.xephi.authme.command.executable; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.command.CommandDescription; import fr.xephi.authme.command.CommandMapper; import fr.xephi.authme.command.FoundCommandResult; import fr.xephi.authme.command.help.HelpProvider; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; import java.util.List; @@ -39,7 +42,8 @@ /** * Test for {@link HelpCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class HelpCommandTest { @InjectMocks @@ -50,6 +54,8 @@ public class HelpCommandTest { @Mock private HelpProvider helpProvider; + @Captor + private ArgumentCaptor captor; @Test public void shouldHandleMissingBaseCommand() { @@ -81,7 +87,6 @@ public void shouldHandleWrongCommandWithSuggestion() { command.executeCommand(sender, arguments); // then - ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); verify(sender).sendMessage(captor.capture()); assertThat(removeColors(captor.getValue()), containsString("Assuming /authme register")); verify(helpProvider).outputHelp(sender, foundCommandResult, HelpProvider.ALL_OPTIONS); @@ -99,7 +104,6 @@ public void shouldHandleWrongCommandWithoutSuggestion() { command.executeCommand(sender, arguments); // then - ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); verify(sender).sendMessage(captor.capture()); assertThat(removeColors(captor.getValue()), containsString("Unknown command")); verifyNoInteractions(helpProvider); @@ -161,3 +165,5 @@ private static String removeColors(String str) { return str; } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/AccountsCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/AccountsCommandTest.java similarity index 93% rename from src/test/java/fr/xephi/authme/command/executable/authme/AccountsCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/authme/AccountsCommandTest.java index dcc058b8fa..77afcf066c 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/AccountsCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/AccountsCommandTest.java @@ -1,17 +1,20 @@ package fr.xephi.authme.command.executable.authme; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.CommonService; import org.bukkit.command.CommandSender; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import java.util.Collections; @@ -30,7 +33,8 @@ /** * Test for {@link AccountsCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class AccountsCommandTest { @InjectMocks @@ -41,6 +45,8 @@ public class AccountsCommandTest { private DataSource dataSource; @Mock private BukkitService bukkitService; + @Captor + private ArgumentCaptor captor; @Test public void shouldGetAccountsOfCurrentUser() { @@ -57,7 +63,6 @@ public void shouldGetAccountsOfCurrentUser() { // then verify(service).send(eq(sender), eq(MessageKey.ACCOUNTS_OWNED_SELF), eq("2")); - ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); verify(sender).sendMessage(captor.capture()); assertThat(captor.getValue(), containsString("Toaster, Pester")); } @@ -160,7 +165,6 @@ public void shouldReturnAccountListForIpQuery() { // then verify(service).send(eq(sender), eq(MessageKey.ACCOUNTS_OWNED_OTHER), eq("98.76.41.122"), eq("3")); - ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); verify(sender).sendMessage(captor.capture()); assertThat(captor.getValue(), containsString("Tester, Lester, Taster")); } @@ -172,3 +176,5 @@ private static PlayerAuth authWithIp(String ip) { .build(); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/AuthMeCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/AuthMeCommandTest.java similarity index 72% rename from src/test/java/fr/xephi/authme/command/executable/authme/AuthMeCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/authme/AuthMeCommandTest.java index 303be56fef..d1957916c5 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/AuthMeCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/AuthMeCommandTest.java @@ -2,8 +2,13 @@ import fr.xephi.authme.command.ExecutableCommand; import org.bukkit.command.CommandSender; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; import java.util.Collections; @@ -16,7 +21,11 @@ /** * Test for {@link AuthMeCommand}. */ +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class AuthMeCommandTest { + @Captor + private ArgumentCaptor messagesCaptor; @Test public void shouldDisplayInformation() { @@ -28,9 +37,9 @@ public void shouldDisplayInformation() { command.executeCommand(sender, Collections.emptyList()); // then - ArgumentCaptor messagesCaptor = ArgumentCaptor.forClass(String.class); verify(sender, times(3)).sendMessage(messagesCaptor.capture()); assertThat(messagesCaptor.getAllValues().get(1), containsString("/authme help")); assertThat(messagesCaptor.getAllValues().get(2), containsString("/authme about")); } } + diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/BackupCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/BackupCommandTest.java similarity index 72% rename from src/test/java/fr/xephi/authme/command/executable/authme/BackupCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/authme/BackupCommandTest.java index 2e1e0b5e82..5b9c62a9ea 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/BackupCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/BackupCommandTest.java @@ -1,12 +1,14 @@ package fr.xephi.authme.command.executable.authme; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.service.BackupService; import org.bukkit.command.CommandSender; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; @@ -16,7 +18,8 @@ /** * Test for {@link BackupCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class BackupCommandTest { @InjectMocks @@ -37,3 +40,5 @@ public void shouldStartBackup() { verify(backupService).doBackup(BackupService.BackupCause.COMMAND, sender); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java similarity index 87% rename from src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java index 8e1a608155..35c36ba569 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java @@ -1,16 +1,18 @@ package fr.xephi.authme.command.executable.authme; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.process.Management; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.service.ValidationService.ValidationResult; import org.bukkit.command.CommandSender; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; @@ -22,7 +24,8 @@ /** * Test for {@link ChangePasswordAdminCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class ChangePasswordAdminCommandTest { @InjectMocks @@ -71,3 +74,5 @@ public void shouldSendErrorToCommandSender() { verifyNoInteractions(management); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/ConverterCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/ConverterCommandTest.java similarity index 93% rename from src/test/java/fr/xephi/authme/command/executable/authme/ConverterCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/authme/ConverterCommandTest.java index aeae96ab66..a15e06e762 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/ConverterCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/ConverterCommandTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.command.executable.authme; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import ch.jalu.injector.factory.Factory; import fr.xephi.authme.TestHelper; import fr.xephi.authme.datasource.converter.Converter; @@ -8,12 +12,10 @@ import fr.xephi.authme.service.CommonService; import fr.xephi.authme.util.StringUtils; import org.bukkit.command.CommandSender; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; import java.util.HashSet; @@ -37,7 +39,8 @@ /** * Test for {@link ConverterCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class ConverterCommandTest { @InjectMocks @@ -52,7 +55,7 @@ public class ConverterCommandTest { @Mock private Factory converterFactory; - @BeforeClass + @BeforeAll public static void initLogger() { TestHelper.setupLogger(); } @@ -147,3 +150,5 @@ private T createMockReturnedByInjector(Class clazz) { } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommandTest.java similarity index 84% rename from src/test/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommandTest.java index 97c1e1ea6c..d14ec23114 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommandTest.java @@ -1,15 +1,17 @@ package fr.xephi.authme.command.executable.authme; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; import fr.xephi.authme.settings.SpawnLoader; import org.bukkit.Location; import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; @@ -23,7 +25,8 @@ /** * Test for {@link FirstSpawnCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class FirstSpawnCommandTest { @InjectMocks @@ -64,3 +67,5 @@ public void shouldHandleMissingFirstSpawn() { verify(player, never()).teleport(any(Location.class)); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/ForceLoginCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/ForceLoginCommandTest.java similarity index 93% rename from src/test/java/fr/xephi/authme/command/executable/authme/ForceLoginCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/authme/ForceLoginCommandTest.java index fccf98687d..c839c53a42 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/ForceLoginCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/ForceLoginCommandTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.command.executable.authme; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; import fr.xephi.authme.permission.PermissionsManager; @@ -8,11 +12,9 @@ import fr.xephi.authme.service.BukkitService; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; @@ -25,7 +27,8 @@ /** * Test for {@link ForceLoginCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class ForceLoginCommandTest { @InjectMocks @@ -137,3 +140,5 @@ private static Player mockPlayer(boolean isOnline) { return player; } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java similarity index 84% rename from src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java index e55defafda..07eff3be2b 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java @@ -1,15 +1,17 @@ package fr.xephi.authme.command.executable.authme; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import ch.jalu.datasourcecolumns.data.DataSourceValueImpl; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.service.CommonService; import org.bukkit.command.CommandSender; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; @@ -21,7 +23,8 @@ /** * Test for {@link GetEmailCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class GetEmailCommandTest { @InjectMocks @@ -62,3 +65,5 @@ public void shouldReturnEmail() { verify(service).send(eq(sender), eq(MessageKey.ADMIN_EMAIL_SHOW), eq(user), eq(email)); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/GetIpCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/GetIpCommandTest.java similarity index 92% rename from src/test/java/fr/xephi/authme/command/executable/authme/GetIpCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/authme/GetIpCommandTest.java index 8fe65e90b6..03e7fe2317 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/GetIpCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/GetIpCommandTest.java @@ -1,16 +1,18 @@ package fr.xephi.authme.command.executable.authme; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.service.BukkitService; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; @@ -26,7 +28,8 @@ /** * Test for {@link GetIpCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class GetIpCommandTest { @InjectMocks @@ -104,3 +107,5 @@ private static Player mockPlayer(String name, String ip) { return player; } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/LastLoginCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/LastLoginCommandTest.java similarity index 92% rename from src/test/java/fr/xephi/authme/command/executable/authme/LastLoginCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/authme/LastLoginCommandTest.java index 402b933aca..89d2e325ed 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/LastLoginCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/LastLoginCommandTest.java @@ -1,16 +1,19 @@ package fr.xephi.authme.command.executable.authme; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.service.CommonService; import org.bukkit.command.CommandSender; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; import java.util.Date; @@ -26,7 +29,8 @@ /** * Test for {@link LastLoginCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class LastLoginCommandTest { private static final long HOUR_IN_MSEC = 3600 * 1000; @@ -40,6 +44,8 @@ public class LastLoginCommandTest { @Mock private CommonService service; + @Captor + private ArgumentCaptor captor; @Test @@ -74,7 +80,6 @@ public void shouldDisplayLastLoginOfUser() { // then verify(dataSource).getAuth(player); - ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); verify(sender, times(3)).sendMessage(captor.capture()); String lastLoginString = new Date(lastLogin).toString(); assertThat(captor.getAllValues().get(0), @@ -102,7 +107,6 @@ public void shouldDisplayLastLoginOfCommandSender() { // then verify(dataSource).getAuth(name); - ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); verify(sender, times(3)).sendMessage(captor.capture()); String lastLoginString = new Date(lastLogin).toString(); assertThat(captor.getAllValues().get(0), @@ -127,9 +131,10 @@ public void shouldHandleNullLastLoginDate() { // then verify(dataSource).getAuth(name); - ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); verify(sender, times(2)).sendMessage(captor.capture()); assertThat(captor.getAllValues().get(0), allOf(containsString(name), containsString("never"))); assertThat(captor.getAllValues().get(1), containsString("123.45.67.89")); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommandTest.java similarity index 88% rename from src/test/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommandTest.java index 7c675bc07a..74b79b9248 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommandTest.java @@ -1,14 +1,16 @@ package fr.xephi.authme.command.executable.authme; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.task.purge.PurgeService; import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; import java.util.HashSet; @@ -26,7 +28,8 @@ /** * Test for {@link PurgeBannedPlayersCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class PurgeBannedPlayersCommandTest { @InjectMocks @@ -73,3 +76,5 @@ private static Set asLowerCaseSet(String... items) { return result; } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/PurgeCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/PurgeCommandTest.java similarity index 86% rename from src/test/java/fr/xephi/authme/command/executable/authme/PurgeCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/authme/PurgeCommandTest.java index 5d8d8d89e8..3cf8f241ca 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/PurgeCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/PurgeCommandTest.java @@ -1,13 +1,16 @@ package fr.xephi.authme.command.executable.authme; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.task.purge.PurgeService; import org.bukkit.command.CommandSender; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Calendar; import java.util.Collections; @@ -25,7 +28,8 @@ /** * Test for {@link PurgeCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class PurgeCommandTest { @InjectMocks @@ -33,6 +37,8 @@ public class PurgeCommandTest { @Mock private PurgeService purgeService; + @Captor + private ArgumentCaptor captor; @Test public void shouldHandleInvalidNumber() { @@ -72,7 +78,6 @@ public void shouldForwardToService() { command.executeCommand(sender, Collections.singletonList(interval)); // then - ArgumentCaptor captor = ArgumentCaptor.forClass(Long.class); verify(purgeService).runPurge(eq(sender), captor.capture()); // Check the timestamp with a certain tolerance @@ -87,3 +92,5 @@ private static void assertIsCloseTo(long value1, long value2, long tolerance) { } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommandTest.java similarity index 91% rename from src/test/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommandTest.java index 57bb21e26b..cb291b4bac 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommandTest.java @@ -1,16 +1,18 @@ package fr.xephi.authme.command.executable.authme; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.bungeecord.BungeeSender; import org.bukkit.command.CommandSender; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import java.util.Collections; @@ -24,7 +26,8 @@ /** * Test for {@link PurgeLastPositionCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class PurgeLastPositionCommandTest { @InjectMocks @@ -116,3 +119,5 @@ private static void verifyPositionWasReset(PlayerAuth auth) { verify(auth).setWorld("world"); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/PurgePlayerCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/PurgePlayerCommandTest.java similarity index 90% rename from src/test/java/fr/xephi/authme/command/executable/authme/PurgePlayerCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/authme/PurgePlayerCommandTest.java index cf48d2a394..5ccdf94d49 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/PurgePlayerCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/PurgePlayerCommandTest.java @@ -1,15 +1,17 @@ package fr.xephi.authme.command.executable.authme; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.task.purge.PurgeExecutor; import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Locale; @@ -27,7 +29,8 @@ /** * Test for {@link PurgePlayerCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class PurgePlayerCommandTest { @InjectMocks @@ -92,3 +95,5 @@ public void shouldExecutePurgeOfRegisteredPlayer() { verify(purgeExecutor).executePurge(singletonList(player), singletonList(name.toLowerCase(Locale.ROOT))); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/RecentPlayersCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/RecentPlayersCommandTest.java similarity index 90% rename from src/test/java/fr/xephi/authme/command/executable/authme/RecentPlayersCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/authme/RecentPlayersCommandTest.java index 30ee0e72e2..a39ae352fe 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/RecentPlayersCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/RecentPlayersCommandTest.java @@ -1,14 +1,16 @@ package fr.xephi.authme.command.executable.authme; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import org.bukkit.command.CommandSender; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; -import org.mockito.junit.MockitoJUnitRunner; import java.time.ZoneId; import java.util.Arrays; @@ -25,7 +27,8 @@ /** * Test for {@link RecentPlayersCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class RecentPlayersCommandTest { @InjectMocks @@ -85,3 +88,5 @@ public void shouldHandlePlayerWithNullLastLogin() { verify(sender).sendMessage(argThat(equalToIgnoringCase("- silvah777 (11:15 PM, 09 Nov with IP 22.11.22.33)"))); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java similarity index 94% rename from src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java index 9a465d53ae..8e2281e231 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.command.executable.authme; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; @@ -12,13 +16,12 @@ import fr.xephi.authme.service.ValidationService.ValidationResult; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import java.util.Locale; @@ -36,7 +39,8 @@ /** * Test for {@link RegisterAdminCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class RegisterAdminCommandTest { @InjectMocks @@ -56,8 +60,10 @@ public class RegisterAdminCommandTest { @Mock private ValidationService validationService; + @Captor + private ArgumentCaptor captor; - @BeforeClass + @BeforeAll public static void setUpLogger() { TestHelper.setupLogger(); } @@ -118,7 +124,6 @@ public void shouldHandleSavingError() { // then verify(validationService).validatePassword(password, user); verify(commandService).send(sender, MessageKey.ERROR); - ArgumentCaptor captor = ArgumentCaptor.forClass(PlayerAuth.class); verify(dataSource).saveAuth(captor.capture()); assertAuthHasInfo(captor.getValue(), user, hashedPassword); } @@ -143,7 +148,6 @@ public void shouldRegisterOfflinePlayer() { // then verify(validationService).validatePassword(password, user); verify(commandService).send(sender, MessageKey.REGISTER_SUCCESS); - ArgumentCaptor captor = ArgumentCaptor.forClass(PlayerAuth.class); verify(dataSource).saveAuth(captor.capture()); assertAuthHasInfo(captor.getValue(), user, hashedPassword); } @@ -172,7 +176,6 @@ public void shouldRegisterOnlinePlayer() { // then verify(validationService).validatePassword(password, user); verify(commandService).send(sender, MessageKey.REGISTER_SUCCESS); - ArgumentCaptor captor = ArgumentCaptor.forClass(PlayerAuth.class); verify(dataSource).saveAuth(captor.capture()); assertAuthHasInfo(captor.getValue(), user, hashedPassword); verify(player).kickPlayer(kickForAdminRegister); @@ -184,3 +187,5 @@ private void assertAuthHasInfo(PlayerAuth auth, String name, HashedPassword hash assertThat(auth.getPassword(), equalTo(hashedPassword)); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/ReloadCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/ReloadCommandTest.java similarity index 92% rename from src/test/java/fr/xephi/authme/command/executable/authme/ReloadCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/authme/ReloadCommandTest.java index 227f1a2630..7261005fb7 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/ReloadCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/ReloadCommandTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.command.executable.authme; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import ch.jalu.injector.factory.SingletonStore; import fr.xephi.authme.AuthMe; import fr.xephi.authme.TestHelper; @@ -16,13 +20,11 @@ import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.SecuritySettings; import org.bukkit.command.CommandSender; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import java.util.Collections; @@ -36,7 +38,8 @@ /** * Test for {@link ReloadCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class ReloadCommandTest { @InjectMocks @@ -63,12 +66,12 @@ public class ReloadCommandTest { @Mock private SingletonStore settingsDependentStore; - @BeforeClass + @BeforeAll public static void setUpLogger() { TestHelper.setupLogger(); } - @Before + @BeforeEach public void setDefaultSettings() { // Mock properties retrieved by ConsoleLogger given(settings.getProperty(PluginSettings.LOG_LEVEL)).willReturn(LogLevel.INFO); @@ -144,3 +147,5 @@ private void verifyReloadingCalls(List reloadables, List singletonStore; + @Captor + private ArgumentCaptor stringCaptor; - @Before + @BeforeEach public void setUpLimboCacheMap() { Map limboMap = new HashMap<>(); limboMap.put("test", mock(LimboPlayer.class)); @@ -83,7 +89,6 @@ public void shouldOutputStatistics() { dataStatistics.execute(sender, Collections.emptyList()); // then - ArgumentCaptor stringCaptor = ArgumentCaptor.forClass(String.class); verify(sender, atLeastOnce()).sendMessage(stringCaptor.capture()); assertThat(stringCaptor.getAllValues(), containsInAnyOrder( ChatColor.BLUE + "AuthMe statistics", @@ -109,7 +114,6 @@ public void shouldOutputCachedDataSourceStatistics() { dataStatistics.execute(sender, Collections.emptyList()); // then - ArgumentCaptor stringCaptor = ArgumentCaptor.forClass(String.class); verify(sender, atLeastOnce()).sendMessage(stringCaptor.capture()); assertThat(stringCaptor.getAllValues(), hasItem("Cached PlayerAuth objects: 11")); } @@ -119,3 +123,5 @@ private static List mockListOfSize(Class mockClass, int size) { return Collections.nCopies(size, mock); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/debug/DebugCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/debug/DebugCommandTest.java similarity index 93% rename from src/test/java/fr/xephi/authme/command/executable/authme/debug/DebugCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/authme/debug/DebugCommandTest.java index a92a065754..67b6daa36b 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/debug/DebugCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/debug/DebugCommandTest.java @@ -1,18 +1,21 @@ package fr.xephi.authme.command.executable.authme.debug; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import ch.jalu.injector.factory.Factory; import fr.xephi.authme.permission.DebugSectionPermissions; import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.permission.PermissionsManager; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import java.util.List; @@ -41,7 +44,8 @@ /** * Test for {@link DebugCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class DebugCommandTest { /** @@ -59,8 +63,10 @@ public class DebugCommandTest { @Mock private PermissionsManager permissionsManager; + @Captor + private ArgumentCaptor strCaptor; - @Before + @BeforeEach @SuppressWarnings("unchecked") public void initFactory() { given(debugSectionFactory.newInstance(any(Class.class))).willAnswer( @@ -86,7 +92,6 @@ public void shouldListAllAvailableDebugSections() { verify(debugSectionFactory, atLeast(MIN_DEBUG_SECTIONS)).newInstance(any(Class.class)); verify(permissionsManager, atLeast(MIN_DEBUG_SECTIONS)).hasPermission(eq(sender), any(DebugSectionPermissions.class)); - ArgumentCaptor strCaptor = ArgumentCaptor.forClass(String.class); verify(sender, times(4)).sendMessage(strCaptor.capture()); assertThat(strCaptor.getAllValues(), contains( equalTo(ChatColor.BLUE + "AuthMe debug utils"), @@ -108,7 +113,6 @@ public void shouldNotListAnyDebugSection() { verify(debugSectionFactory, atLeast(MIN_DEBUG_SECTIONS)).newInstance(any(Class.class)); verify(permissionsManager, atLeast(MIN_DEBUG_SECTIONS)).hasPermission(eq(sender), any(DebugSectionPermissions.class)); - ArgumentCaptor strCaptor = ArgumentCaptor.forClass(String.class); verify(sender, times(3)).sendMessage(strCaptor.capture()); assertThat(strCaptor.getAllValues(), contains( equalTo(ChatColor.BLUE + "AuthMe debug utils"), @@ -156,3 +160,5 @@ public void shouldNotRunSectionForMissingPermission() { verify(sender).sendMessage(argThat(containsString("You don't have permission"))); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/debug/DebugSectionConsistencyTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/debug/DebugSectionConsistencyTest.java similarity index 94% rename from src/test/java/fr/xephi/authme/command/executable/authme/debug/DebugSectionConsistencyTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/authme/debug/DebugSectionConsistencyTest.java index 26286237b9..511f0f6143 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/debug/DebugSectionConsistencyTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/debug/DebugSectionConsistencyTest.java @@ -2,8 +2,8 @@ import fr.xephi.authme.ClassCollector; import fr.xephi.authme.TestHelper; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import java.lang.reflect.Modifier; import java.util.HashSet; @@ -14,7 +14,7 @@ import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; /** * Consistency tests for {@link DebugSection} implementors. @@ -24,7 +24,7 @@ public class DebugSectionConsistencyTest { private static List> debugClasses; private static List debugSections; - @BeforeClass + @BeforeAll public static void collectClasses() { // TODO ljacqu 20171021: Improve ClassCollector (pass pkg by class, improve #getInstancesOfType's instantiation) ClassCollector classCollector = new ClassCollector( @@ -70,3 +70,4 @@ private static DebugSection instantiate(Class clazz) { } } } + diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/debug/DebugSectionUtilsTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/debug/DebugSectionUtilsTest.java similarity index 97% rename from src/test/java/fr/xephi/authme/command/executable/authme/debug/DebugSectionUtilsTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/authme/debug/DebugSectionUtilsTest.java index 7f4d556afa..638b943b19 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/debug/DebugSectionUtilsTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/debug/DebugSectionUtilsTest.java @@ -8,8 +8,8 @@ import fr.xephi.authme.datasource.CacheDataSource; import fr.xephi.authme.datasource.DataSource; import org.bukkit.Location; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; @@ -26,7 +26,7 @@ */ public class DebugSectionUtilsTest { - @Before + @BeforeEach public void initMockLogger() { TestHelper.setupLogger(); } @@ -123,3 +123,4 @@ public void shouldCastOrReturnNull() { assertThat(DebugSectionUtils.castToTypeOrNull(5, Integer.class), equalTo(5)); } } + diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/debug/HasPermissionCheckerTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/debug/HasPermissionCheckerTest.java similarity index 89% rename from src/test/java/fr/xephi/authme/command/executable/authme/debug/HasPermissionCheckerTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/authme/debug/HasPermissionCheckerTest.java index 6d5dc18d42..52c73b0b37 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/debug/HasPermissionCheckerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/debug/HasPermissionCheckerTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.command.executable.authme.debug; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.ClassCollector; import fr.xephi.authme.TestHelper; import fr.xephi.authme.permission.AdminPermission; @@ -8,12 +12,11 @@ import fr.xephi.authme.service.BukkitService; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.List; import java.util.stream.Collectors; @@ -33,7 +36,8 @@ /** * Test for {@link HasPermissionChecker}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class HasPermissionCheckerTest { @InjectMocks @@ -44,6 +48,8 @@ public class HasPermissionCheckerTest { @Mock private BukkitService bukkitService; + @Captor + private ArgumentCaptor msgCaptor; @Test public void shouldListAllPermissionNodeClasses() { @@ -67,7 +73,6 @@ public void shouldShowUsageInfo() { hasPermissionChecker.execute(sender, emptyList()); // then - ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(String.class); verify(sender, atLeast(2)).sendMessage(msgCaptor.capture()); assertThat( msgCaptor.getAllValues().stream().anyMatch(msg -> msg.contains("/authme debug perm bobby my.perm.node")), @@ -94,3 +99,5 @@ public void shouldShowSuccessfulTestWithRegularPlayer() { + "' has permission '" + permission.getNode() + "'"))); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/debug/MySqlDefaultChangerColumnsTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/debug/MySqlDefaultChangerColumnsTest.java similarity index 96% rename from src/test/java/fr/xephi/authme/command/executable/authme/debug/MySqlDefaultChangerColumnsTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/authme/debug/MySqlDefaultChangerColumnsTest.java index 99027e44e4..798df206c7 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/debug/MySqlDefaultChangerColumnsTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/debug/MySqlDefaultChangerColumnsTest.java @@ -1,13 +1,13 @@ package fr.xephi.authme.command.executable.authme.debug; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.HashSet; import java.util.Set; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; /** * Consistency test for {@link MySqlDefaultChanger.Columns} enum. @@ -67,3 +67,4 @@ private void verifyHasSameDefaultValueInNotNullDefinition(MySqlDefaultChanger.Co } } } + diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/debug/MySqlDefaultChangerTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/debug/MySqlDefaultChangerTest.java similarity index 89% rename from src/test/java/fr/xephi/authme/command/executable/authme/debug/MySqlDefaultChangerTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/authme/debug/MySqlDefaultChangerTest.java index 98ec5fba6a..f8d814b760 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/debug/MySqlDefaultChangerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/debug/MySqlDefaultChangerTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.command.executable.authme.debug; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import com.zaxxer.hikari.HikariDataSource; import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.TestHelper; @@ -9,11 +13,9 @@ import fr.xephi.authme.datasource.MySQL; import fr.xephi.authme.datasource.SqlDataSourceTestUtil; import fr.xephi.authme.settings.Settings; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.sql.Connection; import java.sql.SQLException; @@ -29,13 +31,14 @@ /** * Test for {@link MySqlDefaultChanger}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class MySqlDefaultChangerTest { @Mock private Settings settings; - @BeforeClass + @BeforeAll public static void setUpLogger() { TestHelper.setupLogger(); } @@ -96,3 +99,5 @@ private MySqlDefaultChanger createDefaultChanger(DataSource dataSource) { return defaultChanger; } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/debug/PlayerAuthViewerTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/debug/PlayerAuthViewerTest.java similarity index 90% rename from src/test/java/fr/xephi/authme/command/executable/authme/debug/PlayerAuthViewerTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/authme/debug/PlayerAuthViewerTest.java index 3b34f308b0..3372116df8 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/debug/PlayerAuthViewerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/authme/debug/PlayerAuthViewerTest.java @@ -1,14 +1,17 @@ package fr.xephi.authme.command.executable.authme.debug; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import org.bukkit.command.CommandSender; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; @@ -24,7 +27,8 @@ /** * Test for {@link PlayerAuthViewer}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class PlayerAuthViewerTest { @InjectMocks @@ -32,6 +36,8 @@ public class PlayerAuthViewerTest { @Mock private DataSource dataSource; + @Captor + private ArgumentCaptor textCaptor; @Test public void shouldMakeExample() { @@ -73,7 +79,6 @@ public void shouldDisplayAuthInfo() { authViewer.execute(sender, Collections.singletonList("George")); // then - ArgumentCaptor textCaptor = ArgumentCaptor.forClass(String.class); verify(sender, atLeastOnce()).sendMessage(textCaptor.capture()); assertThat(textCaptor.getAllValues(), hasItem(containsString("Player george / George"))); assertThat(textCaptor.getAllValues(), hasItem(containsString("Registration: 2005-03-18T"))); @@ -95,7 +100,6 @@ public void shouldHandleCornerCases() { authViewer.execute(sender, Collections.singletonList("Tar")); // then - ArgumentCaptor textCaptor = ArgumentCaptor.forClass(String.class); verify(sender, atLeastOnce()).sendMessage(textCaptor.capture()); assertThat(textCaptor.getAllValues(), hasItem(containsString("Player tar / Player"))); assertThat(textCaptor.getAllValues(), hasItem(containsString("Registration: Not available (0)"))); @@ -104,3 +108,5 @@ public void shouldHandleCornerCases() { assertThat(textCaptor.getAllValues(), hasItem(containsString("TOTP code (partial): ''"))); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/captcha/CaptchaCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/captcha/CaptchaCommandTest.java similarity index 95% rename from src/test/java/fr/xephi/authme/command/executable/captcha/CaptchaCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/captcha/CaptchaCommandTest.java index 9f47dfae3e..25b2f3a7ec 100644 --- a/src/test/java/fr/xephi/authme/command/executable/captcha/CaptchaCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/captcha/CaptchaCommandTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.command.executable.captcha; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.data.captcha.LoginCaptchaManager; import fr.xephi.authme.data.captcha.RegistrationCaptchaManager; @@ -8,11 +12,9 @@ import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.service.CommonService; import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; @@ -25,7 +27,8 @@ /** * Test for {@link CaptchaCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class CaptchaCommandTest { @InjectMocks @@ -188,3 +191,5 @@ private static Player mockPlayerWithName(String name) { return player; } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java similarity index 93% rename from src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java index d60a743312..a7bcbe44c9 100644 --- a/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.command.executable.changepassword; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.data.VerificationCodeManager; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.message.MessageKey; @@ -11,11 +15,9 @@ import org.bukkit.command.BlockCommandSender; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import java.util.Collections; @@ -35,7 +37,8 @@ /** * Test for {@link ChangePasswordCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class ChangePasswordCommandTest { @InjectMocks @@ -132,3 +135,5 @@ private Player initPlayerWithName(String name, boolean loggedIn) { return player; } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/email/AddEmailCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/email/AddEmailCommandTest.java similarity index 87% rename from src/test/java/fr/xephi/authme/command/executable/email/AddEmailCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/email/AddEmailCommandTest.java index da067d8146..f262901b69 100644 --- a/src/test/java/fr/xephi/authme/command/executable/email/AddEmailCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/email/AddEmailCommandTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.command.executable.email; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; import fr.xephi.authme.process.Management; @@ -7,11 +11,9 @@ import org.bukkit.command.BlockCommandSender; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import java.util.Collections; @@ -25,7 +27,8 @@ /** * Test for {@link AddEmailCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class AddEmailCommandTest { @InjectMocks @@ -85,3 +88,5 @@ public void shouldDefineArgumentMismatchMessage() { assertThat(command.getArgumentsMismatchMessage(), equalTo(MessageKey.USAGE_ADD_EMAIL)); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/email/ChangeEmailCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/email/ChangeEmailCommandTest.java similarity index 90% rename from src/test/java/fr/xephi/authme/command/executable/email/ChangeEmailCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/email/ChangeEmailCommandTest.java index 9b2e7e0e7b..baa379f4e5 100644 --- a/src/test/java/fr/xephi/authme/command/executable/email/ChangeEmailCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/email/ChangeEmailCommandTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.command.executable.email; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.data.VerificationCodeManager; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; @@ -8,11 +12,9 @@ import org.bukkit.command.BlockCommandSender; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import java.util.Collections; @@ -28,7 +30,8 @@ /** * Test for {@link ChangeEmailCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class ChangeEmailCommandTest { @InjectMocks @@ -101,3 +104,5 @@ private Player initPlayerWithName(String name) { return player; } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/email/EmailBaseCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/email/EmailBaseCommandTest.java similarity index 81% rename from src/test/java/fr/xephi/authme/command/executable/email/EmailBaseCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/email/EmailBaseCommandTest.java index 7ca0aff9c0..ddeef1b93a 100644 --- a/src/test/java/fr/xephi/authme/command/executable/email/EmailBaseCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/email/EmailBaseCommandTest.java @@ -1,14 +1,16 @@ package fr.xephi.authme.command.executable.email; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.command.CommandMapper; import fr.xephi.authme.command.FoundCommandResult; import fr.xephi.authme.command.help.HelpProvider; import org.bukkit.command.CommandSender; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; @@ -21,7 +23,8 @@ /** * Test for {@link EmailBaseCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class EmailBaseCommandTest { @InjectMocks @@ -47,3 +50,5 @@ public void shouldDisplayHelp() { verify(helpProvider).outputHelp(sender, result, HelpProvider.SHOW_CHILDREN); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/email/EmailSetPasswordCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/email/EmailSetPasswordCommandTest.java similarity index 90% rename from src/test/java/fr/xephi/authme/command/executable/email/EmailSetPasswordCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/email/EmailSetPasswordCommandTest.java index a0338492e6..5b87821dc4 100644 --- a/src/test/java/fr/xephi/authme/command/executable/email/EmailSetPasswordCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/email/EmailSetPasswordCommandTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.command.executable.email; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.TestHelper; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; @@ -9,12 +13,10 @@ import fr.xephi.authme.service.PasswordRecoveryService; import fr.xephi.authme.service.ValidationService; import org.bukkit.entity.Player; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; @@ -26,7 +28,8 @@ /** * Tests for {@link EmailSetPasswordCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class EmailSetPasswordCommandTest { @InjectMocks @@ -47,7 +50,7 @@ public class EmailSetPasswordCommandTest { @Mock private ValidationService validationService; - @BeforeClass + @BeforeAll public static void setUpLogger() { TestHelper.setupLogger(); } @@ -106,3 +109,5 @@ public void shouldDoNothingCantChangePass() { verify(commonService).send(player, MessageKey.CHANGE_PASSWORD_EXPIRED); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/email/ProcessCodeCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/email/ProcessCodeCommandTest.java similarity index 89% rename from src/test/java/fr/xephi/authme/command/executable/email/ProcessCodeCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/email/ProcessCodeCommandTest.java index b5790cc44e..fe79e59da0 100644 --- a/src/test/java/fr/xephi/authme/command/executable/email/ProcessCodeCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/email/ProcessCodeCommandTest.java @@ -1,15 +1,17 @@ package fr.xephi.authme.command.executable.email; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.PasswordRecoveryService; import fr.xephi.authme.service.RecoveryCodeService; import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; @@ -21,7 +23,8 @@ /** * Tests for {@link ProcessCodeCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class ProcessCodeCommandTest { @InjectMocks @@ -90,3 +93,5 @@ public void shouldProcessCorrectCode() { verify(codeService).removeCode(name); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/email/RecoverEmailCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/email/RecoverEmailCommandTest.java similarity index 97% rename from src/test/java/fr/xephi/authme/command/executable/email/RecoverEmailCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/email/RecoverEmailCommandTest.java index 78fa79813c..63b837660b 100644 --- a/src/test/java/fr/xephi/authme/command/executable/email/RecoverEmailCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/email/RecoverEmailCommandTest.java @@ -1,8 +1,9 @@ package fr.xephi.authme.command.executable.email; +import org.junit.jupiter.api.extension.ExtendWith; +import fr.xephi.authme.DelayedInjectionExtension; import ch.jalu.datasourcecolumns.data.DataSourceValueImpl; import ch.jalu.injector.testing.BeforeInjecting; -import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.InjectDelayed; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerCache; @@ -17,9 +18,8 @@ import fr.xephi.authme.service.RecoveryCodeService; import fr.xephi.authme.settings.properties.SecuritySettings; import org.bukkit.entity.Player; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.Mock; import java.util.Collections; @@ -38,7 +38,7 @@ /** * Test for {@link RecoverEmailCommand}. */ -@RunWith(DelayedInjectionRunner.class) +@ExtendWith(DelayedInjectionExtension.class) public class RecoverEmailCommandTest { private static final String DEFAULT_EMAIL = "your@email.com"; @@ -73,7 +73,7 @@ public class RecoverEmailCommandTest { @Mock private Messages messages; - @BeforeClass + @BeforeAll public static void initLogger() { TestHelper.setupLogger(); } @@ -229,3 +229,5 @@ public void shouldDefineArgumentMismatchMessage() { assertThat(command.getArgumentsMismatchMessage(), equalTo(MessageKey.USAGE_RECOVER_EMAIL)); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/email/ShowEmailCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/email/ShowEmailCommandTest.java similarity index 89% rename from src/test/java/fr/xephi/authme/command/executable/email/ShowEmailCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/email/ShowEmailCommandTest.java index 36e106ede0..2e39276649 100644 --- a/src/test/java/fr/xephi/authme/command/executable/email/ShowEmailCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/email/ShowEmailCommandTest.java @@ -1,16 +1,18 @@ package fr.xephi.authme.command.executable.email; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.settings.properties.SecuritySettings; import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; @@ -21,7 +23,8 @@ /** * Test for {@link ShowEmailCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class ShowEmailCommandTest { private static final String CURRENT_EMAIL = "my.email@example.com"; @@ -93,3 +96,5 @@ private static PlayerAuth newAuthWithNoEmail() { .build(); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/login/LoginCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/login/LoginCommandTest.java similarity index 85% rename from src/test/java/fr/xephi/authme/command/executable/login/LoginCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/login/LoginCommandTest.java index 163dde2181..70f7a6d8d9 100644 --- a/src/test/java/fr/xephi/authme/command/executable/login/LoginCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/login/LoginCommandTest.java @@ -1,16 +1,18 @@ package fr.xephi.authme.command.executable.login; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; import fr.xephi.authme.process.Management; import org.bukkit.command.BlockCommandSender; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; @@ -25,7 +27,8 @@ /** * Test for {@link LoginCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class LoginCommandTest { @InjectMocks @@ -69,3 +72,5 @@ public void shouldDefineArgumentMismatchMessage() { assertThat(command.getArgumentsMismatchMessage(), equalTo(MessageKey.USAGE_LOGIN)); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/logout/LogoutCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/logout/LogoutCommandTest.java similarity index 83% rename from src/test/java/fr/xephi/authme/command/executable/logout/LogoutCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/logout/LogoutCommandTest.java index 55cdd877c8..13dd4e59b9 100644 --- a/src/test/java/fr/xephi/authme/command/executable/logout/LogoutCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/logout/LogoutCommandTest.java @@ -1,16 +1,18 @@ package fr.xephi.authme.command.executable.logout; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; import fr.xephi.authme.process.Management; import org.bukkit.command.BlockCommandSender; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.ArrayList; import java.util.Collections; @@ -24,7 +26,8 @@ /** * Test for {@link LogoutCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class LogoutCommandTest { @InjectMocks @@ -63,3 +66,5 @@ public void shouldCallManagementForPlayerCaller() { } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/register/RegisterCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/register/RegisterCommandTest.java similarity index 93% rename from src/test/java/fr/xephi/authme/command/executable/register/RegisterCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/register/RegisterCommandTest.java index a0c3058845..e643ae396f 100644 --- a/src/test/java/fr/xephi/authme/command/executable/register/RegisterCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/register/RegisterCommandTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.command.executable.register; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.captcha.RegistrationCaptchaManager; import fr.xephi.authme.mail.EmailService; @@ -20,13 +24,11 @@ import org.bukkit.command.BlockCommandSender; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import java.util.Collections; @@ -36,6 +38,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; @@ -44,7 +47,8 @@ /** * Test for {@link RegisterCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class RegisterCommandTest { @InjectMocks @@ -68,16 +72,17 @@ public class RegisterCommandTest { @Mock private RegistrationCaptchaManager registrationCaptchaManager; - @BeforeClass + @BeforeAll public static void setup() { TestHelper.setupLogger(); } - @Before + @BeforeEach public void linkMocksAndProvideSettingDefaults() { - given(commonService.getProperty(SecuritySettings.PASSWORD_HASH)).willReturn(HashAlgorithm.BCRYPT); - given(commonService.getProperty(RegistrationSettings.REGISTRATION_TYPE)).willReturn(RegistrationType.PASSWORD); - given(commonService.getProperty(RegistrationSettings.REGISTER_SECOND_ARGUMENT)).willReturn(RegisterSecondaryArgument.NONE); + lenient().when(commonService.getProperty(SecuritySettings.PASSWORD_HASH)).thenReturn(HashAlgorithm.BCRYPT); + lenient().when(commonService.getProperty(RegistrationSettings.REGISTRATION_TYPE)).thenReturn(RegistrationType.PASSWORD); + lenient().when(commonService.getProperty(RegistrationSettings.REGISTER_SECOND_ARGUMENT)) + .thenReturn(RegisterSecondaryArgument.NONE); } @Test @@ -334,3 +339,5 @@ private static Player mockPlayerWithName(String name) { return player; } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/totp/AddTotpCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/totp/AddTotpCommandTest.java similarity index 90% rename from src/test/java/fr/xephi/authme/command/executable/totp/AddTotpCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/totp/AddTotpCommandTest.java index 598200c584..9897e02313 100644 --- a/src/test/java/fr/xephi/authme/command/executable/totp/AddTotpCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/totp/AddTotpCommandTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.command.executable.totp; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.message.MessageKey; @@ -7,11 +11,9 @@ import fr.xephi.authme.security.totp.GenerateTotpService; import fr.xephi.authme.security.totp.TotpAuthenticator.TotpGenerationResult; import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; @@ -23,7 +25,8 @@ /** * Test for {@link AddTotpCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class AddTotpCommandTest { @InjectMocks @@ -91,3 +94,5 @@ private static Player mockPlayerWithName(String name) { return player; } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommandTest.java similarity index 94% rename from src/test/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommandTest.java index d3d3d025c2..6a80ffd77d 100644 --- a/src/test/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/totp/ConfirmTotpCommandTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.command.executable.totp; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; @@ -9,12 +13,10 @@ import fr.xephi.authme.security.totp.GenerateTotpService; import fr.xephi.authme.security.totp.TotpAuthenticator.TotpGenerationResult; import org.bukkit.entity.Player; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; @@ -32,7 +34,8 @@ /** * Test for {@link ConfirmTotpCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class ConfirmTotpCommandTest { @InjectMocks @@ -47,7 +50,7 @@ public class ConfirmTotpCommandTest { @Mock private Messages messages; - @BeforeClass + @BeforeAll public static void setUpLogger() { TestHelper.setupLogger(); } @@ -156,3 +159,5 @@ public void shouldHandleMissingAuthAccount() { verify(messages).send(player, MessageKey.NOT_LOGGED_IN); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommandTest.java similarity index 93% rename from src/test/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommandTest.java index d0ae8f3410..82db90351f 100644 --- a/src/test/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/totp/RemoveTotpCommandTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.command.executable.totp; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; @@ -8,12 +12,10 @@ import fr.xephi.authme.message.Messages; import fr.xephi.authme.security.totp.TotpAuthenticator; import org.bukkit.entity.Player; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import static java.util.Collections.singletonList; import static org.hamcrest.Matchers.nullValue; @@ -27,7 +29,8 @@ /** * Test for {@link RemoveTotpCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class RemoveTotpCommandTest { @InjectMocks @@ -42,7 +45,7 @@ public class RemoveTotpCommandTest { @Mock private Messages messages; - @BeforeClass + @BeforeAll public static void setUpLogger() { TestHelper.setupLogger(); } @@ -147,3 +150,5 @@ public void shouldHandleNonLoggedInUser() { verify(playerCache, only()).getAuth(name); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/totp/TotpBaseCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/totp/TotpBaseCommandTest.java similarity index 80% rename from src/test/java/fr/xephi/authme/command/executable/totp/TotpBaseCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/totp/TotpBaseCommandTest.java index 0e279f6874..1a0e194aab 100644 --- a/src/test/java/fr/xephi/authme/command/executable/totp/TotpBaseCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/totp/TotpBaseCommandTest.java @@ -1,14 +1,16 @@ package fr.xephi.authme.command.executable.totp; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.command.CommandMapper; import fr.xephi.authme.command.FoundCommandResult; import fr.xephi.authme.command.help.HelpProvider; import org.bukkit.command.CommandSender; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; @@ -19,7 +21,8 @@ /** * Test for {@link TotpBaseCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class TotpBaseCommandTest { @InjectMocks @@ -45,3 +48,5 @@ public void shouldOutputHelp() { verify(helpProvider).outputHelp(sender, mappingResult, HelpProvider.SHOW_CHILDREN); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/unregister/UnregisterCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/unregister/UnregisterCommandTest.java similarity index 92% rename from src/test/java/fr/xephi/authme/command/executable/unregister/UnregisterCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/unregister/UnregisterCommandTest.java index 7667b375cc..0c8385a6e8 100644 --- a/src/test/java/fr/xephi/authme/command/executable/unregister/UnregisterCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/unregister/UnregisterCommandTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.command.executable.unregister; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.data.VerificationCodeManager; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.message.MessageKey; @@ -8,11 +12,9 @@ import fr.xephi.authme.service.CommonService; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; @@ -28,7 +30,8 @@ /** * Test for {@link UnregisterCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class UnregisterCommandTest { @InjectMocks @@ -124,3 +127,5 @@ public void shouldDefineArgumentMismatchMessage() { assertThat(command.getArgumentsMismatchMessage(), equalTo(MessageKey.USAGE_UNREGISTER)); } } + + diff --git a/src/test/java/fr/xephi/authme/command/executable/verification/VerificationCommandTest.java b/authme-core/src/test/java/fr/xephi/authme/command/executable/verification/VerificationCommandTest.java similarity index 93% rename from src/test/java/fr/xephi/authme/command/executable/verification/VerificationCommandTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/executable/verification/VerificationCommandTest.java index 80ae565b7f..f0618c62ab 100644 --- a/src/test/java/fr/xephi/authme/command/executable/verification/VerificationCommandTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/executable/verification/VerificationCommandTest.java @@ -1,16 +1,18 @@ package fr.xephi.authme.command.executable.verification; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.VerificationCodeManager; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.service.CommonService; import org.bukkit.entity.Player; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; @@ -23,7 +25,8 @@ /** * Test for {@link VerificationCommand}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class VerificationCommandTest { @InjectMocks @@ -35,7 +38,7 @@ public class VerificationCommandTest { @Mock private VerificationCodeManager codeManager; - @BeforeClass + @BeforeAll public static void setUpLogger() { TestHelper.setupLogger(); } @@ -166,3 +169,5 @@ private static Player mockPlayerWithName(String name) { return player; } } + + diff --git a/src/test/java/fr/xephi/authme/command/help/HelpMessageAndHelpSectionConsistencyTest.java b/authme-core/src/test/java/fr/xephi/authme/command/help/HelpMessageAndHelpSectionConsistencyTest.java similarity index 93% rename from src/test/java/fr/xephi/authme/command/help/HelpMessageAndHelpSectionConsistencyTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/help/HelpMessageAndHelpSectionConsistencyTest.java index 30eddba19d..1dd2b8df2d 100644 --- a/src/test/java/fr/xephi/authme/command/help/HelpMessageAndHelpSectionConsistencyTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/help/HelpMessageAndHelpSectionConsistencyTest.java @@ -1,14 +1,14 @@ package fr.xephi.authme.command.help; import fr.xephi.authme.util.StringUtils; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.HashSet; import java.util.Set; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; /** * Test for enums {@link HelpMessage} and {@link HelpSection}. @@ -37,3 +37,4 @@ public void shouldHaveUniqueNonEmptyKeys() { } } } + diff --git a/src/test/java/fr/xephi/authme/command/help/HelpMessagesConsistencyTest.java b/authme-core/src/test/java/fr/xephi/authme/command/help/HelpMessagesConsistencyTest.java similarity index 99% rename from src/test/java/fr/xephi/authme/command/help/HelpMessagesConsistencyTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/help/HelpMessagesConsistencyTest.java index b91be4e6ad..0e9cfb3982 100644 --- a/src/test/java/fr/xephi/authme/command/help/HelpMessagesConsistencyTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/help/HelpMessagesConsistencyTest.java @@ -9,7 +9,7 @@ import org.bukkit.configuration.MemorySection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.File; import java.util.Collection; @@ -95,3 +95,4 @@ private static CommandDescription getAuthMeRegisterDescription() { .findFirst().get(); } } + diff --git a/src/test/java/fr/xephi/authme/command/help/HelpMessagesServiceTest.java b/authme-core/src/test/java/fr/xephi/authme/command/help/HelpMessagesServiceTest.java similarity index 96% rename from src/test/java/fr/xephi/authme/command/help/HelpMessagesServiceTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/help/HelpMessagesServiceTest.java index 3c3f267ce7..62b5fe9fdb 100644 --- a/src/test/java/fr/xephi/authme/command/help/HelpMessagesServiceTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/help/HelpMessagesServiceTest.java @@ -11,10 +11,9 @@ import fr.xephi.authme.permission.DefaultPermission; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.PluginSettings; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import fr.xephi.authme.TempFolder; import java.io.File; import java.io.IOException; @@ -37,12 +36,10 @@ public class HelpMessagesServiceTest { private static final Collection COMMANDS = TestCommandsUtil.generateCommands(); private HelpMessagesService helpMessagesService; - - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); + public TempFolder temporaryFolder = new TempFolder(); private File dataFolder; - @Before + @BeforeEach public void initializeHandler() throws IOException { dataFolder = temporaryFolder.newFolder(); new File(dataFolder, "messages").mkdirs(); @@ -158,3 +155,5 @@ private HelpMessagesFileHandler createMessagesFileHandler() { return messagesFileHandler; } } + + diff --git a/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java b/authme-core/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java similarity index 97% rename from src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java rename to authme-core/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java index d1d1755361..c42aecec47 100644 --- a/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java @@ -1,7 +1,8 @@ package fr.xephi.authme.command.help; +import org.junit.jupiter.api.extension.ExtendWith; +import fr.xephi.authme.DelayedInjectionExtension; import ch.jalu.injector.testing.BeforeInjecting; -import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.InjectDelayed; import fr.xephi.authme.command.CommandDescription; import fr.xephi.authme.command.FoundCommandResult; @@ -12,12 +13,12 @@ import fr.xephi.authme.permission.PermissionsManager; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; -import org.mockito.internal.stubbing.answers.ReturnsArgumentAt; +import static org.mockito.AdditionalAnswers.returnsFirstArg; import java.util.Arrays; import java.util.Collection; @@ -51,7 +52,7 @@ /** * Test for {@link HelpProvider}. */ -@RunWith(DelayedInjectionRunner.class) +@ExtendWith(DelayedInjectionExtension.class) public class HelpProviderTest { private static Collection commands; @@ -64,8 +65,10 @@ public class HelpProviderTest { private HelpMessagesService helpMessagesService; @Mock private CommandSender sender; + @Captor + private ArgumentCaptor captor; - @BeforeClass + @BeforeAll public static void setUpCommands() { commands = TestCommandsUtil.generateCommands(); } @@ -437,15 +440,14 @@ private static String removeColors(String str) { return str; } - private static List getLines(CommandSender sender) { - ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + private List getLines(CommandSender sender) { verify(sender, atLeastOnce()).sendMessage(captor.capture()); return captor.getAllValues().stream().map(s -> removeColors(s)).collect(Collectors.toList()); } private static void setDefaultHelpMessages(HelpMessagesService helpMessagesService) { given(helpMessagesService.buildLocalizedDescription(any(CommandDescription.class))) - .willAnswer(new ReturnsArgumentAt(0)); + .willAnswer(returnsFirstArg()); for (HelpMessage key : HelpMessage.values()) { String text = key.name().replace("_", " ").toLowerCase(Locale.ROOT); given(helpMessagesService.getMessage(key)) @@ -464,3 +466,5 @@ private static void setDefaultHelpMessages(HelpMessagesService helpMessagesServi } } + + diff --git a/src/test/java/fr/xephi/authme/data/QuickCommandsProtectionManagerTest.java b/authme-core/src/test/java/fr/xephi/authme/data/QuickCommandsProtectionManagerTest.java similarity index 88% rename from src/test/java/fr/xephi/authme/data/QuickCommandsProtectionManagerTest.java rename to authme-core/src/test/java/fr/xephi/authme/data/QuickCommandsProtectionManagerTest.java index f46bbce126..9352d060f9 100644 --- a/src/test/java/fr/xephi/authme/data/QuickCommandsProtectionManagerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/data/QuickCommandsProtectionManagerTest.java @@ -1,14 +1,16 @@ package fr.xephi.authme.data; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PlayerPermission; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.ProtectionSettings; import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; @@ -18,7 +20,8 @@ /** * Test for {@link QuickCommandsProtectionManager}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class QuickCommandsProtectionManagerTest { @Mock @@ -78,3 +81,5 @@ private static Player mockPlayerWithName(String name) { } } + + diff --git a/src/test/java/fr/xephi/authme/data/TempbanManagerTest.java b/authme-core/src/test/java/fr/xephi/authme/data/TempbanManagerTest.java similarity index 91% rename from src/test/java/fr/xephi/authme/data/TempbanManagerTest.java rename to authme-core/src/test/java/fr/xephi/authme/data/TempbanManagerTest.java index ce5c977480..652f04664e 100644 --- a/src/test/java/fr/xephi/authme/data/TempbanManagerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/data/TempbanManagerTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.data; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.TestHelper; import fr.xephi.authme.message.MessageKey; @@ -9,17 +13,18 @@ import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.util.expiring.TimedCounter; import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Map; +import static fr.xephi.authme.service.BukkitServiceTestHelper.setBukkitServiceToRunOnGlobalRegion; +import static fr.xephi.authme.service.BukkitServiceTestHelper.setBukkitServiceToScheduleSyncTaskFromOptionallyAsyncTask; import static fr.xephi.authme.service.BukkitServiceTestHelper.setBukkitServiceToScheduleSyncDelayedTask; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; @@ -34,7 +39,8 @@ /** * Test for {@link TempbanManager}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class TempbanManagerTest { private static final long DATE_TOLERANCE_MILLISECONDS = 200L; @@ -45,6 +51,8 @@ public class TempbanManagerTest { @Mock private Messages messages; + @Captor + private ArgumentCaptor captor; @Test public void shouldAddCounts() { @@ -152,14 +160,14 @@ public void shouldBanPlayerIp() { given(messages.retrieveSingle(player, MessageKey.TEMPBAN_MAX_LOGINS)).willReturn(banReason); Settings settings = mockSettings(2, 100, ""); TempbanManager manager = new TempbanManager(bukkitService, messages, settings); - setBukkitServiceToScheduleSyncDelayedTask(bukkitService); + setBukkitServiceToRunOnGlobalRegion(bukkitService); + setBukkitServiceToScheduleSyncTaskFromOptionallyAsyncTask(bukkitService); // when manager.tempbanPlayer(player); // then verify(player).kickPlayer(banReason); - ArgumentCaptor captor = ArgumentCaptor.forClass(Date.class); verify(bukkitService).banIp(eq(ip), eq(banReason), captor.capture(), eq("AuthMe")); // Compute the expected expiration date and check that the actual date is within the difference tolerance @@ -179,7 +187,7 @@ public void shouldBanPlayerIpCustom() { String banCommand = "banip %ip% 15d IP ban too many logins"; Settings settings = mockSettings(2, 100, banCommand); TempbanManager manager = new TempbanManager(bukkitService, messages, settings); - setBukkitServiceToScheduleSyncDelayedTask(bukkitService); + setBukkitServiceToRunOnGlobalRegion(bukkitService); // when manager.tempbanPlayer(player); @@ -201,7 +209,8 @@ public void shouldResetCountAfterBan() { manager.increaseCount(ip, "user"); manager.increaseCount(ip, "name2"); manager.increaseCount(ip, "user"); - setBukkitServiceToScheduleSyncDelayedTask(bukkitService); + setBukkitServiceToRunOnGlobalRegion(bukkitService); + setBukkitServiceToScheduleSyncTaskFromOptionallyAsyncTask(bukkitService); // when manager.tempbanPlayer(player); @@ -263,3 +272,5 @@ private static TimedCounter mockCounter() { return mock(TimedCounter.class); } } + + diff --git a/src/test/java/fr/xephi/authme/data/VerificationCodeManagerTest.java b/authme-core/src/test/java/fr/xephi/authme/data/VerificationCodeManagerTest.java similarity index 71% rename from src/test/java/fr/xephi/authme/data/VerificationCodeManagerTest.java rename to authme-core/src/test/java/fr/xephi/authme/data/VerificationCodeManagerTest.java index 7486a895e2..55ebe1d4bd 100644 --- a/src/test/java/fr/xephi/authme/data/VerificationCodeManagerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/data/VerificationCodeManagerTest.java @@ -9,24 +9,35 @@ import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.SecuritySettings; import org.bukkit.entity.Player; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import static fr.xephi.authme.service.BukkitServiceTestHelper.setBukkitServiceToRunTaskAsynchronously; -import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.only; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; /** * Test for {@link VerificationCodeManager}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class VerificationCodeManagerTest { @Mock @@ -44,7 +55,10 @@ public class VerificationCodeManagerTest { @Mock private BukkitService bukkitService; - @Before + @Captor + private ArgumentCaptor runnableCaptor; + + @BeforeEach public void setUpBasicBehavior() { given(emailService.hasAllInformation()).willReturn(true); given(settings.getProperty(SecuritySettings.VERIFICATION_CODE_EXPIRATION_MINUTES)).willReturn(1); @@ -111,6 +125,7 @@ public void shouldGenerateCode() { String player = "ILoveTests"; String email = "ilovetests@test.com"; given(dataSource.getEmail(player)).willReturn(DataSourceValueImpl.of(email)); + setBukkitServiceToRunTaskAsynchronously(bukkitService); VerificationCodeManager codeManager1 = createCodeManager(); VerificationCodeManager codeManager2 = createCodeManager(); codeManager2.codeExistOrGenerateNew(player); @@ -130,6 +145,7 @@ public void shouldRequireCode() { String player = "ILoveTests"; String email = "ilovetests@test.com"; given(dataSource.getEmail(player)).willReturn(DataSourceValueImpl.of(email)); + setBukkitServiceToRunTaskAsynchronously(bukkitService); VerificationCodeManager codeManager1 = createCodeManager(); VerificationCodeManager codeManager2 = createCodeManager(); codeManager2.codeExistOrGenerateNew(player); @@ -150,6 +166,7 @@ public void shouldVerifyCode() { String code = "193458"; String email = "ilovetests@test.com"; given(dataSource.getEmail(player)).willReturn(DataSourceValueImpl.of(email)); + setBukkitServiceToRunTaskAsynchronously(bukkitService); VerificationCodeManager codeManager1 = createCodeManager(); VerificationCodeManager codeManager2 = createCodeManager(); codeManager1.codeExistOrGenerateNew(player); @@ -163,6 +180,45 @@ public void shouldVerifyCode() { assertThat(test2, equalTo(false)); } + @Test + public void shouldLookupEmailOnlyInAsyncTask() { + // given + String player = "ILoveTests"; + String email = "ilovetests@test.com"; + VerificationCodeManager codeManager = createCodeManager(); + + // when + codeManager.codeExistOrGenerateNew(player); + + // then + verify(bukkitService).runTaskAsynchronously(runnableCaptor.capture()); + verifyNoInteractions(dataSource); + verify(emailService, only()).hasAllInformation(); + + given(dataSource.getEmail(player)).willReturn(DataSourceValueImpl.of(email)); + runnableCaptor.getValue().run(); + + assertThat(codeManager.hasCode(player), equalTo(true)); + verify(dataSource).getEmail(player); + verify(emailService).sendVerificationMail(eq(player), eq(email), anyString()); + } + + @Test + public void shouldNotScheduleDuplicateCodeGenerationWhilePending() { + // given + String player = "ILoveTests"; + VerificationCodeManager codeManager = createCodeManager(); + + // when + codeManager.codeExistOrGenerateNew(player); + codeManager.codeExistOrGenerateNew(player); + + // then + verify(bukkitService, times(1)).runTaskAsynchronously(any(Runnable.class)); + verifyNoInteractions(dataSource); + verify(emailService, only()).hasAllInformation(); + } + private VerificationCodeManager createCodeManager() { return new VerificationCodeManager(settings, dataSource, emailService, permissionsManager, bukkitService); } @@ -173,3 +229,5 @@ private static Player mockPlayerWithName(String name) { return player; } } + + diff --git a/src/test/java/fr/xephi/authme/data/auth/PlayerAuthTest.java b/authme-core/src/test/java/fr/xephi/authme/data/auth/PlayerAuthTest.java similarity index 95% rename from src/test/java/fr/xephi/authme/data/auth/PlayerAuthTest.java rename to authme-core/src/test/java/fr/xephi/authme/data/auth/PlayerAuthTest.java index 360abdc986..292674ac22 100644 --- a/src/test/java/fr/xephi/authme/data/auth/PlayerAuthTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/data/auth/PlayerAuthTest.java @@ -1,11 +1,11 @@ package fr.xephi.authme.data.auth; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; /** * Test for {@link PlayerAuth} and its builder. @@ -67,3 +67,4 @@ public void shouldCreatePlayerAuthWithNullValues() { assertThat(auth.getPitch(), equalTo(123.004f)); } } + diff --git a/src/test/java/fr/xephi/authme/data/captcha/LoginCaptchaManagerTest.java b/authme-core/src/test/java/fr/xephi/authme/data/captcha/LoginCaptchaManagerTest.java similarity index 99% rename from src/test/java/fr/xephi/authme/data/captcha/LoginCaptchaManagerTest.java rename to authme-core/src/test/java/fr/xephi/authme/data/captcha/LoginCaptchaManagerTest.java index c313fc571b..19d09171c3 100644 --- a/src/test/java/fr/xephi/authme/data/captcha/LoginCaptchaManagerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/data/captcha/LoginCaptchaManagerTest.java @@ -5,7 +5,7 @@ import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.util.expiring.TimedCounter; import org.bukkit.entity.Player; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Locale; @@ -172,3 +172,4 @@ private static void assertHasCount(LoginCaptchaManager manager, String player, I assertThat(playerCounts.get(player.toLowerCase(Locale.ROOT)), equalTo(count)); } } + diff --git a/src/test/java/fr/xephi/authme/data/captcha/RegistrationCaptchaManagerTest.java b/authme-core/src/test/java/fr/xephi/authme/data/captcha/RegistrationCaptchaManagerTest.java similarity index 99% rename from src/test/java/fr/xephi/authme/data/captcha/RegistrationCaptchaManagerTest.java rename to authme-core/src/test/java/fr/xephi/authme/data/captcha/RegistrationCaptchaManagerTest.java index 07aeaf0b8b..b532a05f6a 100644 --- a/src/test/java/fr/xephi/authme/data/captcha/RegistrationCaptchaManagerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/data/captcha/RegistrationCaptchaManagerTest.java @@ -5,7 +5,7 @@ import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.util.expiring.ExpiringMap; import org.bukkit.entity.Player; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static fr.xephi.authme.AuthMeMatchers.stringWithLength; import static org.hamcrest.Matchers.equalTo; @@ -91,3 +91,4 @@ private static ExpiringMap getCodeMap(RegistrationCaptchaManager return ReflectionTestUtils.getFieldValue(CaptchaCodeStorage.class, captchaStorage, "captchaCodes"); } } + diff --git a/src/test/java/fr/xephi/authme/data/limbo/AllowFlightRestoreTypeTest.java b/authme-core/src/test/java/fr/xephi/authme/data/limbo/AllowFlightRestoreTypeTest.java similarity index 98% rename from src/test/java/fr/xephi/authme/data/limbo/AllowFlightRestoreTypeTest.java rename to authme-core/src/test/java/fr/xephi/authme/data/limbo/AllowFlightRestoreTypeTest.java index eb7ee38936..ac0b55aba2 100644 --- a/src/test/java/fr/xephi/authme/data/limbo/AllowFlightRestoreTypeTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/data/limbo/AllowFlightRestoreTypeTest.java @@ -1,7 +1,7 @@ package fr.xephi.authme.data.limbo; import org.bukkit.entity.Player; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -107,3 +107,4 @@ private static LimboPlayer newLimboWithAllowFlight(boolean allowFlight) { return limbo; } } + diff --git a/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerMatchers.java b/authme-core/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerMatchers.java similarity index 100% rename from src/test/java/fr/xephi/authme/data/limbo/LimboPlayerMatchers.java rename to authme-core/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerMatchers.java diff --git a/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManagerTest.java b/authme-core/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManagerTest.java similarity index 67% rename from src/test/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManagerTest.java rename to authme-core/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManagerTest.java index fdc82abe6b..0143ab626d 100644 --- a/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManagerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/data/limbo/LimboPlayerTaskManagerTest.java @@ -1,24 +1,26 @@ package fr.xephi.authme.data.limbo; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.data.captcha.RegistrationCaptchaManager; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.CancellableTask; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.task.MessageTask; import fr.xephi.authme.task.TimeoutTask; import org.bukkit.entity.Player; -import org.bukkit.scheduler.BukkitTask; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; @@ -39,7 +41,8 @@ /** * Test for {@link LimboPlayerTaskManager}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class LimboPlayerTaskManagerTest { @InjectMocks @@ -60,7 +63,7 @@ public class LimboPlayerTaskManagerTest { @Mock private RegistrationCaptchaManager registrationCaptchaManager; - @BeforeClass + @BeforeAll public static void setupLogger() { TestHelper.setupLogger(); } @@ -74,14 +77,16 @@ public void shouldRegisterMessageTask() { given(messages.retrieveSingle(player, key)).willReturn("Please register!"); int interval = 12; given(settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL)).willReturn(interval); + given(bukkitService.runTaskTimer(eq(player), any(MessageTask.class), anyLong(), anyLong())) + .willReturn(mock(CancellableTask.class)); // when limboPlayerTaskManager.registerMessageTask(player, limboPlayer, LimboMessageType.REGISTER); // then - verify(limboPlayer).setMessageTask(any(MessageTask.class)); + verify(limboPlayer).setMessageTask(any(MessageTask.class), any(CancellableTask.class)); verify(messages).retrieveSingle(player, key); - verify(bukkitService).runTaskTimer( + verify(bukkitService).runTaskTimer(eq(player), any(MessageTask.class), eq(2L * TICKS_PER_SECOND), eq((long) interval * TICKS_PER_SECOND)); } @@ -145,32 +150,64 @@ public void shouldInitializeMessageTaskWithCaptchaMessage() { } @Test - public void shouldRegisterTimeoutTask() { + public void shouldRegisterLoginTimeoutTask() { // given Player player = mock(Player.class); LimboPlayer limboPlayer = mock(LimboPlayer.class); - given(settings.getProperty(RestrictionSettings.TIMEOUT)).willReturn(30); - BukkitTask bukkitTask = mock(BukkitTask.class); - given(bukkitService.runTaskLater(any(TimeoutTask.class), anyLong())).willReturn(bukkitTask); + given(settings.getProperty(RestrictionSettings.LOGIN_TIMEOUT)).willReturn(30); + CancellableTask bukkitTask = mock(CancellableTask.class); + given(bukkitService.runTaskLater(eq(player), any(TimeoutTask.class), anyLong())).willReturn(bukkitTask); // when - limboPlayerTaskManager.registerTimeoutTask(player, limboPlayer); + limboPlayerTaskManager.registerTimeoutTask(player, limboPlayer, LimboMessageType.LOG_IN); // then verify(limboPlayer).setTimeoutTask(bukkitTask); - verify(bukkitService).runTaskLater(any(TimeoutTask.class), eq(600L)); // 30 * TICKS_PER_SECOND + verify(bukkitService).runTaskLater(eq(player), any(TimeoutTask.class), eq(600L)); // 30 * TICKS_PER_SECOND verify(messages).retrieveSingle(player, MessageKey.LOGIN_TIMEOUT_ERROR); } @Test - public void shouldNotRegisterTimeoutTaskForZeroTimeout() { + public void shouldRegisterRegisterTimeoutTask() { // given Player player = mock(Player.class); LimboPlayer limboPlayer = mock(LimboPlayer.class); - given(settings.getProperty(RestrictionSettings.TIMEOUT)).willReturn(0); + given(settings.getProperty(RestrictionSettings.REGISTER_TIMEOUT)).willReturn(60); + CancellableTask bukkitTask = mock(CancellableTask.class); + given(bukkitService.runTaskLater(eq(player), any(TimeoutTask.class), anyLong())).willReturn(bukkitTask); // when - limboPlayerTaskManager.registerTimeoutTask(player, limboPlayer); + limboPlayerTaskManager.registerTimeoutTask(player, limboPlayer, LimboMessageType.REGISTER); + + // then + verify(limboPlayer).setTimeoutTask(bukkitTask); + verify(bukkitService).runTaskLater(eq(player), any(TimeoutTask.class), eq(1200L)); // 60 * TICKS_PER_SECOND + verify(messages).retrieveSingle(player, MessageKey.LOGIN_TIMEOUT_ERROR); + } + + @Test + public void shouldNotRegisterTimeoutTaskForZeroLoginTimeout() { + // given + Player player = mock(Player.class); + LimboPlayer limboPlayer = mock(LimboPlayer.class); + given(settings.getProperty(RestrictionSettings.LOGIN_TIMEOUT)).willReturn(0); + + // when + limboPlayerTaskManager.registerTimeoutTask(player, limboPlayer, LimboMessageType.LOG_IN); + + // then + verifyNoInteractions(limboPlayer, bukkitService); + } + + @Test + public void shouldNotRegisterTimeoutTaskForZeroRegisterTimeout() { + // given + Player player = mock(Player.class); + LimboPlayer limboPlayer = mock(LimboPlayer.class); + given(settings.getProperty(RestrictionSettings.REGISTER_TIMEOUT)).willReturn(0); + + // when + limboPlayerTaskManager.registerTimeoutTask(player, limboPlayer, LimboMessageType.REGISTER); // then verifyNoInteractions(limboPlayer, bukkitService); @@ -181,20 +218,22 @@ public void shouldCancelExistingTimeoutTask() { // given Player player = mock(Player.class); LimboPlayer limboPlayer = new LimboPlayer(null, false, Collections.emptyList(), true, 0.3f, 0.1f); - BukkitTask existingTask = mock(BukkitTask.class); + CancellableTask existingTask = mock(CancellableTask.class); limboPlayer.setTimeoutTask(existingTask); - given(settings.getProperty(RestrictionSettings.TIMEOUT)).willReturn(18); - BukkitTask bukkitTask = mock(BukkitTask.class); - given(bukkitService.runTaskLater(any(TimeoutTask.class), anyLong())).willReturn(bukkitTask); + given(settings.getProperty(RestrictionSettings.LOGIN_TIMEOUT)).willReturn(18); + CancellableTask bukkitTask = mock(CancellableTask.class); + given(bukkitService.runTaskLater(eq(player), any(TimeoutTask.class), anyLong())).willReturn(bukkitTask); // when - limboPlayerTaskManager.registerTimeoutTask(player, limboPlayer); + limboPlayerTaskManager.registerTimeoutTask(player, limboPlayer, LimboMessageType.LOG_IN); // then verify(existingTask).cancel(); assertThat(limboPlayer.getTimeoutTask(), equalTo(bukkitTask)); - verify(bukkitService).runTaskLater(any(TimeoutTask.class), eq(360L)); // 18 * TICKS_PER_SECOND + verify(bukkitService).runTaskLater(eq(player), any(TimeoutTask.class), eq(360L)); // 18 * TICKS_PER_SECOND verify(messages).retrieveSingle(player, MessageKey.LOGIN_TIMEOUT_ERROR); } } + + diff --git a/src/test/java/fr/xephi/authme/data/limbo/LimboServiceHelperTest.java b/authme-core/src/test/java/fr/xephi/authme/data/limbo/LimboServiceHelperTest.java similarity index 89% rename from src/test/java/fr/xephi/authme/data/limbo/LimboServiceHelperTest.java rename to authme-core/src/test/java/fr/xephi/authme/data/limbo/LimboServiceHelperTest.java index f5c3fc21e5..da0278675d 100644 --- a/src/test/java/fr/xephi/authme/data/limbo/LimboServiceHelperTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/data/limbo/LimboServiceHelperTest.java @@ -1,12 +1,14 @@ package fr.xephi.authme.data.limbo; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.TestHelper; import org.bukkit.Location; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Collections; @@ -22,13 +24,14 @@ *

* Note: some methods are tested directly where they are used via {@link LimboServiceTest}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class LimboServiceHelperTest { @InjectMocks private LimboServiceHelper limboServiceHelper; - @BeforeClass + @BeforeAll public static void initLogger() { TestHelper.setupLogger(); } @@ -91,3 +94,5 @@ public void shouldHandleNullInputs() { assertThat(result3, nullValue()); } } + + diff --git a/src/test/java/fr/xephi/authme/data/limbo/LimboServiceTest.java b/authme-core/src/test/java/fr/xephi/authme/data/limbo/LimboServiceTest.java similarity index 85% rename from src/test/java/fr/xephi/authme/data/limbo/LimboServiceTest.java rename to authme-core/src/test/java/fr/xephi/authme/data/limbo/LimboServiceTest.java index aadab82b41..4117d6994e 100644 --- a/src/test/java/fr/xephi/authme/data/limbo/LimboServiceTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/data/limbo/LimboServiceTest.java @@ -1,21 +1,22 @@ package fr.xephi.authme.data.limbo; -import ch.jalu.injector.testing.DelayedInjectionRunner; +import org.junit.jupiter.api.extension.ExtendWith; +import fr.xephi.authme.DelayedInjectionExtension; import ch.jalu.injector.testing.InjectDelayed; import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.limbo.persistence.LimboPersistence; import fr.xephi.authme.permission.PermissionsManager; +import fr.xephi.authme.service.TeleportationService; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.SpawnLoader; import fr.xephi.authme.settings.properties.LimboSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; import org.bukkit.Location; import org.bukkit.entity.Player; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Mockito; @@ -29,6 +30,7 @@ import static org.hamcrest.Matchers.sameInstance; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -39,7 +41,7 @@ /** * Test for {@link LimboService}, and {@link LimboServiceHelper}. */ -@RunWith(DelayedInjectionRunner.class) +@ExtendWith(DelayedInjectionExtension.class) public class LimboServiceTest { @InjectDelayed @@ -66,14 +68,19 @@ public class LimboServiceTest { @Mock private AuthGroupHandler authGroupHandler; - @BeforeClass + @Mock + private TeleportationService teleportationService; + + @BeforeAll public static void initLogger() { TestHelper.setupLogger(); } - @Before + @BeforeEach public void mockSettings() { given(settings.getProperty(RestrictionSettings.ALLOW_UNAUTHED_MOVEMENT)).willReturn(false); + given(teleportationService.consumeOriginalJoinLocation(anyString(), any())) + .willAnswer(invocation -> invocation.getArgument(1)); } @Test @@ -91,7 +98,7 @@ public void shouldCreateLimboPlayer() { // then verify(taskManager).registerMessageTask(eq(player), any(LimboPlayer.class), eq(LimboMessageType.LOG_IN)); - verify(taskManager).registerTimeoutTask(eq(player), any(LimboPlayer.class)); + verify(taskManager).registerTimeoutTask(eq(player), any(LimboPlayer.class), eq(LimboMessageType.LOG_IN)); verify(player).setAllowFlight(false); verify(player).setFlySpeed(0.0f); verify(player).setWalkSpeed(0.0f); @@ -122,7 +129,7 @@ public void shouldNotKeepOpStatusForUnregisteredPlayer() { // then verify(taskManager).registerMessageTask(eq(player), any(LimboPlayer.class), eq(LimboMessageType.REGISTER)); - verify(taskManager).registerTimeoutTask(eq(player), any(LimboPlayer.class)); + verify(taskManager).registerTimeoutTask(eq(player), any(LimboPlayer.class), eq(LimboMessageType.REGISTER)); verify(permissionsManager, only()).hasGroupSupport(); verify(player).setAllowFlight(false); verify(player).setFlySpeed(0.0f); @@ -158,6 +165,25 @@ public void shouldClearTasksOnAlreadyExistingLimbo() { assertThat(newLimbo, not(sameInstance(existingLimbo))); } + @Test + public void shouldUseRememberedJoinLocationWhenCreatingLimboPlayer() { + // given + Player player = newPlayer("Bobby", true, 0.3f, false, 0.2f); + Location fallbackLocation = mock(Location.class); + Location rememberedJoinLocation = mock(Location.class); + given(spawnLoader.getPlayerLocationOrSpawn(player)).willReturn(fallbackLocation); + given(teleportationService.consumeOriginalJoinLocation("bobby", fallbackLocation)) + .willReturn(rememberedJoinLocation); + given(permissionsManager.hasGroupSupport()).willReturn(false); + given(settings.getProperty(LimboSettings.RESTORE_ALLOW_FLIGHT)).willReturn(AllowFlightRestoreType.ENABLE); + + // when + limboService.createLimboPlayer(player, true); + + // then + assertThat(limboService.getLimboPlayer("Bobby").getLocation(), equalTo(rememberedJoinLocation)); + } + @Test public void shouldRestoreData() { // given @@ -226,7 +252,7 @@ public void shouldReplaceTasks() { limboService.replaceTasksAfterRegistration(player); // then - verify(taskManager).registerTimeoutTask(player, limbo); + verify(taskManager).registerTimeoutTask(player, limbo, LimboMessageType.LOG_IN); verify(taskManager).registerMessageTask(player, limbo, LimboMessageType.LOG_IN); verify(authGroupHandler).setGroup(player, limbo, AuthGroupType.REGISTERED_UNAUTHENTICATED); } @@ -268,3 +294,5 @@ private Map getLimboMap() { return ReflectionTestUtils.getFieldValue(LimboService.class, limboService, "entries"); } } + + diff --git a/src/test/java/fr/xephi/authme/data/limbo/WalkFlySpeedRestoreTypeTest.java b/authme-core/src/test/java/fr/xephi/authme/data/limbo/WalkFlySpeedRestoreTypeTest.java similarity index 84% rename from src/test/java/fr/xephi/authme/data/limbo/WalkFlySpeedRestoreTypeTest.java rename to authme-core/src/test/java/fr/xephi/authme/data/limbo/WalkFlySpeedRestoreTypeTest.java index f8aa2719c7..c348c377c8 100644 --- a/src/test/java/fr/xephi/authme/data/limbo/WalkFlySpeedRestoreTypeTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/data/limbo/WalkFlySpeedRestoreTypeTest.java @@ -1,13 +1,11 @@ package fr.xephi.authme.data.limbo; import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; import static fr.xephi.authme.data.limbo.LimboPlayer.DEFAULT_FLY_SPEED; import static fr.xephi.authme.data.limbo.LimboPlayer.DEFAULT_WALK_SPEED; @@ -22,17 +20,11 @@ /** * Test for {@link WalkFlySpeedRestoreType}. */ -@RunWith(Parameterized.class) public class WalkFlySpeedRestoreTypeTest { - private final TestParameters parameters; - - public WalkFlySpeedRestoreTypeTest(TestParameters parameters) { - this.parameters = parameters; - } - - @Test - public void shouldRestoreToExpectedValue() { + @ParameterizedTest(name = "{0}") + @MethodSource("buildParams") + public void shouldRestoreToExpectedValue(TestParameters parameters) { // given LimboPlayer limbo = mock(LimboPlayer.class); given(limbo.getWalkSpeed()).willReturn(parameters.givenLimboWalkSpeed); @@ -50,10 +42,10 @@ public void shouldRestoreToExpectedValue() { verify(player).setWalkSpeed(parameters.expectedWalkSpeed); verify(player).setFlySpeed(parameters.expectedFlySpeed); } + - @Parameterized.Parameters(name = "{0}") - public static List buildParams() { - List parameters = Arrays.asList( + public static List buildParams() { + return Arrays.asList( create(RESTORE).withLimbo(0.1f, 0.4f).withPlayer(0.3f, 0.9f).expect(0.1f, 0.4f), create(RESTORE).withLimbo(0.9f, 0.2f).withPlayer(0.3f, 0.0f).expect(0.9f, 0.2f), create(MAX_RESTORE).withLimbo(0.3f, 0.8f).withPlayer(0.5f, 0.2f).expect(0.5f, 0.8f), @@ -62,9 +54,6 @@ public static List buildParams() { create(RESTORE_NO_ZERO).withLimbo(0.0f, 0.005f).withPlayer(0.4f, 0.8f).expect(DEFAULT_WALK_SPEED, DEFAULT_FLY_SPEED), create(DEFAULT).withLimbo(0.1f, 0.7f).withPlayer(0.4f, 0.0f).expect(DEFAULT_WALK_SPEED, DEFAULT_FLY_SPEED) ); - - // Convert List to List - return parameters.stream().map(p -> new Object[]{p}).collect(Collectors.toList()); } private static TestParameters create(WalkFlySpeedRestoreType testedType) { @@ -106,3 +95,4 @@ public String toString() { } } } + diff --git a/src/test/java/fr/xephi/authme/data/limbo/persistence/DistributedFilesPersistenceHandlerTest.java b/authme-core/src/test/java/fr/xephi/authme/data/limbo/persistence/DistributedFilesPersistenceHandlerTest.java similarity index 96% rename from src/test/java/fr/xephi/authme/data/limbo/persistence/DistributedFilesPersistenceHandlerTest.java rename to authme-core/src/test/java/fr/xephi/authme/data/limbo/persistence/DistributedFilesPersistenceHandlerTest.java index f223bdf1c1..cf413fb67c 100644 --- a/src/test/java/fr/xephi/authme/data/limbo/persistence/DistributedFilesPersistenceHandlerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/data/limbo/persistence/DistributedFilesPersistenceHandlerTest.java @@ -1,7 +1,8 @@ package fr.xephi.authme.data.limbo.persistence; +import org.junit.jupiter.api.extension.ExtendWith; +import fr.xephi.authme.DelayedInjectionExtension; import ch.jalu.injector.testing.BeforeInjecting; -import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.InjectDelayed; import com.google.common.io.Files; import fr.xephi.authme.TestHelper; @@ -15,11 +16,9 @@ import org.bukkit.World; import org.bukkit.entity.Player; import org.hamcrest.Matcher; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import fr.xephi.authme.TempFolder; import org.mockito.Mock; import java.io.File; @@ -43,7 +42,7 @@ /** * Test for {@link DistributedFilesPersistenceHandler}. */ -@RunWith(DelayedInjectionRunner.class) +@ExtendWith(DelayedInjectionExtension.class) public class DistributedFilesPersistenceHandlerTest { /** Player is in seg32-10110 and should be migrated into seg16-f. */ @@ -83,11 +82,9 @@ public class DistributedFilesPersistenceHandlerTest { @DataFolder private File dataFolder; private File playerDataFolder; + public TempFolder temporaryFolder = new TempFolder(); - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); - - @BeforeClass + @BeforeAll public static void initLogger() { TestHelper.setupLogger(); } @@ -214,3 +211,5 @@ private static Location mockLocation(String worldName) { return location; } } + + diff --git a/src/test/java/fr/xephi/authme/data/limbo/persistence/IndividualFilesPersistenceHandlerTest.java b/authme-core/src/test/java/fr/xephi/authme/data/limbo/persistence/IndividualFilesPersistenceHandlerTest.java similarity index 93% rename from src/test/java/fr/xephi/authme/data/limbo/persistence/IndividualFilesPersistenceHandlerTest.java rename to authme-core/src/test/java/fr/xephi/authme/data/limbo/persistence/IndividualFilesPersistenceHandlerTest.java index a7c9ce5c39..80454f7a6c 100644 --- a/src/test/java/fr/xephi/authme/data/limbo/persistence/IndividualFilesPersistenceHandlerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/data/limbo/persistence/IndividualFilesPersistenceHandlerTest.java @@ -1,7 +1,8 @@ package fr.xephi.authme.data.limbo.persistence; +import org.junit.jupiter.api.extension.ExtendWith; +import fr.xephi.authme.DelayedInjectionExtension; import ch.jalu.injector.testing.BeforeInjecting; -import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.InjectDelayed; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.limbo.LimboPlayer; @@ -12,10 +13,8 @@ import org.bukkit.Location; import org.bukkit.World; import org.bukkit.entity.Player; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; +import fr.xephi.authme.TempFolder; import org.mockito.Mock; import java.io.File; @@ -35,7 +34,7 @@ /** * Test for {@link IndividualFilesPersistenceHandler}. */ -@RunWith(DelayedInjectionRunner.class) +@ExtendWith(DelayedInjectionExtension.class) public class IndividualFilesPersistenceHandlerTest { private static final UUID SAMPLE_UUID = UUID.nameUUIDFromBytes("PersistenceTest".getBytes()); @@ -49,9 +48,7 @@ public class IndividualFilesPersistenceHandlerTest { @DataFolder private File dataFolder; - - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); + public TempFolder temporaryFolder = new TempFolder(); @BeforeInjecting public void copyTestFiles() throws IOException { @@ -127,3 +124,5 @@ public void shouldSavePlayerData() { } } + + diff --git a/src/test/java/fr/xephi/authme/data/limbo/persistence/LimboPersistenceTest.java b/authme-core/src/test/java/fr/xephi/authme/data/limbo/persistence/LimboPersistenceTest.java similarity index 95% rename from src/test/java/fr/xephi/authme/data/limbo/persistence/LimboPersistenceTest.java rename to authme-core/src/test/java/fr/xephi/authme/data/limbo/persistence/LimboPersistenceTest.java index e8aba73fe4..70f1d9bac3 100644 --- a/src/test/java/fr/xephi/authme/data/limbo/persistence/LimboPersistenceTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/data/limbo/persistence/LimboPersistenceTest.java @@ -1,8 +1,9 @@ package fr.xephi.authme.data.limbo.persistence; +import org.junit.jupiter.api.extension.ExtendWith; +import fr.xephi.authme.DelayedInjectionExtension; import ch.jalu.injector.factory.Factory; import ch.jalu.injector.testing.BeforeInjecting; -import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.InjectDelayed; import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.TestHelper; @@ -11,9 +12,8 @@ import fr.xephi.authme.settings.properties.LimboSettings; import org.bukkit.entity.Player; import org.hamcrest.Matcher; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.Mock; import java.util.logging.Logger; @@ -37,7 +37,7 @@ /** * Test for {@link LimboPersistence}. */ -@RunWith(DelayedInjectionRunner.class) +@ExtendWith(DelayedInjectionExtension.class) public class LimboPersistenceTest { @SuppressWarnings("unused") @@ -50,7 +50,7 @@ public class LimboPersistenceTest { @Mock private Settings settings; - @BeforeClass + @BeforeAll public static void setUpLogger() { TestHelper.setupLogger(); } @@ -157,3 +157,5 @@ private static Matcher notNullAndDifferentFrom(T o) { return both(not(sameInstance(o))).and(not(nullValue())); } } + + diff --git a/src/test/java/fr/xephi/authme/data/limbo/persistence/LimboPersistenceTypeTest.java b/authme-core/src/test/java/fr/xephi/authme/data/limbo/persistence/LimboPersistenceTypeTest.java similarity index 94% rename from src/test/java/fr/xephi/authme/data/limbo/persistence/LimboPersistenceTypeTest.java rename to authme-core/src/test/java/fr/xephi/authme/data/limbo/persistence/LimboPersistenceTypeTest.java index 33fe6924fc..cf0fb2d314 100644 --- a/src/test/java/fr/xephi/authme/data/limbo/persistence/LimboPersistenceTypeTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/data/limbo/persistence/LimboPersistenceTypeTest.java @@ -1,13 +1,13 @@ package fr.xephi.authme.data.limbo.persistence; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.HashSet; import java.util.Set; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -46,3 +46,4 @@ public void shouldHaveTypeReturnedFromImplementationClass() { } } + diff --git a/src/test/java/fr/xephi/authme/data/limbo/persistence/SegmentNameBuilderTest.java b/authme-core/src/test/java/fr/xephi/authme/data/limbo/persistence/SegmentNameBuilderTest.java similarity index 99% rename from src/test/java/fr/xephi/authme/data/limbo/persistence/SegmentNameBuilderTest.java rename to authme-core/src/test/java/fr/xephi/authme/data/limbo/persistence/SegmentNameBuilderTest.java index 745a7ff876..222442a1c5 100644 --- a/src/test/java/fr/xephi/authme/data/limbo/persistence/SegmentNameBuilderTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/data/limbo/persistence/SegmentNameBuilderTest.java @@ -1,6 +1,6 @@ package fr.xephi.authme.data.limbo.persistence; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.HashSet; import java.util.Set; @@ -139,3 +139,4 @@ public void shouldCreate256Segments() { assertThat(nameBuilder.createSegmentName("7122f"), equalTo("seg256-71")); } } + diff --git a/src/test/java/fr/xephi/authme/data/limbo/persistence/SegmentSizeTest.java b/authme-core/src/test/java/fr/xephi/authme/data/limbo/persistence/SegmentSizeTest.java similarity index 94% rename from src/test/java/fr/xephi/authme/data/limbo/persistence/SegmentSizeTest.java rename to authme-core/src/test/java/fr/xephi/authme/data/limbo/persistence/SegmentSizeTest.java index 1cc34693c5..272777f8b1 100644 --- a/src/test/java/fr/xephi/authme/data/limbo/persistence/SegmentSizeTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/data/limbo/persistence/SegmentSizeTest.java @@ -1,7 +1,7 @@ package fr.xephi.authme.data.limbo.persistence; import com.google.common.collect.ImmutableSet; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.HashSet; import java.util.Set; @@ -9,7 +9,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; /** * Test for {@link SegmentSize}. @@ -45,3 +45,4 @@ public void shouldHaveDifferentSegmentSizes() { } } } + diff --git a/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java b/authme-core/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java similarity index 99% rename from src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java rename to authme-core/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java index 6ced067416..ddd992bb12 100644 --- a/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java @@ -5,7 +5,7 @@ import com.google.common.collect.Lists; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.security.crypts.HashedPassword; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.HashSet; @@ -26,7 +26,7 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assume.assumeThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; /** * Abstract class for data source integration tests. @@ -256,7 +256,7 @@ public void shouldDeletePlayers() { // given DataSource dataSource = getDataSource(); Set playersToDelete = new HashSet<>(Arrays.asList("bobby", "doesNotExist")); - assumeThat(dataSource.getAccountsRegistered(), equalTo(2)); + assumeTrue(dataSource.getAccountsRegistered() == 2); // when dataSource.purgeRecords(playersToDelete); @@ -533,3 +533,4 @@ public void shouldRemoveTotpKey() { assertThat(dataSource.getAuth("user").getTotpKey(), nullValue()); } } + diff --git a/src/test/java/fr/xephi/authme/datasource/AbstractResourceClosingTest.java b/authme-core/src/test/java/fr/xephi/authme/datasource/AbstractResourceClosingTest.java similarity index 89% rename from src/test/java/fr/xephi/authme/datasource/AbstractResourceClosingTest.java rename to authme-core/src/test/java/fr/xephi/authme/datasource/AbstractResourceClosingTest.java index 5b2dca5885..6d93c266ce 100644 --- a/src/test/java/fr/xephi/authme/datasource/AbstractResourceClosingTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/datasource/AbstractResourceClosingTest.java @@ -5,10 +5,9 @@ import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.security.crypts.HashedPassword; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -28,6 +27,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; @@ -46,7 +46,6 @@ * keeping a list of mocks ({@link #closeables}) and then verifying that all have been * closed ({@link #verifyHaveMocksBeenClosed()}). */ -@RunWith(Parameterized.class) public abstract class AbstractResourceClosingTest { /** Collection of values to use to call methods with the parameters they expect. */ @@ -60,42 +59,34 @@ public abstract class AbstractResourceClosingTest { private boolean hasCreatedConnection = false; - /** - * Constructor for the test instance verifying the given method. - * - * @param method The DataSource method to test - * @param name The name of the method - */ - public AbstractResourceClosingTest(Method method, String name) { - // Note ljacqu 20160227: The name parameter is necessary as we pass it from the @Parameters method; - // we use the method name in the annotation to name the test sensibly - this.method = method; - } - - @BeforeClass + @BeforeAll public static void initializeLogger() { TestHelper.setupLogger(); } - /** - * The actual test -- executes the method given through the constructor and then verifies that all - * AutoCloseable mocks it constructed have been closed. - */ - @Test - public void shouldCloseResources() throws IllegalAccessException, InvocationTargetException { + @TestFactory + public List shouldCloseResources() { + return getMethodsToTest().stream() + .map(method -> DynamicTest.dynamicTest(method.getName(), () -> assertResourcesClosed(method))) + .collect(Collectors.toList()); + } + + protected final void assertResourcesClosed(Method method) throws IllegalAccessException, InvocationTargetException { + this.method = method; + this.closeables = new ArrayList<>(); + this.hasCreatedConnection = false; method.invoke(getObjectUnderTest(), buildParamListForMethod(method)); verifyHaveMocksBeenClosed(); } protected abstract Object getObjectUnderTest(); + protected abstract Collection getMethodsToTest(); + /** * Verify that all AutoCloseables that have been created during the method execution have been closed. */ private void verifyHaveMocksBeenClosed() { - if (closeables.isEmpty()) { - System.out.println("Note: detected no AutoCloseables for method '" + method.getName() + "'"); - } try { for (AutoCloseable autoCloseable : closeables) { verify(autoCloseable).close(); @@ -253,3 +244,4 @@ public Boolean answer(InvocationOnMock invocation) { } } + diff --git a/src/test/java/fr/xephi/authme/datasource/AbstractSqlDataSourceResourceClosingTest.java b/authme-core/src/test/java/fr/xephi/authme/datasource/AbstractSqlDataSourceResourceClosingTest.java similarity index 71% rename from src/test/java/fr/xephi/authme/datasource/AbstractSqlDataSourceResourceClosingTest.java rename to authme-core/src/test/java/fr/xephi/authme/datasource/AbstractSqlDataSourceResourceClosingTest.java index 4443c54c74..1ebaefbc63 100644 --- a/src/test/java/fr/xephi/authme/datasource/AbstractSqlDataSourceResourceClosingTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/datasource/AbstractSqlDataSourceResourceClosingTest.java @@ -3,8 +3,7 @@ import com.google.common.collect.ImmutableSet; import fr.xephi.authme.TestHelper; import fr.xephi.authme.settings.Settings; -import org.junit.BeforeClass; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.BeforeAll; import java.lang.reflect.Method; import java.sql.Connection; @@ -27,11 +26,7 @@ public abstract class AbstractSqlDataSourceResourceClosingTest extends AbstractR private static Settings settings; - AbstractSqlDataSourceResourceClosingTest(Method method, String name) { - super(method, name); - } - - @BeforeClass + @BeforeAll public static void initializeSettings() { settings = mock(Settings.class); TestHelper.returnDefaultsForAllProperties(settings); @@ -49,19 +44,9 @@ protected DataSource getObjectUnderTest() { /* Create a DataSource instance with the given mock settings and mock connection. */ protected abstract DataSource createDataSource(Settings settings, Connection connection) throws Exception; - /** - * Initialization method -- provides the parameters to run the test with by scanning all DataSource methods. - * - * @return Test parameters - */ - @Parameterized.Parameters(name = "{1}") - public static Collection data() { - List methods = getDataSourceMethods(); - List data = new ArrayList<>(); - for (Method method : methods) { - data.add(new Object[]{method, method.getName()}); - } - return data; + @Override + protected Collection getMethodsToTest() { + return getDataSourceMethods(); } /* Get all methods of the DataSource interface, minus the ones in the ignored list. */ @@ -71,3 +56,4 @@ private static List getDataSourceMethods() { .collect(Collectors.toList()); } } + diff --git a/src/test/java/fr/xephi/authme/datasource/MySqlIntegrationTest.java b/authme-core/src/test/java/fr/xephi/authme/datasource/MySqlIntegrationTest.java similarity index 95% rename from src/test/java/fr/xephi/authme/datasource/MySqlIntegrationTest.java rename to authme-core/src/test/java/fr/xephi/authme/datasource/MySqlIntegrationTest.java index 027608230e..12314faa5b 100644 --- a/src/test/java/fr/xephi/authme/datasource/MySqlIntegrationTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/datasource/MySqlIntegrationTest.java @@ -6,9 +6,9 @@ import fr.xephi.authme.TestHelper; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.DatabaseSettings; -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; import java.io.IOException; import java.nio.file.Files; @@ -35,7 +35,7 @@ public class MySqlIntegrationTest extends AbstractDataSourceIntegrationTest { /** * Set up the settings mock to return specific values for database settings and load {@link #sqlInitialize}. */ - @BeforeClass + @BeforeAll public static void initializeSettings() throws IOException, ClassNotFoundException { // Check that we have an H2 driver Class.forName("org.h2.jdbcx.JdbcDataSource"); @@ -50,7 +50,7 @@ public static void initializeSettings() throws IOException, ClassNotFoundExcepti sqlInitialize = new String(Files.readAllBytes(sqlInitFile)); } - @Before + @BeforeEach public void initializeConnectionAndTable() throws SQLException { HikariConfig config = new HikariConfig(); config.setDataSourceClassName("org.h2.jdbcx.JdbcDataSource"); @@ -70,7 +70,7 @@ public void initializeConnectionAndTable() throws SQLException { hikariSource = ds; } - @After + @AfterEach public void closeConnection() { silentClose(hikariSource); } @@ -92,3 +92,4 @@ private static void silentClose(HikariDataSource con) { } } + diff --git a/src/test/java/fr/xephi/authme/datasource/MySqlResourceClosingTest.java b/authme-core/src/test/java/fr/xephi/authme/datasource/MySqlResourceClosingTest.java similarity index 89% rename from src/test/java/fr/xephi/authme/datasource/MySqlResourceClosingTest.java rename to authme-core/src/test/java/fr/xephi/authme/datasource/MySqlResourceClosingTest.java index 3500560b57..a794bb4fb3 100644 --- a/src/test/java/fr/xephi/authme/datasource/MySqlResourceClosingTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/datasource/MySqlResourceClosingTest.java @@ -5,7 +5,6 @@ import fr.xephi.authme.datasource.mysqlextensions.MySqlExtensionsFactory; import fr.xephi.authme.settings.Settings; -import java.lang.reflect.Method; import java.sql.Connection; import static org.mockito.ArgumentMatchers.any; @@ -17,10 +16,6 @@ */ public class MySqlResourceClosingTest extends AbstractSqlDataSourceResourceClosingTest { - public MySqlResourceClosingTest(Method method, String name) { - super(method, name); - } - @Override protected DataSource createDataSource(Settings settings, Connection connection) throws Exception { HikariDataSource hikariDataSource = mock(HikariDataSource.class); diff --git a/src/test/java/fr/xephi/authme/datasource/PostgreSqlIntegrationTest.java b/authme-core/src/test/java/fr/xephi/authme/datasource/PostgreSqlIntegrationTest.java similarity index 94% rename from src/test/java/fr/xephi/authme/datasource/PostgreSqlIntegrationTest.java rename to authme-core/src/test/java/fr/xephi/authme/datasource/PostgreSqlIntegrationTest.java index 54a00bf409..22acfb6f1a 100644 --- a/src/test/java/fr/xephi/authme/datasource/PostgreSqlIntegrationTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/datasource/PostgreSqlIntegrationTest.java @@ -6,9 +6,9 @@ import fr.xephi.authme.TestHelper; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.DatabaseSettings; -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; import java.io.IOException; import java.nio.file.Files; @@ -35,7 +35,7 @@ public class PostgreSqlIntegrationTest extends AbstractDataSourceIntegrationTest /** * Set up the settings mock to return specific values for database settings and load {@link #sqlInitialize}. */ - @BeforeClass + @BeforeAll public static void initializeSettings() throws IOException, ClassNotFoundException { // Check that we have an H2 driver Class.forName("org.h2.jdbcx.JdbcDataSource"); @@ -50,7 +50,7 @@ public static void initializeSettings() throws IOException, ClassNotFoundExcepti sqlInitialize = new String(Files.readAllBytes(sqlInitFile)); } - @Before + @BeforeEach public void initializeConnectionAndTable() throws SQLException { HikariConfig config = new HikariConfig(); config.setDataSourceClassName("org.h2.jdbcx.JdbcDataSource"); @@ -68,7 +68,7 @@ public void initializeConnectionAndTable() throws SQLException { hikariSource = ds; } - @After + @AfterEach public void closeConnection() { silentClose(hikariSource); } @@ -90,3 +90,4 @@ private static void silentClose(HikariDataSource con) { } } + diff --git a/src/test/java/fr/xephi/authme/datasource/PostgreSqlResourceClosingTest.java b/authme-core/src/test/java/fr/xephi/authme/datasource/PostgreSqlResourceClosingTest.java similarity index 89% rename from src/test/java/fr/xephi/authme/datasource/PostgreSqlResourceClosingTest.java rename to authme-core/src/test/java/fr/xephi/authme/datasource/PostgreSqlResourceClosingTest.java index 40a1d391f1..85d14febb3 100644 --- a/src/test/java/fr/xephi/authme/datasource/PostgreSqlResourceClosingTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/datasource/PostgreSqlResourceClosingTest.java @@ -5,7 +5,6 @@ import fr.xephi.authme.datasource.mysqlextensions.MySqlExtensionsFactory; import fr.xephi.authme.settings.Settings; -import java.lang.reflect.Method; import java.sql.Connection; import static org.mockito.ArgumentMatchers.any; @@ -17,10 +16,6 @@ */ public class PostgreSqlResourceClosingTest extends AbstractSqlDataSourceResourceClosingTest { - public PostgreSqlResourceClosingTest(Method method, String name) { - super(method, name); - } - @Override protected DataSource createDataSource(Settings settings, Connection connection) throws Exception { HikariDataSource hikariDataSource = mock(HikariDataSource.class); diff --git a/src/test/java/fr/xephi/authme/datasource/SQLiteIntegrationTest.java b/authme-core/src/test/java/fr/xephi/authme/datasource/SQLiteIntegrationTest.java similarity index 95% rename from src/test/java/fr/xephi/authme/datasource/SQLiteIntegrationTest.java rename to authme-core/src/test/java/fr/xephi/authme/datasource/SQLiteIntegrationTest.java index 60b2a7bd89..9e426b5e5d 100644 --- a/src/test/java/fr/xephi/authme/datasource/SQLiteIntegrationTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/datasource/SQLiteIntegrationTest.java @@ -5,10 +5,10 @@ import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.DatabaseSettings; -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.nio.file.Files; @@ -38,7 +38,7 @@ public class SQLiteIntegrationTest extends AbstractDataSourceIntegrationTest { /** * Set up the settings mock to return specific values for database settings and load {@link #sqlInitialize}. */ - @BeforeClass + @BeforeAll public static void initializeSettings() throws IOException, ClassNotFoundException { // Check that we have an implementation for SQLite Class.forName("org.sqlite.JDBC"); @@ -55,7 +55,7 @@ public static void initializeSettings() throws IOException, ClassNotFoundExcepti sqlInitialize = new String(Files.readAllBytes(sqlInitFile)).split(";(\\r?)\\n"); } - @Before + @BeforeEach public void initializeConnectionAndTable() throws SQLException { Connection connection = DriverManager.getConnection("jdbc:sqlite::memory:"); try (Statement st = connection.createStatement()) { @@ -67,7 +67,7 @@ public void initializeConnectionAndTable() throws SQLException { con = connection; } - @After + @AfterEach public void closeConnection() { silentClose(con); } @@ -133,3 +133,4 @@ private static void silentClose(Connection con) { } } } + diff --git a/src/test/java/fr/xephi/authme/datasource/SQLiteResourceClosingTest.java b/authme-core/src/test/java/fr/xephi/authme/datasource/SQLiteResourceClosingTest.java similarity index 76% rename from src/test/java/fr/xephi/authme/datasource/SQLiteResourceClosingTest.java rename to authme-core/src/test/java/fr/xephi/authme/datasource/SQLiteResourceClosingTest.java index 2c8fe5dc15..6f8346511e 100644 --- a/src/test/java/fr/xephi/authme/datasource/SQLiteResourceClosingTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/datasource/SQLiteResourceClosingTest.java @@ -2,7 +2,6 @@ import fr.xephi.authme.settings.Settings; -import java.lang.reflect.Method; import java.sql.Connection; /** @@ -10,10 +9,6 @@ */ public class SQLiteResourceClosingTest extends AbstractSqlDataSourceResourceClosingTest { - public SQLiteResourceClosingTest(Method method, String name) { - super(method, name); - } - @Override protected DataSource createDataSource(Settings settings, Connection connection) throws Exception { return new SQLite(settings, null, connection); diff --git a/src/test/java/fr/xephi/authme/datasource/SqLiteMigraterIntegrationTest.java b/authme-core/src/test/java/fr/xephi/authme/datasource/SqLiteMigraterIntegrationTest.java similarity index 95% rename from src/test/java/fr/xephi/authme/datasource/SqLiteMigraterIntegrationTest.java rename to authme-core/src/test/java/fr/xephi/authme/datasource/SqLiteMigraterIntegrationTest.java index fc49904c93..41f6cb3b99 100644 --- a/src/test/java/fr/xephi/authme/datasource/SqLiteMigraterIntegrationTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/datasource/SqLiteMigraterIntegrationTest.java @@ -5,10 +5,9 @@ import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.settings.Settings; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import fr.xephi.authme.TempFolder; import java.io.File; import java.io.IOException; @@ -34,11 +33,9 @@ public class SqLiteMigraterIntegrationTest { private File dataFolder; private SQLite sqLite; + public TempFolder temporaryFolder = new TempFolder(); - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); - - @Before + @BeforeEach public void setup() throws SQLException, IOException, NoSuchMethodException { TestHelper.setupLogger(); @@ -103,3 +100,5 @@ private static PlayerAuth getByNameOrFail(String name, List auths) { .findFirst().orElseThrow(() -> new IllegalStateException("No PlayerAuth with name '" + name + "'")); } } + + diff --git a/src/test/java/fr/xephi/authme/datasource/SqlDataSourceTestUtil.java b/authme-core/src/test/java/fr/xephi/authme/datasource/SqlDataSourceTestUtil.java similarity index 100% rename from src/test/java/fr/xephi/authme/datasource/SqlDataSourceTestUtil.java rename to authme-core/src/test/java/fr/xephi/authme/datasource/SqlDataSourceTestUtil.java diff --git a/src/test/java/fr/xephi/authme/datasource/SqlDataSourceUtilsTest.java b/authme-core/src/test/java/fr/xephi/authme/datasource/SqlDataSourceUtilsTest.java similarity index 91% rename from src/test/java/fr/xephi/authme/datasource/SqlDataSourceUtilsTest.java rename to authme-core/src/test/java/fr/xephi/authme/datasource/SqlDataSourceUtilsTest.java index 29018b1d03..1ad54ff94f 100644 --- a/src/test/java/fr/xephi/authme/datasource/SqlDataSourceUtilsTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/datasource/SqlDataSourceUtilsTest.java @@ -1,8 +1,8 @@ package fr.xephi.authme.datasource; import fr.xephi.authme.TestHelper; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.sql.DatabaseMetaData; import java.sql.ResultSet; @@ -13,6 +13,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -25,7 +26,7 @@ public class SqlDataSourceUtilsTest { private Logger logger; - @Before + @BeforeEach public void initLogger() { logger = TestHelper.setupLogger(); } @@ -79,7 +80,7 @@ public void shouldReturnFalseForUnknownNullableStatus() throws SQLException { assertThat(result, equalTo(false)); } - @Test(expected = IllegalStateException.class) + @Test public void shouldThrowForUnknownColumnInNullableCheck() throws SQLException { // given String tableName = "data"; @@ -90,9 +91,8 @@ public void shouldThrowForUnknownColumnInNullableCheck() throws SQLException { given(metaData.getColumns(null, null, tableName, columnName)).willReturn(resultSet); // when - SqlDataSourceUtils.isNotNullColumn(metaData, tableName, columnName); - - // then - expect exception + assertThrows(IllegalStateException.class, + () -> SqlDataSourceUtils.isNotNullColumn(metaData, tableName, columnName)); } @Test @@ -113,7 +113,7 @@ public void shouldGetDefaultValue() throws SQLException { assertThat(defaultValue, equalTo("Literature")); } - @Test(expected = IllegalStateException.class) + @Test public void shouldThrowForUnknownColumnInDefaultValueRetrieval() throws SQLException { // given String tableName = "data"; @@ -124,9 +124,8 @@ public void shouldThrowForUnknownColumnInDefaultValueRetrieval() throws SQLExcep given(metaData.getColumns(null, null, tableName, columnName)).willReturn(resultSet); // when - SqlDataSourceUtils.getColumnDefaultValue(metaData, tableName, columnName); - - // then - expect exception + assertThrows(IllegalStateException.class, + () -> SqlDataSourceUtils.getColumnDefaultValue(metaData, tableName, columnName)); } @Test @@ -147,3 +146,4 @@ public void shouldHandleNullDefaultValue() throws SQLException { assertThat(defaultValue, nullValue()); } } + diff --git a/src/test/java/fr/xephi/authme/datasource/converter/AbstractDataSourceConverterTest.java b/authme-core/src/test/java/fr/xephi/authme/datasource/converter/AbstractDataSourceConverterTest.java similarity index 98% rename from src/test/java/fr/xephi/authme/datasource/converter/AbstractDataSourceConverterTest.java rename to authme-core/src/test/java/fr/xephi/authme/datasource/converter/AbstractDataSourceConverterTest.java index 5dd0324444..164d31b37d 100644 --- a/src/test/java/fr/xephi/authme/datasource/converter/AbstractDataSourceConverterTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/datasource/converter/AbstractDataSourceConverterTest.java @@ -5,8 +5,8 @@ import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSourceType; import org.bukkit.command.CommandSender; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.Mockito; import java.util.Arrays; @@ -30,7 +30,7 @@ */ public class AbstractDataSourceConverterTest { - @BeforeClass + @BeforeAll public static void initLogger() { TestHelper.setupLogger(); } @@ -125,3 +125,4 @@ protected S getSource() { } } } + diff --git a/authme-core/src/test/java/fr/xephi/authme/datasource/converter/AuthPlusConverterTest.java b/authme-core/src/test/java/fr/xephi/authme/datasource/converter/AuthPlusConverterTest.java new file mode 100644 index 0000000000..c645f00871 --- /dev/null +++ b/authme-core/src/test/java/fr/xephi/authme/datasource/converter/AuthPlusConverterTest.java @@ -0,0 +1,117 @@ +package fr.xephi.authme.datasource.converter; + +import ch.jalu.injector.testing.InjectDelayed; +import fr.xephi.authme.DelayedInjectionExtension; +import fr.xephi.authme.TestHelper; +import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.initialization.DataFolder; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockedStatic; + +import java.io.File; +import java.util.List; +import java.util.UUID; + +import static fr.xephi.authme.AuthMeMatchers.equalToHash; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; + +/** + * Test for {@link AuthPlusConverter}. + */ +@ExtendWith(DelayedInjectionExtension.class) +public class AuthPlusConverterTest { + + private static final UUID UUID_TESTPLAYER = UUID.fromString("a811b264-a1da-42dd-b1b7-b601234b7cc7"); + private static final UUID UUID_EXISTINGPLAYER = UUID.fromString("b922c375-b2eb-53ee-c2c8-c712345c8dd8"); + private static final UUID UUID_UNKNOWNPLAYER = UUID.fromString("cc033486-c3fc-64ff-d3d9-d823456d9ee9"); + + @InjectDelayed + private AuthPlusConverter converter; + + @Mock + private DataSource dataSource; + + @DataFolder + private File dataFolder = TestHelper.getJarFile(TestHelper.PROJECT_ROOT + "datasource/converter/"); + + @Captor + private ArgumentCaptor authCaptor; + + @BeforeAll + public static void initLogger() { + TestHelper.setupLogger(); + } + + @Test + public void shouldImportAndSkipAccordingly() { + // given + CommandSender sender = mock(CommandSender.class); + given(dataSource.isAuthAvailable("existingplayer")).willReturn(true); + + OfflinePlayer testPlayer = offlinePlayerWithName("TestPlayer"); + OfflinePlayer existingPlayer = offlinePlayerWithName("ExistingPlayer"); + // UUID_UNKNOWNPLAYER resolves to null (player never joined) + OfflinePlayer unknownPlayer = offlinePlayerWithName(null); + + try (MockedStatic bukkit = mockStatic(Bukkit.class)) { + bukkit.when(() -> Bukkit.getOfflinePlayer(UUID_TESTPLAYER)).thenReturn(testPlayer); + bukkit.when(() -> Bukkit.getOfflinePlayer(UUID_EXISTINGPLAYER)).thenReturn(existingPlayer); + bukkit.when(() -> Bukkit.getOfflinePlayer(UUID_UNKNOWNPLAYER)).thenReturn(unknownPlayer); + + // when + converter.execute(sender); + } + + // then + verify(dataSource, times(1)).saveAuth(authCaptor.capture()); + List saved = authCaptor.getAllValues(); + assertThat(saved, hasSize(1)); + assertThat(saved.get(0).getNickname(), equalTo("testplayer")); + assertThat(saved.get(0).getRealName(), equalTo("TestPlayer")); + assertThat(saved.get(0).getUuid(), equalTo(UUID_TESTPLAYER)); + assertThat(saved.get(0).getPassword(), + equalToHash("pbkdf2$120000$JddgMm9rNhbYNfEmf4pOKA==$RcAMtgm/KnKFxfNOpg95tb7s5OzB2Fv4Wj1HOAI/TWY=")); + verify(sender).sendMessage( + org.mockito.hamcrest.MockitoHamcrest.argThat(containsString("1 account(s) imported"))); + } + + @Test + public void shouldReportMissingFile() { + // given + CommandSender sender = mock(CommandSender.class); + File missingFolder = new File("non/existent/path"); + AuthPlusConverter missingConverter = new AuthPlusConverter(missingFolder, dataSource); + + // when + missingConverter.execute(sender); + + // then + verifyNoInteractions(dataSource); + verify(sender).sendMessage( + org.mockito.hamcrest.MockitoHamcrest.argThat(containsString("does not exist"))); + } + + private static OfflinePlayer offlinePlayerWithName(String name) { + OfflinePlayer player = mock(OfflinePlayer.class); + given(player.getName()).willReturn(name); + return player; + } +} diff --git a/src/test/java/fr/xephi/authme/datasource/converter/CrazyLoginConverterTest.java b/authme-core/src/test/java/fr/xephi/authme/datasource/converter/CrazyLoginConverterTest.java similarity index 90% rename from src/test/java/fr/xephi/authme/datasource/converter/CrazyLoginConverterTest.java rename to authme-core/src/test/java/fr/xephi/authme/datasource/converter/CrazyLoginConverterTest.java index e68056eb80..07930e3b7e 100644 --- a/src/test/java/fr/xephi/authme/datasource/converter/CrazyLoginConverterTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/datasource/converter/CrazyLoginConverterTest.java @@ -1,6 +1,7 @@ package fr.xephi.authme.datasource.converter; -import ch.jalu.injector.testing.DelayedInjectionRunner; +import org.junit.jupiter.api.extension.ExtendWith; +import fr.xephi.authme.DelayedInjectionExtension; import ch.jalu.injector.testing.InjectDelayed; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerAuth; @@ -9,10 +10,10 @@ import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.ConverterSettings; import org.bukkit.command.CommandSender; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import java.io.File; @@ -32,7 +33,7 @@ /** * Test for {@link CrazyLoginConverter}. */ -@RunWith(DelayedInjectionRunner.class) +@ExtendWith(DelayedInjectionExtension.class) public class CrazyLoginConverterTest { @InjectDelayed @@ -43,11 +44,13 @@ public class CrazyLoginConverterTest { @Mock private Settings settings; + @Captor + private ArgumentCaptor authCaptor; @DataFolder private File dataFolder = TestHelper.getJarFile(TestHelper.PROJECT_ROOT + "/datasource/converter/"); - @BeforeClass + @BeforeAll public static void initializeLogger() { TestHelper.setupLogger(); } @@ -62,7 +65,6 @@ public void shouldImportUsers() { crazyLoginConverter.execute(sender); // then - ArgumentCaptor authCaptor = ArgumentCaptor.forClass(PlayerAuth.class); verify(dataSource, times(2)).saveAuth(authCaptor.capture()); List savedAuths = authCaptor.getAllValues(); assertNameAndRealName(savedAuths.get(0), "qotato", "qotaTo"); @@ -91,3 +93,5 @@ private static void assertNameAndRealName(PlayerAuth auth, String name, String r } } + + diff --git a/src/test/java/fr/xephi/authme/datasource/converter/LoginSecurityConverterTest.java b/authme-core/src/test/java/fr/xephi/authme/datasource/converter/LoginSecurityConverterTest.java similarity index 95% rename from src/test/java/fr/xephi/authme/datasource/converter/LoginSecurityConverterTest.java rename to authme-core/src/test/java/fr/xephi/authme/datasource/converter/LoginSecurityConverterTest.java index 41979e98ef..2a3fef1657 100644 --- a/src/test/java/fr/xephi/authme/datasource/converter/LoginSecurityConverterTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/datasource/converter/LoginSecurityConverterTest.java @@ -1,7 +1,8 @@ package fr.xephi.authme.datasource.converter; +import org.junit.jupiter.api.extension.ExtendWith; +import fr.xephi.authme.DelayedInjectionExtension; import ch.jalu.injector.testing.BeforeInjecting; -import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.InjectDelayed; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; @@ -12,9 +13,9 @@ import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.ConverterSettings; import org.bukkit.command.CommandSender; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import java.io.File; @@ -39,7 +40,7 @@ /** * Test for {@link LoginSecurityConverter}. */ -@RunWith(DelayedInjectionRunner.class) +@ExtendWith(DelayedInjectionExtension.class) public class LoginSecurityConverterTest { @InjectDelayed @@ -49,6 +50,8 @@ public class LoginSecurityConverterTest { private DataSource dataSource; @Mock private Settings settings; + @Captor + private ArgumentCaptor captor; @DataFolder private File dataFolder = new File("."); // not used but required for injection @@ -69,7 +72,6 @@ public void shouldConvertFromSqlite() throws SQLException { converter.performConversion(sender, connection); // then - ArgumentCaptor captor = ArgumentCaptor.forClass(PlayerAuth.class); verify(dataSource, times(3)).saveAuth(captor.capture()); assertThat(captor.getAllValues().get(0).getNickname(), equalTo("player1")); assertThat(captor.getAllValues().get(0).getRealName(), equalTo("Player1")); @@ -103,7 +105,6 @@ public void shouldConvertFromMySql() throws IOException, SQLException { converter.performConversion(sender, connection); // then - ArgumentCaptor captor = ArgumentCaptor.forClass(PlayerAuth.class); verify(dataSource, times(3)).saveAuth(captor.capture()); assertThat(captor.getAllValues().get(0).getNickname(), equalTo("player1")); assertThat(captor.getAllValues().get(0).getRealName(), equalTo("Player1")); @@ -145,3 +146,5 @@ private static void assertIsCloseTo(long value1, long value2, long tolerance) { assertThat(Math.abs(value1 - value2), not(greaterThan(tolerance))); } } + + diff --git a/src/test/java/fr/xephi/authme/datasource/mysqlextensions/AbstractMySqlExtensionResourceClosingTest.java b/authme-core/src/test/java/fr/xephi/authme/datasource/mysqlextensions/AbstractMySqlExtensionResourceClosingTest.java similarity index 81% rename from src/test/java/fr/xephi/authme/datasource/mysqlextensions/AbstractMySqlExtensionResourceClosingTest.java rename to authme-core/src/test/java/fr/xephi/authme/datasource/mysqlextensions/AbstractMySqlExtensionResourceClosingTest.java index 3089e8b999..cb3a74fd41 100644 --- a/src/test/java/fr/xephi/authme/datasource/mysqlextensions/AbstractMySqlExtensionResourceClosingTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/datasource/mysqlextensions/AbstractMySqlExtensionResourceClosingTest.java @@ -4,11 +4,11 @@ import fr.xephi.authme.datasource.AbstractResourceClosingTest; import fr.xephi.authme.datasource.Columns; import fr.xephi.authme.settings.Settings; -import org.junit.BeforeClass; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.BeforeAll; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.Collection; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @@ -23,11 +23,7 @@ public abstract class AbstractMySqlExtensionResourceClosingTest extends Abstract private static Settings settings; private static Columns columns; - public AbstractMySqlExtensionResourceClosingTest(Method method, String name) { - super(method, name); - } - - @BeforeClass + @BeforeAll public static void initSettings() { settings = mock(Settings.class); TestHelper.returnDefaultsForAllProperties(settings); @@ -41,7 +37,13 @@ protected MySqlExtension getObjectUnderTest() { protected abstract MySqlExtension createExtension(Settings settings, Columns columns); - @Parameterized.Parameters(name = "{1}") + @Override + protected Collection getMethodsToTest() { + return Arrays.stream(MySqlExtension.class.getDeclaredMethods()) + .filter(m -> Modifier.isPublic(m.getModifiers())) + .collect(Collectors.toList()); + } + public static List createParameters() { return Arrays.stream(MySqlExtension.class.getDeclaredMethods()) .filter(m -> Modifier.isPublic(m.getModifiers())) @@ -49,3 +51,4 @@ public static List createParameters() { .collect(Collectors.toList()); } } + diff --git a/src/test/java/fr/xephi/authme/datasource/mysqlextensions/Ipb4ExtensionResourceClosingTest.java b/authme-core/src/test/java/fr/xephi/authme/datasource/mysqlextensions/Ipb4ExtensionResourceClosingTest.java similarity index 76% rename from src/test/java/fr/xephi/authme/datasource/mysqlextensions/Ipb4ExtensionResourceClosingTest.java rename to authme-core/src/test/java/fr/xephi/authme/datasource/mysqlextensions/Ipb4ExtensionResourceClosingTest.java index 8629a6a7d7..4a2d7f1e27 100644 --- a/src/test/java/fr/xephi/authme/datasource/mysqlextensions/Ipb4ExtensionResourceClosingTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/datasource/mysqlextensions/Ipb4ExtensionResourceClosingTest.java @@ -3,17 +3,11 @@ import fr.xephi.authme.datasource.Columns; import fr.xephi.authme.settings.Settings; -import java.lang.reflect.Method; - /** * Resource closing test for {@link Ipb4Extension}. */ public class Ipb4ExtensionResourceClosingTest extends AbstractMySqlExtensionResourceClosingTest { - public Ipb4ExtensionResourceClosingTest(Method method, String name) { - super(method, name); - } - @Override protected MySqlExtension createExtension(Settings settings, Columns columns) { return new Ipb4Extension(settings, columns); diff --git a/src/test/java/fr/xephi/authme/datasource/mysqlextensions/NoOpExtensionTest.java b/authme-core/src/test/java/fr/xephi/authme/datasource/mysqlextensions/NoOpExtensionTest.java similarity index 79% rename from src/test/java/fr/xephi/authme/datasource/mysqlextensions/NoOpExtensionTest.java rename to authme-core/src/test/java/fr/xephi/authme/datasource/mysqlextensions/NoOpExtensionTest.java index 40f98247f9..aadcc23c4b 100644 --- a/src/test/java/fr/xephi/authme/datasource/mysqlextensions/NoOpExtensionTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/datasource/mysqlextensions/NoOpExtensionTest.java @@ -1,14 +1,16 @@ package fr.xephi.authme.datasource.mysqlextensions; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.Columns; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.settings.Settings; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.sql.Connection; import java.sql.SQLException; @@ -19,12 +21,13 @@ /** * Test for {@link NoOpExtension}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class NoOpExtensionTest { private NoOpExtension extension; - @Before + @BeforeEach public void createExtension() { Settings settings = mock(Settings.class); TestHelper.returnDefaultsForAllProperties(settings); @@ -52,3 +55,5 @@ public void shouldNotHaveAnyInteractionsWithConnection() throws SQLException { verifyNoInteractions(connection, auth); } } + + diff --git a/src/test/java/fr/xephi/authme/datasource/mysqlextensions/PhpBbExtensionResourceClosingTest.java b/authme-core/src/test/java/fr/xephi/authme/datasource/mysqlextensions/PhpBbExtensionResourceClosingTest.java similarity index 75% rename from src/test/java/fr/xephi/authme/datasource/mysqlextensions/PhpBbExtensionResourceClosingTest.java rename to authme-core/src/test/java/fr/xephi/authme/datasource/mysqlextensions/PhpBbExtensionResourceClosingTest.java index d1f43e1f45..1156dde9f6 100644 --- a/src/test/java/fr/xephi/authme/datasource/mysqlextensions/PhpBbExtensionResourceClosingTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/datasource/mysqlextensions/PhpBbExtensionResourceClosingTest.java @@ -3,17 +3,11 @@ import fr.xephi.authme.datasource.Columns; import fr.xephi.authme.settings.Settings; -import java.lang.reflect.Method; - /** * Resource closing test for {@link PhpBbExtension}. */ public class PhpBbExtensionResourceClosingTest extends AbstractMySqlExtensionResourceClosingTest { - public PhpBbExtensionResourceClosingTest(Method method, String name) { - super(method, name); - } - @Override protected MySqlExtension createExtension(Settings settings, Columns columns) { return new PhpBbExtension(settings, columns); diff --git a/src/test/java/fr/xephi/authme/datasource/mysqlextensions/WordpressExtensionResourceClosingTest.java b/authme-core/src/test/java/fr/xephi/authme/datasource/mysqlextensions/WordpressExtensionResourceClosingTest.java similarity index 75% rename from src/test/java/fr/xephi/authme/datasource/mysqlextensions/WordpressExtensionResourceClosingTest.java rename to authme-core/src/test/java/fr/xephi/authme/datasource/mysqlextensions/WordpressExtensionResourceClosingTest.java index 1ee141ffe0..20a51a6f43 100644 --- a/src/test/java/fr/xephi/authme/datasource/mysqlextensions/WordpressExtensionResourceClosingTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/datasource/mysqlextensions/WordpressExtensionResourceClosingTest.java @@ -3,17 +3,11 @@ import fr.xephi.authme.datasource.Columns; import fr.xephi.authme.settings.Settings; -import java.lang.reflect.Method; - /** * Resource closing test for {@link WordpressExtension}. */ public class WordpressExtensionResourceClosingTest extends AbstractMySqlExtensionResourceClosingTest { - public WordpressExtensionResourceClosingTest(Method method, String name) { - super(method, name); - } - @Override protected MySqlExtension createExtension(Settings settings, Columns columns) { return new WordpressExtension(settings, columns); diff --git a/src/test/java/fr/xephi/authme/datasource/mysqlextensions/XfBcryptExtensionResourceClosingTest.java b/authme-core/src/test/java/fr/xephi/authme/datasource/mysqlextensions/XfBcryptExtensionResourceClosingTest.java similarity index 75% rename from src/test/java/fr/xephi/authme/datasource/mysqlextensions/XfBcryptExtensionResourceClosingTest.java rename to authme-core/src/test/java/fr/xephi/authme/datasource/mysqlextensions/XfBcryptExtensionResourceClosingTest.java index 7da4fb198b..33a3c53e45 100644 --- a/src/test/java/fr/xephi/authme/datasource/mysqlextensions/XfBcryptExtensionResourceClosingTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/datasource/mysqlextensions/XfBcryptExtensionResourceClosingTest.java @@ -3,17 +3,11 @@ import fr.xephi.authme.datasource.Columns; import fr.xephi.authme.settings.Settings; -import java.lang.reflect.Method; - /** * Resource closing test for {@link XfBcryptExtension}. */ public class XfBcryptExtensionResourceClosingTest extends AbstractMySqlExtensionResourceClosingTest { - public XfBcryptExtensionResourceClosingTest(Method method, String name) { - super(method, name); - } - @Override protected MySqlExtension createExtension(Settings settings, Columns columns) { return new XfBcryptExtension(settings, columns); diff --git a/src/test/java/fr/xephi/authme/events/EventsConsistencyTest.java b/authme-core/src/test/java/fr/xephi/authme/events/EventsConsistencyTest.java similarity index 96% rename from src/test/java/fr/xephi/authme/events/EventsConsistencyTest.java rename to authme-core/src/test/java/fr/xephi/authme/events/EventsConsistencyTest.java index 65b77af49c..3e35d85411 100644 --- a/src/test/java/fr/xephi/authme/events/EventsConsistencyTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/events/EventsConsistencyTest.java @@ -3,8 +3,8 @@ import fr.xephi.authme.ClassCollector; import fr.xephi.authme.TestHelper; import org.bukkit.event.Event; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -22,7 +22,7 @@ public class EventsConsistencyTest { private static final String EVENTS_FOLDER = TestHelper.PROJECT_ROOT + "events/"; private static List> classes; - @BeforeClass + @BeforeAll public static void scanEventClasses() { ClassCollector classCollector = new ClassCollector(TestHelper.SOURCES_FOLDER, EVENTS_FOLDER); classes = classCollector.collectClasses(Event.class); @@ -67,3 +67,4 @@ private static boolean canBeInstantiated(Class clazz) { return !clazz.isInterface() && !clazz.isEnum() && !Modifier.isAbstract(clazz.getModifiers()); } } + diff --git a/src/test/java/fr/xephi/authme/initialization/TaskCloserTest.java b/authme-core/src/test/java/fr/xephi/authme/initialization/TaskCloserTest.java similarity index 88% rename from src/test/java/fr/xephi/authme/initialization/TaskCloserTest.java rename to authme-core/src/test/java/fr/xephi/authme/initialization/TaskCloserTest.java index 36566ec5ef..941a2ebf04 100644 --- a/src/test/java/fr/xephi/authme/initialization/TaskCloserTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/initialization/TaskCloserTest.java @@ -1,20 +1,21 @@ package fr.xephi.authme.initialization; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.AuthMe; -import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.datasource.DataSource; import org.bukkit.Server; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginLogger; -import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitWorker; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import java.util.List; @@ -35,7 +36,8 @@ /** * Test for {@link TaskCloser}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class TaskCloserTest { private static final int[] ACTIVE_WORKERS_ID = {2, 5}; @@ -49,12 +51,14 @@ public class TaskCloserTest { private BukkitScheduler bukkitScheduler; @Mock private DataSource dataSource; + @Captor + private ArgumentCaptor taskIds; - @Before + @BeforeEach public void initAuthMe() { Server server = mock(Server.class); given(server.getScheduler()).willReturn(bukkitScheduler); - ReflectionTestUtils.setField(JavaPlugin.class, authMe, "server", server); + given(authMe.getServer()).willReturn(server); given(authMe.getLogger()).willReturn(logger); taskCloser = spy(new TaskCloser(authMe, dataSource)); } @@ -74,7 +78,6 @@ public void shouldWaitForTasksToClose() throws InterruptedException { // then verify(bukkitScheduler, times(3)).isQueued(anyInt()); - ArgumentCaptor taskIds = ArgumentCaptor.forClass(Integer.class); verify(bukkitScheduler, times(3)).isCurrentlyRunning(taskIds.capture()); assertThat(taskIds.getAllValues(), contains(ACTIVE_WORKERS_ID[0], ACTIVE_WORKERS_ID[1], ACTIVE_WORKERS_ID[1])); verify(taskCloser, times(2)).sleep(); @@ -148,8 +151,12 @@ private void mockActiveWorkers() { private BukkitWorker mockBukkitWorker(Plugin owner, int taskId, boolean isQueued) { BukkitWorker worker = mock(BukkitWorker.class); given(worker.getOwner()).willReturn(owner); - given(worker.getTaskId()).willReturn(taskId); - given(bukkitScheduler.isQueued(taskId)).willReturn(isQueued); + if (owner == authMe) { + given(worker.getTaskId()).willReturn(taskId); + given(bukkitScheduler.isQueued(taskId)).willReturn(isQueued); + } return worker; } } + + diff --git a/src/test/java/fr/xephi/authme/listener/BlockListenerTest.java b/authme-core/src/test/java/fr/xephi/authme/listener/BlockListenerTest.java similarity index 88% rename from src/test/java/fr/xephi/authme/listener/BlockListenerTest.java rename to authme-core/src/test/java/fr/xephi/authme/listener/BlockListenerTest.java index 6925343a43..8cbf90a986 100644 --- a/src/test/java/fr/xephi/authme/listener/BlockListenerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/listener/BlockListenerTest.java @@ -1,13 +1,15 @@ package fr.xephi.authme.listener; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import org.bukkit.entity.Player; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockPlaceEvent; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -17,7 +19,8 @@ /** * Test for {@link BlockListener}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class BlockListenerTest { @InjectMocks @@ -93,3 +96,5 @@ public void shouldDenyBreakEvent() { } } + + diff --git a/src/test/java/fr/xephi/authme/listener/EntityListenerTest.java b/authme-core/src/test/java/fr/xephi/authme/listener/EntityListenerTest.java similarity index 96% rename from src/test/java/fr/xephi/authme/listener/EntityListenerTest.java rename to authme-core/src/test/java/fr/xephi/authme/listener/EntityListenerTest.java index 9fcdd59f53..bf8fd2b6a2 100644 --- a/src/test/java/fr/xephi/authme/listener/EntityListenerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/listener/EntityListenerTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.listener; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.entity.Projectile; @@ -12,11 +16,9 @@ import org.bukkit.event.entity.FoodLevelChangeEvent; import org.bukkit.event.entity.ProjectileLaunchEvent; import org.bukkit.projectiles.ProjectileSource; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import static fr.xephi.authme.listener.EventCancelVerifier.withServiceMock; import static org.hamcrest.Matchers.equalTo; @@ -32,7 +34,8 @@ /** * Test for {@link EntityListener}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class EntityListenerTest { @InjectMocks @@ -245,3 +248,5 @@ public void shouldNotCancelEntityTargetEvent() { verify(event, only()).getTarget(); } } + + diff --git a/src/test/java/fr/xephi/authme/listener/EventCancelVerifier.java b/authme-core/src/test/java/fr/xephi/authme/listener/EventCancelVerifier.java similarity index 100% rename from src/test/java/fr/xephi/authme/listener/EventCancelVerifier.java rename to authme-core/src/test/java/fr/xephi/authme/listener/EventCancelVerifier.java diff --git a/src/test/java/fr/xephi/authme/listener/ListenerConsistencyTest.java b/authme-core/src/test/java/fr/xephi/authme/listener/ListenerConsistencyTest.java similarity index 97% rename from src/test/java/fr/xephi/authme/listener/ListenerConsistencyTest.java rename to authme-core/src/test/java/fr/xephi/authme/listener/ListenerConsistencyTest.java index d7f03a6c5d..77350d1340 100644 --- a/src/test/java/fr/xephi/authme/listener/ListenerConsistencyTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/listener/ListenerConsistencyTest.java @@ -6,8 +6,8 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -18,7 +18,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; /** * Test for verifying that AuthMe listener methods are well-formed. @@ -27,7 +27,7 @@ public final class ListenerConsistencyTest { private static List> classes; - @BeforeClass + @BeforeAll public static void collectListenerClasses() { ClassCollector collector = new ClassCollector(TestHelper.SOURCES_FOLDER, TestHelper.PROJECT_ROOT + "listener"); classes = collector.collectClasses(Listener.class); @@ -120,3 +120,4 @@ private static boolean isTestableMethod(Method method) { } } + diff --git a/src/test/java/fr/xephi/authme/listener/ListenerServiceTest.java b/authme-core/src/test/java/fr/xephi/authme/listener/ListenerServiceTest.java similarity index 97% rename from src/test/java/fr/xephi/authme/listener/ListenerServiceTest.java rename to authme-core/src/test/java/fr/xephi/authme/listener/ListenerServiceTest.java index ba1fe05875..4b3e99f999 100644 --- a/src/test/java/fr/xephi/authme/listener/ListenerServiceTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/listener/ListenerServiceTest.java @@ -1,7 +1,8 @@ package fr.xephi.authme.listener; +import org.junit.jupiter.api.extension.ExtendWith; +import fr.xephi.authme.DelayedInjectionExtension; import ch.jalu.injector.testing.BeforeInjecting; -import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.InjectDelayed; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; @@ -13,8 +14,7 @@ import org.bukkit.event.HandlerList; import org.bukkit.event.entity.EntityEvent; import org.bukkit.event.player.PlayerEvent; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.Mock; import static org.hamcrest.Matchers.equalTo; @@ -27,7 +27,7 @@ /** * Test for {@link ListenerService}. */ -@RunWith(DelayedInjectionRunner.class) +@ExtendWith(DelayedInjectionExtension.class) public class ListenerServiceTest { @InjectDelayed @@ -233,3 +233,5 @@ public HandlerList getHandlers() { } } } + + diff --git a/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java b/authme-core/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java similarity index 84% rename from src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java rename to authme-core/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java index 4f96cef119..b9068c7f36 100644 --- a/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.listener; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; @@ -20,14 +24,11 @@ import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.net.InetAddress; import java.net.UnknownHostException; @@ -37,9 +38,11 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; @@ -48,7 +51,8 @@ /** * Test for {@link OnJoinVerifier}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class OnJoinVerifierTest { @InjectMocks @@ -71,10 +75,7 @@ public class OnJoinVerifierTest { @Mock private Server server; - @Rule - public ExpectedException expectedException = ExpectedException.none(); - - @BeforeClass + @BeforeAll public static void setUpLogger() { TestHelper.setupLogger(); } @@ -91,6 +92,7 @@ public void shouldNotDoAnythingForNormalEvent() { // then assertThat(result, equalTo(false)); verify(event).getResult(); + verify(event, atLeast(0)).getPlayer(); verifyNoMoreInteractions(event); verifyNoInteractions(bukkitService, dataSource, permissionsManager); } @@ -101,9 +103,10 @@ public void shouldRefuseNonVipPlayerForFullServer() { Player player = mock(Player.class); PlayerLoginEvent event = new PlayerLoginEvent(player, "hostname", null); event.setResult(PlayerLoginEvent.Result.KICK_FULL); - given(permissionsManager.hasPermission(player, PlayerStatePermission.IS_VIP)).willReturn(false); + given(player.getName()).willReturn("VipCheck"); + given(permissionsManager.hasPermissionOffline("VipCheck", PlayerStatePermission.IS_VIP)).willReturn(false); String serverFullMessage = "server is full"; - given(messages.retrieveSingle(player, MessageKey.KICK_FULL_SERVER)).willReturn(serverFullMessage); + given(messages.retrieveSingle("VipCheck", MessageKey.KICK_FULL_SERVER)).willReturn(serverFullMessage); // when boolean result = onJoinVerifier.refusePlayerForFullServer(event); @@ -121,13 +124,14 @@ public void shouldKickNonVipForJoiningVipPlayer() { Player player = mock(Player.class); PlayerLoginEvent event = new PlayerLoginEvent(player, "hostname", null); event.setResult(PlayerLoginEvent.Result.KICK_FULL); - given(permissionsManager.hasPermission(player, PlayerStatePermission.IS_VIP)).willReturn(true); + given(player.getName()).willReturn("VipPlayer"); + given(permissionsManager.hasPermissionOffline("VipPlayer", PlayerStatePermission.IS_VIP)).willReturn(true); List onlinePlayers = Arrays.asList(mock(Player.class), mock(Player.class)); given(permissionsManager.hasPermission(onlinePlayers.get(0), PlayerStatePermission.IS_VIP)).willReturn(true); given(permissionsManager.hasPermission(onlinePlayers.get(1), PlayerStatePermission.IS_VIP)).willReturn(false); given(bukkitService.getOnlinePlayers()).willReturn(onlinePlayers); given(server.getMaxPlayers()).willReturn(onlinePlayers.size()); - given(messages.retrieveSingle(player, MessageKey.KICK_FOR_VIP)).willReturn("kick for vip"); + given(messages.retrieveSingle("VipPlayer", MessageKey.KICK_FOR_VIP)).willReturn("kick for vip"); // when boolean result = onJoinVerifier.refusePlayerForFullServer(event); @@ -146,12 +150,13 @@ public void shouldKickVipPlayerIfNoPlayerCanBeKicked() { Player player = mock(Player.class); PlayerLoginEvent event = new PlayerLoginEvent(player, "hostname", null); event.setResult(PlayerLoginEvent.Result.KICK_FULL); - given(permissionsManager.hasPermission(player, PlayerStatePermission.IS_VIP)).willReturn(true); + given(player.getName()).willReturn("VipPlayer"); + given(permissionsManager.hasPermissionOffline("VipPlayer", PlayerStatePermission.IS_VIP)).willReturn(true); List onlinePlayers = Collections.singletonList(mock(Player.class)); given(permissionsManager.hasPermission(onlinePlayers.get(0), PlayerStatePermission.IS_VIP)).willReturn(true); given(bukkitService.getOnlinePlayers()).willReturn(onlinePlayers); given(server.getMaxPlayers()).willReturn(onlinePlayers.size()); - given(messages.retrieveSingle(player, MessageKey.KICK_FULL_SERVER)).willReturn("kick full server"); + given(messages.retrieveSingle("VipPlayer", MessageKey.KICK_FULL_SERVER)).willReturn("kick full server"); // when boolean result = onJoinVerifier.refusePlayerForFullServer(event); @@ -163,16 +168,26 @@ public void shouldKickVipPlayerIfNoPlayerCanBeKicked() { verifyNoInteractions(onlinePlayers.get(0)); } + @Test + public void shouldReturnKickMessageForNonVipPlayerOnFullServer() { + // given + given(permissionsManager.hasPermissionOffline("RegularPlayer", PlayerStatePermission.IS_VIP)).willReturn(false); + given(messages.retrieveSingle("RegularPlayer", MessageKey.KICK_FULL_SERVER)).willReturn("server is full"); + + // when + String kickMessage = onJoinVerifier.getServerFullKickMessageIfDenied("RegularPlayer"); + + // then + assertThat(kickMessage, equalTo("server is full")); + verifyNoInteractions(bukkitService, dataSource); + } + @Test public void shouldKickNonRegistered() throws FailedVerificationException { // given given(settings.getProperty(RestrictionSettings.KICK_NON_REGISTERED)).willReturn(true); - // expect - expectValidationExceptionWith(MessageKey.MUST_REGISTER_MESSAGE); - - // when - onJoinVerifier.checkKickNonRegistered(false); + assertValidationException(() -> onJoinVerifier.checkKickNonRegistered(false), MessageKey.MUST_REGISTER_MESSAGE); } @Test @@ -209,11 +224,8 @@ public void shouldRejectTooLongName() throws FailedVerificationException { given(settings.getProperty(RestrictionSettings.ALLOWED_NICKNAME_CHARACTERS)).willReturn("[a-zA-Z0-9]+"); onJoinVerifier.reload(); // @PostConstruct method - // expect - expectValidationExceptionWith(MessageKey.INVALID_NAME_LENGTH); - - // when - onJoinVerifier.checkIsValidName("longerthaneight"); + assertValidationException(() -> onJoinVerifier.checkIsValidName("longerthaneight"), + MessageKey.INVALID_NAME_LENGTH); } @Test @@ -224,11 +236,7 @@ public void shouldRejectTooShortName() throws FailedVerificationException { given(settings.getProperty(RestrictionSettings.ALLOWED_NICKNAME_CHARACTERS)).willReturn("[a-zA-Z0-9]+"); onJoinVerifier.reload(); // @PostConstruct method - // expect - expectValidationExceptionWith(MessageKey.INVALID_NAME_LENGTH); - - // when - onJoinVerifier.checkIsValidName("abc"); + assertValidationException(() -> onJoinVerifier.checkIsValidName("abc"), MessageKey.INVALID_NAME_LENGTH); } @Test @@ -239,11 +247,8 @@ public void shouldRejectNameWithInvalidCharacters() throws FailedVerificationExc given(settings.getProperty(RestrictionSettings.ALLOWED_NICKNAME_CHARACTERS)).willReturn("[a-zA-Z0-9]+"); onJoinVerifier.reload(); // @PostConstruct method - // expect - expectValidationExceptionWith(MessageKey.INVALID_NAME_CHARACTERS, "[a-zA-Z0-9]+"); - - // when - onJoinVerifier.checkIsValidName("Tester!"); + assertValidationException(() -> onJoinVerifier.checkIsValidName("Tester!"), + MessageKey.INVALID_NAME_CHARACTERS, "[a-zA-Z0-9]+"); } @Test @@ -267,12 +272,8 @@ public void shouldRejectNameWithWrongCasing() throws FailedVerificationException PlayerAuth auth = PlayerAuth.builder().name("tester").realName("testeR").build(); given(settings.getProperty(RegistrationSettings.PREVENT_OTHER_CASE)).willReturn(true); - // expect - expectValidationExceptionWith(MessageKey.INVALID_NAME_CASE, "testeR", "Tester"); - - // when / then - onJoinVerifier.checkNameCasing(name, auth); - verifyNoInteractions(dataSource); + assertValidationException(() -> onJoinVerifier.checkNameCasing(name, auth), + MessageKey.INVALID_NAME_CASE, "testeR", "Tester"); } @Test @@ -354,11 +355,8 @@ public void shouldRejectNameAlreadyOnline() throws FailedVerificationException { given(bukkitService.getPlayerExact("Charlie")).willReturn(onlinePlayer); given(settings.getProperty(RestrictionSettings.FORCE_SINGLE_SESSION)).willReturn(true); - // expect - expectValidationExceptionWith(MessageKey.USERNAME_ALREADY_ONLINE_ERROR); - - // when / then - onJoinVerifier.checkSingleSession(name); + assertValidationException(() -> onJoinVerifier.checkSingleSession(name), + MessageKey.USERNAME_ALREADY_ONLINE_ERROR); } @Test @@ -496,11 +494,8 @@ public void shouldThrowForBannedCountry() throws FailedVerificationException { given(settings.getProperty(ProtectionSettings.ENABLE_PROTECTION)).willReturn(true); given(validationService.isCountryAdmitted(ip)).willReturn(false); - // expect - expectValidationExceptionWith(MessageKey.COUNTRY_BANNED_ERROR); - - // when - onJoinVerifier.checkPlayerCountry(name, ip, false); + assertValidationException(() -> onJoinVerifier.checkPlayerCountry(name, ip, false), + MessageKey.COUNTRY_BANNED_ERROR); } @Test @@ -526,11 +521,8 @@ public void shouldKickPlayerWithUnmatchedNameRestriction() throws FailedVerifica given(validationService.fulfillsNameRestrictions(name, address)).willReturn(false); given(settings.getProperty(RestrictionSettings.BAN_UNKNOWN_IP)).willReturn(false); - // expect - expectValidationExceptionWith(MessageKey.NOT_OWNER_ERROR); - - // when - onJoinVerifier.checkNameRestrictions(name, address); + assertValidationException(() -> onJoinVerifier.checkNameRestrictions(name, address), + MessageKey.NOT_OWNER_ERROR); } @Test @@ -550,7 +542,7 @@ public void shouldBanIpWhenNameRestrictionFailsAndBanIsEnabled() { } // then - IP ban is scheduled - verify(bukkitService).scheduleSyncTaskFromOptionallyAsyncTask(any(Runnable.class)); + verify(bukkitService).runOnGlobalRegion(any(Runnable.class)); } private static InetAddress createInetAddress(String ip) { @@ -561,8 +553,9 @@ private static InetAddress createInetAddress(String ip) { } } - private void expectValidationExceptionWith(MessageKey messageKey, String... args) { - expectedException.expect(exceptionWithData(messageKey, args)); + private void assertValidationException(Executable executable, MessageKey messageKey, String... args) { + FailedVerificationException exception = assertThrows(FailedVerificationException.class, executable); + assertThat(exception, exceptionWithData(messageKey, args)); } private static Matcher exceptionWithData(final MessageKey messageKey, @@ -582,3 +575,5 @@ public void describeTo(Description description) { } } + + diff --git a/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java b/authme-core/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java similarity index 86% rename from src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java rename to authme-core/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java index a903ea003a..c0d40fa345 100644 --- a/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/listener/PlayerListenerTest.java @@ -1,5 +1,10 @@ package fr.xephi.authme.listener; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.data.QuickCommandsProtectionManager; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.limbo.LimboService; @@ -9,6 +14,9 @@ import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PlayerStatePermission; import fr.xephi.authme.process.Management; +import fr.xephi.authme.platform.ChatAdapter; +import fr.xephi.authme.platform.PlatformAdapter; +import fr.xephi.authme.platform.TeleportAdapter; import fr.xephi.authme.service.AntiBotService; import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.JoinMessageService; @@ -25,6 +33,7 @@ import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.block.SignChangeEvent; +import org.bukkit.event.entity.EntityAirChangeEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryOpenEvent; @@ -44,17 +53,18 @@ import org.bukkit.event.player.PlayerKickEvent; import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.event.player.PlayerMoveEvent; -import org.bukkit.event.player.PlayerPickupItemEvent; +import org.bukkit.event.entity.EntityPickupItemEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.event.player.PlayerShearEntityEvent; +import org.bukkit.event.player.PlayerSwapHandItemsEvent; import org.bukkit.inventory.InventoryView; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.spigotmc.event.player.PlayerSpawnLocationEvent; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; +import java.lang.reflect.Field; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; @@ -92,7 +102,8 @@ /** * Test for {@link PlayerListener}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class PlayerListenerTest { @InjectMocks @@ -128,6 +139,19 @@ public class PlayerListenerTest { private PermissionsManager permissionsManager; @Mock private LimboService limboService; + @Mock + private ChatAdapter chatAdapter; + @Mock + private TeleportAdapter teleportAdapter; + @Mock + private PlatformAdapter platformAdapter; + + @AfterEach + public void resetSpawnLocationTracker() throws ReflectiveOperationException { + Field eventCalledField = SpawnLocationTracker.class.getDeclaredField("eventCalled"); + eventCalledField.setAccessible(true); + eventCalledField.setBoolean(null, false); + } /** * #831: If a player is kicked because of "logged in from another location", the kick @@ -137,6 +161,7 @@ public class PlayerListenerTest { public void shouldCancelKick() { // given given(settings.getProperty(RestrictionSettings.FORCE_SINGLE_SESSION)).willReturn(true); + given(chatAdapter.getKickReason(any())).willAnswer(inv -> inv.getArgument(0, PlayerKickEvent.class).getReason()); Player player = mock(Player.class); PlayerKickEvent event = new PlayerKickEvent(player, "You logged in from another location", ""); @@ -170,6 +195,7 @@ public void shouldNotCancelKick() { public void shouldNotCancelOrdinaryKick() { // given given(settings.getProperty(RestrictionSettings.FORCE_SINGLE_SESSION)).willReturn(true); + given(chatAdapter.getKickReason(any())).willAnswer(inv -> inv.getArgument(0, PlayerKickEvent.class).getReason()); String name = "Bobby"; Player player = mockPlayerWithName(name); PlayerKickEvent event = new PlayerKickEvent(player, "No longer desired here!", ""); @@ -189,16 +215,61 @@ public void shouldHandleSimpleCancelableEvents() { withServiceMock(listenerService) .check(listener::onPlayerShear, PlayerShearEntityEvent.class) .check(listener::onPlayerFish, PlayerFishEvent.class) + .check(listener::onPlayerSwapHandItems, PlayerSwapHandItemsEvent.class) .check(listener::onPlayerBedEnter, PlayerBedEnterEvent.class) .check(listener::onPlayerDropItem, PlayerDropItemEvent.class) + .check(listener::onPlayerAirChange, EntityAirChangeEvent.class) .check(listener::onPlayerHitPlayerEvent, EntityDamageByEntityEvent.class) .check(listener::onPlayerConsumeItem, PlayerItemConsumeEvent.class) .check(listener::onPlayerInteract, PlayerInteractEvent.class) - .check(listener::onPlayerPickupItem, PlayerPickupItemEvent.class) .check(listener::onPlayerInteractEntity, PlayerInteractEntityEvent.class) .check(listener::onPlayerHeldItem, PlayerItemHeldEvent.class); } + @Test + public void shouldCancelPickupItemForUnauthenticatedPlayer() { + // given + Player player = mock(Player.class); + EntityPickupItemEvent event = mock(EntityPickupItemEvent.class); + given(event.getEntity()).willReturn(player); + given(listenerService.shouldCancelEvent(player)).willReturn(true); + + // when + listener.onPlayerPickupItem(event); + + // then + verify(event).setCancelled(true); + } + + @Test + public void shouldAllowPickupItemForAuthenticatedPlayer() { + // given + Player player = mock(Player.class); + EntityPickupItemEvent event = mock(EntityPickupItemEvent.class); + given(event.getEntity()).willReturn(player); + given(listenerService.shouldCancelEvent(player)).willReturn(false); + + // when + listener.onPlayerPickupItem(event); + + // then + verify(event, never()).setCancelled(anyBoolean()); + } + + @Test + public void shouldIgnorePickupItemForNonPlayer() { + // given + EntityPickupItemEvent event = mock(EntityPickupItemEvent.class); + given(event.getEntity()).willReturn(mock(org.bukkit.entity.LivingEntity.class)); + + // when + listener.onPlayerPickupItem(event); + + // then + verifyNoInteractions(listenerService); + verify(event, never()).setCancelled(anyBoolean()); + } + @Test public void shouldAllowEssentialsMotd() { // given @@ -244,6 +315,7 @@ public void shouldNotCancelEventForAuthenticatedPlayer() { // then verify(event).getMessage(); + verify(event, atLeast(0)).getPlayer(); verifyNoMoreInteractions(event); verify(listenerService).shouldCancelEvent(player); verifyNoInteractions(messages); @@ -329,7 +401,9 @@ public void shouldSendChatToEveryone() { // then verify(listenerService).shouldCancelEvent(event.getPlayer()); - verifyNoInteractions(event, messages); + verify(event, atLeast(0)).getPlayer(); + verifyNoMoreInteractions(event); + verifyNoInteractions(messages); } @Test @@ -483,7 +557,7 @@ public void shouldTeleportPlayerInDifferentWorldToSpawn() { // then verify(listenerService).shouldCancelEvent(player); - verify(player).teleport(spawn); + verify(teleportAdapter).teleportPlayer(player, spawn); verify(spawnLoader).getSpawnLocation(player); verifyNoModifyingCalls(event); } @@ -539,25 +613,43 @@ public void shouldRejectMovementOutsideOfRadius() { // then verify(listenerService).shouldCancelEvent(player); - verify(player).teleport(spawn); + verify(teleportAdapter).teleportPlayer(player, spawn); verify(spawnLoader).getSpawnLocation(player); verifyNoModifyingCalls(event); } @Test - public void shouldIgnorePlayerRespawnWithNoTeleport() { + public void shouldIgnorePlayerRespawnForAuthenticatedPlayer() { // given Player player = mock(Player.class); Location respawnLocation = mock(Location.class); PlayerRespawnEvent event = spy(new PlayerRespawnEvent(player, respawnLocation, false)); + given(listenerService.shouldCancelEvent(event)).willReturn(false); + + // when + listener.onPlayerRespawn(event); + + // then + verifyNoInteractions(spawnLoader, bukkitService); + verify(event, never()).setRespawnLocation(any()); + } + + @Test + public void shouldSkipTeleportOnRespawnWithNoTeleportButStillScheduleRestrictions() { + // given + Player player = mock(Player.class); + Location respawnLocation = mock(Location.class); + PlayerRespawnEvent event = spy(new PlayerRespawnEvent(player, respawnLocation, false)); + given(listenerService.shouldCancelEvent(event)).willReturn(true); given(settings.getProperty(RestrictionSettings.NO_TELEPORT)).willReturn(true); // when listener.onPlayerRespawn(event); // then - verifyNoInteractions(listenerService); + verifyNoInteractions(spawnLoader); verify(event, never()).setRespawnLocation(any()); + verify(bukkitService).runTaskLater(eq(player), any(Runnable.class), eq(1L)); } @Test @@ -566,7 +658,6 @@ public void shouldIgnorePlayerRespawn() { Player player = mock(Player.class); Location respawnLocation = mock(Location.class); PlayerRespawnEvent event = spy(new PlayerRespawnEvent(player, respawnLocation, false)); - given(settings.getProperty(RestrictionSettings.NO_TELEPORT)).willReturn(false); given(listenerService.shouldCancelEvent(event)).willReturn(false); // when @@ -588,14 +679,15 @@ public void shouldHandlePlayerRespawn() { PlayerRespawnEvent event = spy(new PlayerRespawnEvent(player, originalLocation, false)); given(settings.getProperty(RestrictionSettings.NO_TELEPORT)).willReturn(false); given(listenerService.shouldCancelEvent(event)).willReturn(true); - given(spawnLoader.getSpawnLocation(player)).willReturn(newLocation); + given(spawnLoader.getPlayerRespawnLocationOrSpawn(player)).willReturn(newLocation); // when listener.onPlayerRespawn(event); // then - verify(spawnLoader).getSpawnLocation(player); + verify(spawnLoader).getPlayerRespawnLocationOrSpawn(player); verify(event).setRespawnLocation(newLocation); + verify(bukkitService).runTaskLater(eq(player), any(Runnable.class), eq(1L)); } @Test @@ -608,14 +700,15 @@ public void shouldIgnorePlayerRespawnUnloadedWorld() { PlayerRespawnEvent event = spy(new PlayerRespawnEvent(player, originalLocation, false)); given(settings.getProperty(RestrictionSettings.NO_TELEPORT)).willReturn(false); given(listenerService.shouldCancelEvent(event)).willReturn(true); - given(spawnLoader.getSpawnLocation(player)).willReturn(newLocation); + given(spawnLoader.getPlayerRespawnLocationOrSpawn(player)).willReturn(newLocation); // when listener.onPlayerRespawn(event); // then - verify(spawnLoader).getSpawnLocation(player); + verify(spawnLoader).getPlayerRespawnLocationOrSpawn(player); verify(event, never()).setRespawnLocation(any()); + verify(bukkitService).runTaskLater(eq(player), any(Runnable.class), eq(1L)); } @Test @@ -626,14 +719,15 @@ public void shouldHandlePlayerRespawnNoChanges() { PlayerRespawnEvent event = spy(new PlayerRespawnEvent(player, originalLocation, false)); given(settings.getProperty(RestrictionSettings.NO_TELEPORT)).willReturn(false); given(listenerService.shouldCancelEvent(event)).willReturn(true); - given(spawnLoader.getSpawnLocation(player)).willReturn(null); + given(spawnLoader.getPlayerRespawnLocationOrSpawn(player)).willReturn(null); // when listener.onPlayerRespawn(event); // then - verify(spawnLoader).getSpawnLocation(player); + verify(spawnLoader).getPlayerRespawnLocationOrSpawn(player); verify(event, never()).setRespawnLocation(any()); + verify(bukkitService).runTaskLater(eq(player), any(Runnable.class), eq(1L)); } @Test @@ -650,12 +744,45 @@ public void shouldHandlePlayerJoining() { verify(management).performJoin(player); } + @Test + public void shouldSetCustomSpawnLocationForLegacySpawnEvent() { + // given + Player player = mock(Player.class); + Location originalSpawn = mock(Location.class); + Location customSpawn = mock(Location.class); + PlayerSpawnLocationEvent event = spy(new PlayerSpawnLocationEvent(player, originalSpawn)); + given(platformAdapter.shouldHandlePlayerSpawnLocationEvent()).willReturn(true); + given(teleportationService.prepareOnJoinSpawnLocation(player, originalSpawn)).willReturn(customSpawn); + + // when + listener.onPlayerSpawn(event); + + // then + verify(teleportationService).prepareOnJoinSpawnLocation(player, originalSpawn); + verify(event).setSpawnLocation(customSpawn); + } + + @Test + public void shouldIgnoreLegacySpawnEventWhenPlatformUsesModernEvent() { + // given + PlayerSpawnLocationEvent event = spy(new PlayerSpawnLocationEvent(mock(Player.class), mock(Location.class))); + given(platformAdapter.shouldHandlePlayerSpawnLocationEvent()).willReturn(false); + + // when + listener.onPlayerSpawn(event); + + // then + verifyNoInteractions(teleportationService); + verify(event, never()).setSpawnLocation(any(Location.class)); + } + @Test public void shouldNotInterfereWithUnrestrictedUser() throws FailedVerificationException { // given String name = "Player01"; Player player = mockPlayerWithName(name); PlayerLoginEvent event = spy(new PlayerLoginEvent(player, "", null)); + given(platformAdapter.shouldHandlePlayerLoginEvent()).willReturn(true); given(validationService.isUnrestricted(name)).willReturn(true); // when @@ -674,6 +801,7 @@ public void shouldStopHandlingForFullServer() throws FailedVerificationException String name = "someone"; Player player = mockPlayerWithName(name); PlayerLoginEvent event = spy(new PlayerLoginEvent(player, "", null)); + given(platformAdapter.shouldHandlePlayerLoginEvent()).willReturn(true); given(validationService.isUnrestricted(name)).willReturn(false); given(onJoinVerifier.refusePlayerForFullServer(event)).willReturn(true); @@ -696,6 +824,7 @@ public void shouldStopHandlingEventForBadResult() throws FailedVerificationExcep PlayerLoginEvent event = new PlayerLoginEvent(player, "", null); event.setResult(PlayerLoginEvent.Result.KICK_BANNED); event = spy(event); + given(platformAdapter.shouldHandlePlayerLoginEvent()).willReturn(true); given(validationService.isUnrestricted(name)).willReturn(false); given(onJoinVerifier.refusePlayerForFullServer(event)).willReturn(false); @@ -709,6 +838,20 @@ public void shouldStopHandlingEventForBadResult() throws FailedVerificationExcep verifyNoModifyingCalls(event); } + @Test + public void shouldIgnoreLegacyLoginEventWhenPlatformUsesModernEvents() { + // given + PlayerLoginEvent event = spy(new PlayerLoginEvent(mock(Player.class), "", null)); + given(platformAdapter.shouldHandlePlayerLoginEvent()).willReturn(false); + + // when + listener.onPlayerLogin(event); + + // then + verifyNoInteractions(onJoinVerifier, validationService); + verifyNoModifyingCalls(event); + } + @Test public void shouldPerformAllJoinVerificationsSuccessfullyPreLoginLowest() throws FailedVerificationException { // given @@ -804,6 +947,7 @@ public void shouldPerformAllJoinVerificationsSuccessfullyLogin() { String ip = "12.34.56.78"; PlayerLoginEvent loginEvent = spy(new PlayerLoginEvent(player, "", createInetAddress(ip))); + given(platformAdapter.shouldHandlePlayerLoginEvent()).willReturn(true); given(validationService.isUnrestricted(name)).willReturn(false); given(onJoinVerifier.refusePlayerForFullServer(loginEvent)).willReturn(false); @@ -1121,12 +1265,14 @@ private static AsyncPlayerChatEvent newAsyncChatEvent() { } private static void verifyNoModifyingCalls(PlayerMoveEvent event) { + verify(event, atLeast(0)).getPlayer(); verify(event, atLeast(0)).getFrom(); verify(event, atLeast(0)).getTo(); verifyNoMoreInteractions(event); } private static void verifyNoModifyingCalls(PlayerLoginEvent event) { + verify(event, atLeast(0)).getPlayer(); verify(event, atLeast(0)).getResult(); verify(event, atLeast(0)).getAddress(); verifyNoMoreInteractions(event); @@ -1147,3 +1293,5 @@ public static InetAddress createInetAddress(String ip) { } } } + + diff --git a/src/test/java/fr/xephi/authme/listener/ServerListenerTest.java b/authme-core/src/test/java/fr/xephi/authme/listener/ServerListenerTest.java similarity index 92% rename from src/test/java/fr/xephi/authme/listener/ServerListenerTest.java rename to authme-core/src/test/java/fr/xephi/authme/listener/ServerListenerTest.java index 51ce46f08d..57cea626a7 100644 --- a/src/test/java/fr/xephi/authme/listener/ServerListenerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/listener/ServerListenerTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.listener; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.TestHelper; import fr.xephi.authme.listener.protocollib.ProtocolLibService; import fr.xephi.authme.permission.PermissionsManager; @@ -9,12 +13,10 @@ import org.bukkit.event.server.PluginEnableEvent; import org.bukkit.event.server.PluginEvent; import org.bukkit.plugin.Plugin; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -26,7 +28,8 @@ /** * Test for {@link ServerListener}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class ServerListenerTest { private static final String ESSENTIALS = "Essentials"; @@ -50,7 +53,7 @@ public class ServerListenerTest { @Mock private SpawnLoader spawnLoader; - @BeforeClass + @BeforeAll public static void initLogger() { TestHelper.setupLogger(); } @@ -110,3 +113,5 @@ private static T mockEventWithPluginName(Class event return event; } } + + diff --git a/src/test/java/fr/xephi/authme/mail/EmailServiceTest.java b/authme-core/src/test/java/fr/xephi/authme/mail/EmailServiceTest.java similarity index 91% rename from src/test/java/fr/xephi/authme/mail/EmailServiceTest.java rename to authme-core/src/test/java/fr/xephi/authme/mail/EmailServiceTest.java index debafaf2c8..5535899e13 100644 --- a/src/test/java/fr/xephi/authme/mail/EmailServiceTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/mail/EmailServiceTest.java @@ -1,7 +1,8 @@ package fr.xephi.authme.mail; +import org.junit.jupiter.api.extension.ExtendWith; +import fr.xephi.authme.DelayedInjectionExtension; import ch.jalu.injector.testing.BeforeInjecting; -import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.InjectDelayed; import fr.xephi.authme.TestHelper; import fr.xephi.authme.initialization.DataFolder; @@ -11,12 +12,11 @@ import fr.xephi.authme.settings.properties.SecuritySettings; import org.apache.commons.mail.EmailException; import org.apache.commons.mail.HtmlEmail; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import fr.xephi.authme.TempFolder; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import java.io.File; @@ -37,7 +37,7 @@ /** * Test for {@link EmailService}. */ -@RunWith(DelayedInjectionRunner.class) +@ExtendWith(DelayedInjectionExtension.class) public class EmailServiceTest { @InjectDelayed @@ -47,13 +47,13 @@ public class EmailServiceTest { private Settings settings; @Mock private SendMailSsl sendMailSsl; + @Captor + private ArgumentCaptor messageCaptor; @DataFolder private File dataFolder; + public TempFolder temporaryFolder = new TempFolder(); - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); - - @BeforeClass + @BeforeAll public static void initLogger() { TestHelper.setupLogger(); } @@ -89,7 +89,6 @@ public void shouldSendPasswordMail() throws EmailException { // then assertThat(result, equalTo(true)); verify(sendMailSsl).initializeMail("user@example.com"); - ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(String.class); verify(sendMailSsl).sendEmail(messageCaptor.capture(), eq(email)); assertThat(messageCaptor.getValue(), equalTo("Hi Player, your new password for serverName is new_password")); @@ -124,7 +123,6 @@ public void shouldHandleMailSendingFailure() throws EmailException { // then assertThat(result, equalTo(false)); verify(sendMailSsl).initializeMail("user@example.com"); - ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(String.class); verify(sendMailSsl).sendEmail(messageCaptor.capture(), eq(email)); assertThat(messageCaptor.getValue(), equalTo("Hi bobby, your new pass is myPassw0rd")); } @@ -145,7 +143,6 @@ public void shouldSendRecoveryCode() throws EmailException { // then assertThat(result, equalTo(true)); verify(sendMailSsl).initializeMail("tim@example.com"); - ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(String.class); verify(sendMailSsl).sendEmail(messageCaptor.capture(), eq(email)); assertThat(messageCaptor.getValue(), equalTo("Hi Timmy, your code on serverName is 12C56A (valid 7 hours)")); } @@ -180,9 +177,10 @@ public void shouldHandleFailureToSendRecoveryCode() throws EmailException { // then assertThat(result, equalTo(false)); verify(sendMailSsl).initializeMail("user@example.com"); - ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(String.class); verify(sendMailSsl).sendEmail(messageCaptor.capture(), eq(email)); assertThat(messageCaptor.getValue(), equalTo("Hi John, your code is 1DEF77")); } } + + diff --git a/src/test/java/fr/xephi/authme/mail/SendMailSslTest.java b/authme-core/src/test/java/fr/xephi/authme/mail/SendMailSslTest.java similarity index 94% rename from src/test/java/fr/xephi/authme/mail/SendMailSslTest.java rename to authme-core/src/test/java/fr/xephi/authme/mail/SendMailSslTest.java index 3d0116c06e..e7d4f0120d 100644 --- a/src/test/java/fr/xephi/authme/mail/SendMailSslTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/mail/SendMailSslTest.java @@ -1,7 +1,8 @@ package fr.xephi.authme.mail; +import org.junit.jupiter.api.extension.ExtendWith; +import fr.xephi.authme.DelayedInjectionExtension; import ch.jalu.injector.testing.BeforeInjecting; -import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.InjectDelayed; import fr.xephi.authme.TestHelper; import fr.xephi.authme.output.LogLevel; @@ -10,11 +11,9 @@ import fr.xephi.authme.settings.properties.PluginSettings; import org.apache.commons.mail.EmailException; import org.apache.commons.mail.HtmlEmail; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import fr.xephi.authme.TempFolder; import org.mockito.Mock; import java.io.IOException; @@ -30,7 +29,7 @@ /** * Test for {@link SendMailSsl}. */ -@RunWith(DelayedInjectionRunner.class) +@ExtendWith(DelayedInjectionExtension.class) public class SendMailSslTest { @InjectDelayed @@ -38,11 +37,9 @@ public class SendMailSslTest { @Mock private Settings settings; + public TempFolder temporaryFolder = new TempFolder(); - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); - - @BeforeClass + @BeforeAll public static void initLogger() { TestHelper.setupLogger(); } @@ -141,3 +138,5 @@ public void shouldCreateEmailObjectWithOAuth2() throws EmailException { assertThat(mailProperties.getProperty(OAuth2SaslClientFactory.OAUTH_TOKEN_PROP), equalTo("oAuth2 token")); } } + + diff --git a/src/test/java/fr/xephi/authme/message/HelpMessageConsistencyTest.java b/authme-core/src/test/java/fr/xephi/authme/message/HelpMessageConsistencyTest.java similarity index 97% rename from src/test/java/fr/xephi/authme/message/HelpMessageConsistencyTest.java rename to authme-core/src/test/java/fr/xephi/authme/message/HelpMessageConsistencyTest.java index f675b9e770..0ca214ee4b 100644 --- a/src/test/java/fr/xephi/authme/message/HelpMessageConsistencyTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/message/HelpMessageConsistencyTest.java @@ -7,8 +7,8 @@ import fr.xephi.authme.command.help.HelpSection; import fr.xephi.authme.permission.DefaultPermission; import org.hamcrest.Matcher; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.File; import java.util.Arrays; @@ -30,7 +30,7 @@ public class HelpMessageConsistencyTest { private List helpFiles; - @Before + @BeforeEach public void findHelpMessagesFiles() { File folder = TestHelper.getJarFile("/" + MESSAGES_FOLDER); File[] files = folder.listFiles(); @@ -88,3 +88,4 @@ private static Matcher notEmptyString() { return both(not(emptyString())).and(not(nullValue())); } } + diff --git a/src/test/java/fr/xephi/authme/message/MessageFilePlaceholderTest.java b/authme-core/src/test/java/fr/xephi/authme/message/MessageFilePlaceholderTest.java similarity index 74% rename from src/test/java/fr/xephi/authme/message/MessageFilePlaceholderTest.java rename to authme-core/src/test/java/fr/xephi/authme/message/MessageFilePlaceholderTest.java index 0966902ba3..a4b64addd5 100644 --- a/src/test/java/fr/xephi/authme/message/MessageFilePlaceholderTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/message/MessageFilePlaceholderTest.java @@ -5,9 +5,9 @@ import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; import fr.xephi.authme.TestHelper; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import java.io.File; import java.util.ArrayList; @@ -15,16 +15,15 @@ import java.util.Collections; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.Stream; import static fr.xephi.authme.message.MessagePathHelper.MESSAGES_FOLDER; -import static org.junit.Assert.fail; -import static tools.utils.FileIoUtils.listFilesOrThrow; +import static org.junit.jupiter.api.Assertions.fail; /** * Checks that the entries in messages_xx.yml files have the {@link MessageKey#getTags() placeholders} * that are defined for the given message. */ -@RunWith(Parameterized.class) public class MessageFilePlaceholderTest { /** Defines exclusions: a (key, tag) pair in this map will not be checked in the test. */ @@ -33,17 +32,9 @@ public class MessageFilePlaceholderTest { .putAll(MessageKey.MAX_REGISTER_EXCEEDED, "%max_acc", "%reg_count", "%reg_names") .build(); - private File messagesFile; - private String messagesFilename; - - // Note ljacqu 20170506: We pass the file name separately so we can reference it for the name in @Parameters - public MessageFilePlaceholderTest(File messagesFile, String name) { - this.messagesFile = messagesFile; - this.messagesFilename = name; - } - - @Test - public void shouldHaveAllPlaceholders() { + @ParameterizedTest(name = "{1}") + @MethodSource("buildParams") + public void shouldHaveAllPlaceholders(File messagesFile, String messagesFilename) { // given PropertyReader reader = new YamlFileReader(messagesFile); List errors = new ArrayList<>(0); @@ -72,19 +63,22 @@ private List findMissingTags(MessageKey key, PropertyReader reader) { return Collections.emptyList(); } - @Parameterized.Parameters(name = "{1}") - public static List buildParams() { + public static Stream buildParams() { File folder = TestHelper.getJarFile("/" + MESSAGES_FOLDER); - List messageFiles = Arrays.stream(listFilesOrThrow(folder)) + File[] files = folder.listFiles(); + if (files == null) { + throw new IllegalStateException("Could not read folder '" + folder + "'"); + } + + List messageFiles = Arrays.stream(files) .filter(file -> MessagePathHelper.isMessagesFile(file.getName())) - .map(file -> new Object[]{file, file.getName()}) + .map(file -> Arguments.of(file, file.getName())) .collect(Collectors.toList()); if (messageFiles.isEmpty()) { throw new IllegalStateException("Found zero messages files (is the folder correct?)"); } - return messageFiles; + return messageFiles.stream(); } - - } + diff --git a/src/test/java/fr/xephi/authme/message/MessageKeyTest.java b/authme-core/src/test/java/fr/xephi/authme/message/MessageKeyTest.java similarity index 93% rename from src/test/java/fr/xephi/authme/message/MessageKeyTest.java rename to authme-core/src/test/java/fr/xephi/authme/message/MessageKeyTest.java index 87c1da9a97..c93bb58ebc 100644 --- a/src/test/java/fr/xephi/authme/message/MessageKeyTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/message/MessageKeyTest.java @@ -1,7 +1,7 @@ package fr.xephi.authme.message; import fr.xephi.authme.util.StringUtils; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.HashSet; @@ -9,7 +9,7 @@ import static org.hamcrest.Matchers.matchesPattern; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; /** * Test for {@link MessageKey}. @@ -47,3 +47,4 @@ public void shouldHaveWellFormedPlaceholders() { } } } + diff --git a/src/test/java/fr/xephi/authme/message/MessagePathHelperTest.java b/authme-core/src/test/java/fr/xephi/authme/message/MessagePathHelperTest.java similarity index 98% rename from src/test/java/fr/xephi/authme/message/MessagePathHelperTest.java rename to authme-core/src/test/java/fr/xephi/authme/message/MessagePathHelperTest.java index 3b03815b11..77d3fbc4ca 100644 --- a/src/test/java/fr/xephi/authme/message/MessagePathHelperTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/message/MessagePathHelperTest.java @@ -1,7 +1,7 @@ package fr.xephi.authme.message; import fr.xephi.authme.settings.properties.PluginSettings; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.nullValue; @@ -65,3 +65,4 @@ public void shouldRecognizeIfIsHelpFile() { assertThat(MessagePathHelper.isHelpFile(""), equalTo(false)); } } + diff --git a/src/test/java/fr/xephi/authme/message/MessagesFileConsistencyTest.java b/authme-core/src/test/java/fr/xephi/authme/message/MessagesFileConsistencyTest.java similarity index 95% rename from src/test/java/fr/xephi/authme/message/MessagesFileConsistencyTest.java rename to authme-core/src/test/java/fr/xephi/authme/message/MessagesFileConsistencyTest.java index 5243528fc2..d93e892c6c 100644 --- a/src/test/java/fr/xephi/authme/message/MessagesFileConsistencyTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/message/MessagesFileConsistencyTest.java @@ -4,13 +4,13 @@ import ch.jalu.configme.resource.YamlFileReader; import fr.xephi.authme.TestHelper; import fr.xephi.authme.util.StringUtils; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.File; import java.util.ArrayList; import java.util.List; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; /** * Tests that the project's default language file contains a text for all message keys. @@ -59,3 +59,4 @@ private static void validateMessage(MessageKey messageKey, PropertyReader reader } } } + diff --git a/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java b/authme-core/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java similarity index 95% rename from src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java rename to authme-core/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java index 18502a0cf3..a1eec10967 100644 --- a/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/message/MessagesIntegrationTest.java @@ -12,14 +12,16 @@ import fr.xephi.authme.util.expiring.Duration; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import fr.xephi.authme.TempFolder; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; import java.io.File; import java.io.IOException; @@ -43,6 +45,7 @@ /** * Test for {@link Messages}. */ +@ExtendWith(MockitoExtension.class) public class MessagesIntegrationTest { private static final String TEST_MESSAGES_LOCAL_PATH = "message/messages_test.yml"; @@ -50,11 +53,12 @@ public class MessagesIntegrationTest { private Messages messages; private MessagesFileHandler messagesFileHandler; - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); + @Captor + private ArgumentCaptor captor; + public TempFolder temporaryFolder = new TempFolder(); private File dataFolder; - @BeforeClass + @BeforeAll public static void setup() { TestHelper.setupLogger(); } @@ -67,7 +71,7 @@ public static void setup() { * Similarly, the {@code messages_default.yml} from the test resources represents a default * file that should contain all messages, but again, for testing, it just contains a few. */ - @Before + @BeforeEach public void setUpMessages() throws IOException { dataFolder = temporaryFolder.newFolder(); File testFile = new File(dataFolder, MessagePathHelper.createMessageFilePath("test")); @@ -79,7 +83,7 @@ public void setUpMessages() throws IOException { messages = new Messages(messagesFileHandler); } - @AfterClass + @AfterAll public static void removeLoggerReferences() { ConsoleLogger.initialize(null, null); } @@ -170,7 +174,6 @@ public void shouldSendMultiLineMessageToPlayer() { // then String[] lines = new String[]{"We've got", "new lines", "and ' apostrophes"}; - ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); verify(player, times(3)).sendMessage(captor.capture()); assertThat(captor.getAllValues(), contains(lines)); } @@ -316,3 +319,5 @@ private MessagesFileHandler createMessagesFileHandler() { return messagesFileHandler; } } + + diff --git a/authme-core/src/test/java/fr/xephi/authme/message/PlayerLocaleResolverTest.java b/authme-core/src/test/java/fr/xephi/authme/message/PlayerLocaleResolverTest.java new file mode 100644 index 0000000000..09c49d16ce --- /dev/null +++ b/authme-core/src/test/java/fr/xephi/authme/message/PlayerLocaleResolverTest.java @@ -0,0 +1,49 @@ +package fr.xephi.authme.message; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.nullValue; + +/** + * Test for {@link PlayerLocaleResolver}. + */ +class PlayerLocaleResolverTest { + + @ParameterizedTest + @CsvSource({ + "fr_fr, fr", + "de_de, de", + "en_us, en", + "en_gb, en", + "ru_ru, ru", + "it_it, it", + "nl_nl, nl", + "pl_pl, pl", + "ko_kr, ko", + "ja_jp, ja", + "tr_tr, tr", + // special cases: locale without underscore + "fr, fr", + "de, de", + // special overrides + "pt_br, br", + "zh_cn, zhcn", + "zh_tw, zhtw", + "zh_hk, zhhk", + }) + void shouldMapLocaleToLanguageCode(String locale, String expected) { + assertThat(PlayerLocaleResolver.toLanguageCode(locale), equalTo(expected)); + } + + @ParameterizedTest + @NullAndEmptySource + @ValueSource(strings = {" ", "\t"}) + void shouldReturnNullForBlankOrNullInput(String locale) { + assertThat(PlayerLocaleResolver.toLanguageCode(locale), nullValue()); + } +} diff --git a/src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java b/authme-core/src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java similarity index 89% rename from src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java rename to authme-core/src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java index 59c7b1b5a5..8711abc258 100644 --- a/src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java @@ -6,8 +6,8 @@ import fr.xephi.authme.command.help.HelpSection; import fr.xephi.authme.util.ExceptionUtils; import fr.xephi.authme.util.StringUtils; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import java.io.File; import java.util.ArrayList; @@ -15,8 +15,7 @@ import java.util.function.Predicate; import static fr.xephi.authme.message.MessagePathHelper.MESSAGES_FOLDER; -import static org.junit.Assert.fail; -import static tools.utils.FileIoUtils.listFilesOrThrow; +import static org.junit.jupiter.api.Assertions.fail; /** * Tests that all YML text files can be loaded. @@ -26,10 +25,13 @@ public class YamlTextFileCheckerTest { /** Contains all files of the MESSAGES_FOLDER. */ private static File[] messageFiles; - @BeforeClass + @BeforeAll public static void loadMessagesFiles() { File folder = TestHelper.getJarFile("/" + MESSAGES_FOLDER); - messageFiles = listFilesOrThrow(folder); + messageFiles = folder.listFiles(); + if (messageFiles == null) { + throw new IllegalStateException("Could not read folder '" + folder + "'"); + } } @Test @@ -89,3 +91,4 @@ private void checkFile(File file, String mandatoryKey, List errors) { } } } + diff --git a/src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java b/authme-core/src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java similarity index 96% rename from src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java rename to authme-core/src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java index 10182a6e85..cde030411a 100644 --- a/src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/message/updater/MessageUpdaterTest.java @@ -6,10 +6,9 @@ import com.google.common.io.Files; import fr.xephi.authme.TestHelper; import fr.xephi.authme.message.MessageKey; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import fr.xephi.authme.TempFolder; import java.io.File; import java.io.IOException; @@ -29,11 +28,9 @@ public class MessageUpdaterTest { private MessageUpdater messageUpdater = new MessageUpdater(); + public TempFolder temporaryFolder = new TempFolder(); - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); - - @BeforeClass + @BeforeAll public static void setUpLogger() { TestHelper.setupLogger(); } @@ -147,3 +144,5 @@ public void shouldHaveCommentForAllRootPathsInConfigurationData() { assertThat(comments.keySet(), equalTo(rootPaths)); } } + + diff --git a/src/test/java/fr/xephi/authme/message/updater/MigraterYamlFileResourceTest.java b/authme-core/src/test/java/fr/xephi/authme/message/updater/MigraterYamlFileResourceTest.java similarity index 94% rename from src/test/java/fr/xephi/authme/message/updater/MigraterYamlFileResourceTest.java rename to authme-core/src/test/java/fr/xephi/authme/message/updater/MigraterYamlFileResourceTest.java index f15e808b7c..c2970449ef 100644 --- a/src/test/java/fr/xephi/authme/message/updater/MigraterYamlFileResourceTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/message/updater/MigraterYamlFileResourceTest.java @@ -7,9 +7,8 @@ import ch.jalu.configme.resource.PropertyReader; import com.google.common.io.Files; import fr.xephi.authme.TestHelper; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.Test; +import fr.xephi.authme.TempFolder; import java.io.File; import java.io.IOException; @@ -25,9 +24,7 @@ public class MigraterYamlFileResourceTest { private static final String CHINESE_MESSAGES_FILE = TestHelper.PROJECT_ROOT + "message/chinese_texts.yml"; - - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); + public TempFolder temporaryFolder = new TempFolder(); @Test public void shouldReadChineseFile() { @@ -73,3 +70,5 @@ private static ConfigurationData buildConfigurationData() { return ConfigurationDataBuilder.createConfiguration(properties); } } + + diff --git a/src/test/java/fr/xephi/authme/message/updater/OldMessageKeysMigraterTest.java b/authme-core/src/test/java/fr/xephi/authme/message/updater/OldMessageKeysMigraterTest.java similarity index 93% rename from src/test/java/fr/xephi/authme/message/updater/OldMessageKeysMigraterTest.java rename to authme-core/src/test/java/fr/xephi/authme/message/updater/OldMessageKeysMigraterTest.java index be10559ef5..e803d7bd18 100644 --- a/src/test/java/fr/xephi/authme/message/updater/OldMessageKeysMigraterTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/message/updater/OldMessageKeysMigraterTest.java @@ -1,7 +1,7 @@ package fr.xephi.authme.message.updater; import fr.xephi.authme.message.MessageKey; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.HashSet; @@ -9,7 +9,7 @@ import java.util.Set; import java.util.stream.Collectors; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; /** * Test for {@link OldMessageKeysMigrater}. @@ -39,3 +39,4 @@ private Set collectParentPathsFromMessageKeys() { .collect(Collectors.toSet()); } } + diff --git a/src/test/java/fr/xephi/authme/output/ConsoleFilterTest.java b/authme-core/src/test/java/fr/xephi/authme/output/ConsoleFilterTest.java similarity index 98% rename from src/test/java/fr/xephi/authme/output/ConsoleFilterTest.java rename to authme-core/src/test/java/fr/xephi/authme/output/ConsoleFilterTest.java index 55c76fa7a7..f2c74a5e1c 100644 --- a/src/test/java/fr/xephi/authme/output/ConsoleFilterTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/output/ConsoleFilterTest.java @@ -1,6 +1,6 @@ package fr.xephi.authme.output; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mockito.Mockito; import java.util.logging.LogRecord; @@ -75,3 +75,4 @@ private static LogRecord createRecord(String message) { return record; } } + diff --git a/src/test/java/fr/xephi/authme/output/ConsoleLoggerFactoryTest.java b/authme-core/src/test/java/fr/xephi/authme/output/ConsoleLoggerFactoryTest.java similarity index 95% rename from src/test/java/fr/xephi/authme/output/ConsoleLoggerFactoryTest.java rename to authme-core/src/test/java/fr/xephi/authme/output/ConsoleLoggerFactoryTest.java index 38e88fdc47..bd27fa6bcb 100644 --- a/src/test/java/fr/xephi/authme/output/ConsoleLoggerFactoryTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/output/ConsoleLoggerFactoryTest.java @@ -7,9 +7,9 @@ import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.SecuritySettings; -import org.junit.After; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import java.util.Map; @@ -26,13 +26,13 @@ */ public class ConsoleLoggerFactoryTest { - @BeforeClass + @BeforeAll public static void initLogger() { removeSettingsAndClearMap(); TestHelper.setupLogger(); } - @After + @AfterEach public void resetConsoleLoggerFactoryToDefaults() { removeSettingsAndClearMap(); } @@ -91,3 +91,4 @@ private static Map getConsoleLoggerMap() { return ReflectionTestUtils.getFieldValue(ConsoleLoggerFactory.class, null, "consoleLoggers"); } } + diff --git a/src/test/java/fr/xephi/authme/output/Log4JFilterTest.java b/authme-core/src/test/java/fr/xephi/authme/output/Log4JFilterTest.java similarity index 99% rename from src/test/java/fr/xephi/authme/output/Log4JFilterTest.java rename to authme-core/src/test/java/fr/xephi/authme/output/Log4JFilterTest.java index 5c5a7ab92f..621dd9368d 100644 --- a/src/test/java/fr/xephi/authme/output/Log4JFilterTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/output/Log4JFilterTest.java @@ -10,7 +10,7 @@ import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.Logger; import org.apache.logging.log4j.message.Message; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mockito.Mockito; /** @@ -231,3 +231,4 @@ private static Message mockMessage(String formattedMessage) { } } + diff --git a/src/test/java/fr/xephi/authme/output/LogFilterHelperTest.java b/authme-core/src/test/java/fr/xephi/authme/output/LogFilterHelperTest.java similarity index 98% rename from src/test/java/fr/xephi/authme/output/LogFilterHelperTest.java rename to authme-core/src/test/java/fr/xephi/authme/output/LogFilterHelperTest.java index 5e8de0e524..efc14f6078 100644 --- a/src/test/java/fr/xephi/authme/output/LogFilterHelperTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/output/LogFilterHelperTest.java @@ -3,7 +3,7 @@ import com.google.common.collect.Lists; import fr.xephi.authme.command.CommandDescription; import fr.xephi.authme.command.CommandInitializer; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; @@ -87,3 +87,4 @@ private static List getCommandPrefixes(CommandDescription command) { .collect(Collectors.toList()); } } + diff --git a/src/test/java/fr/xephi/authme/output/LogLevelTest.java b/authme-core/src/test/java/fr/xephi/authme/output/LogLevelTest.java similarity index 96% rename from src/test/java/fr/xephi/authme/output/LogLevelTest.java rename to authme-core/src/test/java/fr/xephi/authme/output/LogLevelTest.java index 76e8105442..92595d6642 100644 --- a/src/test/java/fr/xephi/authme/output/LogLevelTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/output/LogLevelTest.java @@ -1,6 +1,6 @@ package fr.xephi.authme.output; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static java.lang.String.format; import static org.hamcrest.Matchers.equalTo; @@ -29,3 +29,4 @@ private void checkLevelInclusion(LogLevel level, boolean... expectedValues) { } } + diff --git a/src/test/java/fr/xephi/authme/permission/AbstractPermissionsEnumTest.java b/authme-core/src/test/java/fr/xephi/authme/permission/AbstractPermissionsEnumTest.java similarity index 93% rename from src/test/java/fr/xephi/authme/permission/AbstractPermissionsEnumTest.java rename to authme-core/src/test/java/fr/xephi/authme/permission/AbstractPermissionsEnumTest.java index 4b327ec45f..53658dda63 100644 --- a/src/test/java/fr/xephi/authme/permission/AbstractPermissionsEnumTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/permission/AbstractPermissionsEnumTest.java @@ -1,11 +1,11 @@ package fr.xephi.authme.permission; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.HashSet; import java.util.Set; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; /** * Has common tests for enums implementing {@link PermissionNode}. @@ -50,3 +50,4 @@ public void shouldHaveUniqueNodes() { protected abstract String getRequiredPrefix(); } + diff --git a/src/test/java/fr/xephi/authme/permission/AdminPermissionTest.java b/authme-core/src/test/java/fr/xephi/authme/permission/AdminPermissionTest.java similarity index 100% rename from src/test/java/fr/xephi/authme/permission/AdminPermissionTest.java rename to authme-core/src/test/java/fr/xephi/authme/permission/AdminPermissionTest.java diff --git a/src/test/java/fr/xephi/authme/permission/DebugSectionPermissionsTest.java b/authme-core/src/test/java/fr/xephi/authme/permission/DebugSectionPermissionsTest.java similarity index 100% rename from src/test/java/fr/xephi/authme/permission/DebugSectionPermissionsTest.java rename to authme-core/src/test/java/fr/xephi/authme/permission/DebugSectionPermissionsTest.java diff --git a/src/test/java/fr/xephi/authme/permission/PermissionConsistencyTest.java b/authme-core/src/test/java/fr/xephi/authme/permission/PermissionConsistencyTest.java similarity index 98% rename from src/test/java/fr/xephi/authme/permission/PermissionConsistencyTest.java rename to authme-core/src/test/java/fr/xephi/authme/permission/PermissionConsistencyTest.java index 6d7a37e2f6..61f1bacec1 100644 --- a/src/test/java/fr/xephi/authme/permission/PermissionConsistencyTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/permission/PermissionConsistencyTest.java @@ -8,8 +8,8 @@ import org.bukkit.configuration.MemorySection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Arrays; @@ -22,7 +22,7 @@ import java.util.stream.Collectors; import static fr.xephi.authme.TestHelper.getJarFile; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; /** * Tests that the permissions listed in plugin.yml correspond to the ones in the code. @@ -45,7 +45,7 @@ public class PermissionConsistencyTest { /** All permissions listed in plugin.yml. */ private static Map pluginYmlPermissions; - @BeforeClass + @BeforeAll public static void gatherPermissionNodes() { permissionClasses = new ClassCollector(TestHelper.SOURCES_FOLDER, TestHelper.PROJECT_ROOT + "permission") .collectClasses(PermissionNode.class).stream() @@ -281,3 +281,4 @@ private static Set removeStart(String start, List list) { } } + diff --git a/src/test/java/fr/xephi/authme/permission/PermissionsManagerInitializationTest.java b/authme-core/src/test/java/fr/xephi/authme/permission/PermissionsManagerInitializationTest.java similarity index 81% rename from src/test/java/fr/xephi/authme/permission/PermissionsManagerInitializationTest.java rename to authme-core/src/test/java/fr/xephi/authme/permission/PermissionsManagerInitializationTest.java index a0fc1d7c67..f1cf6dabdb 100644 --- a/src/test/java/fr/xephi/authme/permission/PermissionsManagerInitializationTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/permission/PermissionsManagerInitializationTest.java @@ -19,17 +19,17 @@ import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.ServicesManager; -import org.junit.AssumptionViolatedException; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.tyrannyofheaven.bukkit.zPermissions.ZPermissionsService; -import java.util.Collection; import java.util.Map; import java.util.stream.Collectors; +import java.util.stream.Stream; import static com.google.common.collect.Sets.newHashSet; import static fr.xephi.authme.permission.PermissionsSystemType.LUCK_PERMS; @@ -48,35 +48,30 @@ /** * Tests the initialization of {@link PermissionHandler} in {@link PermissionsManager}. */ -@RunWith(Parameterized.class) public class PermissionsManagerInitializationTest { - @Parameterized.Parameter(0) - public PermissionsSystemType permissionsSystemType; - @Parameterized.Parameter(1) - public Class expectedHandlerType; - private Settings settings = mock(Settings.class); private ServicesManager servicesManager = mock(ServicesManager.class); private Server server = mock(Server.class); private PluginManager pluginManager = mock(PluginManager.class); private PermissionsManager permissionsManager = new PermissionsManager(server, pluginManager, settings); - @BeforeClass + @BeforeAll public static void setUpLogger() { TestHelper.setupLogger(); } - @Before + @BeforeEach public void setUp() { ReflectionTestUtils.setField(Bukkit.class, null, "server", server); given(server.getServicesManager()).willReturn(servicesManager); } - @Test - public void shouldInitializeHandler() { + @ParameterizedTest(name = "{0}") + @MethodSource("createParameters") + public void shouldInitializeHandler(PermissionsSystemType permissionsSystemType, Class expectedHandlerType) { // given - setUpForPermissionSystemTest(); + setUpForPermissionSystemTest(permissionsSystemType); given(settings.getProperty(PluginSettings.FORCE_VAULT_HOOK)).willReturn(false); Plugin plugin = mock(Plugin.class); given(plugin.isEnabled()).willReturn(true); @@ -91,8 +86,10 @@ public void shouldInitializeHandler() { assertThat(handler.getPermissionSystem(), equalTo(permissionsSystemType)); } - @Test - public void shouldInitializeToVaultIfSoConfigured() { + @ParameterizedTest(name = "{0}") + @MethodSource("createParameters") + public void shouldInitializeToVaultIfSoConfigured(PermissionsSystemType permissionsSystemType, + Class expectedHandlerType) { // given setUpForVault(); given(settings.getProperty(PluginSettings.FORCE_VAULT_HOOK)).willReturn(true); @@ -109,8 +106,10 @@ public void shouldInitializeToVaultIfSoConfigured() { verify(pluginManager, only()).getPlugin(VAULT.getPluginName()); } - @Test - public void shouldNotHookIntoDisabledPlugin() { + @ParameterizedTest(name = "{0}") + @MethodSource("createParameters") + public void shouldNotHookIntoDisabledPlugin(PermissionsSystemType permissionsSystemType, + Class expectedHandlerType) { // given given(settings.getProperty(PluginSettings.FORCE_VAULT_HOOK)).willReturn(false); Plugin plugin = mock(Plugin.class); @@ -124,8 +123,10 @@ public void shouldNotHookIntoDisabledPlugin() { assertThat(getHandlerFieldValue(), nullValue()); } - @Test - public void shouldCatchInitializationException() { + @ParameterizedTest(name = "{0}") + @MethodSource("createParameters") + public void shouldCatchInitializationException(PermissionsSystemType permissionsSystemType, + Class expectedHandlerType) { // given given(settings.getProperty(PluginSettings.FORCE_VAULT_HOOK)).willReturn(false); Plugin plugin = mock(Plugin.class); @@ -140,8 +141,7 @@ public void shouldCatchInitializationException() { assertThat(getHandlerFieldValue(), nullValue()); } - @Parameterized.Parameters(name = "{0}") - public static Collection createParameters() { + public static Stream createParameters() { Map> handlersByPermissionSystemType = ImmutableMap.of( LUCK_PERMS, LuckPermsHandler.class, PERMISSIONS_EX, PermissionsExHandler.class, @@ -156,16 +156,15 @@ public static Collection createParameters() { // Wrap the above map in a Collection to satisfy JUnit return handlersByPermissionSystemType.entrySet().stream() - .map(e -> new Object[]{ e.getKey(), e.getValue() }) - .collect(Collectors.toList()); + .map(e -> Arguments.of(e.getKey(), e.getValue())); } - private void setUpForPermissionSystemTest() { + private void setUpForPermissionSystemTest(PermissionsSystemType permissionsSystemType) { if (permissionsSystemType == LUCK_PERMS) { LuckPerms api = mock(LuckPerms.class); ReflectionTestUtils.setField(LuckPermsProvider.class, null, "instance", api); } else if (permissionsSystemType == PERMISSIONS_EX) { - throw new AssumptionViolatedException( + Assumptions.assumeFalse(true, "PermissionsEx instance cannot be mocked because of missing dependencies -- skipping"); } else if (permissionsSystemType == Z_PERMISSIONS) { ZPermissionsService zPermissionsService = mock(ZPermissionsService.class); @@ -188,3 +187,4 @@ private PermissionHandler getHandlerFieldValue() { return ReflectionTestUtils.getFieldValue(PermissionsManager.class, permissionsManager, "handler"); } } + diff --git a/src/test/java/fr/xephi/authme/permission/PermissionsManagerTest.java b/authme-core/src/test/java/fr/xephi/authme/permission/PermissionsManagerTest.java similarity index 92% rename from src/test/java/fr/xephi/authme/permission/PermissionsManagerTest.java rename to authme-core/src/test/java/fr/xephi/authme/permission/PermissionsManagerTest.java index d0b8569ce5..60c9592167 100644 --- a/src/test/java/fr/xephi/authme/permission/PermissionsManagerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/permission/PermissionsManagerTest.java @@ -1,14 +1,16 @@ package fr.xephi.authme.permission; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import org.bukkit.Server; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.plugin.PluginManager; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; @@ -18,7 +20,8 @@ /** * Test for {@link PermissionsManager}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class PermissionsManagerTest { @InjectMocks @@ -149,3 +152,5 @@ public void shouldHandleNullPermissionForPlayer() { assertThat(result, equalTo(true)); } } + + diff --git a/src/test/java/fr/xephi/authme/permission/PermissionsSystemTypeTest.java b/authme-core/src/test/java/fr/xephi/authme/permission/PermissionsSystemTypeTest.java similarity index 97% rename from src/test/java/fr/xephi/authme/permission/PermissionsSystemTypeTest.java rename to authme-core/src/test/java/fr/xephi/authme/permission/PermissionsSystemTypeTest.java index 73658e2f92..2f93843fef 100644 --- a/src/test/java/fr/xephi/authme/permission/PermissionsSystemTypeTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/permission/PermissionsSystemTypeTest.java @@ -1,6 +1,6 @@ package fr.xephi.authme.permission; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; @@ -43,3 +43,4 @@ public void shouldRecognizePermissionSystemType() { } } + diff --git a/src/test/java/fr/xephi/authme/permission/PlayerPermissionTest.java b/authme-core/src/test/java/fr/xephi/authme/permission/PlayerPermissionTest.java similarity index 100% rename from src/test/java/fr/xephi/authme/permission/PlayerPermissionTest.java rename to authme-core/src/test/java/fr/xephi/authme/permission/PlayerPermissionTest.java diff --git a/src/test/java/fr/xephi/authme/permission/PlayerStatePermissionTest.java b/authme-core/src/test/java/fr/xephi/authme/permission/PlayerStatePermissionTest.java similarity index 93% rename from src/test/java/fr/xephi/authme/permission/PlayerStatePermissionTest.java rename to authme-core/src/test/java/fr/xephi/authme/permission/PlayerStatePermissionTest.java index 56e5e1ce1e..c22db23acf 100644 --- a/src/test/java/fr/xephi/authme/permission/PlayerStatePermissionTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/permission/PlayerStatePermissionTest.java @@ -1,12 +1,12 @@ package fr.xephi.authme.permission; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Collection; import java.util.Set; import static com.google.common.collect.Sets.newHashSet; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; /** * Test for {@link PlayerStatePermission}. @@ -45,3 +45,4 @@ private static boolean startsWithAny(String node, Collection prefixes) { return false; } } + diff --git a/src/test/java/fr/xephi/authme/permission/TestPermissions.java b/authme-core/src/test/java/fr/xephi/authme/permission/TestPermissions.java similarity index 100% rename from src/test/java/fr/xephi/authme/permission/TestPermissions.java rename to authme-core/src/test/java/fr/xephi/authme/permission/TestPermissions.java diff --git a/src/test/java/fr/xephi/authme/permission/handlers/VaultHandlerTest.java b/authme-core/src/test/java/fr/xephi/authme/permission/handlers/VaultHandlerTest.java similarity index 98% rename from src/test/java/fr/xephi/authme/permission/handlers/VaultHandlerTest.java rename to authme-core/src/test/java/fr/xephi/authme/permission/handlers/VaultHandlerTest.java index 636072746a..62a406b00e 100644 --- a/src/test/java/fr/xephi/authme/permission/handlers/VaultHandlerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/permission/handlers/VaultHandlerTest.java @@ -4,7 +4,7 @@ import net.milkbowl.vault.permission.Permission; import org.bukkit.Server; import org.bukkit.entity.Player; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.List; import java.util.stream.Collectors; @@ -82,3 +82,4 @@ Permission getVaultPermission(Server server) { } } } + diff --git a/authme-core/src/test/java/fr/xephi/authme/platform/AbstractSpigotPlatformAdapterTest.java b/authme-core/src/test/java/fr/xephi/authme/platform/AbstractSpigotPlatformAdapterTest.java new file mode 100644 index 0000000000..0097532d40 --- /dev/null +++ b/authme-core/src/test/java/fr/xephi/authme/platform/AbstractSpigotPlatformAdapterTest.java @@ -0,0 +1,35 @@ +package fr.xephi.authme.platform; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +class AbstractSpigotPlatformAdapterTest { + + @Test + void shouldUseBedSpawnLocationByDefault() { + // given + AbstractSpigotPlatformAdapter adapter = new AbstractSpigotPlatformAdapter() { + @Override + public String getPlatformName() { + return "test"; + } + }; + Player player = mock(Player.class); + World world = mock(World.class); + Location bedSpawn = new Location(world, 10.0, 64.0, -2.0); + given(player.getBedSpawnLocation()).willReturn(bedSpawn); + + // when + Location result = adapter.getPlayerRespawnLocation(player); + + // then + assertThat(result, equalTo(bedSpawn)); + } +} diff --git a/src/test/java/fr/xephi/authme/process/changepassword/AsyncChangePasswordTest.java b/authme-core/src/test/java/fr/xephi/authme/process/changepassword/AsyncChangePasswordTest.java similarity index 92% rename from src/test/java/fr/xephi/authme/process/changepassword/AsyncChangePasswordTest.java rename to authme-core/src/test/java/fr/xephi/authme/process/changepassword/AsyncChangePasswordTest.java index b0dd97bbee..97ffbe3435 100644 --- a/src/test/java/fr/xephi/authme/process/changepassword/AsyncChangePasswordTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/process/changepassword/AsyncChangePasswordTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.process.changepassword; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; @@ -9,12 +13,10 @@ import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.bungeecord.BungeeSender; import org.bukkit.command.CommandSender; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -24,7 +26,8 @@ /** * Test for {@link AsyncChangePassword}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class AsyncChangePasswordTest { @InjectMocks @@ -41,7 +44,7 @@ public class AsyncChangePasswordTest { @Mock private BungeeSender bungeeSender; - @Before + @BeforeEach public void setUpLogger() { TestHelper.setupLogger(); } @@ -128,3 +131,5 @@ public void shouldReportWhenSaveFailed() { } } + + diff --git a/src/test/java/fr/xephi/authme/process/email/AsyncAddEmailTest.java b/authme-core/src/test/java/fr/xephi/authme/process/email/AsyncAddEmailTest.java similarity index 95% rename from src/test/java/fr/xephi/authme/process/email/AsyncAddEmailTest.java rename to authme-core/src/test/java/fr/xephi/authme/process/email/AsyncAddEmailTest.java index 6d2fdd2227..b49f20fbd8 100644 --- a/src/test/java/fr/xephi/authme/process/email/AsyncAddEmailTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/process/email/AsyncAddEmailTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.process.email; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; @@ -11,12 +15,10 @@ import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.service.bungeecord.BungeeSender; import org.bukkit.entity.Player; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.function.Function; import static org.mockito.ArgumentMatchers.any; @@ -29,7 +31,8 @@ /** * Test for {@link AsyncAddEmail}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class AsyncAddEmailTest { @InjectMocks @@ -56,7 +59,7 @@ public class AsyncAddEmailTest { @Mock private BukkitService bukkitService; - @BeforeClass + @BeforeAll public static void setUp() { TestHelper.setupLogger(); } @@ -219,3 +222,5 @@ public void shouldNotAddOnCancelledEvent() { } } + + diff --git a/src/test/java/fr/xephi/authme/process/email/AsyncChangeEmailTest.java b/authme-core/src/test/java/fr/xephi/authme/process/email/AsyncChangeEmailTest.java similarity index 96% rename from src/test/java/fr/xephi/authme/process/email/AsyncChangeEmailTest.java rename to authme-core/src/test/java/fr/xephi/authme/process/email/AsyncChangeEmailTest.java index b0f3e971c1..25e21678a1 100644 --- a/src/test/java/fr/xephi/authme/process/email/AsyncChangeEmailTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/process/email/AsyncChangeEmailTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.process.email; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; @@ -11,12 +15,10 @@ import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.service.bungeecord.BungeeSender; import org.bukkit.entity.Player; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.function.Function; @@ -31,7 +33,8 @@ /** * Test for {@link AsyncChangeEmail}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class AsyncChangeEmailTest { @InjectMocks @@ -58,7 +61,7 @@ public class AsyncChangeEmailTest { @Mock private BukkitService bukkitService; - @BeforeClass + @BeforeAll public static void setUp() { TestHelper.setupLogger(); } @@ -271,3 +274,5 @@ private static PlayerAuth authWithMail(String email) { } } + + diff --git a/src/test/java/fr/xephi/authme/process/login/AsynchronousLoginTest.java b/authme-core/src/test/java/fr/xephi/authme/process/login/AsynchronousLoginTest.java similarity index 80% rename from src/test/java/fr/xephi/authme/process/login/AsynchronousLoginTest.java rename to authme-core/src/test/java/fr/xephi/authme/process/login/AsynchronousLoginTest.java index a3dbfb389b..b763735b59 100644 --- a/src/test/java/fr/xephi/authme/process/login/AsynchronousLoginTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/process/login/AsynchronousLoginTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.process.login; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; @@ -15,13 +19,11 @@ import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; import org.bukkit.entity.Player; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; -import org.mockito.junit.MockitoJUnitRunner; import org.mockito.stubbing.Answer; import java.util.Arrays; @@ -37,12 +39,13 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.internal.verification.VerificationModeFactory.only; +import static org.mockito.Mockito.only; /** * Test for {@link AsynchronousLogin}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class AsynchronousLoginTest { @InjectMocks @@ -60,7 +63,7 @@ public class AsynchronousLoginTest { @Mock private BukkitService bukkitService; - @BeforeClass + @BeforeAll public static void initLogger() { TestHelper.setupLogger(); } @@ -176,7 +179,7 @@ public void shouldPassMaxLoginPerIpCheck() { Player player = mockPlayer("Carl"); given(commonService.getProperty(RestrictionSettings.MAX_LOGIN_PER_IP)).willReturn(2); given(commonService.hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS)).willReturn(false); - mockOnlinePlayersInBukkitService(); + mockOnlinePlayersInBukkitService("1.1.1.1"); // when boolean result = asynchronousLogin.hasReachedMaxLoggedInPlayersForIp(player, "1.1.1.1"); @@ -190,7 +193,7 @@ public void shouldPassMaxLoginPerIpCheck() { @Test public void shouldSkipIpCheckForZeroThreshold() { // given - Player player = mockPlayer("Fiona"); + Player player = mock(Player.class); given(commonService.getProperty(RestrictionSettings.MAX_LOGIN_PER_IP)).willReturn(0); // when @@ -204,7 +207,7 @@ public void shouldSkipIpCheckForZeroThreshold() { @Test public void shouldSkipIpCheckForPlayerWithMultipleAccountsPermission() { // given - Player player = mockPlayer("Frank"); + Player player = mock(Player.class); given(commonService.getProperty(RestrictionSettings.MAX_LOGIN_PER_IP)).willReturn(1); given(commonService.hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS)).willReturn(true); @@ -223,7 +226,7 @@ public void shouldFailIpCheckForIpWithTooManyPlayersOnline() { Player player = mockPlayer("Ian"); given(commonService.getProperty(RestrictionSettings.MAX_LOGIN_PER_IP)).willReturn(2); given(commonService.hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS)).willReturn(false); - mockOnlinePlayersInBukkitService(); + mockOnlinePlayersInBukkitService("2.2.2.2"); // when boolean result = asynchronousLogin.hasReachedMaxLoggedInPlayersForIp(player, "2.2.2.2"); @@ -240,32 +243,43 @@ private static Player mockPlayer(String name) { return player; } - private void mockOnlinePlayersInBukkitService() { - // 1.1.1.1: albania (online), brazil (offline) - Player playerA = mockPlayer("albania"); - TestHelper.mockIpAddressToPlayer(playerA, "1.1.1.1"); - given(dataSource.isLogged(playerA.getName())).willReturn(true); - Player playerB = mockPlayer("brazil"); - TestHelper.mockIpAddressToPlayer(playerB, "1.1.1.1"); - given(dataSource.isLogged(playerB.getName())).willReturn(false); - - // 2.2.2.2: congo (online), denmark (offline), ecuador (online) - Player playerC = mockPlayer("congo"); - TestHelper.mockIpAddressToPlayer(playerC, "2.2.2.2"); - given(dataSource.isLogged(playerC.getName())).willReturn(true); - Player playerD = mockPlayer("denmark"); - TestHelper.mockIpAddressToPlayer(playerD, "2.2.2.2"); - given(dataSource.isLogged(playerD.getName())).willReturn(false); - Player playerE = mockPlayer("ecuador"); - TestHelper.mockIpAddressToPlayer(playerE, "2.2.2.2"); - given(dataSource.isLogged(playerE.getName())).willReturn(true); - - // 3.3.3.3: france (offline) - Player playerF = mockPlayer("france"); - TestHelper.mockIpAddressToPlayer(playerF, "3.3.3.3"); - - List onlinePlayers = Arrays.asList(playerA, playerB, playerC, playerD, playerE, playerF); + private void mockOnlinePlayersInBukkitService(String checkedIp) { + Player primaryOnline; + Player primaryOffline; + Player extraSameIpOnline = mock(Player.class); + + if ("1.1.1.1".equals(checkedIp)) { + primaryOnline = mockPlayer("albania"); + TestHelper.mockIpAddressToPlayer(primaryOnline, "1.1.1.1"); + given(dataSource.isLogged("albania")).willReturn(true); + + primaryOffline = mockPlayer("brazil"); + TestHelper.mockIpAddressToPlayer(primaryOffline, "1.1.1.1"); + given(dataSource.isLogged("brazil")).willReturn(false); + + TestHelper.mockIpAddressToPlayer(extraSameIpOnline, "2.2.2.2"); + } else { + primaryOnline = mockPlayer("congo"); + TestHelper.mockIpAddressToPlayer(primaryOnline, "2.2.2.2"); + given(dataSource.isLogged("congo")).willReturn(true); + + primaryOffline = mockPlayer("denmark"); + TestHelper.mockIpAddressToPlayer(primaryOffline, "2.2.2.2"); + given(dataSource.isLogged("denmark")).willReturn(false); + + Player playerE = mockPlayer("ecuador"); + TestHelper.mockIpAddressToPlayer(playerE, "2.2.2.2"); + given(dataSource.isLogged("ecuador")).willReturn(true); + extraSameIpOnline = playerE; + } + + Player otherIpPlayer = mock(Player.class); + TestHelper.mockIpAddressToPlayer(otherIpPlayer, "3.3.3.3"); + + List onlinePlayers = Arrays.asList(primaryOnline, primaryOffline, extraSameIpOnline, otherIpPlayer); given(bukkitService.getOnlinePlayers()).willReturn(onlinePlayers); } } + + diff --git a/src/test/java/fr/xephi/authme/process/register/AsyncRegisterTest.java b/authme-core/src/test/java/fr/xephi/authme/process/register/AsyncRegisterTest.java similarity index 86% rename from src/test/java/fr/xephi/authme/process/register/AsyncRegisterTest.java rename to authme-core/src/test/java/fr/xephi/authme/process/register/AsyncRegisterTest.java index 00e8be2407..8d5bcc7fa1 100644 --- a/src/test/java/fr/xephi/authme/process/register/AsyncRegisterTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/process/register/AsyncRegisterTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.process.register; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import ch.jalu.injector.factory.SingletonStore; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerCache; @@ -15,11 +19,9 @@ import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.function.Function; @@ -33,7 +35,8 @@ /** * Test for {@link AsyncRegister}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class AsyncRegisterTest { @InjectMocks @@ -56,15 +59,13 @@ public void shouldDetectAlreadyLoggedInPlayer() { String name = "robert"; Player player = mockPlayerWithName(name); given(playerCache.isAuthenticated(name)).willReturn(true); - RegistrationExecutor executor = mock(RegistrationExecutor.class); - singletonStoreWillReturn(registrationExecutorStore, executor); // when asyncRegister.register(RegistrationMethod.PASSWORD_REGISTRATION, PasswordRegisterParams.of(player, "abc", null)); // then verify(commonService).send(player, MessageKey.ALREADY_LOGGED_IN_ERROR); - verifyNoInteractions(dataSource, executor); + verifyNoInteractions(dataSource); } @Test @@ -74,15 +75,13 @@ public void shouldStopForDisabledRegistration() { Player player = mockPlayerWithName(name); given(playerCache.isAuthenticated(name)).willReturn(false); given(commonService.getProperty(RegistrationSettings.IS_ENABLED)).willReturn(false); - RegistrationExecutor executor = mock(RegistrationExecutor.class); - singletonStoreWillReturn(registrationExecutorStore, executor); // when asyncRegister.register(RegistrationMethod.TWO_FACTOR_REGISTRATION, TwoFactorRegisterParams.of(player)); // then verify(commonService).send(player, MessageKey.REGISTRATION_DISABLED); - verifyNoInteractions(dataSource, executor); + verifyNoInteractions(dataSource); } @Test @@ -93,8 +92,6 @@ public void shouldStopForAlreadyRegisteredName() { given(playerCache.isAuthenticated(name)).willReturn(false); given(commonService.getProperty(RegistrationSettings.IS_ENABLED)).willReturn(true); given(dataSource.isAuthAvailable(name)).willReturn(true); - RegistrationExecutor executor = mock(RegistrationExecutor.class); - singletonStoreWillReturn(registrationExecutorStore, executor); // when asyncRegister.register(RegistrationMethod.TWO_FACTOR_REGISTRATION, TwoFactorRegisterParams.of(player)); @@ -102,7 +99,6 @@ public void shouldStopForAlreadyRegisteredName() { // then verify(commonService).send(player, MessageKey.NAME_ALREADY_REGISTERED); verify(dataSource, only()).isAuthAvailable(name); - verifyNoInteractions(executor); } @Test @@ -111,13 +107,10 @@ public void shouldStopForCanceledEvent() { // given String name = "edbert"; Player player = mockPlayerWithName(name); - TestHelper.mockIpAddressToPlayer(player, "33.44.55.66"); given(playerCache.isAuthenticated(name)).willReturn(false); given(commonService.getProperty(RegistrationSettings.IS_ENABLED)).willReturn(true); given(dataSource.isAuthAvailable(name)).willReturn(false); - RegistrationExecutor executor = mock(RegistrationExecutor.class); TwoFactorRegisterParams params = TwoFactorRegisterParams.of(player); - singletonStoreWillReturn(registrationExecutorStore, executor); AuthMeAsyncPreRegisterEvent canceledEvent = new AuthMeAsyncPreRegisterEvent(player, true); canceledEvent.setCanRegister(false); @@ -169,3 +162,5 @@ private static void singletonStoreWillReturn(SingletonStore> eventFunctionCaptor; - @BeforeClass + @BeforeAll public static void initLogger() { TestHelper.setupLogger(); } @@ -113,7 +119,7 @@ public void shouldPerformUnregister() { given(dataSource.removeAuth(name)).willReturn(true); given(service.getProperty(RegistrationSettings.FORCE)).willReturn(true); given(service.getProperty(RegistrationSettings.APPLY_BLIND_EFFECT)).willReturn(true); - given(service.getProperty(RestrictionSettings.TIMEOUT)).willReturn(21); + given(service.getProperty(RestrictionSettings.REGISTER_TIMEOUT)).willReturn(21); setBukkitServiceToScheduleSyncTaskFromOptionallyAsyncTask(bukkitService); given(bukkitService.createBlindnessEffect(21 * 20)).willReturn(mock(PotionEffect.class)); @@ -301,10 +307,10 @@ public void shouldHandleDatabaseErrorForAdminUnregister() { @SuppressWarnings("unchecked") private void verifyCalledUnregisterEventFor(Player player) { - ArgumentCaptor> eventFunctionCaptor = - ArgumentCaptor.forClass(Function.class); verify(bukkitService).createAndCallEvent(eventFunctionCaptor.capture()); AbstractUnregisterEvent event = eventFunctionCaptor.getValue().apply(true); assertThat(event.getPlayer(), equalTo(player)); } } + + diff --git a/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java b/authme-core/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java similarity index 97% rename from src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java rename to authme-core/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java index 7af6c38bf1..13147dcb87 100644 --- a/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java @@ -12,8 +12,8 @@ import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.util.StringUtils; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import java.util.HashSet; import java.util.LinkedList; @@ -22,7 +22,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -33,7 +33,7 @@ public class HashAlgorithmIntegrationTest { private static Injector injector; - @BeforeClass + @BeforeAll public static void setUpConfigAndInjector() { Settings settings = mock(Settings.class); given(settings.getProperty(HooksSettings.BCRYPT_LOG2_ROUND)).willReturn(8); @@ -107,3 +107,4 @@ public void shouldBeDeprecatedIfEncryptionClassIsDeprecated() throws NoSuchField } } } + diff --git a/src/test/java/fr/xephi/authme/security/HashUtilsTest.java b/authme-core/src/test/java/fr/xephi/authme/security/HashUtilsTest.java similarity index 99% rename from src/test/java/fr/xephi/authme/security/HashUtilsTest.java rename to authme-core/src/test/java/fr/xephi/authme/security/HashUtilsTest.java index 37c2b8aa37..763586c64e 100644 --- a/src/test/java/fr/xephi/authme/security/HashUtilsTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/security/HashUtilsTest.java @@ -1,6 +1,6 @@ package fr.xephi.authme.security; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.security.MessageDigest; import java.util.ArrayList; @@ -134,3 +134,4 @@ public void shouldCompareStrings() { assertThat(HashUtils.isEqual("test", "tëst"), equalTo(false)); } } + diff --git a/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java b/authme-core/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java similarity index 98% rename from src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java rename to authme-core/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java index a3dfbb8131..529952f554 100644 --- a/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java @@ -1,10 +1,11 @@ package fr.xephi.authme.security; +import org.junit.jupiter.api.extension.ExtendWith; +import fr.xephi.authme.DelayedInjectionExtension; import ch.jalu.injector.Injector; import ch.jalu.injector.InjectorBuilder; import ch.jalu.injector.factory.Factory; import ch.jalu.injector.testing.BeforeInjecting; -import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.InjectDelayed; import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.TestHelper; @@ -19,9 +20,8 @@ import fr.xephi.authme.settings.properties.SecuritySettings; import org.bukkit.event.Event; import org.bukkit.plugin.PluginManager; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -48,7 +48,7 @@ /** * Test for {@link PasswordSecurity}. */ -@RunWith(DelayedInjectionRunner.class) +@ExtendWith(DelayedInjectionExtension.class) public class PasswordSecurityTest { @InjectDelayed @@ -71,7 +71,7 @@ public class PasswordSecurityTest { private Class caughtClassInEvent; - @BeforeClass + @BeforeAll public static void setUpTest() { TestHelper.setupLogger(); } @@ -287,3 +287,5 @@ public void shouldReloadSettings() { equalTo(legacyHashesSet)); } } + + diff --git a/src/test/java/fr/xephi/authme/security/crypts/AbstractEncryptionMethodTest.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/AbstractEncryptionMethodTest.java similarity index 88% rename from src/test/java/fr/xephi/authme/security/crypts/AbstractEncryptionMethodTest.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/AbstractEncryptionMethodTest.java index 8789cde63b..d0dad37ea1 100644 --- a/src/test/java/fr/xephi/authme/security/crypts/AbstractEncryptionMethodTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/security/crypts/AbstractEncryptionMethodTest.java @@ -4,18 +4,18 @@ import com.google.common.collect.ImmutableMap; import fr.xephi.authme.TestHelper; import fr.xephi.authme.security.crypts.description.AsciiRestricted; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import java.util.List; import java.util.Locale; import java.util.Map; import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeThat; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; /** * Test for implementations of {@link EncryptionMethod}. @@ -97,7 +97,7 @@ public AbstractEncryptionMethodTest(EncryptionMethod method, HashedPassword resu GIVEN_PASSWORDS[3], result3); } - @BeforeClass + @BeforeAll public static void setupLogger() { TestHelper.setupLogger(); } @@ -119,8 +119,8 @@ public void testGivenPasswords() { // Test entries in GIVEN_PASSWORDS except the last one for (int i = start; i < GIVEN_PASSWORDS.length - 1; ++i) { String password = GIVEN_PASSWORDS[i]; - assertTrue("Hash for password '" + password + "' should match", - doesGivenHashMatch(password, method)); + assertTrue(doesGivenHashMatch(password, method), + "Hash for password '" + password + "' should match"); } // Note #375: Windows console seems to use its own character encoding (Windows-1252?) and it seems impossible to @@ -151,17 +151,17 @@ public void testPasswordEquality() { hash, equalTo(method.computeHash(password, salt, USERNAME))); } - assertTrue("Generated hash for '" + password + "' should match password (hash = '" + hash + "')", - method.comparePassword(password, hashedPassword, USERNAME)); - assumeThat(SKIP_LONG_TESTS, equalTo(false)); + assertTrue(method.comparePassword(password, hashedPassword, USERNAME), + "Generated hash for '" + password + "' should match password (hash = '" + hash + "')"); + assumeTrue(!SKIP_LONG_TESTS); if (!password.equals(password.toLowerCase(Locale.ROOT))) { - assertFalse("Lower-case of '" + password + "' should not match generated hash '" + hash + "'", - method.comparePassword(password.toLowerCase(Locale.ROOT), hashedPassword, USERNAME)); + assertFalse(method.comparePassword(password.toLowerCase(Locale.ROOT), hashedPassword, USERNAME), + "Lower-case of '" + password + "' should not match generated hash '" + hash + "'"); } if (!password.equals(password.toUpperCase(Locale.ROOT))) { - assertFalse("Upper-case of '" + password + "' should not match generated hash '" + hash + "'", - method.comparePassword(password.toUpperCase(Locale.ROOT), hashedPassword, USERNAME)); + assertFalse(method.comparePassword(password.toUpperCase(Locale.ROOT), hashedPassword, USERNAME), + "Upper-case of '" + password + "' should not match generated hash '" + hash + "'"); } } } @@ -169,12 +169,12 @@ public void testPasswordEquality() { /** Tests various strings to ensure that encryption methods don't rely on the hash's format too much. */ @Test public void testMalformedHashes() { - assumeThat(SKIP_LONG_TESTS, equalTo(false)); + assumeTrue(!SKIP_LONG_TESTS); String salt = method.hasSeparateSalt() ? "testSalt" : null; for (String bogusHash : BOGUS_HASHES) { HashedPassword hashedPwd = new HashedPassword(bogusHash, salt); - assertFalse("Passing bogus hash '" + bogusHash + "' does not result in an error", - method.comparePassword("Password", hashedPwd, "player")); + assertFalse(method.comparePassword("Password", hashedPwd, "player"), + "Passing bogus hash '" + bogusHash + "' does not result in an error"); } } @@ -237,3 +237,4 @@ protected boolean testHashEqualityForSameSalt() { } } + diff --git a/src/test/java/fr/xephi/authme/security/crypts/Argon2Test.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/Argon2Test.java similarity index 82% rename from src/test/java/fr/xephi/authme/security/crypts/Argon2Test.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/Argon2Test.java index 46b315d567..a1736148fe 100644 --- a/src/test/java/fr/xephi/authme/security/crypts/Argon2Test.java +++ b/authme-core/src/test/java/fr/xephi/authme/security/crypts/Argon2Test.java @@ -1,7 +1,6 @@ package fr.xephi.authme.security.crypts; -import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assume.assumeThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; /** * Test for {@link Argon2}. @@ -17,8 +16,7 @@ public Argon2Test() { "$argon2i$v=19$m=65536,t=2,p=1$58v7dWNn9/bpD00QLzSebw$7cMC7p0qceE3Mgf2yQp4X7c+UkO9oyJwQ7S6XTBubNs", // &^%te$t?Pw@_ "$argon2i$v=19$m=65536,t=2,p=1$93OSU71DgBOzpmhti7+6rQ$sSSI6QQQdoG9DlGwLjYz576kTek89nwr9CyNpy6bsL0"); // âË_3(íù* - assumeThat("Argon2 library is not loaded - skipping test", - IS_LIBRARY_LOADED, equalTo(true)); + assumeTrue(IS_LIBRARY_LOADED, "Argon2 library is not loaded - skipping test"); } @Override diff --git a/src/test/java/fr/xephi/authme/security/crypts/BCrypt2yTest.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/BCrypt2yTest.java similarity index 96% rename from src/test/java/fr/xephi/authme/security/crypts/BCrypt2yTest.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/BCrypt2yTest.java index a8cdabdc6a..e5a381259b 100644 --- a/src/test/java/fr/xephi/authme/security/crypts/BCrypt2yTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/security/crypts/BCrypt2yTest.java @@ -1,6 +1,6 @@ package fr.xephi.authme.security.crypts; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.startsWith; @@ -32,3 +32,4 @@ public void shouldGenerateWith2yPrefixAndCostFactor10() { assertThat(result.getSalt(), nullValue()); } } + diff --git a/src/test/java/fr/xephi/authme/security/crypts/BCryptTest.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/BCryptTest.java similarity index 97% rename from src/test/java/fr/xephi/authme/security/crypts/BCryptTest.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/BCryptTest.java index 093621a159..db58ea78c5 100644 --- a/src/test/java/fr/xephi/authme/security/crypts/BCryptTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/security/crypts/BCryptTest.java @@ -2,7 +2,7 @@ import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.HooksSettings; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.hamcrest.Matchers.startsWith; import static org.hamcrest.MatcherAssert.assertThat; @@ -41,3 +41,4 @@ private static Settings mockSettings() { return settings; } } + diff --git a/src/test/java/fr/xephi/authme/security/crypts/CmwCryptTest.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/CmwCryptTest.java similarity index 100% rename from src/test/java/fr/xephi/authme/security/crypts/CmwCryptTest.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/CmwCryptTest.java diff --git a/src/test/java/fr/xephi/authme/security/crypts/CrazyCrypt1Test.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/CrazyCrypt1Test.java similarity index 100% rename from src/test/java/fr/xephi/authme/security/crypts/CrazyCrypt1Test.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/CrazyCrypt1Test.java diff --git a/src/test/java/fr/xephi/authme/security/crypts/DoubleMd5Test.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/DoubleMd5Test.java similarity index 100% rename from src/test/java/fr/xephi/authme/security/crypts/DoubleMd5Test.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/DoubleMd5Test.java diff --git a/src/test/java/fr/xephi/authme/security/crypts/Ipb3Test.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/Ipb3Test.java similarity index 100% rename from src/test/java/fr/xephi/authme/security/crypts/Ipb3Test.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/Ipb3Test.java diff --git a/src/test/java/fr/xephi/authme/security/crypts/Ipb4Test.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/Ipb4Test.java similarity index 95% rename from src/test/java/fr/xephi/authme/security/crypts/Ipb4Test.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/Ipb4Test.java index 4ea5addf04..adcdd72e31 100644 --- a/src/test/java/fr/xephi/authme/security/crypts/Ipb4Test.java +++ b/authme-core/src/test/java/fr/xephi/authme/security/crypts/Ipb4Test.java @@ -1,12 +1,12 @@ package fr.xephi.authme.security.crypts; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static fr.xephi.authme.AuthMeMatchers.stringWithLength; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.startsWith; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; /** * Test for {@link Ipb4}. @@ -50,3 +50,4 @@ public void shouldThrowForInvalidSalt() { } } } + diff --git a/src/test/java/fr/xephi/authme/security/crypts/JoomlaTest.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/JoomlaTest.java similarity index 100% rename from src/test/java/fr/xephi/authme/security/crypts/JoomlaTest.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/JoomlaTest.java diff --git a/src/test/java/fr/xephi/authme/security/crypts/Md5Test.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/Md5Test.java similarity index 100% rename from src/test/java/fr/xephi/authme/security/crypts/Md5Test.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/Md5Test.java diff --git a/src/test/java/fr/xephi/authme/security/crypts/Md5vBTest.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/Md5vBTest.java similarity index 100% rename from src/test/java/fr/xephi/authme/security/crypts/Md5vBTest.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/Md5vBTest.java diff --git a/src/test/java/fr/xephi/authme/security/crypts/MyBBTest.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/MyBBTest.java similarity index 100% rename from src/test/java/fr/xephi/authme/security/crypts/MyBBTest.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/MyBBTest.java diff --git a/authme-core/src/test/java/fr/xephi/authme/security/crypts/Pbkdf2Base64Test.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/Pbkdf2Base64Test.java new file mode 100644 index 0000000000..4edc80cc4c --- /dev/null +++ b/authme-core/src/test/java/fr/xephi/authme/security/crypts/Pbkdf2Base64Test.java @@ -0,0 +1,38 @@ +package fr.xephi.authme.security.crypts; + +import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.SecuritySettings; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Test for {@link Pbkdf2Base64}. + */ +public class Pbkdf2Base64Test extends AbstractEncryptionMethodTest { + + public Pbkdf2Base64Test() { + super(new Pbkdf2Base64(mockSettings()), + "pbkdf2$4128$JddgMm9rNhbYNfEmf4pOKA==$9Q7QyaEDhqAj8KfF7FdPRq6f9uAWhMwioKvzAWMxSu8=", // password + "pbkdf2$4128$obLD1OX2p7jJ0OHyo7TF1g==$dyEnM1cYBRhXl0bDgxfQ3p7Jecj4VerT1Vsag5HJTmo=", // PassWord1 + "pbkdf2$4128$3q2+78r+AQIDBAUGBwgJCg==$L1stW/OuuWuLfFiEqpOSumXwSJROYDUwSDfiU/7CkYA=", // &^%te$t?Pw@_ + "pbkdf2$4128$AQIDBAUGBwgJCgsMDQ4PEA==$djo4l/sF2mo7hNBflk/JZNFURfNbYDT2KUjJ4n1K/8Y="); // âË_3(íù* + } + + @Test + public void shouldMatchHashWithDifferentRoundNumber() { + // given / when / then — verifies that stored iterations override the configured value + Pbkdf2Base64 pbkdf2Base64 = new Pbkdf2Base64(mockSettings()); + String hash = "pbkdf2$120000$JddgMm9rNhbYNfEmf4pOKA==$RcAMtgm/KnKFxfNOpg95tb7s5OzB2Fv4Wj1HOAI/TWY="; + assertThat(pbkdf2Base64.comparePassword("azerty123", new HashedPassword(hash), ""), equalTo(true)); + } + + private static Settings mockSettings() { + Settings settings = mock(Settings.class); + given(settings.getProperty(SecuritySettings.PBKDF2_NUMBER_OF_ROUNDS)).willReturn(4128); + return settings; + } +} diff --git a/src/test/java/fr/xephi/authme/security/crypts/Pbkdf2DjangoTest.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/Pbkdf2DjangoTest.java similarity index 100% rename from src/test/java/fr/xephi/authme/security/crypts/Pbkdf2DjangoTest.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/Pbkdf2DjangoTest.java diff --git a/src/test/java/fr/xephi/authme/security/crypts/Pbkdf2Test.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/Pbkdf2Test.java similarity index 98% rename from src/test/java/fr/xephi/authme/security/crypts/Pbkdf2Test.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/Pbkdf2Test.java index 0d5674cdc4..121d1bb618 100644 --- a/src/test/java/fr/xephi/authme/security/crypts/Pbkdf2Test.java +++ b/authme-core/src/test/java/fr/xephi/authme/security/crypts/Pbkdf2Test.java @@ -2,7 +2,7 @@ import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.SecuritySettings; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; @@ -42,3 +42,4 @@ private static Settings mockSettings() { return settings; } } + diff --git a/src/test/java/fr/xephi/authme/security/crypts/PhpBBTest.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/PhpBBTest.java similarity index 96% rename from src/test/java/fr/xephi/authme/security/crypts/PhpBBTest.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/PhpBBTest.java index 743a7b46af..bdf133c26d 100644 --- a/src/test/java/fr/xephi/authme/security/crypts/PhpBBTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/security/crypts/PhpBBTest.java @@ -1,11 +1,11 @@ package fr.xephi.authme.security.crypts; import com.google.common.collect.ImmutableMap; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Map; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; /** * Test for {@link PhpBB}. @@ -57,3 +57,4 @@ public void shouldMatchUnsaltedMd5Hashes() { } } + diff --git a/src/test/java/fr/xephi/authme/security/crypts/PhpFusionTest.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/PhpFusionTest.java similarity index 100% rename from src/test/java/fr/xephi/authme/security/crypts/PhpFusionTest.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/PhpFusionTest.java diff --git a/src/test/java/fr/xephi/authme/security/crypts/RoyalAuthTest.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/RoyalAuthTest.java similarity index 100% rename from src/test/java/fr/xephi/authme/security/crypts/RoyalAuthTest.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/RoyalAuthTest.java diff --git a/src/test/java/fr/xephi/authme/security/crypts/Salted2Md5Test.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/Salted2Md5Test.java similarity index 100% rename from src/test/java/fr/xephi/authme/security/crypts/Salted2Md5Test.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/Salted2Md5Test.java diff --git a/src/test/java/fr/xephi/authme/security/crypts/SaltedSha512Test.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/SaltedSha512Test.java similarity index 100% rename from src/test/java/fr/xephi/authme/security/crypts/SaltedSha512Test.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/SaltedSha512Test.java diff --git a/src/test/java/fr/xephi/authme/security/crypts/Sha1Test.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/Sha1Test.java similarity index 100% rename from src/test/java/fr/xephi/authme/security/crypts/Sha1Test.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/Sha1Test.java diff --git a/src/test/java/fr/xephi/authme/security/crypts/Sha256Test.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/Sha256Test.java similarity index 100% rename from src/test/java/fr/xephi/authme/security/crypts/Sha256Test.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/Sha256Test.java diff --git a/src/test/java/fr/xephi/authme/security/crypts/Sha512Test.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/Sha512Test.java similarity index 100% rename from src/test/java/fr/xephi/authme/security/crypts/Sha512Test.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/Sha512Test.java diff --git a/src/test/java/fr/xephi/authme/security/crypts/SmfTest.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/SmfTest.java similarity index 97% rename from src/test/java/fr/xephi/authme/security/crypts/SmfTest.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/SmfTest.java index 2750574e1f..a71a12c87e 100644 --- a/src/test/java/fr/xephi/authme/security/crypts/SmfTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/security/crypts/SmfTest.java @@ -1,6 +1,6 @@ package fr.xephi.authme.security.crypts; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static fr.xephi.authme.AuthMeMatchers.stringWithLength; import static org.hamcrest.Matchers.equalTo; @@ -41,3 +41,4 @@ public void shouldGenerateFourCharSalt() { } } } + diff --git a/src/test/java/fr/xephi/authme/security/crypts/TwoFactorTest.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/TwoFactorTest.java similarity index 94% rename from src/test/java/fr/xephi/authme/security/crypts/TwoFactorTest.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/TwoFactorTest.java index da764e6062..adee202959 100644 --- a/src/test/java/fr/xephi/authme/security/crypts/TwoFactorTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/security/crypts/TwoFactorTest.java @@ -1,8 +1,8 @@ package fr.xephi.authme.security.crypts; import fr.xephi.authme.TestHelper; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; @@ -12,7 +12,7 @@ */ public class TwoFactorTest { - @BeforeClass + @BeforeAll public static void initLogger() { TestHelper.setupLogger(); } @@ -61,3 +61,4 @@ public void shouldHandleInvalidInput() { assertThat(result, equalTo(false)); } } + diff --git a/src/test/java/fr/xephi/authme/security/crypts/Wbb3Test.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/Wbb3Test.java similarity index 100% rename from src/test/java/fr/xephi/authme/security/crypts/Wbb3Test.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/Wbb3Test.java diff --git a/src/test/java/fr/xephi/authme/security/crypts/Wbb4Test.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/Wbb4Test.java similarity index 100% rename from src/test/java/fr/xephi/authme/security/crypts/Wbb4Test.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/Wbb4Test.java diff --git a/src/test/java/fr/xephi/authme/security/crypts/WordpressTest.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/WordpressTest.java similarity index 100% rename from src/test/java/fr/xephi/authme/security/crypts/WordpressTest.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/WordpressTest.java diff --git a/src/test/java/fr/xephi/authme/security/crypts/XfBCryptTest.java b/authme-core/src/test/java/fr/xephi/authme/security/crypts/XfBCryptTest.java similarity index 96% rename from src/test/java/fr/xephi/authme/security/crypts/XfBCryptTest.java rename to authme-core/src/test/java/fr/xephi/authme/security/crypts/XfBCryptTest.java index 0459d9b0cb..5d7d71bfa6 100644 --- a/src/test/java/fr/xephi/authme/security/crypts/XfBCryptTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/security/crypts/XfBCryptTest.java @@ -1,6 +1,6 @@ package fr.xephi.authme.security.crypts; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.startsWith; @@ -32,3 +32,4 @@ public void shouldGenerateWith2aPrefixAndCostFactor10() { assertThat(result.getSalt(), nullValue()); } } + diff --git a/src/test/java/fr/xephi/authme/security/totp/GenerateTotpServiceTest.java b/authme-core/src/test/java/fr/xephi/authme/security/totp/GenerateTotpServiceTest.java similarity index 91% rename from src/test/java/fr/xephi/authme/security/totp/GenerateTotpServiceTest.java rename to authme-core/src/test/java/fr/xephi/authme/security/totp/GenerateTotpServiceTest.java index b17d26bb79..8ba80b5967 100644 --- a/src/test/java/fr/xephi/authme/security/totp/GenerateTotpServiceTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/security/totp/GenerateTotpServiceTest.java @@ -1,14 +1,16 @@ package fr.xephi.authme.security.totp; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.security.totp.TotpAuthenticator.TotpGenerationResult; import fr.xephi.authme.util.expiring.ExpiringMap; import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.concurrent.TimeUnit; @@ -22,7 +24,8 @@ /** * Test for {@link GenerateTotpService}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class GenerateTotpServiceTest { @InjectMocks @@ -70,6 +73,7 @@ public void shouldCheckGeneratedTotpKey() { given(totpAuthenticator.generateTotpKey(player)).willReturn(givenGenerationResult); generateTotpService.generateTotpKey(player); String validCode = "928374"; + given(totpAuthenticator.checkCode("Aria", generatedKey, "000000")).willReturn(false); given(totpAuthenticator.checkCode("Aria", generatedKey, validCode)).willReturn(true); // when @@ -111,3 +115,5 @@ private static Player mockPlayerWithName(String name) { return player; } } + + diff --git a/src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java b/authme-core/src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java similarity index 93% rename from src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java rename to authme-core/src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java index 52974238da..5416c5d0d5 100644 --- a/src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/security/totp/TotpAuthenticatorTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.security.totp; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import com.google.common.collect.Table; import com.warrenstrange.googleauth.IGoogleAuthenticator; import fr.xephi.authme.ReflectionTestUtils; @@ -9,11 +13,9 @@ import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.util.Utils; import org.bukkit.entity.Player; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import static fr.xephi.authme.AuthMeMatchers.stringWithLength; import static org.hamcrest.Matchers.containsString; @@ -29,7 +31,8 @@ /** * Test for {@link TotpAuthenticator}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class TotpAuthenticatorTest { private TotpAuthenticator totpAuthenticator; @@ -40,7 +43,7 @@ public class TotpAuthenticatorTest { @Mock private IGoogleAuthenticator googleAuthenticator; - @Before + @BeforeEach public void initializeTotpAuthenticator() { totpAuthenticator = new TotpAuthenticatorTestImpl(settings); } @@ -143,3 +146,5 @@ protected IGoogleAuthenticator createGoogleAuthenticator() { } } } + + diff --git a/src/test/java/fr/xephi/authme/service/AntiBotServiceTest.java b/authme-core/src/test/java/fr/xephi/authme/service/AntiBotServiceTest.java similarity index 82% rename from src/test/java/fr/xephi/authme/service/AntiBotServiceTest.java rename to authme-core/src/test/java/fr/xephi/authme/service/AntiBotServiceTest.java index f26847d386..b576548e9d 100644 --- a/src/test/java/fr/xephi/authme/service/AntiBotServiceTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/service/AntiBotServiceTest.java @@ -1,7 +1,8 @@ package fr.xephi.authme.service; +import org.junit.jupiter.api.extension.ExtendWith; +import fr.xephi.authme.DelayedInjectionExtension; import ch.jalu.injector.testing.BeforeInjecting; -import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.InjectDelayed; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; @@ -10,17 +11,17 @@ import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.ProtectionSettings; import org.bukkit.entity.Player; -import org.bukkit.scheduler.BukkitTask; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import java.util.Arrays; import java.util.List; -import static fr.xephi.authme.service.BukkitServiceTestHelper.setBukkitServiceToScheduleSyncDelayedTaskWithDelay; import static fr.xephi.authme.service.BukkitServiceTestHelper.setBukkitServiceToScheduleSyncTaskFromOptionallyAsyncTask; +import static fr.xephi.authme.service.BukkitServiceTestHelper.setBukkitServiceToRunOnGlobalRegion; +import static fr.xephi.authme.service.BukkitServiceTestHelper.setBukkitServiceToRunTaskLaterOnGlobalRegion; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -35,7 +36,7 @@ /** * Test for {@link AntiBotService}. */ -@RunWith(DelayedInjectionRunner.class) +@ExtendWith(DelayedInjectionExtension.class) public class AntiBotServiceTest { @InjectDelayed @@ -49,6 +50,8 @@ public class AntiBotServiceTest { private PermissionsManager permissionsManager; @Mock private BukkitService bukkitService; + @Captor + private ArgumentCaptor runnableCaptor; @BeforeInjecting public void initSettings() { @@ -57,7 +60,8 @@ public void initSettings() { given(settings.getProperty(ProtectionSettings.ANTIBOT_SENSIBILITY)).willReturn(5); given(settings.getProperty(ProtectionSettings.ENABLE_ANTIBOT)).willReturn(true); given(settings.getProperty(ProtectionSettings.ANTIBOT_DELAY)).willReturn(8); - setBukkitServiceToScheduleSyncDelayedTaskWithDelay(bukkitService); + setBukkitServiceToRunTaskLaterOnGlobalRegion(bukkitService); + setBukkitServiceToRunOnGlobalRegion(bukkitService); } @Test @@ -83,8 +87,8 @@ public void shouldNotListenForDisabledSetting() { @Test public void shouldActivateAntibot() { // given - listening antibot - BukkitTask task = mock(BukkitTask.class); - given(bukkitService.runTaskLater(any(Runnable.class), anyLong())).willReturn(task); + CancellableTask task = mock(CancellableTask.class); + given(bukkitService.runTaskLaterOnGlobalRegion(any(Runnable.class), anyLong())).willReturn(task); // when antiBotService.overrideAntiBotStatus(true); @@ -92,9 +96,8 @@ public void shouldActivateAntibot() { // then assertThat(antiBotService.getAntiBotStatus(), equalTo(AntiBotService.AntiBotStatus.ACTIVE)); // Check that a task is scheduled to disable again - ArgumentCaptor runnableCaptor = ArgumentCaptor.forClass(Runnable.class); - verify(bukkitService).runTaskLater(runnableCaptor.capture(), anyLong()); - runnableCaptor.getValue().run(); + verify(bukkitService, org.mockito.Mockito.times(2)).runTaskLaterOnGlobalRegion(runnableCaptor.capture(), anyLong()); + runnableCaptor.getAllValues().get(1).run(); assertThat(antiBotService.getAntiBotStatus(), equalTo(AntiBotService.AntiBotStatus.LISTENING)); } @@ -162,6 +165,7 @@ public void shouldInformPlayersOnActivation() { given(permissionsManager.hasPermission(players.get(0), AdminPermission.ANTIBOT_MESSAGES)).willReturn(false); given(permissionsManager.hasPermission(players.get(1), AdminPermission.ANTIBOT_MESSAGES)).willReturn(true); setBukkitServiceToScheduleSyncTaskFromOptionallyAsyncTask(bukkitService); + setBukkitServiceToRunOnGlobalRegion(bukkitService); // when antiBotService.overrideAntiBotStatus(true); @@ -169,13 +173,14 @@ public void shouldInformPlayersOnActivation() { // then verify(permissionsManager).hasPermission(players.get(0), AdminPermission.ANTIBOT_MESSAGES); verify(permissionsManager).hasPermission(players.get(1), AdminPermission.ANTIBOT_MESSAGES); - verify(messages, only()).send(players.get(1), MessageKey.ANTIBOT_AUTO_ENABLED_MESSAGE); + verify(bukkitService).scheduleSyncTaskFromOptionallyAsyncTask(org.mockito.ArgumentMatchers.eq(players.get(0)), any(Runnable.class)); + verify(bukkitService).scheduleSyncTaskFromOptionallyAsyncTask(org.mockito.ArgumentMatchers.eq(players.get(1)), any(Runnable.class)); } @Test public void shouldImmediatelyStartAfterFirstStartup() { // given - listening antibot - given(bukkitService.runTaskLater(any(Runnable.class), anyLong())).willReturn(mock(BukkitTask.class)); + given(bukkitService.runTaskLaterOnGlobalRegion(any(Runnable.class), anyLong())).willReturn(mock(CancellableTask.class)); antiBotService.overrideAntiBotStatus(true); // when @@ -186,3 +191,5 @@ public void shouldImmediatelyStartAfterFirstStartup() { } } + + diff --git a/src/test/java/fr/xephi/authme/service/BukkitServiceTest.java b/authme-core/src/test/java/fr/xephi/authme/service/BukkitServiceTest.java similarity index 80% rename from src/test/java/fr/xephi/authme/service/BukkitServiceTest.java rename to authme-core/src/test/java/fr/xephi/authme/service/BukkitServiceTest.java index cecdda92f2..050cc5726f 100644 --- a/src/test/java/fr/xephi/authme/service/BukkitServiceTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/service/BukkitServiceTest.java @@ -1,31 +1,36 @@ package fr.xephi.authme.service; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.AuthMe; import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.events.FailedLoginEvent; +import fr.xephi.authme.platform.SchedulingAdapter; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.PluginSettings; import org.bukkit.Bukkit; import org.bukkit.Server; import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.plugin.PluginManager; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitTask; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.only; import static org.mockito.Mockito.verify; @@ -34,7 +39,8 @@ /** * Test for {@link BukkitService}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class BukkitServiceTest { private BukkitService bukkitService; @@ -49,14 +55,16 @@ public class BukkitServiceTest { private BukkitScheduler scheduler; @Mock private PluginManager pluginManager; + @Mock + private SchedulingAdapter schedulingAdapter; - @Before + @BeforeEach public void constructBukkitService() { ReflectionTestUtils.setField(Bukkit.class, null, "server", server); - given(server.getScheduler()).willReturn(scheduler); - given(server.getPluginManager()).willReturn(pluginManager); - given(settings.getProperty(PluginSettings.USE_ASYNC_TASKS)).willReturn(true); - bukkitService = new BukkitService(authMe, settings); + lenient().when(server.getScheduler()).thenReturn(scheduler); + lenient().when(server.getPluginManager()).thenReturn(pluginManager); + lenient().when(settings.getProperty(PluginSettings.USE_ASYNC_TASKS)).thenReturn(true); + bukkitService = new BukkitService(authMe, settings, schedulingAdapter); } @Test @@ -118,32 +126,59 @@ public void shouldScheduleSyncDelayedTaskWithDelay() { @Test public void shouldScheduleSyncTask() { // given - BukkitService spy = Mockito.spy(bukkitService); - doReturn(1).when(spy).scheduleSyncDelayedTask(any(Runnable.class)); Runnable task = mock(Runnable.class); + given(schedulingAdapter.isGlobalThread()).willReturn(false); // when - spy.scheduleSyncTaskFromOptionallyAsyncTask(task); + bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(task); // then - verify(spy).scheduleSyncDelayedTask(task); + verify(schedulingAdapter).runOnGlobalThread(authMe, task); verifyNoInteractions(task); } @Test public void shouldRunTaskDirectly() { // given - given(server.isPrimaryThread()).willReturn(true); + given(schedulingAdapter.isGlobalThread()).willReturn(true); bukkitService.reload(settings); - BukkitService spy = Mockito.spy(bukkitService); Runnable task = mock(Runnable.class); // when - spy.scheduleSyncTaskFromOptionallyAsyncTask(task); + bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(task); + + // then + verify(task).run(); + verify(schedulingAdapter).isGlobalThread(); + } + + @Test + public void shouldScheduleEntityTask() { + // given + Entity entity = mock(Player.class); + Runnable task = mock(Runnable.class); + given(schedulingAdapter.isOwnedByCurrentThread(entity)).willReturn(false); + + // when + bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(entity, task); + + // then + verify(schedulingAdapter).runOnEntityThread(authMe, entity, task); + verifyNoInteractions(task); + } + + @Test + public void shouldRunEntityTaskDirectly() { + // given + Entity entity = mock(Player.class); + Runnable task = mock(Runnable.class); + given(schedulingAdapter.isOwnedByCurrentThread(entity)).willReturn(true); + + // when + bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(entity, task); // then verify(task).run(); - verify(spy, only()).scheduleSyncTaskFromOptionallyAsyncTask(task); } @Test @@ -317,3 +352,5 @@ public void shouldReturnServerIp() { assertThat(result, equalTo(ip)); } } + + diff --git a/src/test/java/fr/xephi/authme/service/BukkitServiceTestHelper.java b/authme-core/src/test/java/fr/xephi/authme/service/BukkitServiceTestHelper.java similarity index 65% rename from src/test/java/fr/xephi/authme/service/BukkitServiceTestHelper.java rename to authme-core/src/test/java/fr/xephi/authme/service/BukkitServiceTestHelper.java index 9807e4f5b8..0f903b59a6 100644 --- a/src/test/java/fr/xephi/authme/service/BukkitServiceTestHelper.java +++ b/authme-core/src/test/java/fr/xephi/authme/service/BukkitServiceTestHelper.java @@ -1,8 +1,12 @@ package fr.xephi.authme.service; +import org.bukkit.entity.Player; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; /** * Offers utility methods for testing involving a {@link BukkitService} mock. @@ -19,11 +23,17 @@ private BukkitServiceTestHelper() { * @param bukkitService the mock to set behavior on */ public static void setBukkitServiceToScheduleSyncTaskFromOptionallyAsyncTask(BukkitService bukkitService) { - doAnswer(invocation -> { + lenient().doAnswer(invocation -> { Runnable runnable = invocation.getArgument(0); runnable.run(); return null; }).when(bukkitService).scheduleSyncTaskFromOptionallyAsyncTask(any(Runnable.class)); + + lenient().doAnswer(invocation -> { + Runnable runnable = invocation.getArgument(1); + runnable.run(); + return null; + }).when(bukkitService).scheduleSyncTaskFromOptionallyAsyncTask(any(Player.class), any(Runnable.class)); } /** @@ -33,7 +43,7 @@ public static void setBukkitServiceToScheduleSyncTaskFromOptionallyAsyncTask(Buk * @param bukkitService the mock to set behavior on */ public static void setBukkitServiceToRunTaskAsynchronously(BukkitService bukkitService) { - doAnswer(invocation -> { + lenient().doAnswer(invocation -> { Runnable runnable = invocation.getArgument(0); runnable.run(); return null; @@ -47,7 +57,7 @@ public static void setBukkitServiceToRunTaskAsynchronously(BukkitService bukkitS * @param bukkitService the mock to set behavior on */ public static void setBukkitServiceToRunTaskOptionallyAsync(BukkitService bukkitService) { - doAnswer(invocation -> { + lenient().doAnswer(invocation -> { Runnable runnable = invocation.getArgument(0); runnable.run(); return null; @@ -61,7 +71,7 @@ public static void setBukkitServiceToRunTaskOptionallyAsync(BukkitService bukkit * @param bukkitService the mock to set behavior on */ public static void setBukkitServiceToScheduleSyncDelayedTask(BukkitService bukkitService) { - doAnswer(invocation -> { + lenient().doAnswer(invocation -> { Runnable runnable = invocation.getArgument(0); runnable.run(); return null; @@ -75,10 +85,32 @@ public static void setBukkitServiceToScheduleSyncDelayedTask(BukkitService bukki * @param bukkitService the mock to set behavior on */ public static void setBukkitServiceToScheduleSyncDelayedTaskWithDelay(BukkitService bukkitService) { - doAnswer(invocation -> { + lenient().doAnswer(invocation -> { Runnable runnable = invocation.getArgument(0); runnable.run(); return null; }).when(bukkitService).scheduleSyncDelayedTask(any(Runnable.class), anyLong()); + + lenient().doAnswer(invocation -> { + Runnable runnable = invocation.getArgument(1); + runnable.run(); + return null; + }).when(bukkitService).scheduleSyncDelayedTask(any(Player.class), any(Runnable.class), anyLong()); + } + + public static void setBukkitServiceToRunOnGlobalRegion(BukkitService bukkitService) { + lenient().doAnswer(invocation -> { + Runnable runnable = invocation.getArgument(0); + runnable.run(); + return null; + }).when(bukkitService).runOnGlobalRegion(any(Runnable.class)); + } + + public static void setBukkitServiceToRunTaskLaterOnGlobalRegion(BukkitService bukkitService) { + lenient().doAnswer(invocation -> { + Runnable runnable = invocation.getArgument(0); + runnable.run(); + return mock(CancellableTask.class); + }).when(bukkitService).runTaskLaterOnGlobalRegion(any(Runnable.class), anyLong()); } } diff --git a/src/test/java/fr/xephi/authme/service/CommonServiceTest.java b/authme-core/src/test/java/fr/xephi/authme/service/CommonServiceTest.java similarity index 90% rename from src/test/java/fr/xephi/authme/service/CommonServiceTest.java rename to authme-core/src/test/java/fr/xephi/authme/service/CommonServiceTest.java index 309e996260..9a5079b566 100644 --- a/src/test/java/fr/xephi/authme/service/CommonServiceTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/service/CommonServiceTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.service; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.Messages; import fr.xephi.authme.permission.PermissionNode; @@ -9,11 +13,9 @@ import fr.xephi.authme.settings.properties.SecuritySettings; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; @@ -24,7 +26,8 @@ /** * Test for {@link CommonService}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class CommonServiceTest { @InjectMocks @@ -110,3 +113,5 @@ public void shouldCheckPermission() { assertThat(result, equalTo(true)); } } + + diff --git a/src/test/java/fr/xephi/authme/service/GeoIpServiceTest.java b/authme-core/src/test/java/fr/xephi/authme/service/GeoIpServiceTest.java similarity index 90% rename from src/test/java/fr/xephi/authme/service/GeoIpServiceTest.java rename to authme-core/src/test/java/fr/xephi/authme/service/GeoIpServiceTest.java index 329f973414..c726b6b234 100644 --- a/src/test/java/fr/xephi/authme/service/GeoIpServiceTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/service/GeoIpServiceTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.service; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import java.io.File; import java.io.IOException; import java.net.InetAddress; @@ -14,13 +18,10 @@ import com.maxmind.geoip2.record.Country; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.ProtectionSettings; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import fr.xephi.authme.TempFolder; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -35,7 +36,8 @@ /** * Test for {@link GeoIpService}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class GeoIpServiceTest { private GeoIpService geoIpService; @@ -48,11 +50,9 @@ public class GeoIpServiceTest { @Mock private Settings settings; + public TempFolder temporaryFolder = new TempFolder(); - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); - - @Before + @BeforeEach public void initializeGeoLiteApi() throws IOException { File dataFolder = temporaryFolder.newFolder(); geoIpService = new GeoIpService(dataFolder, bukkitService, settings, lookupService); @@ -141,3 +141,5 @@ private static CountryResponse createCountryResponse(String countryCode, String return new CountryResponse(continent, country, null, country, null, null); } } + + diff --git a/src/test/java/fr/xephi/authme/service/HelpTranslationGeneratorIntegrationTest.java b/authme-core/src/test/java/fr/xephi/authme/service/HelpTranslationGeneratorIntegrationTest.java similarity index 95% rename from src/test/java/fr/xephi/authme/service/HelpTranslationGeneratorIntegrationTest.java rename to authme-core/src/test/java/fr/xephi/authme/service/HelpTranslationGeneratorIntegrationTest.java index 0b4329e511..9d66eec3c1 100644 --- a/src/test/java/fr/xephi/authme/service/HelpTranslationGeneratorIntegrationTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/service/HelpTranslationGeneratorIntegrationTest.java @@ -1,7 +1,8 @@ package fr.xephi.authme.service; +import org.junit.jupiter.api.extension.ExtendWith; +import fr.xephi.authme.DelayedInjectionExtension; import ch.jalu.injector.testing.BeforeInjecting; -import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.InjectDelayed; import com.google.common.io.Files; import fr.xephi.authme.TestHelper; @@ -16,11 +17,9 @@ import org.bukkit.configuration.MemorySection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import fr.xephi.authme.TempFolder; import org.mockito.Mock; import java.io.File; @@ -30,13 +29,13 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.BDDMockito.given; /** * Integration test for {@link HelpTranslationGenerator}. */ -@RunWith(DelayedInjectionRunner.class) +@ExtendWith(DelayedInjectionExtension.class) public class HelpTranslationGeneratorIntegrationTest { @InjectDelayed @@ -54,11 +53,9 @@ public class HelpTranslationGeneratorIntegrationTest { @Mock private Settings settings; + public TempFolder temporaryFolder = new TempFolder(); - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); - - @BeforeClass + @BeforeAll public static void setUpLogger() { TestHelper.setupLogger(); } @@ -184,3 +181,5 @@ private static final class Argument { } } } + + diff --git a/src/test/java/fr/xephi/authme/service/MigrationServiceTest.java b/authme-core/src/test/java/fr/xephi/authme/service/MigrationServiceTest.java similarity index 92% rename from src/test/java/fr/xephi/authme/service/MigrationServiceTest.java rename to authme-core/src/test/java/fr/xephi/authme/service/MigrationServiceTest.java index e43a0449fa..fcfddde994 100644 --- a/src/test/java/fr/xephi/authme/service/MigrationServiceTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/service/MigrationServiceTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.service; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; @@ -8,11 +12,9 @@ import fr.xephi.authme.security.crypts.Sha256; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.SecuritySettings; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import java.util.Locale; @@ -31,7 +33,8 @@ /** * Test for {@link MigrationService}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class MigrationServiceTest { @Mock @@ -43,7 +46,7 @@ public class MigrationServiceTest { @Mock private Sha256 sha256; - @BeforeClass + @BeforeAll public static void setUpLogger() { TestHelper.setupLogger(); } @@ -123,3 +126,5 @@ private static void setSha256MockToUppercase(Sha256 sha256) { }); } } + + diff --git a/src/test/java/fr/xephi/authme/service/PasswordRecoveryServiceTest.java b/authme-core/src/test/java/fr/xephi/authme/service/PasswordRecoveryServiceTest.java similarity index 95% rename from src/test/java/fr/xephi/authme/service/PasswordRecoveryServiceTest.java rename to authme-core/src/test/java/fr/xephi/authme/service/PasswordRecoveryServiceTest.java index a89885a03b..d6ced51ea4 100644 --- a/src/test/java/fr/xephi/authme/service/PasswordRecoveryServiceTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/service/PasswordRecoveryServiceTest.java @@ -1,7 +1,8 @@ package fr.xephi.authme.service; +import org.junit.jupiter.api.extension.ExtendWith; +import fr.xephi.authme.DelayedInjectionExtension; import ch.jalu.injector.testing.BeforeInjecting; -import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.InjectDelayed; import fr.xephi.authme.TestHelper; import fr.xephi.authme.datasource.DataSource; @@ -11,8 +12,7 @@ import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.settings.properties.SecuritySettings; import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.Mock; import static org.hamcrest.Matchers.equalTo; @@ -24,7 +24,7 @@ /** * Tests for {@link PasswordRecoveryService}. */ -@RunWith(DelayedInjectionRunner.class) +@ExtendWith(DelayedInjectionExtension.class) public class PasswordRecoveryServiceTest { @InjectDelayed @@ -120,3 +120,5 @@ public void shouldRemovePlayerFromSuccessfulRecovers() { assertThat(recoveryService.canChangePassword(other), equalTo(false)); } } + + diff --git a/src/test/java/fr/xephi/authme/service/PluginHookServiceTest.java b/authme-core/src/test/java/fr/xephi/authme/service/PluginHookServiceTest.java similarity index 96% rename from src/test/java/fr/xephi/authme/service/PluginHookServiceTest.java rename to authme-core/src/test/java/fr/xephi/authme/service/PluginHookServiceTest.java index ef1d019f4b..b3e43c6950 100644 --- a/src/test/java/fr/xephi/authme/service/PluginHookServiceTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/service/PluginHookServiceTest.java @@ -5,16 +5,15 @@ import com.onarandombox.MultiverseCore.MultiverseCore; import com.onarandombox.MultiverseCore.api.MVWorldManager; import com.onarandombox.MultiverseCore.api.MultiverseWorld; -import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.TestHelper; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; -import org.bukkit.plugin.java.JavaPlugin; -import org.junit.BeforeClass; -import org.junit.Test; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import java.io.File; @@ -40,7 +39,7 @@ public class PluginHookServiceTest { /** The plugin name of Multiverse-Core. */ private static final String MULTIVERSE = "Multiverse-Core"; - @BeforeClass + @BeforeAll public static void setLogger() { TestHelper.setupLogger(); } @@ -104,8 +103,8 @@ public void shouldReturnEssentialsDataFolder() { // given Essentials ess = mock(Essentials.class); File essDataFolder = new File("test/data-folder"); - // Need to set the data folder with reflections because getDataFolder() is declared final - ReflectionTestUtils.setField(JavaPlugin.class, ess, "dataFolder", essDataFolder); + // Need to stub getDataFolder() because getDataFolder() is declared final + given(ess.getDataFolder()).willReturn(essDataFolder); PluginManager pluginManager = mock(PluginManager.class); setPluginAvailable(pluginManager, ESSENTIALS, ess); @@ -269,3 +268,4 @@ private static void setPluginAvailable(PluginManager managerM } } + diff --git a/src/test/java/fr/xephi/authme/service/RecoveryCodeServiceTest.java b/authme-core/src/test/java/fr/xephi/authme/service/RecoveryCodeServiceTest.java similarity index 95% rename from src/test/java/fr/xephi/authme/service/RecoveryCodeServiceTest.java rename to authme-core/src/test/java/fr/xephi/authme/service/RecoveryCodeServiceTest.java index 0ec71f5ca0..59faa6fda3 100644 --- a/src/test/java/fr/xephi/authme/service/RecoveryCodeServiceTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/service/RecoveryCodeServiceTest.java @@ -1,14 +1,14 @@ package fr.xephi.authme.service; +import org.junit.jupiter.api.extension.ExtendWith; +import fr.xephi.authme.DelayedInjectionExtension; import ch.jalu.injector.testing.BeforeInjecting; -import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.InjectDelayed; import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.util.expiring.ExpiringMap; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.Mock; import static fr.xephi.authme.AuthMeMatchers.stringWithLength; @@ -20,7 +20,7 @@ /** * Test for {@link RecoveryCodeService}. */ -@RunWith(DelayedInjectionRunner.class) +@ExtendWith(DelayedInjectionExtension.class) public class RecoveryCodeServiceTest { @InjectDelayed @@ -129,3 +129,5 @@ private ExpiringMap getTriesCounter() { return ReflectionTestUtils.getFieldValue(RecoveryCodeService.class, recoveryCodeService, "playerTries"); } } + + diff --git a/src/test/java/fr/xephi/authme/service/SessionServiceTest.java b/authme-core/src/test/java/fr/xephi/authme/service/SessionServiceTest.java similarity index 97% rename from src/test/java/fr/xephi/authme/service/SessionServiceTest.java rename to authme-core/src/test/java/fr/xephi/authme/service/SessionServiceTest.java index f67b51854b..c1c241224f 100644 --- a/src/test/java/fr/xephi/authme/service/SessionServiceTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/service/SessionServiceTest.java @@ -1,7 +1,8 @@ package fr.xephi.authme.service; +import org.junit.jupiter.api.extension.ExtendWith; +import fr.xephi.authme.DelayedInjectionExtension; import ch.jalu.injector.testing.BeforeInjecting; -import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.InjectDelayed; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerAuth; @@ -10,9 +11,8 @@ import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.settings.properties.PluginSettings; import org.bukkit.entity.Player; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.Mock; import java.util.function.Function; @@ -31,7 +31,7 @@ /** * Test for {@link SessionService}. */ -@RunWith(DelayedInjectionRunner.class) +@ExtendWith(DelayedInjectionExtension.class) public class SessionServiceTest { @InjectDelayed @@ -44,7 +44,7 @@ public class SessionServiceTest { @Mock private BukkitService bukkitService; - @BeforeClass + @BeforeAll public static void initLogger() { TestHelper.setupLogger(); } @@ -244,3 +244,5 @@ private static Player mockPlayerWithNameAndIp(String name, String ip) { return player; } } + + diff --git a/src/test/java/fr/xephi/authme/service/TeleportationServiceTest.java b/authme-core/src/test/java/fr/xephi/authme/service/TeleportationServiceTest.java similarity index 66% rename from src/test/java/fr/xephi/authme/service/TeleportationServiceTest.java rename to authme-core/src/test/java/fr/xephi/authme/service/TeleportationServiceTest.java index 13125b45c2..293f450380 100644 --- a/src/test/java/fr/xephi/authme/service/TeleportationServiceTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/service/TeleportationServiceTest.java @@ -1,11 +1,16 @@ package fr.xephi.authme.service; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.data.limbo.LimboPlayer; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.events.FirstSpawnTeleportEvent; import fr.xephi.authme.events.SpawnTeleportEvent; +import fr.xephi.authme.platform.PlatformAdapter; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.SpawnLoader; import fr.xephi.authme.settings.properties.RegistrationSettings; @@ -13,13 +18,12 @@ import org.bukkit.Location; import org.bukkit.World; import org.bukkit.entity.Player; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; @@ -27,7 +31,9 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -38,7 +44,8 @@ /** * Test for {@link TeleportationService}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class TeleportationServiceTest { @InjectMocks @@ -59,14 +66,19 @@ public class TeleportationServiceTest { @Mock private DataSource dataSource; - @Before + @Mock + private PlatformAdapter platformAdapter; + @Captor + private ArgumentCaptor locationCaptor; + + @BeforeEach public void setUpForcedWorlds() { - given(settings.getProperty(RestrictionSettings.FORCE_SPAWN_ON_WORLDS)) - .willReturn(Arrays.asList("forced1", "OtherForced")); + lenient().when(settings.getProperty(RestrictionSettings.FORCE_SPAWN_ON_WORLDS)) + .thenReturn(Arrays.asList("forced1", "OtherForced")); teleportationService.reload(); - given(settings.getProperty(RestrictionSettings.NO_TELEPORT)).willReturn(false); - given(settings.getProperty(RegistrationSettings.FORCE)).willReturn(true); + lenient().when(settings.getProperty(RestrictionSettings.NO_TELEPORT)).thenReturn(false); + lenient().when(settings.getProperty(RegistrationSettings.FORCE)).thenReturn(true); } // ----------- @@ -100,7 +112,7 @@ public void shouldTeleportPlayerToFirstSpawn() { teleportationService.teleportNewPlayerToFirstSpawn(player); // then - verify(player).teleport(firstSpawn); + verify(platformAdapter).teleportPlayer(player, firstSpawn); verify(bukkitService).callEvent(any(FirstSpawnTeleportEvent.class)); verify(spawnLoader).getFirstSpawn(); verify(spawnLoader, never()).getSpawnLocation(any(Player.class)); @@ -120,7 +132,7 @@ public void shouldTeleportPlayerToSpawn() { teleportationService.teleportOnJoin(player); // then - verify(player).teleport(spawn); + verify(platformAdapter).teleportPlayer(player, spawn); verify(bukkitService).callEvent(any(SpawnTeleportEvent.class)); verify(spawnLoader).getSpawnLocation(player); } @@ -136,7 +148,7 @@ public void shouldNotTeleportNewPlayer() { teleportationService.teleportNewPlayerToFirstSpawn(player); // then - verify(player, never()).teleport(any(Location.class)); + verify(platformAdapter, never()).teleportPlayer(eq(player), any(Location.class)); verify(spawnLoader).getFirstSpawn(); verify(spawnLoader, never()).getSpawnLocation(any(Player.class)); verifyNoInteractions(bukkitService); @@ -152,7 +164,7 @@ public void shouldNotTeleportPlayerToFirstSpawnIfNoTeleportEnabled() { teleportationService.teleportNewPlayerToFirstSpawn(player); // then - verify(player, never()).teleport(any(Location.class)); + verify(platformAdapter, never()).teleportPlayer(eq(player), any(Location.class)); verifyNoInteractions(bukkitService); } @@ -166,7 +178,7 @@ public void shouldNotTeleportNotNewPlayerToFirstSpawn() { teleportationService.teleportNewPlayerToFirstSpawn(player); // then - verify(player, never()).teleport(any(Location.class)); + verify(platformAdapter, never()).teleportPlayer(eq(player), any(Location.class)); verifyNoInteractions(bukkitService); } @@ -190,7 +202,7 @@ public void shouldNotTeleportPlayerForRemovedLocationInEvent() { // then verify(bukkitService).callEvent(any(SpawnTeleportEvent.class)); - verify(player, never()).teleport(any(Location.class)); + verify(platformAdapter, never()).teleportPlayer(eq(player), any(Location.class)); } @Test @@ -213,7 +225,7 @@ public void shouldNotTeleportPlayerForCanceledEvent() { // then verify(bukkitService).callEvent(any(SpawnTeleportEvent.class)); - verify(player, never()).teleport(any(Location.class)); + verify(platformAdapter, never()).teleportPlayer(eq(player), any(Location.class)); } @Test @@ -230,7 +242,7 @@ public void shouldNotTeleportUnregisteredPlayerOnJoinIfRegistrationOptional() { // then verifyNoInteractions(bukkitService, spawnLoader); - verify(player, never()).teleport(any(Location.class)); + verify(platformAdapter, never()).teleportPlayer(eq(player), any(Location.class)); } @Test @@ -249,6 +261,62 @@ public void shouldNotTeleportUnregisteredPlayerViaPrepareOnJoinIfRegistrationOpt assertThat(result, equalTo(null)); } + @Test + public void shouldPrepareCustomSpawnLocationWithoutPlayerInstance() { + // given + given(settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN)).willReturn(true); + World world = mock(World.class); + Location spawn = mockLocation(); + given(spawnLoader.getSpawnLocation(world)).willReturn(spawn); + + // when + Location result = teleportationService.prepareOnJoinSpawnLocation("Bobby", world); + + // then + assertThat(result, equalTo(spawn)); + verify(spawnLoader).getSpawnLocation(world); + verifyNoInteractions(bukkitService); + } + + @Test + public void shouldRememberOriginalJoinLocationForAsyncJoinEvent() { + // given + given(settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN)).willReturn(true); + World world = mock(World.class); + Location originalSpawn = new Location(world, 1.0, 64.0, 1.0); + Location spawn = mockLocation(); + given(spawnLoader.getSpawnLocation(world)).willReturn(spawn); + + // when + Location result = teleportationService.prepareOnJoinSpawnLocation("Bobby", originalSpawn); + + // then + assertThat(result, equalTo(spawn)); + assertThat(teleportationService.consumeOriginalJoinLocation("Bobby", null), equalTo(originalSpawn)); + } + + @Test + public void shouldUseModifiedSpawnLocationFromJoinEvent() { + // given + given(settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN)).willReturn(true); + Player player = mock(Player.class); + given(player.getName()).willReturn("Bobby"); + Location originalSpawn = mockLocation(); + Location modifiedSpawn = mockLocation(); + given(spawnLoader.getSpawnLocation(player)).willReturn(originalSpawn); + doAnswer(invocation -> { + SpawnTeleportEvent event = invocation.getArgument(0); + event.setTo(modifiedSpawn); + return null; + }).when(bukkitService).callEvent(any(SpawnTeleportEvent.class)); + + // when + Location result = teleportationService.prepareOnJoinSpawnLocation(player); + + // then + assertThat(result, equalTo(modifiedSpawn)); + } + // --------- // LOGIN // --------- @@ -286,7 +354,7 @@ public void shouldTeleportPlayerToSpawnAfterLogin() { teleportationService.teleportOnLogin(player, auth, limbo); // then - verify(player).teleport(spawn); + verify(platformAdapter).teleportPlayer(player, spawn); } @Test @@ -307,7 +375,7 @@ public void shouldNotTeleportToSpawnForOtherCaseInWorld() { teleportationService.teleportOnLogin(player, auth, limbo); // then - verify(player, never()).teleport(spawn); + verify(platformAdapter, never()).teleportPlayer(eq(player), eq(spawn)); verifyNoInteractions(bukkitService, spawnLoader); } @@ -333,8 +401,7 @@ public void shouldTeleportBackToPlayerAuthLocation() { teleportationService.teleportOnLogin(player, auth, limbo); // then - ArgumentCaptor locationCaptor = ArgumentCaptor.forClass(Location.class); - verify(player).teleport(locationCaptor.capture()); + verify(platformAdapter).teleportPlayer(eq(player), locationCaptor.capture()); assertCorrectLocation(locationCaptor.getValue(), auth, world); } @@ -361,11 +428,84 @@ public void shouldTeleportAccordingToPlayerAuthAndPlayerWorldAsFallback() { teleportationService.teleportOnLogin(player, auth, limbo); // then - ArgumentCaptor locationCaptor = ArgumentCaptor.forClass(Location.class); - verify(player).teleport(locationCaptor.capture()); + verify(platformAdapter).teleportPlayer(eq(player), locationCaptor.capture()); assertCorrectLocation(locationCaptor.getValue(), auth, world); } + @Test + public void shouldNotTeleportIfPlayerIsAlreadyAtQuitLocation() { + // given + given(settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN)).willReturn(true); + given(settings.getProperty(RestrictionSettings.SAVE_QUIT_LOCATION)).willReturn(true); + + PlayerAuth auth = createAuthWithLocation(); + auth.setWorld("myWorld"); + World world = mock(World.class); + given(bukkitService.getWorld("myWorld")).willReturn(world); + + Player player = mock(Player.class); + given(player.getLocation()).willReturn(new Location(world, auth.getQuitLocX(), auth.getQuitLocY(), + auth.getQuitLocZ(), 90.0f, 45.0f)); + + // when + teleportationService.teleportOnLogin(player, auth, null); + + // then + verify(bukkitService).getWorld("myWorld"); + verify(player).getLocation(); + verifyNoInteractions(platformAdapter, spawnLoader); + } + + @Test + public void shouldUseRememberedJoinLocationIfQuitLocationIsStillAtSchemaDefaults() { + // given + given(settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN)).willReturn(true); + given(settings.getProperty(RestrictionSettings.SAVE_QUIT_LOCATION)).willReturn(true); + + PlayerAuth auth = PlayerAuth.builder() + .name("bobby") + .build(); + Player player = mock(Player.class); + given(player.getName()).willReturn("Bobby"); + given(player.isOnline()).willReturn(true); + World world = mock(World.class); + Location rememberedJoinLocation = new Location(world, 12.0, 70.0, -8.0); + teleportationService.rememberOriginalJoinLocation("Bobby", rememberedJoinLocation); + setBukkitServiceToScheduleSyncTaskFromOptionallyAsyncTask(bukkitService); + + // when + teleportationService.teleportOnLogin(player, auth, null); + + // then + verify(platformAdapter).teleportPlayer(eq(player), locationCaptor.capture()); + assertThat(locationCaptor.getValue(), equalTo(rememberedJoinLocation)); + verifyNoInteractions(spawnLoader); + } + + @Test + public void shouldUseLimboLocationIfQuitLocationIsStillAtSchemaDefaults() { + // given + given(settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN)).willReturn(true); + given(settings.getProperty(RestrictionSettings.SAVE_QUIT_LOCATION)).willReturn(true); + + PlayerAuth auth = PlayerAuth.builder() + .name("bobby") + .build(); + Player player = mock(Player.class); + given(player.isOnline()).willReturn(true); + LimboPlayer limbo = mock(LimboPlayer.class); + Location limboLocation = mockLocation(); + given(limbo.getLocation()).willReturn(limboLocation); + setBukkitServiceToScheduleSyncTaskFromOptionallyAsyncTask(bukkitService); + + // when + teleportationService.teleportOnLogin(player, auth, limbo); + + // then + verify(platformAdapter).teleportPlayer(player, limboLocation); + verifyNoInteractions(spawnLoader); + } + @Test public void shouldTeleportWithLimboPlayerIfSaveQuitLocIsDisabled() { // given @@ -384,7 +524,7 @@ public void shouldTeleportWithLimboPlayerIfSaveQuitLocIsDisabled() { teleportationService.teleportOnLogin(player, auth, limbo); // then - verify(player).teleport(location); + verify(platformAdapter).teleportPlayer(player, location); } @Test @@ -401,8 +541,8 @@ public void shouldNotTeleportForNullLocationInLimboPlayer() { teleportationService.teleportOnLogin(player, auth, limbo); // then - verifyNoInteractions(player); - verify(limbo, times(2)).getLocation(); + verify(player).getName(); + verify(limbo).getLocation(); } private static void assertCorrectLocation(Location location, PlayerAuth auth, World world) { @@ -414,9 +554,7 @@ private static void assertCorrectLocation(Location location, PlayerAuth auth, Wo // We check that the World in Location is set, this method creates a mock World in Location for us private static Location mockLocation() { - Location location = mock(Location.class); - given(location.getWorld()).willReturn(mock(World.class)); - return location; + return new Location(mock(World.class), 0.0, 0.0, 0.0); } private static PlayerAuth createAuthWithLocation() { @@ -427,3 +565,5 @@ private static PlayerAuth createAuthWithLocation() { } } + + diff --git a/src/test/java/fr/xephi/authme/service/ValidationServiceTest.java b/authme-core/src/test/java/fr/xephi/authme/service/ValidationServiceTest.java similarity index 98% rename from src/test/java/fr/xephi/authme/service/ValidationServiceTest.java rename to authme-core/src/test/java/fr/xephi/authme/service/ValidationServiceTest.java index 7ca60c0a57..51e5d80565 100644 --- a/src/test/java/fr/xephi/authme/service/ValidationServiceTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/service/ValidationServiceTest.java @@ -1,7 +1,8 @@ package fr.xephi.authme.service; +import org.junit.jupiter.api.extension.ExtendWith; +import fr.xephi.authme.DelayedInjectionExtension; import ch.jalu.injector.testing.BeforeInjecting; -import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.InjectDelayed; import com.google.common.base.Strings; import fr.xephi.authme.TestHelper; @@ -17,9 +18,9 @@ import fr.xephi.authme.settings.properties.SecuritySettings; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.Mockito; @@ -42,7 +43,7 @@ /** * Test for {@link ValidationService}. */ -@RunWith(DelayedInjectionRunner.class) +@ExtendWith(DelayedInjectionExtension.class) public class ValidationServiceTest { @InjectDelayed @@ -55,6 +56,8 @@ public class ValidationServiceTest { private PermissionsManager permissionsManager; @Mock private GeoIpService geoIpService; + @Captor + private ArgumentCaptor stringCaptor; @BeforeInjecting public void createService() { @@ -403,7 +406,6 @@ public void shouldLogWarningForInvalidRestrictionRule() { validationService.reload(); // then - ArgumentCaptor stringCaptor = ArgumentCaptor.forClass(String.class); verify(logger).warning(stringCaptor.capture()); assertThat(stringCaptor.getValue(), containsString("Tamara;")); } @@ -421,3 +423,5 @@ private static void assertErrorEquals(ValidationResult validationResult, Message assertThat(validationResult.getArgs(), equalTo(args)); } } + + diff --git a/src/test/java/fr/xephi/authme/service/yaml/YamlFileResourceProviderTest.java b/authme-core/src/test/java/fr/xephi/authme/service/yaml/YamlFileResourceProviderTest.java similarity index 93% rename from src/test/java/fr/xephi/authme/service/yaml/YamlFileResourceProviderTest.java rename to authme-core/src/test/java/fr/xephi/authme/service/yaml/YamlFileResourceProviderTest.java index 97dcd99e34..ffc42baa55 100644 --- a/src/test/java/fr/xephi/authme/service/yaml/YamlFileResourceProviderTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/service/yaml/YamlFileResourceProviderTest.java @@ -2,7 +2,7 @@ import ch.jalu.configme.resource.YamlFileResource; import fr.xephi.authme.TestHelper; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.yaml.snakeyaml.parser.ParserException; import java.io.File; @@ -10,7 +10,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; /** * Test for {@link YamlFileResourceProvider}. @@ -46,3 +46,4 @@ public void shouldThrowForInvalidFile() { } } } + diff --git a/src/test/java/fr/xephi/authme/settings/SettingsConsistencyTest.java b/authme-core/src/test/java/fr/xephi/authme/settings/SettingsConsistencyTest.java similarity index 97% rename from src/test/java/fr/xephi/authme/settings/SettingsConsistencyTest.java rename to authme-core/src/test/java/fr/xephi/authme/settings/SettingsConsistencyTest.java index f589e6c33a..e6d26482a5 100644 --- a/src/test/java/fr/xephi/authme/settings/SettingsConsistencyTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/settings/SettingsConsistencyTest.java @@ -6,8 +6,8 @@ import com.google.common.collect.ImmutableSet; import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever; import fr.xephi.authme.settings.properties.SecuritySettings; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Arrays; @@ -18,7 +18,7 @@ import java.util.stream.Collectors; import static fr.xephi.authme.ReflectionTestUtils.getFieldValue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; /** * Tests the consistency of the settings configuration. @@ -38,7 +38,7 @@ public class SettingsConsistencyTest { private static ConfigurationData configurationData; - @BeforeClass + @BeforeAll public static void buildConfigurationData() { configurationData = AuthMeSettingsRetriever.buildConfigurationData(); } @@ -138,3 +138,4 @@ private static boolean isDeprecated(Enum enumValue) { } } } + diff --git a/src/test/java/fr/xephi/authme/settings/SettingsIntegrationTest.java b/authme-core/src/test/java/fr/xephi/authme/settings/SettingsIntegrationTest.java similarity index 96% rename from src/test/java/fr/xephi/authme/settings/SettingsIntegrationTest.java rename to authme-core/src/test/java/fr/xephi/authme/settings/SettingsIntegrationTest.java index 17e4149435..760139ddbd 100644 --- a/src/test/java/fr/xephi/authme/settings/SettingsIntegrationTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/settings/SettingsIntegrationTest.java @@ -11,11 +11,10 @@ import fr.xephi.authme.TestHelper; import fr.xephi.authme.settings.properties.TestConfiguration; import fr.xephi.authme.settings.properties.TestEnum; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import fr.xephi.authme.TempFolder; import java.io.File; import java.io.IOException; @@ -39,18 +38,16 @@ public class SettingsIntegrationTest { private static ConfigurationData CONFIG_DATA = ConfigurationDataBuilder.createConfiguration(TestConfiguration.class); - - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); + public TempFolder temporaryFolder = new TempFolder(); private File testPluginFolder; - @BeforeClass + @BeforeAll public static void setUpLogger() { TestHelper.setupLogger(); } - @Before + @BeforeEach public void setUpTestPluginFolder() throws IOException { testPluginFolder = temporaryFolder.newFolder(); } @@ -144,3 +141,5 @@ private File copyFileFromResources(String path) { } } + + diff --git a/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java b/authme-core/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java similarity index 97% rename from src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java rename to authme-core/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java index d1d7b8bf85..cd5b72e377 100644 --- a/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java @@ -12,10 +12,9 @@ import fr.xephi.authme.process.register.RegistrationType; import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import fr.xephi.authme.TempFolder; import java.io.File; import java.io.IOException; @@ -47,11 +46,9 @@ public class SettingsMigrationServiceTest { private static final String OLD_CONFIG_FILE = TestHelper.PROJECT_ROOT + "settings/config-old.yml"; + public TempFolder temporaryFolder = new TempFolder(); - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); - - @BeforeClass + @BeforeAll public static void setUpLogger() { TestHelper.setupLogger(); } @@ -153,3 +150,5 @@ protected boolean performMigrations(PropertyReader reader, ConfigurationData con } } } + + diff --git a/src/test/java/fr/xephi/authme/settings/SettingsTest.java b/authme-core/src/test/java/fr/xephi/authme/settings/SettingsTest.java similarity index 93% rename from src/test/java/fr/xephi/authme/settings/SettingsTest.java rename to authme-core/src/test/java/fr/xephi/authme/settings/SettingsTest.java index fa78014a64..962f3b1ab3 100644 --- a/src/test/java/fr/xephi/authme/settings/SettingsTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/settings/SettingsTest.java @@ -6,11 +6,10 @@ import ch.jalu.configme.resource.PropertyResource; import fr.xephi.authme.TestHelper; import fr.xephi.authme.settings.properties.TestConfiguration; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import fr.xephi.authme.TempFolder; import java.io.File; import java.io.IOException; @@ -31,17 +30,15 @@ public class SettingsTest { private static final ConfigurationData CONFIG_DATA = ConfigurationDataBuilder.createConfiguration(TestConfiguration.class); - - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); + public TempFolder temporaryFolder = new TempFolder(); private File testPluginFolder; - @BeforeClass + @BeforeAll public static void setUpLogger() { TestHelper.setupLogger(); } - @Before + @BeforeEach public void setUpTestPluginFolder() throws IOException { testPluginFolder = temporaryFolder.newFolder(); } @@ -118,3 +115,5 @@ private static void createFile(File file) { } } + + diff --git a/src/test/java/fr/xephi/authme/settings/SettingsWarnerTest.java b/authme-core/src/test/java/fr/xephi/authme/settings/SettingsWarnerTest.java similarity index 91% rename from src/test/java/fr/xephi/authme/settings/SettingsWarnerTest.java rename to authme-core/src/test/java/fr/xephi/authme/settings/SettingsWarnerTest.java index fbea9f9afd..6823108cc6 100644 --- a/src/test/java/fr/xephi/authme/settings/SettingsWarnerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/settings/SettingsWarnerTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.settings; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.AuthMe; import fr.xephi.authme.TestHelper; import fr.xephi.authme.security.HashAlgorithm; @@ -9,11 +13,9 @@ import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.settings.properties.SecuritySettings; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Optional; import java.util.logging.Logger; @@ -22,12 +24,13 @@ import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.internal.verification.VerificationModeFactory.times; +import static org.mockito.Mockito.times; /** * Test for {@link SettingsWarner}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class SettingsWarnerTest { @InjectMocks @@ -97,3 +100,5 @@ public void shouldNotLogAnyWarning() { verifyNoInteractions(logger); } } + + diff --git a/authme-core/src/test/java/fr/xephi/authme/settings/SpawnLoaderTest.java b/authme-core/src/test/java/fr/xephi/authme/settings/SpawnLoaderTest.java new file mode 100644 index 0000000000..46b8d2cef9 --- /dev/null +++ b/authme-core/src/test/java/fr/xephi/authme/settings/SpawnLoaderTest.java @@ -0,0 +1,152 @@ +package fr.xephi.authme.settings; + +import org.junit.jupiter.api.extension.ExtendWith; +import fr.xephi.authme.DelayedInjectionExtension; +import ch.jalu.injector.testing.BeforeInjecting; +import ch.jalu.injector.testing.InjectDelayed; +import com.google.common.io.Files; +import fr.xephi.authme.TestHelper; +import fr.xephi.authme.initialization.DataFolder; +import fr.xephi.authme.platform.TeleportAdapter; +import fr.xephi.authme.service.PluginHookService; +import fr.xephi.authme.settings.properties.RestrictionSettings; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.configuration.file.YamlConfiguration; +import org.junit.jupiter.api.Test; +import fr.xephi.authme.TempFolder; +import org.mockito.Mock; +import org.mockito.MockedStatic; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; + +/** + * Test for {@link SpawnLoader}. + */ +@ExtendWith(DelayedInjectionExtension.class) +public class SpawnLoaderTest { + + @InjectDelayed + private SpawnLoader spawnLoader; + + @Mock + private Settings settings; + + @Mock + private PluginHookService pluginHookService; + + @Mock + private TeleportAdapter teleportAdapter; + public TempFolder temporaryFolder = new TempFolder(); + + @DataFolder + private File testFolder; + + @BeforeInjecting + public void setup() throws IOException { + TestHelper.setupLogger(); + + // Copy test config into a new temporary folder + testFolder = temporaryFolder.newFolder(); + File source = TestHelper.getJarFile(TestHelper.PROJECT_ROOT + "settings/spawn-firstspawn.yml"); + File destination = new File(testFolder, "spawn.yml"); + Files.copy(source, destination); + + // Create a settings mock with default values + given(settings.getProperty(RestrictionSettings.SPAWN_PRIORITY)) + .willReturn("authme, essentials, multiverse, default"); + } + + @Test + public void shouldSetSpawn() { + // given + World world = mock(World.class); + given(world.getName()).willReturn("new_world"); + Location newSpawn = new Location(world, 123, 45.0, -67.89); + + // when + boolean result = spawnLoader.setSpawn(newSpawn); + + // then + assertThat(result, equalTo(true)); + YamlConfiguration configuration = YamlConfiguration.loadConfiguration(new File(testFolder, "spawn.yml")); + assertThat(configuration.getDouble("spawn.x"), equalTo(123.0)); + assertThat(configuration.getDouble("spawn.y"), equalTo(45.0)); + assertThat(configuration.getDouble("spawn.z"), equalTo(-67.89)); + assertThat(configuration.getString("spawn.world"), equalTo("new_world")); + } + + @Test + public void shouldReturnBedSpawnLocationForDeadPlayer() { + // given + Player player = mock(Player.class); + given(player.getHealth()).willReturn(0.0); + World world = mock(World.class); + Location bedSpawnLocation = new Location(world, 10.0, 70.0, -3.0); + given(teleportAdapter.getPlayerRespawnLocation(player)).willReturn(bedSpawnLocation); + + // when + Location result = spawnLoader.getPlayerLocationOrSpawn(player); + + // then + assertThat(result, equalTo(bedSpawnLocation)); + } + + @Test + public void shouldFallbackToConfiguredSpawnIfDeadPlayerHasNoRespawnLocation() { + // given + given(settings.getProperty(RestrictionSettings.SPAWN_PRIORITY)).willReturn("default"); + spawnLoader.reload(); + + Player player = mock(Player.class); + given(player.getHealth()).willReturn(0.0); + given(teleportAdapter.getPlayerRespawnLocation(player)).willReturn(null); + World world = mock(World.class); + Location worldSpawn = new Location(world, 5.0, 65.0, 5.0); + given(player.getWorld()).willReturn(world); + given(world.getSpawnLocation()).willReturn(worldSpawn); + + // when + Location result = spawnLoader.getPlayerLocationOrSpawn(player); + + // then + assertThat(result, equalTo(worldSpawn)); + } + + @Test + public void shouldIgnoreNullCandidateWorldSpawnLocations() { + // given + given(settings.getProperty(RestrictionSettings.SPAWN_PRIORITY)).willReturn("default"); + spawnLoader.reload(); + + World world = mock(World.class); + Location worldSpawn = new Location(world, 0.0, 0.0, 0.0); + given(world.getSpawnLocation()).willReturn(worldSpawn); + + World candidateWorld = mock(World.class); + given(candidateWorld.getSpawnLocation()).willReturn(null); + + // when + Location result; + try (MockedStatic bukkitMock = mockStatic(Bukkit.class)) { + bukkitMock.when(Bukkit::getWorlds).thenReturn(List.of(candidateWorld)); + result = spawnLoader.getSpawnLocation(world); + } + + // then + assertThat(result, equalTo(worldSpawn)); + } + +} + + diff --git a/src/test/java/fr/xephi/authme/settings/WelcomeMessageConfigurationTest.java b/authme-core/src/test/java/fr/xephi/authme/settings/WelcomeMessageConfigurationTest.java similarity index 94% rename from src/test/java/fr/xephi/authme/settings/WelcomeMessageConfigurationTest.java rename to authme-core/src/test/java/fr/xephi/authme/settings/WelcomeMessageConfigurationTest.java index 4daad61f1f..558a198fc7 100644 --- a/src/test/java/fr/xephi/authme/settings/WelcomeMessageConfigurationTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/settings/WelcomeMessageConfigurationTest.java @@ -1,7 +1,8 @@ package fr.xephi.authme.settings; +import org.junit.jupiter.api.extension.ExtendWith; +import fr.xephi.authme.DelayedInjectionExtension; import ch.jalu.injector.testing.BeforeInjecting; -import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.InjectDelayed; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerCache; @@ -14,10 +15,8 @@ import org.bukkit.Server; import org.bukkit.World; import org.bukkit.entity.Player; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; +import fr.xephi.authme.TempFolder; import org.mockito.Mock; import java.io.File; @@ -37,7 +36,7 @@ /** * Test for {@link WelcomeMessageConfiguration}. */ -@RunWith(DelayedInjectionRunner.class) +@ExtendWith(DelayedInjectionExtension.class) public class WelcomeMessageConfigurationTest { @InjectDelayed @@ -56,9 +55,7 @@ public class WelcomeMessageConfigurationTest { private File testPluginFolder; private File welcomeFile; - - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); + public TempFolder temporaryFolder = new TempFolder(); @BeforeInjecting public void createPluginFolder() throws IOException { @@ -142,3 +139,5 @@ private void setWelcomeMessageAndReload(String welcomeMessage) { welcomeMessageConfiguration.reload(); } } + + diff --git a/src/test/java/fr/xephi/authme/settings/commandconfig/CommandManagerTest.java b/authme-core/src/test/java/fr/xephi/authme/settings/commandconfig/CommandManagerTest.java similarity index 82% rename from src/test/java/fr/xephi/authme/settings/commandconfig/CommandManagerTest.java rename to authme-core/src/test/java/fr/xephi/authme/settings/commandconfig/CommandManagerTest.java index 2a586c5769..5ad1d4bac1 100644 --- a/src/test/java/fr/xephi/authme/settings/commandconfig/CommandManagerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/settings/commandconfig/CommandManagerTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.settings.commandconfig; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import com.google.common.io.Files; import fr.xephi.authme.TestHelper; import fr.xephi.authme.service.BukkitService; @@ -7,14 +11,11 @@ import fr.xephi.authme.service.GeoIpService; import fr.xephi.authme.settings.SettingsMigrationService; import org.bukkit.entity.Player; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import fr.xephi.authme.TempFolder; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.io.File; import java.io.IOException; @@ -24,6 +25,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.only; import static org.mockito.Mockito.times; @@ -34,7 +36,8 @@ /** * Test for {@link CommandManager}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class CommandManagerTest { private static final String TEST_FILES_FOLDER = "/fr/xephi/authme/settings/commandconfig/"; @@ -51,13 +54,11 @@ public class CommandManagerTest { private GeoIpService geoIpService; @Mock private SettingsMigrationService settingsMigrationService; - - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); + public TempFolder temporaryFolder = new TempFolder(); private File testFolder; - @Before + @BeforeEach public void setup() throws IOException { testFolder = temporaryFolder.newFolder(); player = mockPlayer(); @@ -77,8 +78,8 @@ public void shouldExecuteCommandsOnLogin() { verify(bukkitService).dispatchConsoleCommand("msg Bobby Welcome back"); verify(bukkitService).dispatchCommand(any(Player.class), eq("motd")); verify(bukkitService).dispatchCommand(any(Player.class), eq("list")); - verify(bukkitService).scheduleSyncDelayedTask(any(Runnable.class), eq(60L)); - verify(bukkitService).scheduleSyncDelayedTask(any(Runnable.class), eq(120L)); + verify(bukkitService).scheduleSyncDelayedTask(eq(player), any(Runnable.class), eq(60L)); + verify(bukkitService).scheduleSyncDelayedTask(eq(player), any(Runnable.class), eq(120L)); verifyNoMoreInteractions(bukkitService); verifyNoInteractions(geoIpService); } @@ -97,9 +98,9 @@ public void shouldExecuteCommandsOnLoginWithTwoAlts() { verify(bukkitService).dispatchCommand(player, "motd"); verify(bukkitService).dispatchCommand(player, "list"); verify(bukkitService).dispatchConsoleCommand("helpop Player Bobby has more than 1 account"); - verify(bukkitService).scheduleSyncDelayedTask(any(Runnable.class), eq(60L)); - verify(bukkitService).scheduleSyncDelayedTask(any(Runnable.class), eq(120L)); - verify(bukkitService).scheduleSyncDelayedTask(any(Runnable.class), eq(180L)); + verify(bukkitService).scheduleSyncDelayedTask(eq(player), any(Runnable.class), eq(60L)); + verify(bukkitService).scheduleSyncDelayedTask(eq(player), any(Runnable.class), eq(120L)); + verify(bukkitService).scheduleSyncDelayedTask(eq(player), any(Runnable.class), eq(180L)); verifyNoMoreInteractions(bukkitService); verifyNoInteractions(geoIpService); } @@ -119,10 +120,10 @@ public void shouldExecuteCommandsOnLoginWithFifteenAlts() { verify(bukkitService).dispatchCommand(player, "list"); verify(bukkitService).dispatchConsoleCommand("helpop Player Bobby has more than 1 account"); verify(bukkitService).dispatchConsoleCommand("log Bobby 127.0.0.3 many accounts"); - verify(bukkitService).scheduleSyncDelayedTask(any(Runnable.class), eq(60L)); - verify(bukkitService).scheduleSyncDelayedTask(any(Runnable.class), eq(120L)); - verify(bukkitService).scheduleSyncDelayedTask(any(Runnable.class), eq(180L)); - verify(bukkitService).scheduleSyncDelayedTask(any(Runnable.class), eq(240L)); + verify(bukkitService).scheduleSyncDelayedTask(eq(player), any(Runnable.class), eq(60L)); + verify(bukkitService).scheduleSyncDelayedTask(eq(player), any(Runnable.class), eq(120L)); + verify(bukkitService).scheduleSyncDelayedTask(eq(player), any(Runnable.class), eq(180L)); + verify(bukkitService).scheduleSyncDelayedTask(eq(player), any(Runnable.class), eq(240L)); verifyNoMoreInteractions(bukkitService); verifyNoInteractions(geoIpService); } @@ -141,9 +142,9 @@ public void shouldExecuteCommandsOnLoginWithTwentyFiveAlts() { verify(bukkitService).dispatchCommand(player, "motd"); verify(bukkitService).dispatchCommand(player, "list"); verify(bukkitService).dispatchConsoleCommand("helpop Player Bobby has more than 1 account"); - verify(bukkitService).scheduleSyncDelayedTask(any(Runnable.class), eq(60L)); - verify(bukkitService).scheduleSyncDelayedTask(any(Runnable.class), eq(120L)); - verify(bukkitService).scheduleSyncDelayedTask(any(Runnable.class), eq(180L)); + verify(bukkitService).scheduleSyncDelayedTask(eq(player), any(Runnable.class), eq(60L)); + verify(bukkitService).scheduleSyncDelayedTask(eq(player), any(Runnable.class), eq(120L)); + verify(bukkitService).scheduleSyncDelayedTask(eq(player), any(Runnable.class), eq(180L)); verifyNoMoreInteractions(bukkitService); verifyNoInteractions(geoIpService); } @@ -161,7 +162,7 @@ public void shouldExecuteCommandsOnLoginWithIncompleteConfig() { // then verify(bukkitService).dispatchConsoleCommand("msg Bobby Welcome back, bob"); verify(bukkitService).dispatchCommand(any(Player.class), eq("list")); - verify(bukkitService).scheduleSyncDelayedTask(any(Runnable.class), eq(100L)); + verify(bukkitService).scheduleSyncDelayedTask(eq(player), any(Runnable.class), eq(100L)); verifyNoMoreInteractions(bukkitService); verifyNoInteractions(geoIpService); } @@ -249,7 +250,7 @@ public void shouldExecuteCommandsOnRegister() { // then verify(bukkitService).dispatchCommand(any(Player.class), eq("me I just registered")); verify(bukkitService).dispatchConsoleCommand("log Bobby (127.0.0.3, Syldavia) registered"); - verify(bukkitService, times(2)).scheduleSyncDelayedTask(any(Runnable.class), eq(100L)); + verify(bukkitService, times(2)).scheduleSyncDelayedTask(eq(player), any(Runnable.class), eq(100L)); verifyNoMoreInteractions(bukkitService); } @@ -310,11 +311,13 @@ private void copyJarFileAsCommandsYml(String path) { private Player mockPlayer() { Player player = mock(Player.class); - given(player.getName()).willReturn("Bobby"); - given(player.getDisplayName()).willReturn("bob"); + lenient().when(player.getName()).thenReturn("Bobby"); + lenient().when(player.getDisplayName()).thenReturn("bob"); String ip = "127.0.0.3"; TestHelper.mockIpAddressToPlayer(player, ip); - given(geoIpService.getCountryName(ip)).willReturn("Syldavia"); + lenient().when(geoIpService.getCountryName(ip)).thenReturn("Syldavia"); return player; } } + + diff --git a/src/test/java/fr/xephi/authme/settings/commandconfig/CommandMigrationServiceTest.java b/authme-core/src/test/java/fr/xephi/authme/settings/commandconfig/CommandMigrationServiceTest.java similarity index 94% rename from src/test/java/fr/xephi/authme/settings/commandconfig/CommandMigrationServiceTest.java rename to authme-core/src/test/java/fr/xephi/authme/settings/commandconfig/CommandMigrationServiceTest.java index 8a146a5544..a3816d0cdb 100644 --- a/src/test/java/fr/xephi/authme/settings/commandconfig/CommandMigrationServiceTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/settings/commandconfig/CommandMigrationServiceTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.settings.commandconfig; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import ch.jalu.configme.beanmapper.propertydescription.BeanDescriptionFactoryImpl; import ch.jalu.configme.beanmapper.propertydescription.BeanPropertyDescription; import ch.jalu.configme.configurationdata.ConfigurationData; @@ -8,12 +12,10 @@ import ch.jalu.configme.resource.YamlFileResource; import fr.xephi.authme.TestHelper; import fr.xephi.authme.settings.SettingsMigrationService; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.io.File; import java.util.List; @@ -31,7 +33,8 @@ /** * Test for {@link CommandMigrationService}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class CommandMigrationServiceTest { @InjectMocks @@ -40,7 +43,7 @@ public class CommandMigrationServiceTest { @Mock private SettingsMigrationService settingsMigrationService; - @BeforeClass + @BeforeAll public static void setUpLogger() { TestHelper.setupLogger(); } @@ -144,3 +147,5 @@ private static OnLoginCommand getUnknownOnLoginCommand(Map> classes; - @BeforeClass + @BeforeAll public static void scanForSettingsClasses() { ClassCollector collector = new ClassCollector(TestHelper.SOURCES_FOLDER, SETTINGS_FOLDER); classes = collector.collectClasses(SettingsHolder.class); @@ -103,3 +103,4 @@ private static boolean isValidConstantField(Field field) { return Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers); } } + diff --git a/src/test/java/fr/xephi/authme/settings/properties/TestConfiguration.java b/authme-core/src/test/java/fr/xephi/authme/settings/properties/TestConfiguration.java similarity index 100% rename from src/test/java/fr/xephi/authme/settings/properties/TestConfiguration.java rename to authme-core/src/test/java/fr/xephi/authme/settings/properties/TestConfiguration.java diff --git a/src/test/java/fr/xephi/authme/settings/properties/TestEnum.java b/authme-core/src/test/java/fr/xephi/authme/settings/properties/TestEnum.java similarity index 100% rename from src/test/java/fr/xephi/authme/settings/properties/TestEnum.java rename to authme-core/src/test/java/fr/xephi/authme/settings/properties/TestEnum.java diff --git a/src/test/java/fr/xephi/authme/task/CleanupTaskTest.java b/authme-core/src/test/java/fr/xephi/authme/task/CleanupTaskTest.java similarity index 77% rename from src/test/java/fr/xephi/authme/task/CleanupTaskTest.java rename to authme-core/src/test/java/fr/xephi/authme/task/CleanupTaskTest.java index 4269f9bd78..6d61073c26 100644 --- a/src/test/java/fr/xephi/authme/task/CleanupTaskTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/task/CleanupTaskTest.java @@ -1,12 +1,14 @@ package fr.xephi.authme.task; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import ch.jalu.injector.factory.SingletonStore; import fr.xephi.authme.initialization.HasCleanup; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.List; @@ -18,7 +20,8 @@ /** * Test for {@link CleanupTask}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class CleanupTaskTest { @InjectMocks @@ -42,3 +45,5 @@ public void shouldPerformCleanup() { verify(services.get(2)).performCleanup(); } } + + diff --git a/src/test/java/fr/xephi/authme/task/purge/PurgeServiceTest.java b/authme-core/src/test/java/fr/xephi/authme/task/purge/PurgeServiceTest.java similarity index 89% rename from src/test/java/fr/xephi/authme/task/purge/PurgeServiceTest.java rename to authme-core/src/test/java/fr/xephi/authme/task/purge/PurgeServiceTest.java index d13450c264..9900f1c8ce 100644 --- a/src/test/java/fr/xephi/authme/task/purge/PurgeServiceTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/task/purge/PurgeServiceTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.task.purge; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.TestHelper; import fr.xephi.authme.datasource.DataSource; @@ -10,13 +14,12 @@ import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import java.util.Calendar; @@ -45,7 +48,8 @@ /** * Test for {@link PurgeService}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class PurgeServiceTest { @InjectMocks @@ -61,8 +65,12 @@ public class PurgeServiceTest { private PermissionsManager permissionsManager; @Mock private PurgeExecutor executor; + @Captor + private ArgumentCaptor longCaptor; + @Captor + private ArgumentCaptor purgeTaskCaptor; - @BeforeClass + @BeforeAll public static void initLogger() { TestHelper.setupLogger(); } @@ -105,9 +113,8 @@ public void shouldRunAutoPurge() { purgeService.runAutoPurge(); // then - ArgumentCaptor captor = ArgumentCaptor.forClass(Long.class); - verify(dataSource).getRecordsToPurge(captor.capture()); - assertCorrectPurgeTimestamp(captor.getValue(), 60); + verify(dataSource).getRecordsToPurge(longCaptor.capture()); + assertCorrectPurgeTimestamp(longCaptor.getValue(), 60); assertThat(Boolean.TRUE, equalTo( ReflectionTestUtils.getFieldValue(PurgeService.class, purgeService, "isPurging"))); verifyScheduledPurgeTask(null, playerNames); @@ -188,9 +195,8 @@ private void assertCorrectPurgeTimestamp(long timestamp, int configuredDays) { } private void verifyScheduledPurgeTask(UUID senderUuid, Set names) { - ArgumentCaptor captor = ArgumentCaptor.forClass(PurgeTask.class); - verify(bukkitService).runTaskTimerAsynchronously(captor.capture(), eq(0L), eq(1L)); - PurgeTask task = captor.getValue(); + verify(bukkitService).runTaskTimerAsynchronously(purgeTaskCaptor.capture(), eq(0L), eq(1L)); + PurgeTask task = purgeTaskCaptor.getValue(); Object senderInTask = ReflectionTestUtils.getFieldValue(PurgeTask.class, task, "sender"); Set namesInTask = ReflectionTestUtils.getFieldValue(PurgeTask.class, task, "toPurge"); @@ -198,3 +204,5 @@ private void verifyScheduledPurgeTask(UUID senderUuid, Set names) { assertThat(namesInTask, containsInAnyOrder(names.toArray())); } } + + diff --git a/src/test/java/fr/xephi/authme/task/purge/PurgeTaskTest.java b/authme-core/src/test/java/fr/xephi/authme/task/purge/PurgeTaskTest.java similarity index 95% rename from src/test/java/fr/xephi/authme/task/purge/PurgeTaskTest.java rename to authme-core/src/test/java/fr/xephi/authme/task/purge/PurgeTaskTest.java index 2863fa4df4..8e6d238e48 100644 --- a/src/test/java/fr/xephi/authme/task/purge/PurgeTaskTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/task/purge/PurgeTaskTest.java @@ -1,5 +1,9 @@ package fr.xephi.authme.task.purge; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.TestHelper; import fr.xephi.authme.permission.PermissionNode; @@ -13,14 +17,12 @@ import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitTask; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; -import org.mockito.junit.MockitoJUnitRunner; import org.mockito.stubbing.Answer; import java.util.ArrayList; @@ -50,7 +52,8 @@ /** * Test for {@link PurgeTask}. */ -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) public class PurgeTaskTest { private static final PermissionNode BYPASS_NODE = PlayerStatePermission.BYPASS_PURGE; @@ -69,7 +72,7 @@ public class PurgeTaskTest { @Captor private ArgumentCaptor> namesCaptor; - @BeforeClass + @BeforeAll public static void initLogger() { TestHelper.setupLogger(); } @@ -114,6 +117,7 @@ public void shouldRunTask() { // Third round: no more OfflinePlayer objects, but some names remain reset(purgeService, permissionsManager); given(permissionsManager.hasPermissionOffline("india", BYPASS_NODE)).willReturn(true); + given(permissionsManager.hasPermissionOffline("foxtrot", BYPASS_NODE)).willReturn(false); // when (3) task.run(); @@ -245,3 +249,5 @@ private void assertRanPurgeWithNames(String... names) { } } + + diff --git a/src/test/java/fr/xephi/authme/util/ExceptionUtilsTest.java b/authme-core/src/test/java/fr/xephi/authme/util/ExceptionUtilsTest.java similarity index 98% rename from src/test/java/fr/xephi/authme/util/ExceptionUtilsTest.java rename to authme-core/src/test/java/fr/xephi/authme/util/ExceptionUtilsTest.java index 4eb8ef71ff..26022c2790 100644 --- a/src/test/java/fr/xephi/authme/util/ExceptionUtilsTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/util/ExceptionUtilsTest.java @@ -1,7 +1,7 @@ package fr.xephi.authme.util; import fr.xephi.authme.ReflectionTestUtils; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.net.MalformedURLException; import java.util.ConcurrentModificationException; @@ -76,3 +76,4 @@ public synchronized Throwable getCause() { } } } + diff --git a/src/test/java/fr/xephi/authme/util/FileUtilsTest.java b/authme-core/src/test/java/fr/xephi/authme/util/FileUtilsTest.java similarity index 96% rename from src/test/java/fr/xephi/authme/util/FileUtilsTest.java rename to authme-core/src/test/java/fr/xephi/authme/util/FileUtilsTest.java index fa5b9ccc66..d82b7c23a1 100644 --- a/src/test/java/fr/xephi/authme/util/FileUtilsTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/util/FileUtilsTest.java @@ -2,10 +2,9 @@ import com.google.common.io.Files; import fr.xephi.authme.TestHelper; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import fr.xephi.authme.TempFolder; import java.io.File; import java.io.IOException; @@ -24,13 +23,11 @@ public class FileUtilsTest { /** Regex that matches timestamps such as 20180211_1048. */ private static final String BACKUP_TIMESTAMP_PATTERN = "20\\d{6}_\\d{4}"; - @BeforeClass + @BeforeAll public static void initLogger() { TestHelper.setupLogger(); } - - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); + public TempFolder temporaryFolder = new TempFolder(); @Test public void shouldNotCopyFile() throws IOException { @@ -217,3 +214,5 @@ private static void createFiles(File... files) throws IOException { } } + + diff --git a/src/test/java/fr/xephi/authme/util/InternetProtocolUtilsTest.java b/authme-core/src/test/java/fr/xephi/authme/util/InternetProtocolUtilsTest.java similarity index 98% rename from src/test/java/fr/xephi/authme/util/InternetProtocolUtilsTest.java rename to authme-core/src/test/java/fr/xephi/authme/util/InternetProtocolUtilsTest.java index 9fa11a255a..44e4246e18 100644 --- a/src/test/java/fr/xephi/authme/util/InternetProtocolUtilsTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/util/InternetProtocolUtilsTest.java @@ -1,6 +1,6 @@ package fr.xephi.authme.util; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; @@ -50,3 +50,4 @@ public void testIsLoopback() { assertThat(InternetProtocolUtils.isLoopbackAddress("::1"), equalTo(true)); } } + diff --git a/src/test/java/fr/xephi/authme/util/PlayerUtilsTest.java b/authme-core/src/test/java/fr/xephi/authme/util/PlayerUtilsTest.java similarity index 93% rename from src/test/java/fr/xephi/authme/util/PlayerUtilsTest.java rename to authme-core/src/test/java/fr/xephi/authme/util/PlayerUtilsTest.java index 9330c7af29..f8bf06e5bf 100644 --- a/src/test/java/fr/xephi/authme/util/PlayerUtilsTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/util/PlayerUtilsTest.java @@ -2,8 +2,8 @@ import fr.xephi.authme.TestHelper; import org.bukkit.entity.Player; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; @@ -15,7 +15,7 @@ */ public class PlayerUtilsTest { - @BeforeClass + @BeforeAll public static void setAuthmeInstance() { TestHelper.setupLogger(); } @@ -51,3 +51,4 @@ public void shouldCheckIfIsNpc() { assertThat(result2, equalTo(true)); } } + diff --git a/src/test/java/fr/xephi/authme/util/RandomStringUtilsTest.java b/authme-core/src/test/java/fr/xephi/authme/util/RandomStringUtilsTest.java similarity index 93% rename from src/test/java/fr/xephi/authme/util/RandomStringUtilsTest.java rename to authme-core/src/test/java/fr/xephi/authme/util/RandomStringUtilsTest.java index 956a54ec90..05c24e9713 100644 --- a/src/test/java/fr/xephi/authme/util/RandomStringUtilsTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/util/RandomStringUtilsTest.java @@ -1,11 +1,12 @@ package fr.xephi.authme.util; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.regex.Pattern; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Test for {@link RandomStringUtils}. @@ -76,11 +77,9 @@ public void shouldGenerateRandomNumberString() { } } - @Test(expected = IllegalArgumentException.class) + @Test public void shouldThrowForInvalidLength() { - // given/when - RandomStringUtils.generate(-3); - - // then - throw exception + assertThrows(IllegalArgumentException.class, () -> RandomStringUtils.generate(-3)); } } + diff --git a/src/test/java/fr/xephi/authme/util/StringUtilsTest.java b/authme-core/src/test/java/fr/xephi/authme/util/StringUtilsTest.java similarity index 94% rename from src/test/java/fr/xephi/authme/util/StringUtilsTest.java rename to authme-core/src/test/java/fr/xephi/authme/util/StringUtilsTest.java index d4e41ca40d..6451b09625 100644 --- a/src/test/java/fr/xephi/authme/util/StringUtilsTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/util/StringUtilsTest.java @@ -1,13 +1,13 @@ package fr.xephi.authme.util; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static java.util.Arrays.asList; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; -import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Test for {@link StringUtils}. @@ -86,3 +86,4 @@ public void shouldCheckIfHasNeedleInWord() { assertThat(StringUtils.isInsideString('@', "D@Z"), equalTo(true)); } } + diff --git a/src/test/java/fr/xephi/authme/util/UtilsTest.java b/authme-core/src/test/java/fr/xephi/authme/util/UtilsTest.java similarity index 98% rename from src/test/java/fr/xephi/authme/util/UtilsTest.java rename to authme-core/src/test/java/fr/xephi/authme/util/UtilsTest.java index 5475274a36..9b5077261a 100644 --- a/src/test/java/fr/xephi/authme/util/UtilsTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/util/UtilsTest.java @@ -5,8 +5,8 @@ import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.entity.Player; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collection; @@ -26,7 +26,7 @@ */ public class UtilsTest { - @BeforeClass + @BeforeAll public static void initLogger() { TestHelper.setupLogger(); } @@ -173,3 +173,4 @@ public void shouldDetectIfEmailIsEmpty() { assertThat(Utils.isEmailEmpty("hey"), equalTo(false)); } } + diff --git a/src/test/java/fr/xephi/authme/util/UuidUtilsTest.java b/authme-core/src/test/java/fr/xephi/authme/util/UuidUtilsTest.java similarity index 95% rename from src/test/java/fr/xephi/authme/util/UuidUtilsTest.java rename to authme-core/src/test/java/fr/xephi/authme/util/UuidUtilsTest.java index fd94e2a373..728c3c4c69 100644 --- a/src/test/java/fr/xephi/authme/util/UuidUtilsTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/util/UuidUtilsTest.java @@ -1,6 +1,6 @@ package fr.xephi.authme.util; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.UUID; @@ -27,3 +27,4 @@ public void shouldParseUuidSafely() { assertThat(UuidUtils.parseUuidSafely("8e0a9aaa-5eda-42ef-InvalidEnding"), nullValue()); } } + diff --git a/src/test/java/fr/xephi/authme/util/expiring/DurationTest.java b/authme-core/src/test/java/fr/xephi/authme/util/expiring/DurationTest.java similarity index 97% rename from src/test/java/fr/xephi/authme/util/expiring/DurationTest.java rename to authme-core/src/test/java/fr/xephi/authme/util/expiring/DurationTest.java index 5577443f0e..b852a9fe28 100644 --- a/src/test/java/fr/xephi/authme/util/expiring/DurationTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/util/expiring/DurationTest.java @@ -1,6 +1,6 @@ package fr.xephi.authme.util.expiring; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.concurrent.TimeUnit; @@ -41,3 +41,4 @@ private static void check(Duration duration, long expectedDuration, TimeUnit exp assertThat(duration.getDuration(), equalTo(expectedDuration)); } } + diff --git a/src/test/java/fr/xephi/authme/util/expiring/ExpiringMapTest.java b/authme-core/src/test/java/fr/xephi/authme/util/expiring/ExpiringMapTest.java similarity index 98% rename from src/test/java/fr/xephi/authme/util/expiring/ExpiringMapTest.java rename to authme-core/src/test/java/fr/xephi/authme/util/expiring/ExpiringMapTest.java index 72b2389f93..51b34cedf6 100644 --- a/src/test/java/fr/xephi/authme/util/expiring/ExpiringMapTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/util/expiring/ExpiringMapTest.java @@ -1,6 +1,6 @@ package fr.xephi.authme.util.expiring; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -93,3 +93,4 @@ public void shouldReturnIfIsEmpty() { assertThat(map.isEmpty(), equalTo(true)); } } + diff --git a/src/test/java/fr/xephi/authme/util/expiring/ExpiringSetTest.java b/authme-core/src/test/java/fr/xephi/authme/util/expiring/ExpiringSetTest.java similarity index 99% rename from src/test/java/fr/xephi/authme/util/expiring/ExpiringSetTest.java rename to authme-core/src/test/java/fr/xephi/authme/util/expiring/ExpiringSetTest.java index fe8807ff99..4daea65794 100644 --- a/src/test/java/fr/xephi/authme/util/expiring/ExpiringSetTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/util/expiring/ExpiringSetTest.java @@ -1,6 +1,6 @@ package fr.xephi.authme.util.expiring; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.concurrent.TimeUnit; @@ -141,3 +141,4 @@ private static void assertIsDuration(Duration duration, long expectedDuration, T assertThat(duration.getDuration(), equalTo(expectedDuration)); } } + diff --git a/src/test/java/fr/xephi/authme/util/expiring/TimedCounterTest.java b/authme-core/src/test/java/fr/xephi/authme/util/expiring/TimedCounterTest.java similarity index 98% rename from src/test/java/fr/xephi/authme/util/expiring/TimedCounterTest.java rename to authme-core/src/test/java/fr/xephi/authme/util/expiring/TimedCounterTest.java index 3bb275f231..d3632f012c 100644 --- a/src/test/java/fr/xephi/authme/util/expiring/TimedCounterTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/util/expiring/TimedCounterTest.java @@ -1,6 +1,6 @@ package fr.xephi.authme.util.expiring; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.concurrent.TimeUnit; @@ -68,3 +68,4 @@ public void shouldSumUpEntries() { assertThat(totals, equalTo(14)); } } + diff --git a/src/test/java/fr/xephi/authme/util/lazytags/TagBuilderTest.java b/authme-core/src/test/java/fr/xephi/authme/util/lazytags/TagBuilderTest.java similarity index 96% rename from src/test/java/fr/xephi/authme/util/lazytags/TagBuilderTest.java rename to authme-core/src/test/java/fr/xephi/authme/util/lazytags/TagBuilderTest.java index f52ddca1ca..6374b9abda 100644 --- a/src/test/java/fr/xephi/authme/util/lazytags/TagBuilderTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/util/lazytags/TagBuilderTest.java @@ -1,6 +1,6 @@ package fr.xephi.authme.util.lazytags; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.function.Function; import java.util.function.Supplier; @@ -40,3 +40,4 @@ public void shouldCreateDependentTag() { assertThat(tag.getValue(24d), equalTo("26.4")); } } + diff --git a/src/test/java/fr/xephi/authme/util/lazytags/TagReplacerTest.java b/authme-core/src/test/java/fr/xephi/authme/util/lazytags/TagReplacerTest.java similarity index 98% rename from src/test/java/fr/xephi/authme/util/lazytags/TagReplacerTest.java rename to authme-core/src/test/java/fr/xephi/authme/util/lazytags/TagReplacerTest.java index 9ad3d404ae..bbaaffa69b 100644 --- a/src/test/java/fr/xephi/authme/util/lazytags/TagReplacerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/util/lazytags/TagReplacerTest.java @@ -1,6 +1,6 @@ package fr.xephi.authme.util.lazytags; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; @@ -88,3 +88,4 @@ List> getAvailableTags() { } } + diff --git a/src/test/java/fr/xephi/authme/util/lazytags/WrappedTagReplacerTest.java b/authme-core/src/test/java/fr/xephi/authme/util/lazytags/WrappedTagReplacerTest.java similarity index 98% rename from src/test/java/fr/xephi/authme/util/lazytags/WrappedTagReplacerTest.java rename to authme-core/src/test/java/fr/xephi/authme/util/lazytags/WrappedTagReplacerTest.java index 6a9a613b47..9dffada697 100644 --- a/src/test/java/fr/xephi/authme/util/lazytags/WrappedTagReplacerTest.java +++ b/authme-core/src/test/java/fr/xephi/authme/util/lazytags/WrappedTagReplacerTest.java @@ -4,7 +4,7 @@ import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; @@ -74,3 +74,4 @@ String getDescription() { } } } + diff --git a/src/test/resources/fr/xephi/authme/command/help/help_test.yml b/authme-core/src/test/resources/fr/xephi/authme/command/help/help_test.yml similarity index 100% rename from src/test/resources/fr/xephi/authme/command/help/help_test.yml rename to authme-core/src/test/resources/fr/xephi/authme/command/help/help_test.yml diff --git a/src/test/resources/fr/xephi/authme/config.test.yml b/authme-core/src/test/resources/fr/xephi/authme/config.test.yml similarity index 100% rename from src/test/resources/fr/xephi/authme/config.test.yml rename to authme-core/src/test/resources/fr/xephi/authme/config.test.yml diff --git a/src/test/resources/fr/xephi/authme/data/backup/sample-folder/data.json b/authme-core/src/test/resources/fr/xephi/authme/data/backup/sample-folder/data.json similarity index 100% rename from src/test/resources/fr/xephi/authme/data/backup/sample-folder/data.json rename to authme-core/src/test/resources/fr/xephi/authme/data/backup/sample-folder/data.json diff --git a/src/test/resources/fr/xephi/authme/data/limbo/seg16-8-limbo.json b/authme-core/src/test/resources/fr/xephi/authme/data/limbo/seg16-8-limbo.json similarity index 100% rename from src/test/resources/fr/xephi/authme/data/limbo/seg16-8-limbo.json rename to authme-core/src/test/resources/fr/xephi/authme/data/limbo/seg16-8-limbo.json diff --git a/src/test/resources/fr/xephi/authme/data/limbo/seg16-f-limbo.json b/authme-core/src/test/resources/fr/xephi/authme/data/limbo/seg16-f-limbo.json similarity index 100% rename from src/test/resources/fr/xephi/authme/data/limbo/seg16-f-limbo.json rename to authme-core/src/test/resources/fr/xephi/authme/data/limbo/seg16-f-limbo.json diff --git a/src/test/resources/fr/xephi/authme/data/limbo/seg32-01011-limbo.json b/authme-core/src/test/resources/fr/xephi/authme/data/limbo/seg32-01011-limbo.json similarity index 100% rename from src/test/resources/fr/xephi/authme/data/limbo/seg32-01011-limbo.json rename to authme-core/src/test/resources/fr/xephi/authme/data/limbo/seg32-01011-limbo.json diff --git a/src/test/resources/fr/xephi/authme/data/limbo/seg32-10110-limbo.json b/authme-core/src/test/resources/fr/xephi/authme/data/limbo/seg32-10110-limbo.json similarity index 100% rename from src/test/resources/fr/xephi/authme/data/limbo/seg32-10110-limbo.json rename to authme-core/src/test/resources/fr/xephi/authme/data/limbo/seg32-10110-limbo.json diff --git a/authme-core/src/test/resources/fr/xephi/authme/datasource/Auth/players.yml b/authme-core/src/test/resources/fr/xephi/authme/datasource/Auth/players.yml new file mode 100644 index 0000000000..4aa317bd0d --- /dev/null +++ b/authme-core/src/test/resources/fr/xephi/authme/datasource/Auth/players.yml @@ -0,0 +1,6 @@ +a811b264-a1da-42dd-b1b7-b601234b7cc7: + hash: pbkdf2$120000$JddgMm9rNhbYNfEmf4pOKA==$RcAMtgm/KnKFxfNOpg95tb7s5OzB2Fv4Wj1HOAI/TWY= +b922c375-b2eb-53ee-c2c8-c712345c8dd8: + hash: pbkdf2$4128$obLD1OX2p7jJ0OHyo7TF1g==$dyEnM1cYBRhXl0bDgxfQ3p7Jecj4VerT1Vsag5HJTmo= +cc033486-c3fc-64ff-d3d9-d823456d9ee9: + hash: pbkdf2$4128$3q2+78r+AQIDBAUGBwgJCg==$L1stW/OuuWuLfFiEqpOSumXwSJROYDUwSDfiU/7CkYA= diff --git a/src/test/resources/fr/xephi/authme/datasource/converter/LoginSecurity.db b/authme-core/src/test/resources/fr/xephi/authme/datasource/converter/LoginSecurity.db similarity index 100% rename from src/test/resources/fr/xephi/authme/datasource/converter/LoginSecurity.db rename to authme-core/src/test/resources/fr/xephi/authme/datasource/converter/LoginSecurity.db diff --git a/src/test/resources/fr/xephi/authme/datasource/converter/crazylogin.db b/authme-core/src/test/resources/fr/xephi/authme/datasource/converter/crazylogin.db similarity index 100% rename from src/test/resources/fr/xephi/authme/datasource/converter/crazylogin.db rename to authme-core/src/test/resources/fr/xephi/authme/datasource/converter/crazylogin.db diff --git a/src/test/resources/fr/xephi/authme/datasource/converter/loginsecurity.sql b/authme-core/src/test/resources/fr/xephi/authme/datasource/converter/loginsecurity.sql similarity index 100% rename from src/test/resources/fr/xephi/authme/datasource/converter/loginsecurity.sql rename to authme-core/src/test/resources/fr/xephi/authme/datasource/converter/loginsecurity.sql diff --git a/src/test/resources/fr/xephi/authme/datasource/flatfile-test.txt b/authme-core/src/test/resources/fr/xephi/authme/datasource/flatfile-test.txt similarity index 100% rename from src/test/resources/fr/xephi/authme/datasource/flatfile-test.txt rename to authme-core/src/test/resources/fr/xephi/authme/datasource/flatfile-test.txt diff --git a/src/test/resources/fr/xephi/authme/datasource/sql-initialize.sql b/authme-core/src/test/resources/fr/xephi/authme/datasource/sql-initialize.sql similarity index 100% rename from src/test/resources/fr/xephi/authme/datasource/sql-initialize.sql rename to authme-core/src/test/resources/fr/xephi/authme/datasource/sql-initialize.sql diff --git a/src/test/resources/fr/xephi/authme/datasource/sqlite.april2016.db b/authme-core/src/test/resources/fr/xephi/authme/datasource/sqlite.april2016.db similarity index 100% rename from src/test/resources/fr/xephi/authme/datasource/sqlite.april2016.db rename to authme-core/src/test/resources/fr/xephi/authme/datasource/sqlite.april2016.db diff --git a/src/test/resources/fr/xephi/authme/message/chinese_texts.yml b/authme-core/src/test/resources/fr/xephi/authme/message/chinese_texts.yml similarity index 100% rename from src/test/resources/fr/xephi/authme/message/chinese_texts.yml rename to authme-core/src/test/resources/fr/xephi/authme/message/chinese_texts.yml diff --git a/src/test/resources/fr/xephi/authme/message/help_test.yml b/authme-core/src/test/resources/fr/xephi/authme/message/help_test.yml similarity index 100% rename from src/test/resources/fr/xephi/authme/message/help_test.yml rename to authme-core/src/test/resources/fr/xephi/authme/message/help_test.yml diff --git a/src/test/resources/fr/xephi/authme/message/messages_default.yml b/authme-core/src/test/resources/fr/xephi/authme/message/messages_default.yml similarity index 100% rename from src/test/resources/fr/xephi/authme/message/messages_default.yml rename to authme-core/src/test/resources/fr/xephi/authme/message/messages_default.yml diff --git a/src/test/resources/fr/xephi/authme/message/messages_en_old.yml b/authme-core/src/test/resources/fr/xephi/authme/message/messages_en_old.yml similarity index 100% rename from src/test/resources/fr/xephi/authme/message/messages_en_old.yml rename to authme-core/src/test/resources/fr/xephi/authme/message/messages_en_old.yml diff --git a/src/test/resources/fr/xephi/authme/message/messages_test.yml b/authme-core/src/test/resources/fr/xephi/authme/message/messages_test.yml similarity index 100% rename from src/test/resources/fr/xephi/authme/message/messages_test.yml rename to authme-core/src/test/resources/fr/xephi/authme/message/messages_test.yml diff --git a/src/test/resources/fr/xephi/authme/message/messages_test2.yml b/authme-core/src/test/resources/fr/xephi/authme/message/messages_test2.yml similarity index 100% rename from src/test/resources/fr/xephi/authme/message/messages_test2.yml rename to authme-core/src/test/resources/fr/xephi/authme/message/messages_test2.yml diff --git a/src/test/resources/fr/xephi/authme/service/yaml/invalidYaml.yml b/authme-core/src/test/resources/fr/xephi/authme/service/yaml/invalidYaml.yml similarity index 100% rename from src/test/resources/fr/xephi/authme/service/yaml/invalidYaml.yml rename to authme-core/src/test/resources/fr/xephi/authme/service/yaml/invalidYaml.yml diff --git a/src/test/resources/fr/xephi/authme/service/yaml/validYaml.yml b/authme-core/src/test/resources/fr/xephi/authme/service/yaml/validYaml.yml similarity index 100% rename from src/test/resources/fr/xephi/authme/service/yaml/validYaml.yml rename to authme-core/src/test/resources/fr/xephi/authme/service/yaml/validYaml.yml diff --git a/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.complete.yml b/authme-core/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.complete.yml similarity index 100% rename from src/test/resources/fr/xephi/authme/settings/commandconfig/commands.complete.yml rename to authme-core/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.complete.yml diff --git a/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.empty.yml b/authme-core/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.empty.yml similarity index 100% rename from src/test/resources/fr/xephi/authme/settings/commandconfig/commands.empty.yml rename to authme-core/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.empty.yml diff --git a/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.incomplete.yml b/authme-core/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.incomplete.yml similarity index 100% rename from src/test/resources/fr/xephi/authme/settings/commandconfig/commands.incomplete.yml rename to authme-core/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.incomplete.yml diff --git a/src/test/resources/fr/xephi/authme/settings/config-incomplete-sample.yml b/authme-core/src/test/resources/fr/xephi/authme/settings/config-incomplete-sample.yml similarity index 100% rename from src/test/resources/fr/xephi/authme/settings/config-incomplete-sample.yml rename to authme-core/src/test/resources/fr/xephi/authme/settings/config-incomplete-sample.yml diff --git a/src/test/resources/fr/xephi/authme/settings/config-old.yml b/authme-core/src/test/resources/fr/xephi/authme/settings/config-old.yml similarity index 100% rename from src/test/resources/fr/xephi/authme/settings/config-old.yml rename to authme-core/src/test/resources/fr/xephi/authme/settings/config-old.yml diff --git a/src/test/resources/fr/xephi/authme/settings/config-sample-values.yml b/authme-core/src/test/resources/fr/xephi/authme/settings/config-sample-values.yml similarity index 100% rename from src/test/resources/fr/xephi/authme/settings/config-sample-values.yml rename to authme-core/src/test/resources/fr/xephi/authme/settings/config-sample-values.yml diff --git a/src/test/resources/fr/xephi/authme/settings/spawn-firstspawn.yml b/authme-core/src/test/resources/fr/xephi/authme/settings/spawn-firstspawn.yml similarity index 100% rename from src/test/resources/fr/xephi/authme/settings/spawn-firstspawn.yml rename to authme-core/src/test/resources/fr/xephi/authme/settings/spawn-firstspawn.yml diff --git a/authme-core/src/test/resources/plugin.yml b/authme-core/src/test/resources/plugin.yml new file mode 100644 index 0000000000..bb8f8265eb --- /dev/null +++ b/authme-core/src/test/resources/plugin.yml @@ -0,0 +1,322 @@ +# noinspection YAMLSchemaValidation +name: AuthMe +# noinspection YAMLSchemaValidation +authors: [Xephi, sgdc3, DNx5, timvisee, games647, ljacqu, Rtz23, Yannici, Norska, Aerodactyl1997] +website: https://github.com/AuthMe/AuthMeReloaded +description: The most popular authentication plugin for CraftBukkit/Spigot! +# noinspection YAMLSchemaValidation +main: fr.xephi.authme.AuthMe +version: 5.7.0-SNAPSHOT +api-version: 1.16 +softdepend: + - Vault + - LuckPerms + - PermissionsEx + - bPermissions + - zPermissions + - Multiverse-Core + - Essentials + - EssentialsSpawn + - ProtocolLib +commands: + authme: + description: AuthMe op commands + usage: /authme register|unregister|forcelogin|password|lastlogin|accounts|email|setemail|getip|totp|disabletotp|spawn|setspawn|firstspawn|setfirstspawn|purge|purgeplayer|backup|resetpos|purgebannedplayers|switchantibot|reload|version|converter|messages|recent|debug + email: + description: Add email or recover password + usage: /email show|add|change|recover|code|setpassword + login: + description: Login command + usage: /login + aliases: + - l + - log + logout: + description: Logout command + usage: /logout + register: + description: Register an account + usage: /register [password] [verifyPassword] + aliases: + - reg + unregister: + description: Unregister an account + usage: /unregister + aliases: + - unreg + changepassword: + description: Change password of an account + usage: /changepassword + aliases: + - changepass + - cp + totp: + description: TOTP commands + usage: /totp code|add|confirm|remove + aliases: + - 2fa + captcha: + description: Captcha command + usage: /captcha + verification: + description: Verification command + usage: /verification +permissions: + authme.admin.*: + description: Gives access to all admin commands + children: + authme.admin.accounts: true + authme.admin.antibotmessages: true + authme.admin.backup: true + authme.admin.changemail: true + authme.admin.changepassword: true + authme.admin.converter: true + authme.admin.firstspawn: true + authme.admin.forcelogin: true + authme.admin.getemail: true + authme.admin.getip: true + authme.admin.lastlogin: true + authme.admin.purge: true + authme.admin.purgebannedplayers: true + authme.admin.purgelastpos: true + authme.admin.purgeplayer: true + authme.admin.register: true + authme.admin.reload: true + authme.admin.seeotheraccounts: true + authme.admin.seerecent: true + authme.admin.setfirstspawn: true + authme.admin.setspawn: true + authme.admin.spawn: true + authme.admin.switchantibot: true + authme.admin.totpdisable: true + authme.admin.totpviewstatus: true + authme.admin.unregister: true + authme.admin.updatemessages: true + authme.admin.accounts: + description: Administrator command to see all accounts associated with a user. + default: op + authme.admin.antibotmessages: + description: Permission to see Antibot messages. + default: op + authme.admin.backup: + description: Allows to use the backup command. + default: op + authme.admin.changemail: + description: Administrator command to set or change the email address of a user. + default: op + authme.admin.changepassword: + description: Administrator command to change the password of a user. + default: op + authme.admin.converter: + description: Administrator command to convert old or other data to AuthMe data. + default: op + authme.admin.firstspawn: + description: Administrator command to teleport to the first AuthMe spawn. + default: op + authme.admin.forcelogin: + description: Administrator command to force-login an existing user. + default: op + authme.admin.getemail: + description: Administrator command to get the email address of a user, if set. + default: op + authme.admin.getip: + description: Administrator command to get the last known IP of a user. + default: op + authme.admin.lastlogin: + description: Administrator command to see the last login date and time of a user. + default: op + authme.admin.purge: + description: Administrator command to purge old user data. + default: op + authme.admin.purgebannedplayers: + description: Administrator command to purge all data associated with banned players. + default: op + authme.admin.purgelastpos: + description: Administrator command to purge the last position of a user. + default: op + authme.admin.purgeplayer: + description: Administrator command to purge a given player. + default: op + authme.admin.register: + description: Administrator command to register a new user. + default: op + authme.admin.reload: + description: Administrator command to reload the plugin configuration. + default: op + authme.admin.seeotheraccounts: + description: Permission to see the other accounts of the players that log in. + default: op + authme.admin.seerecent: + description: Administrator command to see the last recently logged in players. + default: op + authme.admin.setfirstspawn: + description: Administrator command to set the first AuthMe spawn. + default: op + authme.admin.setspawn: + description: Administrator command to set the AuthMe spawn. + default: op + authme.admin.spawn: + description: Administrator command to teleport to the AuthMe spawn. + default: op + authme.admin.switchantibot: + description: Administrator command to toggle the AntiBot protection status. + default: op + authme.admin.totpdisable: + description: Administrator command to disable the two-factor auth of a user. + default: op + authme.admin.totpviewstatus: + description: Administrator command to see whether a player has enabled two-factor + authentication. + default: op + authme.admin.unregister: + description: Administrator command to unregister an existing user. + default: op + authme.admin.updatemessages: + description: Permission to use the update messages command. + default: op + authme.allowchatbeforelogin: + description: Permission to send chat messages before being logged in. + default: false + authme.allowmultipleaccounts: + description: Permission to be able to register multiple accounts. + default: op + authme.bypassantibot: + description: Permission node to bypass AntiBot protection. + default: op + authme.bypassbungeesend: + description: Permission node to bypass BungeeCord server teleportation. + default: false + authme.bypasscountrycheck: + description: Permission to bypass the GeoIp country code check. + default: false + authme.bypassforcesurvival: + description: Permission for users to bypass force-survival mode. + default: op + authme.bypasspurge: + description: Permission to bypass the purging process. + default: false + authme.debug: + description: Gives access to /authme debug and all its sections + children: + authme.debug.command: true + authme.debug.country: true + authme.debug.db: true + authme.debug.group: true + authme.debug.limbo: true + authme.debug.mail: true + authme.debug.mysqldef: true + authme.debug.perm: true + authme.debug.spawn: true + authme.debug.stats: true + authme.debug.valid: true + authme.debug.command: + description: General permission to use the /authme debug command. + default: op + authme.debug.country: + description: Permission to use the country lookup section. + default: op + authme.debug.db: + description: Permission to view data from the database. + default: op + authme.debug.group: + description: Permission to view permission groups. + default: op + authme.debug.limbo: + description: Permission to use the limbo data viewer. + default: op + authme.debug.mail: + description: Permission to use the test email sender. + default: op + authme.debug.mysqldef: + description: Permission to change nullable status of MySQL columns. + default: op + authme.debug.perm: + description: Permission to use the permission checker. + default: op + authme.debug.spawn: + description: Permission to view spawn information. + default: op + authme.debug.stats: + description: Permission to use the stats section. + default: op + authme.debug.valid: + description: Permission to use sample validation. + default: op + authme.player.*: + description: Gives access to all player commands + children: + authme.player.canbeforced: true + authme.player.captcha: true + authme.player.changepassword: true + authme.player.email.add: true + authme.player.email.change: true + authme.player.email.recover: true + authme.player.email.see: true + authme.player.login: true + authme.player.logout: true + authme.player.protection.quickcommandsprotection: true + authme.player.register: true + authme.player.security.verificationcode: true + authme.player.seeownaccounts: true + authme.player.totpadd: true + authme.player.totpremove: true + authme.player.unregister: true + authme.player.canbeforced: + description: Permission for users a login can be forced to. + default: true + authme.player.captcha: + description: Command permission to use captcha. + default: true + authme.player.changepassword: + description: Command permission to change the password. + default: true + authme.player.email: + description: Gives access to all email commands + children: + authme.player.email.add: true + authme.player.email.change: true + authme.player.email.recover: true + authme.player.email.see: true + authme.player.email.add: + description: Command permission to add an email address. + default: true + authme.player.email.change: + description: Command permission to change the email address. + default: true + authme.player.email.recover: + description: Command permission to recover an account using its email address. + default: true + authme.player.email.see: + description: Command permission to see the own email address. + default: true + authme.player.login: + description: Command permission to login. + default: true + authme.player.logout: + description: Command permission to logout. + default: true + authme.player.protection.quickcommandsprotection: + description: Permission that enables on join quick commands checks for the player. + default: true + authme.player.register: + description: Command permission to register. + default: true + authme.player.security.verificationcode: + description: Permission to use the email verification codes feature. + default: true + authme.player.seeownaccounts: + description: Permission to use to see own other accounts. + default: true + authme.player.totpadd: + description: Permission to enable two-factor authentication. + default: true + authme.player.totpremove: + description: Permission to disable two-factor authentication. + default: true + authme.player.unregister: + description: Command permission to unregister. + default: true + authme.vip: + description: When the server is full and someone with this permission joins the + server, someone will be kicked. + default: false diff --git a/src/test/resources/simplelogger.properties b/authme-core/src/test/resources/simplelogger.properties similarity index 100% rename from src/test/resources/simplelogger.properties rename to authme-core/src/test/resources/simplelogger.properties diff --git a/authme-folia/pom.xml b/authme-folia/pom.xml new file mode 100644 index 0000000000..596e8f9f89 --- /dev/null +++ b/authme-folia/pom.xml @@ -0,0 +1,152 @@ + + + 4.0.0 + + + fr.xephi + authme-parent + ${revision} + + + authme-folia + jar + AuthMe Folia + AuthMe plugin for Folia 1.21 + + + 21 + 1.21.11 + Folia + + + + + fr.xephi + authme-paper-common + ${project.version} + compile + + + fr.xephi + authme-core + ${project.version} + compile + + + dev.folia + folia-api + provided + + + + ch.jalu + configme + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + + + org.mockito + mockito-junit-jupiter + test + + + org.hamcrest + hamcrest + test + + + + + AuthMe-${project.version}-${module.classifier} + + + + src/main/resources + true + + + .. + false + + LICENSE + + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + shade + package + + shade + + + + + + org.apache.maven.plugins + maven-source-plugin + + ${project.build.finalName} + + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + ${project.build.finalName} + ${module.java.version} + false + + + + attach-javadoc + + jar + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + remove-original-shaded-jar + package + + run + + + + + + + + + + + + diff --git a/authme-folia/src/main/java/fr/xephi/authme/listener/FoliaChatListener.java b/authme-folia/src/main/java/fr/xephi/authme/listener/FoliaChatListener.java new file mode 100644 index 0000000000..5feb32f495 --- /dev/null +++ b/authme-folia/src/main/java/fr/xephi/authme/listener/FoliaChatListener.java @@ -0,0 +1,12 @@ +package fr.xephi.authme.listener; + +import io.papermc.paper.event.player.AsyncChatEvent; + +/** + * Chat listener for Folia that handles {@link AsyncChatEvent} + * (replaces the deprecated {@code AsyncPlayerChatEvent}). + * Mirrors the logic of {@code PlayerListener#onPlayerChat} for full feature parity, + * including HIDE_CHAT recipient filtering via the Adventure API. + */ +public class FoliaChatListener extends AbstractPaperAsyncChatListener { +} diff --git a/authme-folia/src/main/java/fr/xephi/authme/listener/FoliaPlayerSpawnLocationListener.java b/authme-folia/src/main/java/fr/xephi/authme/listener/FoliaPlayerSpawnLocationListener.java new file mode 100644 index 0000000000..dc79e6d5e7 --- /dev/null +++ b/authme-folia/src/main/java/fr/xephi/authme/listener/FoliaPlayerSpawnLocationListener.java @@ -0,0 +1,10 @@ +package fr.xephi.authme.listener; + +import org.bukkit.Location; +public class FoliaPlayerSpawnLocationListener extends AbstractPaperAsyncPlayerSpawnLocationListener { + + @Override + protected Location determineCustomSpawnLocation(String playerName, Location originalSpawnLocation) { + return teleportationService.prepareOnJoinSpawnLocation(playerName, originalSpawnLocation); + } +} diff --git a/authme-folia/src/main/java/fr/xephi/authme/platform/FoliaPlatformAdapter.java b/authme-folia/src/main/java/fr/xephi/authme/platform/FoliaPlatformAdapter.java new file mode 100644 index 0000000000..3523e254bc --- /dev/null +++ b/authme-folia/src/main/java/fr/xephi/authme/platform/FoliaPlatformAdapter.java @@ -0,0 +1,98 @@ +package fr.xephi.authme.platform; + +import fr.xephi.authme.AuthMe; +import fr.xephi.authme.listener.FoliaChatListener; +import fr.xephi.authme.listener.PaperLoginValidationListener; +import fr.xephi.authme.listener.FoliaPlayerSpawnLocationListener; +import fr.xephi.authme.listener.PlayerOpenSignListener; +import fr.xephi.authme.service.CancellableTask; +import io.papermc.paper.threadedregions.scheduler.ScheduledTask; +import org.bukkit.entity.Entity; +import org.bukkit.event.Listener; +import org.bukkit.Bukkit; + +import java.util.Arrays; +import java.util.List; + +/** + * Platform adapter implementation for Folia 1.21.11+. + * Reuses the Paper-derived API surface exposed by Folia while relying on + * the core scheduler abstraction for region-safe task dispatch. + */ +public class FoliaPlatformAdapter extends AbstractPaperPlatformAdapter { + + private static final CancellableTask NO_OP_TASK = () -> { }; + + @Override + public boolean isOwnedByCurrentThread(Entity entity) { + return Bukkit.isOwnedByCurrentRegion(entity); + } + + @Override + public boolean isGlobalThread() { + return Bukkit.isGlobalTickThread(); + } + + @Override + public void runOnEntityThread(AuthMe plugin, Entity entity, Runnable task) { + ScheduledTask scheduledTask = entity.getScheduler().run(plugin, ignored -> task.run(), null); + if (scheduledTask == null) { + plugin.getLogger().fine("Skipped entity task because the entity scheduler was retired"); + } + } + + @Override + public CancellableTask runDelayedOnEntityThread(AuthMe plugin, Entity entity, Runnable task, long delay) { + ScheduledTask scheduledTask = entity.getScheduler().runDelayed(plugin, ignored -> task.run(), null, delay); + if (scheduledTask == null) { + plugin.getLogger().fine("Skipped delayed entity task because the entity scheduler was retired"); + return NO_OP_TASK; + } + return scheduledTask::cancel; + } + + @Override + public CancellableTask runAtFixedRateOnEntityThread(AuthMe plugin, Entity entity, Runnable task, + long delay, long period) { + ScheduledTask scheduledTask = entity.getScheduler().runAtFixedRate(plugin, ignored -> task.run(), null, + delay, period); + if (scheduledTask == null) { + plugin.getLogger().fine("Skipped repeating entity task because the entity scheduler was retired"); + return NO_OP_TASK; + } + return scheduledTask::cancel; + } + + @Override + public void runOnGlobalThread(AuthMe plugin, Runnable task) { + Bukkit.getGlobalRegionScheduler().run(plugin, ignored -> task.run()); + } + + @Override + public CancellableTask runDelayedOnGlobalThread(AuthMe plugin, Runnable task, long delay) { + return Bukkit.getGlobalRegionScheduler().runDelayed(plugin, ignored -> task.run(), delay)::cancel; + } + + @Override + public String getPlatformName() { + return "folia-1.21"; + } + + @Override + public String getCompatibilityError() { + return getCompatibilityError("This AuthMe Folia build requires the Folia 1.21.11+ API.", + "io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler", + "io.papermc.paper.event.player.AsyncChatEvent", + "io.papermc.paper.event.player.AsyncPlayerSpawnLocationEvent", + "io.papermc.paper.event.player.PlayerOpenSignEvent"); + } + + @Override + public List> getAdditionalListeners() { + return Arrays.asList( + FoliaChatListener.class, + FoliaPlayerSpawnLocationListener.class, + PaperLoginValidationListener.class, + PlayerOpenSignListener.class); + } +} diff --git a/authme-folia/src/main/resources/META-INF/services/fr.xephi.authme.platform.PlatformAdapter b/authme-folia/src/main/resources/META-INF/services/fr.xephi.authme.platform.PlatformAdapter new file mode 100644 index 0000000000..ca722d8730 --- /dev/null +++ b/authme-folia/src/main/resources/META-INF/services/fr.xephi.authme.platform.PlatformAdapter @@ -0,0 +1 @@ +fr.xephi.authme.platform.FoliaPlatformAdapter diff --git a/authme-folia/src/main/resources/plugin.yml b/authme-folia/src/main/resources/plugin.yml new file mode 100644 index 0000000000..d647b1e1e4 --- /dev/null +++ b/authme-folia/src/main/resources/plugin.yml @@ -0,0 +1,341 @@ +# noinspection YAMLSchemaValidation +name: ${pluginDescription.name} +# noinspection YAMLSchemaValidation +authors: [${pluginDescription.authors}] +website: ${project.url} +description: ${project.description} +# noinspection YAMLSchemaValidation +main: ${pluginDescription.main} +version: ${pluginDescription.version} +api-version: ${plugin.api-version} +folia-supported: true +softdepend: + - Vault + - LuckPerms + - PermissionsEx + - bPermissions + - zPermissions + - Multiverse-Core + - Essentials + - EssentialsSpawn + - ProtocolLib +libraries: + - ch.jalu:injector:${dependencies.injector.version} + - net.ricecode:string-similarity:${dependencies.string-similarity.version} + - com.maxmind.geoip2:geoip2:${dependencies.geoip2.version} + - javatar:javatar:${dependencies.javatar.version} + - org.apache.commons:commons-email:${dependencies.commons-email.version} + - com.zaxxer:HikariCP:${dependencies.hikaricp.version} + - org.slf4j:slf4j-simple:${dependencies.slf4j.version} + - ch.jalu:datasourcecolumns:${dependencies.datasourcecolumns.version} + - com.mysql:mysql-connector-j:${dependencies.mysql-connector-j.version} + - org.mariadb.jdbc:mariadb-java-client:${dependencies.mariadb-java-client.version} + - org.postgresql:postgresql:${dependencies.postgresql.version} + - de.rtner:PBKDF2:${dependencies.pbkdf2.version} + - de.mkammerer:argon2-jvm-nolibs:${dependencies.argon2-jvm-nolibs.version} + - at.favre.lib:bcrypt:${dependencies.bcrypt.version} + - com.warrenstrange:googleauth:${dependencies.googleauth.version} + - ch.jalu:configme:${dependencies.configme.version} + - org.bstats:bstats-bukkit:${dependencies.bstats.version} +commands: + authme: + description: AuthMe op commands + usage: /authme register|unregister|forcelogin|password|lastlogin|accounts|email|setemail|getip|totp|disabletotp|spawn|setspawn|firstspawn|setfirstspawn|purge|purgeplayer|backup|resetpos|purgebannedplayers|switchantibot|reload|version|converter|messages|recent|debug + email: + description: Add email or recover password + usage: /email show|add|change|recover|code|setpassword + login: + description: Login command + usage: /login + aliases: + - l + - log + logout: + description: Logout command + usage: /logout + register: + description: Register an account + usage: /register [password] [verifyPassword] + aliases: + - reg + unregister: + description: Unregister an account + usage: /unregister + aliases: + - unreg + changepassword: + description: Change password of an account + usage: /changepassword + aliases: + - changepass + - cp + totp: + description: TOTP commands + usage: /totp code|add|confirm|remove + aliases: + - 2fa + captcha: + description: Captcha command + usage: /captcha + verification: + description: Verification command + usage: /verification +permissions: + authme.admin.*: + description: Gives access to all admin commands + children: + authme.admin.accounts: true + authme.admin.antibotmessages: true + authme.admin.backup: true + authme.admin.changemail: true + authme.admin.changepassword: true + authme.admin.converter: true + authme.admin.firstspawn: true + authme.admin.forcelogin: true + authme.admin.getemail: true + authme.admin.getip: true + authme.admin.lastlogin: true + authme.admin.purge: true + authme.admin.purgebannedplayers: true + authme.admin.purgelastpos: true + authme.admin.purgeplayer: true + authme.admin.register: true + authme.admin.reload: true + authme.admin.seeotheraccounts: true + authme.admin.seerecent: true + authme.admin.setfirstspawn: true + authme.admin.setspawn: true + authme.admin.spawn: true + authme.admin.switchantibot: true + authme.admin.totpdisable: true + authme.admin.totpviewstatus: true + authme.admin.unregister: true + authme.admin.updatemessages: true + authme.admin.accounts: + description: Administrator command to see all accounts associated with a user. + default: op + authme.admin.antibotmessages: + description: Permission to see Antibot messages. + default: op + authme.admin.backup: + description: Allows to use the backup command. + default: op + authme.admin.changemail: + description: Administrator command to set or change the email address of a user. + default: op + authme.admin.changepassword: + description: Administrator command to change the password of a user. + default: op + authme.admin.converter: + description: Administrator command to convert old or other data to AuthMe data. + default: op + authme.admin.firstspawn: + description: Administrator command to teleport to the first AuthMe spawn. + default: op + authme.admin.forcelogin: + description: Administrator command to force-login an existing user. + default: op + authme.admin.getemail: + description: Administrator command to get the email address of a user, if set. + default: op + authme.admin.getip: + description: Administrator command to get the last known IP of a user. + default: op + authme.admin.lastlogin: + description: Administrator command to see the last login date and time of a user. + default: op + authme.admin.purge: + description: Administrator command to purge old user data. + default: op + authme.admin.purgebannedplayers: + description: Administrator command to purge all data associated with banned players. + default: op + authme.admin.purgelastpos: + description: Administrator command to purge the last position of a user. + default: op + authme.admin.purgeplayer: + description: Administrator command to purge a given player. + default: op + authme.admin.register: + description: Administrator command to register a new user. + default: op + authme.admin.reload: + description: Administrator command to reload the plugin configuration. + default: op + authme.admin.seeotheraccounts: + description: Permission to see the other accounts of the players that log in. + default: op + authme.admin.seerecent: + description: Administrator command to see the last recently logged in players. + default: op + authme.admin.setfirstspawn: + description: Administrator command to set the first AuthMe spawn. + default: op + authme.admin.setspawn: + description: Administrator command to set the AuthMe spawn. + default: op + authme.admin.spawn: + description: Administrator command to teleport to the AuthMe spawn. + default: op + authme.admin.switchantibot: + description: Administrator command to toggle the AntiBot protection status. + default: op + authme.admin.totpdisable: + description: Administrator command to disable the two-factor auth of a user. + default: op + authme.admin.totpviewstatus: + description: Administrator command to see whether a player has enabled two-factor + authentication. + default: op + authme.admin.unregister: + description: Administrator command to unregister an existing user. + default: op + authme.admin.updatemessages: + description: Permission to use the update messages command. + default: op + authme.allowchatbeforelogin: + description: Permission to send chat messages before being logged in. + default: false + authme.allowmultipleaccounts: + description: Permission to be able to register multiple accounts. + default: op + authme.bypassantibot: + description: Permission node to bypass AntiBot protection. + default: op + authme.bypassbungeesend: + description: Permission node to bypass BungeeCord server teleportation. + default: false + authme.bypasscountrycheck: + description: Permission to bypass the GeoIp country code check. + default: false + authme.bypassforcesurvival: + description: Permission for users to bypass force-survival mode. + default: op + authme.bypasspurge: + description: Permission to bypass the purging process. + default: false + authme.debug: + description: Gives access to /authme debug and all its sections + children: + authme.debug.command: true + authme.debug.country: true + authme.debug.db: true + authme.debug.group: true + authme.debug.limbo: true + authme.debug.mail: true + authme.debug.mysqldef: true + authme.debug.perm: true + authme.debug.spawn: true + authme.debug.stats: true + authme.debug.valid: true + authme.debug.command: + description: General permission to use the /authme debug command. + default: op + authme.debug.country: + description: Permission to use the country lookup section. + default: op + authme.debug.db: + description: Permission to view data from the database. + default: op + authme.debug.group: + description: Permission to view permission groups. + default: op + authme.debug.limbo: + description: Permission to use the limbo data viewer. + default: op + authme.debug.mail: + description: Permission to use the test email sender. + default: op + authme.debug.mysqldef: + description: Permission to change nullable status of MySQL columns. + default: op + authme.debug.perm: + description: Permission to use the permission checker. + default: op + authme.debug.spawn: + description: Permission to view spawn information. + default: op + authme.debug.stats: + description: Permission to use the stats section. + default: op + authme.debug.valid: + description: Permission to use sample validation. + default: op + authme.player.*: + description: Gives access to all player commands + children: + authme.player.canbeforced: true + authme.player.captcha: true + authme.player.changepassword: true + authme.player.email.add: true + authme.player.email.change: true + authme.player.email.recover: true + authme.player.email.see: true + authme.player.login: true + authme.player.logout: true + authme.player.protection.quickcommandsprotection: true + authme.player.register: true + authme.player.security.verificationcode: true + authme.player.seeownaccounts: true + authme.player.totpadd: true + authme.player.totpremove: true + authme.player.unregister: true + authme.player.canbeforced: + description: Permission for users a login can be forced to. + default: true + authme.player.captcha: + description: Command permission to use captcha. + default: true + authme.player.changepassword: + description: Command permission to change the password. + default: true + authme.player.email: + description: Gives access to all email commands + children: + authme.player.email.add: true + authme.player.email.change: true + authme.player.email.recover: true + authme.player.email.see: true + authme.player.email.add: + description: Command permission to add an email address. + default: true + authme.player.email.change: + description: Command permission to change the email address. + default: true + authme.player.email.recover: + description: Command permission to recover an account using its email address. + default: true + authme.player.email.see: + description: Command permission to see the own email address. + default: true + authme.player.login: + description: Command permission to login. + default: true + authme.player.logout: + description: Command permission to logout. + default: true + authme.player.protection.quickcommandsprotection: + description: Permission that enables on join quick commands checks for the player. + default: true + authme.player.register: + description: Command permission to register. + default: true + authme.player.security.verificationcode: + description: Permission to use the email verification codes feature. + default: true + authme.player.seeownaccounts: + description: Permission to use to see own other accounts. + default: true + authme.player.totpadd: + description: Permission to enable two-factor authentication. + default: true + authme.player.totpremove: + description: Permission to disable two-factor authentication. + default: true + authme.player.unregister: + description: Command permission to unregister. + default: true + authme.vip: + description: When the server is full and someone with this permission joins the + server, someone will be kicked. + default: false diff --git a/authme-folia/src/test/java/fr/xephi/authme/listener/FoliaPlayerSpawnLocationListenerTest.java b/authme-folia/src/test/java/fr/xephi/authme/listener/FoliaPlayerSpawnLocationListenerTest.java new file mode 100644 index 0000000000..b43f1dcd4a --- /dev/null +++ b/authme-folia/src/test/java/fr/xephi/authme/listener/FoliaPlayerSpawnLocationListenerTest.java @@ -0,0 +1,54 @@ +package fr.xephi.authme.listener; + +import com.destroystokyo.paper.profile.PlayerProfile; +import fr.xephi.authme.service.TeleportationService; +import io.papermc.paper.connection.PlayerConfigurationConnection; +import io.papermc.paper.event.player.AsyncPlayerSpawnLocationEvent; +import org.bukkit.Location; +import org.bukkit.World; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) +public class FoliaPlayerSpawnLocationListenerTest { + + @InjectMocks + private FoliaPlayerSpawnLocationListener listener; + + @Mock + private TeleportationService teleportationService; + + @Test + public void shouldApplyCustomSpawnLocationFromFoliaAsyncEvent() { + // given + World world = mock(World.class); + Location originalSpawn = new Location(world, 1.0, 64.0, 1.0); + Location customSpawn = new Location(world, 10.0, 70.0, 10.0); + PlayerProfile profile = mock(PlayerProfile.class); + given(profile.getName()).willReturn("Bobby"); + PlayerConfigurationConnection connection = mock(PlayerConfigurationConnection.class); + given(connection.getProfile()).willReturn(profile); + given(teleportationService.prepareOnJoinSpawnLocation("Bobby", originalSpawn)).willReturn(customSpawn); + + AsyncPlayerSpawnLocationEvent event = new AsyncPlayerSpawnLocationEvent(connection, originalSpawn, false); + + // when + listener.onPlayerSpawn(event); + + // then + verify(teleportationService).prepareOnJoinSpawnLocation("Bobby", originalSpawn); + assertThat(event.getSpawnLocation(), is(customSpawn)); + } +} diff --git a/authme-folia/src/test/java/fr/xephi/authme/platform/FoliaPlatformAdapterTest.java b/authme-folia/src/test/java/fr/xephi/authme/platform/FoliaPlatformAdapterTest.java new file mode 100644 index 0000000000..a38b004eb4 --- /dev/null +++ b/authme-folia/src/test/java/fr/xephi/authme/platform/FoliaPlatformAdapterTest.java @@ -0,0 +1,128 @@ +package fr.xephi.authme.platform; + +import fr.xephi.authme.listener.FoliaChatListener; +import fr.xephi.authme.listener.FoliaPlayerSpawnLocationListener; +import fr.xephi.authme.listener.PaperLoginValidationListener; +import fr.xephi.authme.listener.PlayerOpenSignListener; +import fr.xephi.authme.process.register.RegisterSecondaryArgument; +import fr.xephi.authme.process.register.RegistrationType; +import net.kyori.adventure.text.Component; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerKickEvent; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.is; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.verify; + +public class FoliaPlatformAdapterTest { + + private final FoliaPlatformAdapter adapter = new FoliaPlatformAdapter(); + + @Test + public void getPlatformNameReturnsExpectedValue() { + assertThat(adapter.getPlatformName(), is("folia-1.21")); + } + + @Test + public void getCompatibilityErrorReturnsNullForCompatibleClasspath() { + assertThat(adapter.getCompatibilityError(), is((String) null)); + } + + @Test + public void isDialogSupportedReturnsTrueWhenApiIsOnClasspath() { + // The Paper dialog API is a test dependency, so DIALOG_AVAILABLE is true + assertThat(adapter.isDialogSupported(), is(true)); + } + + @Test + public void getAdditionalListenersContainsBothFoliaListeners() { + List> listeners = adapter.getAdditionalListeners(); + + assertThat(listeners, containsInAnyOrder( + FoliaChatListener.class, + FoliaPlayerSpawnLocationListener.class, + PaperLoginValidationListener.class, + PlayerOpenSignListener.class)); + } + + @Test + public void shouldDisableLegacyPlayerLoginEventHandling() { + assertThat(adapter.shouldHandlePlayerLoginEvent(), is(false)); + } + + @Test + public void shouldDisableLegacyPlayerSpawnLocationEventHandling() { + assertThat(adapter.shouldHandlePlayerSpawnLocationEvent(), is(false)); + } + + @Test + public void showLoginDialogDelegatesToPaperDialogHelper() { + // given + Player player = mock(Player.class); + + // when / then - FoliaDialogHelper uses API calls that require a running server. + // Use mockStatic to verify delegation without invoking the full Paper server stack. + try (MockedStatic helperMock = mockStatic(PaperDialogHelper.class)) { + adapter.showLoginDialog(player); + + helperMock.verify(() -> PaperDialogHelper.showLoginDialog(player)); + } + } + + @Test + public void showRegisterDialogDelegatesToPaperDialogHelper() { + // given + Player player = mock(Player.class); + + // when / then + try (MockedStatic helperMock = mockStatic(PaperDialogHelper.class)) { + adapter.showRegisterDialog(player, RegistrationType.EMAIL, RegisterSecondaryArgument.CONFIRMATION); + + helperMock.verify(() -> PaperDialogHelper.showRegisterDialog( + eq(player), + eq(RegistrationType.EMAIL), + eq(RegisterSecondaryArgument.CONFIRMATION))); + } + } + + @Test + public void teleportPlayerCallsTeleportAsync() { + // given + Player player = mock(Player.class); + Location location = mock(Location.class); + + // when + adapter.teleportPlayer(player, location); + + // then + verify(player).teleportAsync(location); + } + + @Test + public void getKickReasonReturnsPlainTextFromComponent() { + // given + // PlayerKickEvent is not final; reason() is a Paper API method, stubable with Mockito 5 + PlayerKickEvent event = mock(PlayerKickEvent.class); + given(event.reason()).willReturn(Component.text("You were kicked")); + + // when + String reason = adapter.getKickReason(event); + + // then + assertThat(reason, is("You were kicked")); + } +} + + diff --git a/authme-paper-common/pom.xml b/authme-paper-common/pom.xml new file mode 100644 index 0000000000..dc909fed8b --- /dev/null +++ b/authme-paper-common/pom.xml @@ -0,0 +1,60 @@ + + + 4.0.0 + + + fr.xephi + authme-parent + ${revision} + + + authme-paper-common + jar + AuthMe Paper Common + Shared Paper-derived implementation code for AuthMe Paper and Folia + + + 21 + + + + + fr.xephi + authme-core + ${project.version} + compile + + + io.papermc.paper + paper-api + provided + + + + ch.jalu + configme + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + + + org.mockito + mockito-junit-jupiter + test + + + org.hamcrest + hamcrest + test + + + diff --git a/authme-paper-common/src/main/java/fr/xephi/authme/listener/AbstractPaperAsyncChatListener.java b/authme-paper-common/src/main/java/fr/xephi/authme/listener/AbstractPaperAsyncChatListener.java new file mode 100644 index 0000000000..78c3d07f53 --- /dev/null +++ b/authme-paper-common/src/main/java/fr/xephi/authme/listener/AbstractPaperAsyncChatListener.java @@ -0,0 +1,61 @@ +package fr.xephi.authme.listener; + +import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.message.Messages; +import fr.xephi.authme.permission.PermissionsManager; +import fr.xephi.authme.permission.PlayerStatePermission; +import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.RestrictionSettings; +import io.papermc.paper.event.player.AsyncChatEvent; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; + +import javax.inject.Inject; + +/** + * Shared async chat listener logic for Paper-derived platforms. + */ +public abstract class AbstractPaperAsyncChatListener implements Listener { + + @Inject + private Settings settings; + + @Inject + private Messages messages; + + @Inject + private ListenerService listenerService; + + @Inject + private PermissionsManager permissionsManager; + + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) + public void onPlayerChat(AsyncChatEvent event) { + if (settings.getProperty(RestrictionSettings.ALLOW_CHAT)) { + return; + } + + Player player = event.getPlayer(); + boolean mayPlayerSendChat = !listenerService.shouldCancelEvent(player) + || permissionsManager.hasPermission(player, PlayerStatePermission.ALLOW_CHAT_BEFORE_LOGIN); + + if (mayPlayerSendChat) { + removeUnauthorizedViewers(event); + } else { + event.setCancelled(true); + messages.send(player, MessageKey.DENIED_CHAT); + } + } + + private void removeUnauthorizedViewers(AsyncChatEvent event) { + if (settings.getProperty(RestrictionSettings.HIDE_CHAT)) { + event.viewers().removeIf( + viewer -> viewer instanceof Player p && listenerService.shouldCancelEvent(p)); + if (event.viewers().isEmpty()) { + event.setCancelled(true); + } + } + } +} diff --git a/authme-paper-common/src/main/java/fr/xephi/authme/listener/AbstractPaperAsyncPlayerSpawnLocationListener.java b/authme-paper-common/src/main/java/fr/xephi/authme/listener/AbstractPaperAsyncPlayerSpawnLocationListener.java new file mode 100644 index 0000000000..7ec2f6f05d --- /dev/null +++ b/authme-paper-common/src/main/java/fr/xephi/authme/listener/AbstractPaperAsyncPlayerSpawnLocationListener.java @@ -0,0 +1,34 @@ +package fr.xephi.authme.listener; + +import fr.xephi.authme.service.TeleportationService; +import io.papermc.paper.event.player.AsyncPlayerSpawnLocationEvent; +import org.bukkit.Location; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; + +import javax.inject.Inject; + +/** + * Shared async spawn-location listener shell for Paper-derived platforms. + */ +public abstract class AbstractPaperAsyncPlayerSpawnLocationListener implements Listener { + + @Inject + protected TeleportationService teleportationService; + + @EventHandler(priority = EventPriority.HIGH) + public void onPlayerSpawn(AsyncPlayerSpawnLocationEvent event) { + SpawnLocationTracker.markEventCalled(); + + String playerName = event.getConnection().getProfile().getName(); + Location originalSpawnLocation = event.getSpawnLocation(); + + Location customSpawnLocation = determineCustomSpawnLocation(playerName, originalSpawnLocation); + if (customSpawnLocation != null) { + event.setSpawnLocation(customSpawnLocation); + } + } + + protected abstract Location determineCustomSpawnLocation(String playerName, Location originalSpawnLocation); +} diff --git a/authme-paper-common/src/main/java/fr/xephi/authme/listener/PaperLoginValidationListener.java b/authme-paper-common/src/main/java/fr/xephi/authme/listener/PaperLoginValidationListener.java new file mode 100644 index 0000000000..a507163ae3 --- /dev/null +++ b/authme-paper-common/src/main/java/fr/xephi/authme/listener/PaperLoginValidationListener.java @@ -0,0 +1,75 @@ +package fr.xephi.authme.listener; + +import com.destroystokyo.paper.profile.PlayerProfile; +import fr.xephi.authme.message.Messages; +import io.papermc.paper.connection.PlayerLoginConnection; +import io.papermc.paper.event.connection.PlayerConnectionValidateLoginEvent; +import io.papermc.paper.event.player.PlayerServerFullCheckEvent; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; + +import javax.inject.Inject; + +/** + * Shared login validation listener for Paper-derived platforms. + */ +public class PaperLoginValidationListener implements Listener { + + private static final LegacyComponentSerializer LEGACY_SERIALIZER = LegacyComponentSerializer.legacySection(); + + @Inject + private OnJoinVerifier onJoinVerifier; + + @Inject + private Messages messages; + + @EventHandler(priority = EventPriority.LOW) + public void onPlayerConnectionValidateLogin(PlayerConnectionValidateLoginEvent event) { + if (!(event.getConnection() instanceof PlayerLoginConnection loginConnection)) { + return; + } + + String playerName = getPlayerName(loginConnection); + if (playerName == null) { + return; + } + + try { + onJoinVerifier.checkSingleSession(playerName); + } catch (FailedVerificationException e) { + String kickMessage = messages.retrieveSingle(playerName, e.getReason(), e.getArgs()); + event.kickMessage(LEGACY_SERIALIZER.deserialize(kickMessage)); + } + } + + @EventHandler(priority = EventPriority.LOW) + public void onPlayerServerFullCheck(PlayerServerFullCheckEvent event) { + if (event.isAllowed()) { + return; + } + + String playerName = event.getPlayerProfile().getName(); + if (playerName == null) { + return; + } + + String kickMessage = onJoinVerifier.getServerFullKickMessageIfDenied(playerName); + if (kickMessage == null) { + event.allow(true); + } else { + event.deny(LEGACY_SERIALIZER.deserialize(kickMessage)); + } + } + + private static String getPlayerName(PlayerLoginConnection connection) { + PlayerProfile profile = connection.getAuthenticatedProfile(); + if (profile != null && profile.getName() != null) { + return profile.getName(); + } + + profile = connection.getUnsafeProfile(); + return profile == null ? null : profile.getName(); + } +} diff --git a/authme-paper-common/src/main/java/fr/xephi/authme/listener/PlayerOpenSignListener.java b/authme-paper-common/src/main/java/fr/xephi/authme/listener/PlayerOpenSignListener.java new file mode 100644 index 0000000000..6cd4bec6ef --- /dev/null +++ b/authme-paper-common/src/main/java/fr/xephi/authme/listener/PlayerOpenSignListener.java @@ -0,0 +1,29 @@ +package fr.xephi.authme.listener; + +import io.papermc.paper.event.player.PlayerOpenSignEvent; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; + +import javax.inject.Inject; + +/** + * Blocks unauthenticated players from reading signs. + */ +public class PlayerOpenSignListener implements Listener { + + @Inject + private ListenerService listenerService; + + PlayerOpenSignListener() { + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) + public void onPlayerOpenSign(PlayerOpenSignEvent event) { + Player player = event.getPlayer(); + if (listenerService.shouldCancelEvent(player)) { + event.setCancelled(true); + } + } +} diff --git a/authme-paper-common/src/main/java/fr/xephi/authme/platform/AbstractPaperPlatformAdapter.java b/authme-paper-common/src/main/java/fr/xephi/authme/platform/AbstractPaperPlatformAdapter.java new file mode 100644 index 0000000000..9910da842b --- /dev/null +++ b/authme-paper-common/src/main/java/fr/xephi/authme/platform/AbstractPaperPlatformAdapter.java @@ -0,0 +1,75 @@ +package fr.xephi.authme.platform; + +import fr.xephi.authme.AuthMe; +import fr.xephi.authme.command.CommandDescription; +import fr.xephi.authme.command.CommandHandler; +import fr.xephi.authme.process.register.RegisterSecondaryArgument; +import fr.xephi.authme.process.register.RegistrationType; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerKickEvent; + +import java.util.Collection; + +/** + * Shared platform adapter behavior for Paper-derived servers. + */ +public abstract class AbstractPaperPlatformAdapter extends AbstractSpigotPlatformAdapter { + + private static final boolean DIALOG_AVAILABLE = hasDialogApi(); + + @Override + public void teleportPlayer(Player player, Location location) { + player.teleportAsync(location); + } + + @Override + public Location getPlayerRespawnLocation(Player player) { + return player.getRespawnLocation(); + } + + @Override + public String getKickReason(PlayerKickEvent event) { + return PlainTextComponentSerializer.plainText().serialize(event.reason()); + } + + @Override + public boolean isDialogSupported() { + return DIALOG_AVAILABLE; + } + + @Override + public void showLoginDialog(Player player) { + PaperDialogHelper.showLoginDialog(player); + } + + @Override + public void showRegisterDialog(Player player, RegistrationType type, RegisterSecondaryArgument secondArg) { + PaperDialogHelper.showRegisterDialog(player, type, secondArg); + } + + @Override + public void registerCommands(AuthMe plugin, CommandHandler commandHandler, Collection commands) { + new PaperBrigadierCommandRegistrar(commandHandler::processCommand).registerCommands(plugin, commands); + } + + @Override + public boolean shouldHandlePlayerLoginEvent() { + return false; + } + + @Override + public boolean shouldHandlePlayerSpawnLocationEvent() { + return false; + } + + private static boolean hasDialogApi() { + try { + Class.forName("io.papermc.paper.dialog.Dialog", false, AbstractPaperPlatformAdapter.class.getClassLoader()); + return true; + } catch (ClassNotFoundException ignored) { + return false; + } + } +} diff --git a/authme-paper-common/src/main/java/fr/xephi/authme/platform/PaperBrigadierCommandRegistrar.java b/authme-paper-common/src/main/java/fr/xephi/authme/platform/PaperBrigadierCommandRegistrar.java new file mode 100644 index 0000000000..912c650446 --- /dev/null +++ b/authme-paper-common/src/main/java/fr/xephi/authme/platform/PaperBrigadierCommandRegistrar.java @@ -0,0 +1,107 @@ +package fr.xephi.authme.platform; + +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import fr.xephi.authme.AuthMe; +import fr.xephi.authme.command.CommandArgumentDescription; +import fr.xephi.authme.command.CommandDescription; +import fr.xephi.authme.command.CommandUtils; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import io.papermc.paper.command.brigadier.Commands; +import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents; +import org.bukkit.command.CommandSender; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.function.BiFunction; + +/** + * Registers AuthMe commands in the Paper-derived Brigadier tree while delegating + * all execution to the existing command handler. + */ +final class PaperBrigadierCommandRegistrar { + + private static final int COMMAND_SUCCESS = 1; + private static final String FALLBACK_ARGUMENT_NAME = "unparsed"; + private static final String EXTRA_ARGUMENTS_NAME = "extraArgs"; + + private final BiFunction, Boolean> commandExecutor; + + PaperBrigadierCommandRegistrar(BiFunction, Boolean> commandExecutor) { + this.commandExecutor = commandExecutor; + } + + void registerCommands(AuthMe plugin, Collection commands) { + plugin.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, event -> { + Commands registrar = event.registrar(); + for (PaperBrigadierCommandRegistration command : buildRegistrations(commands)) { + registrar.register(command.getNode(), command.getDescription(), command.getAliases()); + } + }); + } + + List buildRegistrations(Collection commands) { + List registrations = new ArrayList<>(commands.size()); + for (CommandDescription command : commands) { + registrations.add(buildRegistration(command)); + } + return registrations; + } + + private PaperBrigadierCommandRegistration buildRegistration(CommandDescription command) { + return new PaperBrigadierCommandRegistration( + buildLiteralNode(command.getLabels().get(0), command).build(), + command.getDescription(), + command.getLabels().subList(1, command.getLabels().size())); + } + + private LiteralArgumentBuilder buildLiteralNode(String label, CommandDescription command) { + LiteralArgumentBuilder builder = LiteralArgumentBuilder.literal(label) + .executes(this::executeInput); + + if (!command.getChildren().isEmpty()) { + for (CommandDescription child : command.getChildren()) { + for (String childLabel : child.getLabels()) { + builder.then(buildLiteralNode(childLabel, child)); + } + } + builder.then(createFallbackArgument(FALLBACK_ARGUMENT_NAME)); + return builder; + } + + addArgumentChain(builder, command.getArguments()); + if (command.getArguments().isEmpty()) { + builder.then(createFallbackArgument(EXTRA_ARGUMENTS_NAME)); + } + return builder; + } + + private void addArgumentChain(ArgumentBuilder parent, + List arguments) { + ArgumentBuilder currentParent = parent; + for (int index = 0; index < arguments.size(); ++index) { + CommandArgumentDescription argument = arguments.get(index); + boolean isLastArgument = index == arguments.size() - 1; + RequiredArgumentBuilder argumentBuilder = + RequiredArgumentBuilder.argument(argument.getName(), + isLastArgument ? StringArgumentType.greedyString() : StringArgumentType.word()) + .executes(this::executeInput); + currentParent.then(argumentBuilder); + currentParent = argumentBuilder; + } + } + + private RequiredArgumentBuilder createFallbackArgument(String name) { + return RequiredArgumentBuilder.argument(name, StringArgumentType.greedyString()) + .executes(this::executeInput); + } + + private int executeInput(CommandContext context) { + commandExecutor.apply(context.getSource().getSender(), CommandUtils.splitInput(context.getInput())); + return COMMAND_SUCCESS; + } +} diff --git a/authme-paper-common/src/main/java/fr/xephi/authme/platform/PaperBrigadierCommandRegistration.java b/authme-paper-common/src/main/java/fr/xephi/authme/platform/PaperBrigadierCommandRegistration.java new file mode 100644 index 0000000000..15f982de3e --- /dev/null +++ b/authme-paper-common/src/main/java/fr/xephi/authme/platform/PaperBrigadierCommandRegistration.java @@ -0,0 +1,35 @@ +package fr.xephi.authme.platform; + +import com.mojang.brigadier.tree.LiteralCommandNode; +import io.papermc.paper.command.brigadier.CommandSourceStack; + +import java.util.Collection; + +/** + * Immutable registration data for a single Paper-derived Brigadier root command. + */ +final class PaperBrigadierCommandRegistration { + + private final LiteralCommandNode node; + private final String description; + private final Collection aliases; + + PaperBrigadierCommandRegistration(LiteralCommandNode node, String description, + Collection aliases) { + this.node = node; + this.description = description; + this.aliases = aliases; + } + + LiteralCommandNode getNode() { + return node; + } + + String getDescription() { + return description; + } + + Collection getAliases() { + return aliases; + } +} diff --git a/authme-paper-common/src/main/java/fr/xephi/authme/platform/PaperDialogHelper.java b/authme-paper-common/src/main/java/fr/xephi/authme/platform/PaperDialogHelper.java new file mode 100644 index 0000000000..b7048721c0 --- /dev/null +++ b/authme-paper-common/src/main/java/fr/xephi/authme/platform/PaperDialogHelper.java @@ -0,0 +1,86 @@ +package fr.xephi.authme.platform; + +import fr.xephi.authme.process.register.RegisterSecondaryArgument; +import fr.xephi.authme.process.register.RegistrationType; +import io.papermc.paper.dialog.Dialog; +import io.papermc.paper.registry.data.dialog.ActionButton; +import io.papermc.paper.registry.data.dialog.DialogBase; +import io.papermc.paper.registry.data.dialog.action.DialogAction; +import io.papermc.paper.registry.data.dialog.input.DialogInput; +import io.papermc.paper.registry.data.dialog.type.DialogType; +import net.kyori.adventure.text.Component; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; + +/** + * Encapsulates the Paper-derived dialog API calls used by Paper and Folia. + */ +final class PaperDialogHelper { + + private PaperDialogHelper() { + } + + static void showLoginDialog(Player player) { + DialogInput passwordInput = DialogInput.text("password", Component.text("Password")) + .maxLength(100) + .build(); + + DialogBase base = DialogBase.builder(Component.text("Login")) + .inputs(List.of(passwordInput)) + .afterAction(DialogBase.DialogAfterAction.CLOSE) + .build(); + + ActionButton loginButton = ActionButton.builder(Component.text("Login")) + .action(DialogAction.commandTemplate("login $(password)")) + .build(); + + Dialog dialog = Dialog.create(factory -> factory.empty() + .base(base) + .type(DialogType.multiAction(List.of(loginButton)).build())); + + player.showDialog(dialog); + } + + static void showRegisterDialog(Player player, RegistrationType type, RegisterSecondaryArgument secondArg) { + List inputs = new ArrayList<>(); + String template; + + if (type == RegistrationType.EMAIL) { + inputs.add(DialogInput.text("email", Component.text("Email")).maxLength(100).build()); + if (secondArg == RegisterSecondaryArgument.CONFIRMATION) { + inputs.add(DialogInput.text("confirm", Component.text("Confirm Email")).maxLength(100).build()); + template = "register $(email) $(confirm)"; + } else { + template = "register $(email)"; + } + } else { + inputs.add(DialogInput.text("password", Component.text("Password")).maxLength(100).build()); + if (secondArg == RegisterSecondaryArgument.CONFIRMATION) { + inputs.add(DialogInput.text("confirm", Component.text("Confirm Password")).maxLength(100).build()); + template = "register $(password) $(confirm)"; + } else if (secondArg == RegisterSecondaryArgument.EMAIL_MANDATORY) { + inputs.add(DialogInput.text("email", Component.text("Email")).maxLength(100).build()); + template = "register $(password) $(email)"; + } else { + template = "register $(password)"; + } + } + + DialogBase base = DialogBase.builder(Component.text("Register")) + .inputs(inputs) + .afterAction(DialogBase.DialogAfterAction.CLOSE) + .build(); + + ActionButton registerButton = ActionButton.builder(Component.text("Register")) + .action(DialogAction.commandTemplate(template)) + .build(); + + Dialog dialog = Dialog.create(factory -> factory.empty() + .base(base) + .type(DialogType.multiAction(List.of(registerButton)).build())); + + player.showDialog(dialog); + } +} diff --git a/authme-paper-common/src/test/java/fr/xephi/authme/listener/AbstractPaperAsyncChatListenerTest.java b/authme-paper-common/src/test/java/fr/xephi/authme/listener/AbstractPaperAsyncChatListenerTest.java new file mode 100644 index 0000000000..5a9531dd10 --- /dev/null +++ b/authme-paper-common/src/test/java/fr/xephi/authme/listener/AbstractPaperAsyncChatListenerTest.java @@ -0,0 +1,138 @@ +package fr.xephi.authme.listener; + +import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.message.Messages; +import fr.xephi.authme.permission.PermissionsManager; +import fr.xephi.authme.permission.PlayerStatePermission; +import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.RestrictionSettings; +import io.papermc.paper.event.player.AsyncChatEvent; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.chat.SignedMessage; +import net.kyori.adventure.text.Component; +import org.bukkit.entity.Player; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import java.util.HashSet; +import java.util.Set; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.is; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) +public class AbstractPaperAsyncChatListenerTest { + + @InjectMocks + private TestAsyncChatListener listener; + + @Mock + private Settings settings; + @Mock + private Messages messages; + @Mock + private ListenerService listenerService; + @Mock + private PermissionsManager permissionsManager; + + @Test + public void shouldAllowChatWhenGlobalChatIsEnabled() { + given(settings.getProperty(RestrictionSettings.ALLOW_CHAT)).willReturn(true); + AsyncChatEvent event = newAsyncChatEvent(mock(Player.class), new HashSet<>()); + + listener.onPlayerChat(event); + + verifyNoInteractions(listenerService, messages); + assertThat(event.isCancelled(), is(false)); + } + + @Test + public void shouldCancelChatForUnauthenticatedPlayer() { + given(settings.getProperty(RestrictionSettings.ALLOW_CHAT)).willReturn(false); + Player player = mock(Player.class); + AsyncChatEvent event = newAsyncChatEvent(player, new HashSet<>()); + given(listenerService.shouldCancelEvent(player)).willReturn(true); + given(permissionsManager.hasPermission(player, PlayerStatePermission.ALLOW_CHAT_BEFORE_LOGIN)).willReturn(false); + + listener.onPlayerChat(event); + + assertThat(event.isCancelled(), is(true)); + verify(messages).send(player, MessageKey.DENIED_CHAT); + } + + @Test + public void shouldAllowChatForPlayerWithBypassPermission() { + given(settings.getProperty(RestrictionSettings.ALLOW_CHAT)).willReturn(false); + given(settings.getProperty(RestrictionSettings.HIDE_CHAT)).willReturn(false); + Player player = mock(Player.class); + AsyncChatEvent event = newAsyncChatEvent(player, new HashSet<>()); + given(listenerService.shouldCancelEvent(player)).willReturn(true); + given(permissionsManager.hasPermission(player, PlayerStatePermission.ALLOW_CHAT_BEFORE_LOGIN)).willReturn(true); + + listener.onPlayerChat(event); + + assertThat(event.isCancelled(), is(false)); + verifyNoInteractions(messages); + } + + @Test + public void shouldFilterUnauthenticatedViewersWhenHideChatEnabled() { + given(settings.getProperty(RestrictionSettings.ALLOW_CHAT)).willReturn(false); + given(settings.getProperty(RestrictionSettings.HIDE_CHAT)).willReturn(true); + Player sender = mock(Player.class); + Player unauthViewer = mock(Player.class); + Player authViewer = mock(Player.class); + Set viewers = new HashSet<>(); + viewers.add(unauthViewer); + viewers.add(authViewer); + AsyncChatEvent event = newAsyncChatEvent(sender, viewers); + given(listenerService.shouldCancelEvent(sender)).willReturn(false); + given(listenerService.shouldCancelEvent(unauthViewer)).willReturn(true); + given(listenerService.shouldCancelEvent(authViewer)).willReturn(false); + + listener.onPlayerChat(event); + + assertThat(viewers, contains(authViewer)); + assertThat(event.isCancelled(), is(false)); + } + + @Test + public void shouldCancelEventWhenAllViewersFiltered() { + given(settings.getProperty(RestrictionSettings.ALLOW_CHAT)).willReturn(false); + given(settings.getProperty(RestrictionSettings.HIDE_CHAT)).willReturn(true); + Player sender = mock(Player.class); + Player unauthViewer = mock(Player.class); + Set viewers = new HashSet<>(); + viewers.add(unauthViewer); + AsyncChatEvent event = newAsyncChatEvent(sender, viewers); + given(listenerService.shouldCancelEvent(sender)).willReturn(false); + given(listenerService.shouldCancelEvent(unauthViewer)).willReturn(true); + + listener.onPlayerChat(event); + + assertThat(viewers, empty()); + assertThat(event.isCancelled(), is(true)); + } + + private static AsyncChatEvent newAsyncChatEvent(Player player, Set viewers) { + return new AsyncChatEvent(true, player, viewers, + (source, sourceDisplayName, message, viewer) -> message, + Component.text("test"), Component.text("test"), + mock(SignedMessage.class)); + } + + private static final class TestAsyncChatListener extends AbstractPaperAsyncChatListener { + } +} diff --git a/authme-paper-common/src/test/java/fr/xephi/authme/listener/PaperLoginValidationListenerTest.java b/authme-paper-common/src/test/java/fr/xephi/authme/listener/PaperLoginValidationListenerTest.java new file mode 100644 index 0000000000..99c908761b --- /dev/null +++ b/authme-paper-common/src/test/java/fr/xephi/authme/listener/PaperLoginValidationListenerTest.java @@ -0,0 +1,94 @@ +package fr.xephi.authme.listener; + +import com.destroystokyo.paper.profile.PlayerProfile; +import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.message.Messages; +import io.papermc.paper.connection.PlayerConfigurationConnection; +import io.papermc.paper.connection.PlayerLoginConnection; +import io.papermc.paper.event.connection.PlayerConnectionValidateLoginEvent; +import io.papermc.paper.event.player.PlayerServerFullCheckEvent; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) +public class PaperLoginValidationListenerTest { + + @InjectMocks + private PaperLoginValidationListener listener; + + @Mock + private OnJoinVerifier onJoinVerifier; + + @Mock + private Messages messages; + + @Test + public void shouldIgnoreConfigurationStageValidationEvent() { + PlayerConfigurationConnection connection = mock(PlayerConfigurationConnection.class); + PlayerConnectionValidateLoginEvent event = new PlayerConnectionValidateLoginEvent(connection, null); + + listener.onPlayerConnectionValidateLogin(event); + + verifyNoInteractions(onJoinVerifier, messages); + assertThat(event.isAllowed(), is(true)); + } + + @Test + public void shouldKickDuplicateSessionOnValidationEvent() throws FailedVerificationException { + PlayerLoginConnection connection = mock(PlayerLoginConnection.class); + PlayerProfile profile = mock(PlayerProfile.class); + given(connection.getAuthenticatedProfile()).willReturn(profile); + given(profile.getName()).willReturn("Bobby"); + given(messages.retrieveSingle("Bobby", MessageKey.USERNAME_ALREADY_ONLINE_ERROR)) + .willReturn("&cAlready online"); + FailedVerificationException exception = new FailedVerificationException(MessageKey.USERNAME_ALREADY_ONLINE_ERROR); + org.mockito.BDDMockito.willThrow(exception).given(onJoinVerifier).checkSingleSession("Bobby"); + PlayerConnectionValidateLoginEvent event = new PlayerConnectionValidateLoginEvent(connection, null); + + listener.onPlayerConnectionValidateLogin(event); + + assertThat(event.isAllowed(), is(false)); + assertThat(LegacyComponentSerializer.legacySection().serialize(event.getKickMessage()), is("&cAlready online")); + } + + @Test + public void shouldAllowServerFullEventWhenVerifierMakesRoom() { + PlayerProfile profile = mock(PlayerProfile.class); + given(profile.getName()).willReturn("VipPlayer"); + given(onJoinVerifier.getServerFullKickMessageIfDenied("VipPlayer")).willReturn(null); + PlayerServerFullCheckEvent event = new PlayerServerFullCheckEvent(profile, Component.text("full"), true); + + listener.onPlayerServerFullCheck(event); + + assertThat(event.isAllowed(), is(true)); + } + + @Test + public void shouldDenyServerFullEventWithVerifierMessage() { + PlayerProfile profile = mock(PlayerProfile.class); + given(profile.getName()).willReturn("RegularPlayer"); + given(onJoinVerifier.getServerFullKickMessageIfDenied("RegularPlayer")).willReturn("&cServer full"); + PlayerServerFullCheckEvent event = new PlayerServerFullCheckEvent(profile, Component.text("full"), true); + + listener.onPlayerServerFullCheck(event); + + assertThat(event.isAllowed(), is(false)); + assertThat(LegacyComponentSerializer.legacySection().serialize(event.kickMessage()), is("&cServer full")); + verify(onJoinVerifier).getServerFullKickMessageIfDenied("RegularPlayer"); + } +} diff --git a/authme-paper-common/src/test/java/fr/xephi/authme/listener/PlayerOpenSignListenerTest.java b/authme-paper-common/src/test/java/fr/xephi/authme/listener/PlayerOpenSignListenerTest.java new file mode 100644 index 0000000000..4e80041d0e --- /dev/null +++ b/authme-paper-common/src/test/java/fr/xephi/authme/listener/PlayerOpenSignListenerTest.java @@ -0,0 +1,63 @@ +package fr.xephi.authme.listener; + +import io.papermc.paper.event.player.PlayerOpenSignEvent; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; +import org.bukkit.block.Sign; +import org.bukkit.block.sign.Side; +import org.bukkit.entity.Player; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) +public class PlayerOpenSignListenerTest { + + @InjectMocks + private PlayerOpenSignListener listener; + + @Mock + private ListenerService listenerService; + + @Test + public void shouldCancelEventForUnauthenticated() { + // given + // PlayerOpenSignEvent#getPlayer is final (PlayerEvent), so use spy on a real instance + Player player = mock(Player.class); + PlayerOpenSignEvent event = spy(new PlayerOpenSignEvent( + player, mock(Sign.class), Side.FRONT, PlayerOpenSignEvent.Cause.UNKNOWN)); + given(listenerService.shouldCancelEvent(player)).willReturn(true); + + // when + listener.onPlayerOpenSign(event); + + verify(event).setCancelled(true); + } + + @Test + public void shouldNotCancelEventForAuthenticatedPlayer() { + Player player = mock(Player.class); + PlayerOpenSignEvent event = spy(new PlayerOpenSignEvent( + player, mock(Sign.class), Side.FRONT, PlayerOpenSignEvent.Cause.UNKNOWN)); + given(listenerService.shouldCancelEvent(player)).willReturn(false); + + // when + listener.onPlayerOpenSign(event); + + verify(event, never()).setCancelled(anyBoolean()); + } +} diff --git a/authme-paper-common/src/test/java/fr/xephi/authme/platform/PaperBrigadierCommandRegistrarTest.java b/authme-paper-common/src/test/java/fr/xephi/authme/platform/PaperBrigadierCommandRegistrarTest.java new file mode 100644 index 0000000000..403d8dc3a7 --- /dev/null +++ b/authme-paper-common/src/test/java/fr/xephi/authme/platform/PaperBrigadierCommandRegistrarTest.java @@ -0,0 +1,134 @@ +package fr.xephi.authme.platform; + +import com.mojang.brigadier.CommandDispatcher; +import fr.xephi.authme.command.CommandArgumentDescription; +import fr.xephi.authme.command.CommandDescription; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import org.bukkit.command.CommandSender; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) +public class PaperBrigadierCommandRegistrarTest { + + private PaperBrigadierCommandRegistrar registrar; + private CommandDispatcher dispatcher; + private AtomicReference executingSender; + private List> executedCommands; + + @Mock + private CommandSourceStack sourceStack; + @Mock + private CommandSender sender; + + @BeforeEach + public void setUp() { + executingSender = new AtomicReference<>(); + executedCommands = new ArrayList<>(); + registrar = new PaperBrigadierCommandRegistrar((commandSender, parts) -> { + executingSender.set(commandSender); + executedCommands.add(parts); + return true; + }); + dispatcher = new CommandDispatcher<>(); + lenient().when(sourceStack.getSender()).thenReturn(sender); + + for (PaperBrigadierCommandRegistration registration + : registrar.buildRegistrations(createCommands())) { + dispatcher.getRoot().addChild(registration.getNode()); + } + } + + @Test + public void shouldExposeBaseCommandAliases() { + PaperBrigadierCommandRegistration registration = registrar.buildRegistrations(createCommands()) + .stream() + .filter(command -> "login".equals(command.getNode().getLiteral())) + .findFirst() + .orElse(null); + + assertThat(registration, notNullValue()); + assertThat(registration.getAliases(), contains("l", "log")); + } + + @Test + public void shouldDelegateBaseCommandExecutionToExistingHandler() throws Exception { + dispatcher.execute("authme", sourceStack); + + assertThat(executingSender.get(), equalTo(sender)); + assertThat(executedCommands, contains(List.of("authme"))); + } + + @Test + public void shouldDelegateUnknownSubcommandThroughFallback() throws Exception { + dispatcher.execute("authme typo", sourceStack); + + assertThat(executedCommands, contains(List.of("authme", "typo"))); + } + + @Test + public void shouldDelegateMissingArgumentsToExistingHandler() throws Exception { + dispatcher.execute("login", sourceStack); + + assertThat(executedCommands, contains(List.of("login"))); + } + + @Test + public void shouldDelegateExtraArgumentsToExistingHandler() throws Exception { + dispatcher.execute("login password extra", sourceStack); + + assertThat(executedCommands, contains(List.of("login", "password", "extra"))); + } + + @Test + public void shouldRegisterChildAliasesInBrigadierTree() { + assertThat(dispatcher.getCompletionSuggestions(dispatcher.parse("authme ", sourceStack)).join() + .getList().stream().map(suggestion -> suggestion.getText()).toList(), hasItem("reg")); + } + + private static Collection createCommands() { + CommandDescription authmeBase = command(List.of("authme"), "AuthMe root", List.of(), List.of()); + CommandDescription authmeRegister = command(List.of("register", "reg"), + "Register player", List.of( + new CommandArgumentDescription("player", "Player name", false), + new CommandArgumentDescription("password", "Password", false)), + List.of()); + lenient().when(authmeBase.getChildren()).thenReturn(List.of(authmeRegister)); + + CommandDescription loginBase = command(List.of("login", "l", "log"), + "Login", List.of(new CommandArgumentDescription("password", "Password", false)), List.of()); + + return List.of(authmeBase, loginBase); + } + + private static CommandDescription command(List labels, String description, + List arguments, + List children) { + CommandDescription command = mock(CommandDescription.class); + lenient().when(command.getLabels()).thenReturn(labels); + lenient().when(command.getDescription()).thenReturn(description); + lenient().when(command.getArguments()).thenReturn(arguments); + lenient().when(command.getChildren()).thenReturn(children); + return command; + } +} diff --git a/authme-paper/pom.xml b/authme-paper/pom.xml new file mode 100644 index 0000000000..44a92563ca --- /dev/null +++ b/authme-paper/pom.xml @@ -0,0 +1,152 @@ + + + 4.0.0 + + + fr.xephi + authme-parent + ${revision} + + + authme-paper + jar + AuthMe Paper + AuthMe plugin for PaperMC 1.21 + + + 21 + 1.21.11 + Paper + + + + + fr.xephi + authme-paper-common + ${project.version} + compile + + + fr.xephi + authme-core + ${project.version} + compile + + + io.papermc.paper + paper-api + provided + + + + ch.jalu + configme + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + + + org.mockito + mockito-junit-jupiter + test + + + org.hamcrest + hamcrest + test + + + + + AuthMe-${project.version}-${module.classifier} + + + + src/main/resources + true + + + .. + false + + LICENSE + + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + shade + package + + shade + + + + + + org.apache.maven.plugins + maven-source-plugin + + ${project.build.finalName} + + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + ${project.build.finalName} + ${module.java.version} + false + + + + attach-javadoc + + jar + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + remove-original-shaded-jar + package + + run + + + + + + + + + + + + diff --git a/authme-paper/src/main/java/fr/xephi/authme/listener/PaperChatListener.java b/authme-paper/src/main/java/fr/xephi/authme/listener/PaperChatListener.java new file mode 100644 index 0000000000..5723f7f482 --- /dev/null +++ b/authme-paper/src/main/java/fr/xephi/authme/listener/PaperChatListener.java @@ -0,0 +1,18 @@ +package fr.xephi.authme.listener; + +import io.papermc.paper.event.player.AsyncChatEvent; + +/** + * Chat listener for PaperMC that handles {@link AsyncChatEvent} + * (replaces the deprecated {@code AsyncPlayerChatEvent} on Paper). + * Mirrors the logic of {@code PlayerListener#onPlayerChat} for full feature parity, + * including HIDE_CHAT recipient filtering via the Adventure API. + */ +public class PaperChatListener extends AbstractPaperAsyncChatListener { + + /** + * Constructor. + */ + public PaperChatListener() { + } +} diff --git a/authme-paper/src/main/java/fr/xephi/authme/listener/PaperPlayerSpawnLocationListener.java b/authme-paper/src/main/java/fr/xephi/authme/listener/PaperPlayerSpawnLocationListener.java new file mode 100644 index 0000000000..2495f66f40 --- /dev/null +++ b/authme-paper/src/main/java/fr/xephi/authme/listener/PaperPlayerSpawnLocationListener.java @@ -0,0 +1,28 @@ +package fr.xephi.authme.listener; + +import fr.xephi.authme.service.BukkitService; +import io.papermc.paper.event.player.AsyncPlayerSpawnLocationEvent; +import org.bukkit.Location; + +import javax.inject.Inject; + +/** + * Paper listener that resolves custom join spawn locations for async spawn events. + */ +public class PaperPlayerSpawnLocationListener extends AbstractPaperAsyncPlayerSpawnLocationListener { + + @Inject + private BukkitService bukkitService; + + /** + * Constructor. + */ + public PaperPlayerSpawnLocationListener() { + } + + @Override + protected Location determineCustomSpawnLocation(String playerName, Location originalSpawnLocation) { + return bukkitService.callSyncMethodFromOptionallyAsyncTask( + () -> teleportationService.prepareOnJoinSpawnLocation(playerName, originalSpawnLocation)); + } +} diff --git a/authme-paper/src/main/java/fr/xephi/authme/platform/PaperPlatformAdapter.java b/authme-paper/src/main/java/fr/xephi/authme/platform/PaperPlatformAdapter.java new file mode 100644 index 0000000000..4ab4e666af --- /dev/null +++ b/authme-paper/src/main/java/fr/xephi/authme/platform/PaperPlatformAdapter.java @@ -0,0 +1,56 @@ +package fr.xephi.authme.platform; + +import fr.xephi.authme.AuthMe; +import fr.xephi.authme.command.CommandDescription; +import fr.xephi.authme.command.CommandHandler; +import fr.xephi.authme.listener.PaperChatListener; +import fr.xephi.authme.listener.PaperLoginValidationListener; +import fr.xephi.authme.listener.PlayerOpenSignListener; +import fr.xephi.authme.listener.PaperPlayerSpawnLocationListener; +import fr.xephi.authme.process.register.RegisterSecondaryArgument; +import fr.xephi.authme.process.register.RegistrationType; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.Listener; + +import java.util.Arrays; +import java.util.List; + +/** + * Platform adapter implementation for PaperMC 1.21.11+. + * Uses Paper's async teleport API for non-blocking player teleportation, + * the Adventure API for text components, and the Paper dialog API for + * graphical login/register dialogs (available since 1.21.11). + * On older Paper versions the dialog feature is gracefully disabled. + */ +public class PaperPlatformAdapter extends AbstractPaperPlatformAdapter { + + /** + * Constructor. + */ + public PaperPlatformAdapter() { + } + + @Override + public String getPlatformName() { + return "paper-1.21"; + } + + @Override + public String getCompatibilityError() { + return getCompatibilityError("This AuthMe Paper build requires the Paper 1.21.11+ API.", + "io.papermc.paper.event.player.AsyncChatEvent", + "io.papermc.paper.event.player.AsyncPlayerSpawnLocationEvent", + "io.papermc.paper.event.player.PlayerOpenSignEvent"); + } + + @Override + public List> getAdditionalListeners() { + return Arrays.asList( + PaperChatListener.class, + PaperPlayerSpawnLocationListener.class, + PaperLoginValidationListener.class, + PlayerOpenSignListener.class); + } +} diff --git a/authme-paper/src/main/resources/META-INF/services/fr.xephi.authme.platform.PlatformAdapter b/authme-paper/src/main/resources/META-INF/services/fr.xephi.authme.platform.PlatformAdapter new file mode 100644 index 0000000000..a37812448c --- /dev/null +++ b/authme-paper/src/main/resources/META-INF/services/fr.xephi.authme.platform.PlatformAdapter @@ -0,0 +1 @@ +fr.xephi.authme.platform.PaperPlatformAdapter diff --git a/authme-paper/src/main/resources/plugin.yml b/authme-paper/src/main/resources/plugin.yml new file mode 100644 index 0000000000..e29a4e685f --- /dev/null +++ b/authme-paper/src/main/resources/plugin.yml @@ -0,0 +1,340 @@ +# noinspection YAMLSchemaValidation +name: ${pluginDescription.name} +# noinspection YAMLSchemaValidation +authors: [${pluginDescription.authors}] +website: ${project.url} +description: ${project.description} +# noinspection YAMLSchemaValidation +main: ${pluginDescription.main} +version: ${pluginDescription.version} +api-version: ${plugin.api-version} +softdepend: + - Vault + - LuckPerms + - PermissionsEx + - bPermissions + - zPermissions + - Multiverse-Core + - Essentials + - EssentialsSpawn + - ProtocolLib +libraries: + - ch.jalu:injector:${dependencies.injector.version} + - net.ricecode:string-similarity:${dependencies.string-similarity.version} + - com.maxmind.geoip2:geoip2:${dependencies.geoip2.version} + - javatar:javatar:${dependencies.javatar.version} + - org.apache.commons:commons-email:${dependencies.commons-email.version} + - com.zaxxer:HikariCP:${dependencies.hikaricp.version} + - org.slf4j:slf4j-simple:${dependencies.slf4j.version} + - ch.jalu:datasourcecolumns:${dependencies.datasourcecolumns.version} + - com.mysql:mysql-connector-j:${dependencies.mysql-connector-j.version} + - org.mariadb.jdbc:mariadb-java-client:${dependencies.mariadb-java-client.version} + - org.postgresql:postgresql:${dependencies.postgresql.version} + - de.rtner:PBKDF2:${dependencies.pbkdf2.version} + - de.mkammerer:argon2-jvm-nolibs:${dependencies.argon2-jvm-nolibs.version} + - at.favre.lib:bcrypt:${dependencies.bcrypt.version} + - com.warrenstrange:googleauth:${dependencies.googleauth.version} + - ch.jalu:configme:${dependencies.configme.version} + - org.bstats:bstats-bukkit:${dependencies.bstats.version} +commands: + authme: + description: AuthMe op commands + usage: /authme register|unregister|forcelogin|password|lastlogin|accounts|email|setemail|getip|totp|disabletotp|spawn|setspawn|firstspawn|setfirstspawn|purge|purgeplayer|backup|resetpos|purgebannedplayers|switchantibot|reload|version|converter|messages|recent|debug + email: + description: Add email or recover password + usage: /email show|add|change|recover|code|setpassword + login: + description: Login command + usage: /login + aliases: + - l + - log + logout: + description: Logout command + usage: /logout + register: + description: Register an account + usage: /register [password] [verifyPassword] + aliases: + - reg + unregister: + description: Unregister an account + usage: /unregister + aliases: + - unreg + changepassword: + description: Change password of an account + usage: /changepassword + aliases: + - changepass + - cp + totp: + description: TOTP commands + usage: /totp code|add|confirm|remove + aliases: + - 2fa + captcha: + description: Captcha command + usage: /captcha + verification: + description: Verification command + usage: /verification +permissions: + authme.admin.*: + description: Gives access to all admin commands + children: + authme.admin.accounts: true + authme.admin.antibotmessages: true + authme.admin.backup: true + authme.admin.changemail: true + authme.admin.changepassword: true + authme.admin.converter: true + authme.admin.firstspawn: true + authme.admin.forcelogin: true + authme.admin.getemail: true + authme.admin.getip: true + authme.admin.lastlogin: true + authme.admin.purge: true + authme.admin.purgebannedplayers: true + authme.admin.purgelastpos: true + authme.admin.purgeplayer: true + authme.admin.register: true + authme.admin.reload: true + authme.admin.seeotheraccounts: true + authme.admin.seerecent: true + authme.admin.setfirstspawn: true + authme.admin.setspawn: true + authme.admin.spawn: true + authme.admin.switchantibot: true + authme.admin.totpdisable: true + authme.admin.totpviewstatus: true + authme.admin.unregister: true + authme.admin.updatemessages: true + authme.admin.accounts: + description: Administrator command to see all accounts associated with a user. + default: op + authme.admin.antibotmessages: + description: Permission to see Antibot messages. + default: op + authme.admin.backup: + description: Allows to use the backup command. + default: op + authme.admin.changemail: + description: Administrator command to set or change the email address of a user. + default: op + authme.admin.changepassword: + description: Administrator command to change the password of a user. + default: op + authme.admin.converter: + description: Administrator command to convert old or other data to AuthMe data. + default: op + authme.admin.firstspawn: + description: Administrator command to teleport to the first AuthMe spawn. + default: op + authme.admin.forcelogin: + description: Administrator command to force-login an existing user. + default: op + authme.admin.getemail: + description: Administrator command to get the email address of a user, if set. + default: op + authme.admin.getip: + description: Administrator command to get the last known IP of a user. + default: op + authme.admin.lastlogin: + description: Administrator command to see the last login date and time of a user. + default: op + authme.admin.purge: + description: Administrator command to purge old user data. + default: op + authme.admin.purgebannedplayers: + description: Administrator command to purge all data associated with banned players. + default: op + authme.admin.purgelastpos: + description: Administrator command to purge the last position of a user. + default: op + authme.admin.purgeplayer: + description: Administrator command to purge a given player. + default: op + authme.admin.register: + description: Administrator command to register a new user. + default: op + authme.admin.reload: + description: Administrator command to reload the plugin configuration. + default: op + authme.admin.seeotheraccounts: + description: Permission to see the other accounts of the players that log in. + default: op + authme.admin.seerecent: + description: Administrator command to see the last recently logged in players. + default: op + authme.admin.setfirstspawn: + description: Administrator command to set the first AuthMe spawn. + default: op + authme.admin.setspawn: + description: Administrator command to set the AuthMe spawn. + default: op + authme.admin.spawn: + description: Administrator command to teleport to the AuthMe spawn. + default: op + authme.admin.switchantibot: + description: Administrator command to toggle the AntiBot protection status. + default: op + authme.admin.totpdisable: + description: Administrator command to disable the two-factor auth of a user. + default: op + authme.admin.totpviewstatus: + description: Administrator command to see whether a player has enabled two-factor + authentication. + default: op + authme.admin.unregister: + description: Administrator command to unregister an existing user. + default: op + authme.admin.updatemessages: + description: Permission to use the update messages command. + default: op + authme.allowchatbeforelogin: + description: Permission to send chat messages before being logged in. + default: false + authme.allowmultipleaccounts: + description: Permission to be able to register multiple accounts. + default: op + authme.bypassantibot: + description: Permission node to bypass AntiBot protection. + default: op + authme.bypassbungeesend: + description: Permission node to bypass BungeeCord server teleportation. + default: false + authme.bypasscountrycheck: + description: Permission to bypass the GeoIp country code check. + default: false + authme.bypassforcesurvival: + description: Permission for users to bypass force-survival mode. + default: op + authme.bypasspurge: + description: Permission to bypass the purging process. + default: false + authme.debug: + description: Gives access to /authme debug and all its sections + children: + authme.debug.command: true + authme.debug.country: true + authme.debug.db: true + authme.debug.group: true + authme.debug.limbo: true + authme.debug.mail: true + authme.debug.mysqldef: true + authme.debug.perm: true + authme.debug.spawn: true + authme.debug.stats: true + authme.debug.valid: true + authme.debug.command: + description: General permission to use the /authme debug command. + default: op + authme.debug.country: + description: Permission to use the country lookup section. + default: op + authme.debug.db: + description: Permission to view data from the database. + default: op + authme.debug.group: + description: Permission to view permission groups. + default: op + authme.debug.limbo: + description: Permission to use the limbo data viewer. + default: op + authme.debug.mail: + description: Permission to use the test email sender. + default: op + authme.debug.mysqldef: + description: Permission to change nullable status of MySQL columns. + default: op + authme.debug.perm: + description: Permission to use the permission checker. + default: op + authme.debug.spawn: + description: Permission to view spawn information. + default: op + authme.debug.stats: + description: Permission to use the stats section. + default: op + authme.debug.valid: + description: Permission to use sample validation. + default: op + authme.player.*: + description: Gives access to all player commands + children: + authme.player.canbeforced: true + authme.player.captcha: true + authme.player.changepassword: true + authme.player.email.add: true + authme.player.email.change: true + authme.player.email.recover: true + authme.player.email.see: true + authme.player.login: true + authme.player.logout: true + authme.player.protection.quickcommandsprotection: true + authme.player.register: true + authme.player.security.verificationcode: true + authme.player.seeownaccounts: true + authme.player.totpadd: true + authme.player.totpremove: true + authme.player.unregister: true + authme.player.canbeforced: + description: Permission for users a login can be forced to. + default: true + authme.player.captcha: + description: Command permission to use captcha. + default: true + authme.player.changepassword: + description: Command permission to change the password. + default: true + authme.player.email: + description: Gives access to all email commands + children: + authme.player.email.add: true + authme.player.email.change: true + authme.player.email.recover: true + authme.player.email.see: true + authme.player.email.add: + description: Command permission to add an email address. + default: true + authme.player.email.change: + description: Command permission to change the email address. + default: true + authme.player.email.recover: + description: Command permission to recover an account using its email address. + default: true + authme.player.email.see: + description: Command permission to see the own email address. + default: true + authme.player.login: + description: Command permission to login. + default: true + authme.player.logout: + description: Command permission to logout. + default: true + authme.player.protection.quickcommandsprotection: + description: Permission that enables on join quick commands checks for the player. + default: true + authme.player.register: + description: Command permission to register. + default: true + authme.player.security.verificationcode: + description: Permission to use the email verification codes feature. + default: true + authme.player.seeownaccounts: + description: Permission to use to see own other accounts. + default: true + authme.player.totpadd: + description: Permission to enable two-factor authentication. + default: true + authme.player.totpremove: + description: Permission to disable two-factor authentication. + default: true + authme.player.unregister: + description: Command permission to unregister. + default: true + authme.vip: + description: When the server is full and someone with this permission joins the + server, someone will be kicked. + default: false diff --git a/authme-paper/src/test/java/fr/xephi/authme/listener/PaperPlayerSpawnLocationListenerTest.java b/authme-paper/src/test/java/fr/xephi/authme/listener/PaperPlayerSpawnLocationListenerTest.java new file mode 100644 index 0000000000..120c786ef3 --- /dev/null +++ b/authme-paper/src/test/java/fr/xephi/authme/listener/PaperPlayerSpawnLocationListenerTest.java @@ -0,0 +1,67 @@ +package fr.xephi.authme.listener; + +import com.destroystokyo.paper.profile.PlayerProfile; +import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.TeleportationService; +import io.papermc.paper.connection.PlayerConfigurationConnection; +import io.papermc.paper.event.player.AsyncPlayerSpawnLocationEvent; +import org.bukkit.Location; +import org.bukkit.World; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; + +import java.util.function.Supplier; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) +public class PaperPlayerSpawnLocationListenerTest { + + @InjectMocks + private PaperPlayerSpawnLocationListener listener; + + @Mock + private BukkitService bukkitService; + + @Mock + private TeleportationService teleportationService; + + @Test + public void shouldApplyCustomSpawnLocationFromPaperAsyncEvent() { + // given + doAnswer(invocation -> { + Supplier supplier = invocation.getArgument(0); + return supplier.get(); + }).when(bukkitService).callSyncMethodFromOptionallyAsyncTask(any()); + + World world = mock(World.class); + Location originalSpawn = new Location(world, 1.0, 64.0, 1.0); + Location customSpawn = new Location(world, 10.0, 70.0, 10.0); + PlayerProfile profile = mock(PlayerProfile.class); + given(profile.getName()).willReturn("Bobby"); + PlayerConfigurationConnection connection = mock(PlayerConfigurationConnection.class); + given(connection.getProfile()).willReturn(profile); + given(teleportationService.prepareOnJoinSpawnLocation("Bobby", originalSpawn)).willReturn(customSpawn); + + AsyncPlayerSpawnLocationEvent event = new AsyncPlayerSpawnLocationEvent(connection, originalSpawn, false); + + // when + listener.onPlayerSpawn(event); + + // then + verify(teleportationService).prepareOnJoinSpawnLocation("Bobby", originalSpawn); + assertThat(event.getSpawnLocation(), is(customSpawn)); + } +} diff --git a/authme-paper/src/test/java/fr/xephi/authme/platform/PaperPlatformAdapterTest.java b/authme-paper/src/test/java/fr/xephi/authme/platform/PaperPlatformAdapterTest.java new file mode 100644 index 0000000000..693a7d7819 --- /dev/null +++ b/authme-paper/src/test/java/fr/xephi/authme/platform/PaperPlatformAdapterTest.java @@ -0,0 +1,128 @@ +package fr.xephi.authme.platform; + +import fr.xephi.authme.listener.PaperChatListener; +import fr.xephi.authme.listener.PaperLoginValidationListener; +import fr.xephi.authme.listener.PaperPlayerSpawnLocationListener; +import fr.xephi.authme.listener.PlayerOpenSignListener; +import fr.xephi.authme.process.register.RegisterSecondaryArgument; +import fr.xephi.authme.process.register.RegistrationType; +import net.kyori.adventure.text.Component; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerKickEvent; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.is; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.verify; + +public class PaperPlatformAdapterTest { + + private final PaperPlatformAdapter adapter = new PaperPlatformAdapter(); + + @Test + public void getPlatformNameReturnsExpectedValue() { + assertThat(adapter.getPlatformName(), is("paper-1.21")); + } + + @Test + public void getCompatibilityErrorReturnsNullForCompatibleClasspath() { + assertThat(adapter.getCompatibilityError(), is((String) null)); + } + + @Test + public void isDialogSupportedReturnsTrueWhenApiIsOnClasspath() { + // The Paper dialog API is a test dependency, so DIALOG_AVAILABLE is true + assertThat(adapter.isDialogSupported(), is(true)); + } + + @Test + public void getAdditionalListenersContainsBothPaperListeners() { + List> listeners = adapter.getAdditionalListeners(); + + assertThat(listeners, containsInAnyOrder( + PaperChatListener.class, + PaperPlayerSpawnLocationListener.class, + PaperLoginValidationListener.class, + PlayerOpenSignListener.class)); + } + + @Test + public void shouldDisableLegacyPlayerLoginEventHandling() { + assertThat(adapter.shouldHandlePlayerLoginEvent(), is(false)); + } + + @Test + public void shouldDisableLegacyPlayerSpawnLocationEventHandling() { + assertThat(adapter.shouldHandlePlayerSpawnLocationEvent(), is(false)); + } + + @Test + public void showLoginDialogDelegatesToPaperDialogHelper() { + // given + Player player = mock(Player.class); + + // when / then - PaperDialogHelper uses Paper API calls that require a running server. + // Use mockStatic to verify delegation without invoking the full Paper server stack. + try (MockedStatic helperMock = mockStatic(PaperDialogHelper.class)) { + adapter.showLoginDialog(player); + + helperMock.verify(() -> PaperDialogHelper.showLoginDialog(player)); + } + } + + @Test + public void showRegisterDialogDelegatesToPaperDialogHelper() { + // given + Player player = mock(Player.class); + + // when / then + try (MockedStatic helperMock = mockStatic(PaperDialogHelper.class)) { + adapter.showRegisterDialog(player, RegistrationType.EMAIL, RegisterSecondaryArgument.CONFIRMATION); + + helperMock.verify(() -> PaperDialogHelper.showRegisterDialog( + eq(player), + eq(RegistrationType.EMAIL), + eq(RegisterSecondaryArgument.CONFIRMATION))); + } + } + + @Test + public void teleportPlayerCallsTeleportAsync() { + // given + Player player = mock(Player.class); + Location location = mock(Location.class); + + // when + adapter.teleportPlayer(player, location); + + // then + verify(player).teleportAsync(location); + } + + @Test + public void getKickReasonReturnsPlainTextFromComponent() { + // given + // PlayerKickEvent is not final; reason() is a Paper API method, stubable with Mockito 5 + PlayerKickEvent event = mock(PlayerKickEvent.class); + given(event.reason()).willReturn(Component.text("You were kicked")); + + // when + String reason = adapter.getKickReason(event); + + // then + assertThat(reason, is("You were kicked")); + } +} + + diff --git a/authme-spigot-1.21/pom.xml b/authme-spigot-1.21/pom.xml new file mode 100644 index 0000000000..e5e665d697 --- /dev/null +++ b/authme-spigot-1.21/pom.xml @@ -0,0 +1,169 @@ + + + 4.0.0 + + + fr.xephi + authme-parent + ${revision} + + + authme-spigot-1.21 + jar + AuthMe Spigot 1.21 + AuthMe plugin for Spigot 1.20.x and 1.21.x + + + 21 + 1.21.11-R0.2-SNAPSHOT + 1.21.11 + Spigot-1.21 + + + + + fr.xephi + authme-core + ${project.version} + compile + + + org.spigotmc + spigot-api + ${spigot.api.version} + provided + + + org.joml + joml + + + net.md-5 + bungeecord-chat + + + + + javax.inject + javax.inject + 1 + provided + + + ch.jalu + configme + test + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + + + org.mockito + mockito-junit-jupiter + test + + + org.hamcrest + hamcrest + test + + + net.md-5 + bungeecord-dialog + 1.21-R0.3 + provided + + + + + AuthMe-${project.version}-${module.classifier} + + + + src/main/resources + true + + + .. + false + + LICENSE + + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + shade + package + + shade + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + remove-original-shaded-jar + package + + run + + + + + + + + + + + org.apache.maven.plugins + maven-source-plugin + + ${project.build.finalName} + + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + ${project.build.finalName} + ${module.java.version} + + + + attach-javadoc + + jar + + + + + + + diff --git a/authme-spigot-1.21/src/main/java/fr/xephi/authme/listener/PlayerSignOpenListener.java b/authme-spigot-1.21/src/main/java/fr/xephi/authme/listener/PlayerSignOpenListener.java new file mode 100644 index 0000000000..b74f57662a --- /dev/null +++ b/authme-spigot-1.21/src/main/java/fr/xephi/authme/listener/PlayerSignOpenListener.java @@ -0,0 +1,29 @@ +package fr.xephi.authme.listener; + +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerSignOpenEvent; + +import javax.inject.Inject; + +/** + * Blocks unauthenticated players from reading signs (Spigot 1.20.1+). + */ +public class PlayerSignOpenListener implements Listener { + + @Inject + private ListenerService listenerService; + + PlayerSignOpenListener() { + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) + public void onPlayerSignOpen(PlayerSignOpenEvent event) { + Player player = event.getPlayer(); + if (listenerService.shouldCancelEvent(player)) { + event.setCancelled(true); + } + } +} diff --git a/authme-spigot-1.21/src/main/java/fr/xephi/authme/platform/SpigotDialogHelper.java b/authme-spigot-1.21/src/main/java/fr/xephi/authme/platform/SpigotDialogHelper.java new file mode 100644 index 0000000000..69399455f6 --- /dev/null +++ b/authme-spigot-1.21/src/main/java/fr/xephi/authme/platform/SpigotDialogHelper.java @@ -0,0 +1,73 @@ +package fr.xephi.authme.platform; + +import fr.xephi.authme.process.register.RegisterSecondaryArgument; +import fr.xephi.authme.process.register.RegistrationType; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.dialog.DialogBase; +import net.md_5.bungee.api.dialog.MultiActionDialog; +import net.md_5.bungee.api.dialog.action.ActionButton; +import net.md_5.bungee.api.dialog.action.RunCommandAction; +import net.md_5.bungee.api.dialog.input.DialogInput; +import net.md_5.bungee.api.dialog.input.TextInput; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; + +/** + * Encapsulates the BungeeCord dialog API calls for Spigot 1.21.6+. + * + *

This class is intentionally isolated so that it is only loaded by the JVM when dialog + * functionality is actually invoked. On servers that do not ship the BungeeCord dialog API + * (Spigot < 1.21.6), this class is never loaded, preventing {@link NoClassDefFoundError}.

+ */ +final class SpigotDialogHelper { + + private SpigotDialogHelper() { + } + + static void showLoginDialog(Player player) { + List inputs = new ArrayList<>(); + inputs.add(new TextInput("password", new TextComponent("Password")).maxLength(100)); + + DialogBase base = new DialogBase(new TextComponent("Login")) + .inputs(inputs) + .afterAction(DialogBase.AfterAction.CLOSE); + + player.showDialog(new MultiActionDialog(base, + new ActionButton(new TextComponent("Login"), new RunCommandAction("login $(password)")))); + } + + static void showRegisterDialog(Player player, RegistrationType type, RegisterSecondaryArgument secondArg) { + List inputs = new ArrayList<>(); + String template; + + if (type == RegistrationType.EMAIL) { + inputs.add(new TextInput("email", new TextComponent("Email")).maxLength(100)); + if (secondArg == RegisterSecondaryArgument.CONFIRMATION) { + inputs.add(new TextInput("confirm", new TextComponent("Confirm Email")).maxLength(100)); + template = "register $(email) $(confirm)"; + } else { + template = "register $(email)"; + } + } else { + inputs.add(new TextInput("password", new TextComponent("Password")).maxLength(100)); + if (secondArg == RegisterSecondaryArgument.CONFIRMATION) { + inputs.add(new TextInput("confirm", new TextComponent("Confirm Password")).maxLength(100)); + template = "register $(password) $(confirm)"; + } else if (secondArg == RegisterSecondaryArgument.EMAIL_MANDATORY) { + inputs.add(new TextInput("email", new TextComponent("Email")).maxLength(100)); + template = "register $(password) $(email)"; + } else { + template = "register $(password)"; + } + } + + DialogBase base = new DialogBase(new TextComponent("Register")) + .inputs(inputs) + .afterAction(DialogBase.AfterAction.CLOSE); + + player.showDialog(new MultiActionDialog(base, + new ActionButton(new TextComponent("Register"), new RunCommandAction(template)))); + } +} diff --git a/authme-spigot-1.21/src/main/java/fr/xephi/authme/platform/SpigotPlatformAdapter.java b/authme-spigot-1.21/src/main/java/fr/xephi/authme/platform/SpigotPlatformAdapter.java new file mode 100644 index 0000000000..c0ef30dab1 --- /dev/null +++ b/authme-spigot-1.21/src/main/java/fr/xephi/authme/platform/SpigotPlatformAdapter.java @@ -0,0 +1,78 @@ +package fr.xephi.authme.platform; + +import fr.xephi.authme.listener.PlayerSignOpenListener; +import fr.xephi.authme.process.register.RegisterSecondaryArgument; +import fr.xephi.authme.process.register.RegistrationType; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.Listener; + +import java.util.Collections; +import java.util.List; + +/** + * Platform adapter implementation for Spigot 1.20.x and 1.21.x. + * Dialog UI is supported on Spigot 1.21.6+ (BungeeCord dialog API). + * On older server versions in this range the dialog feature is gracefully disabled. + */ +public class SpigotPlatformAdapter extends AbstractSpigotPlatformAdapter { + + /** + * True when the BungeeCord dialog API is present on the running server (Spigot 1.21.6+). + * Checked once at class-load time via Class.forName so that SpigotDialogHelper — which + * directly references those classes — is never loaded on servers that don't have them. + */ + private static final boolean DIALOG_AVAILABLE; + + static { + boolean available = false; + try { + Class.forName("net.md_5.bungee.api.dialog.MultiActionDialog"); + available = true; + } catch (ClassNotFoundException ignored) { + // BungeeCord dialog API absent (Spigot < 1.21.6) + } + DIALOG_AVAILABLE = available; + } + + @Override + public String getPlatformName() { + return "spigot-1.21"; + } + + @Override + public String getCompatibilityError() { + return getCompatibilityError("This AuthMe Spigot 1.21 build requires the Spigot 1.20+ API.", + "org.spigotmc.event.player.PlayerSpawnLocationEvent", + "org.bukkit.event.player.PlayerSignOpenEvent"); + } + + @Override + public List> getAdditionalListeners() { + return Collections.singletonList(PlayerSignOpenListener.class); + } + + /** + * Spigot 1.20.6+ exposes {@link Player#getRespawnLocation()}, which preserves the server's actual respawn target + * instead of limiting us to the legacy bed-only API. + */ + @Override + public Location getPlayerRespawnLocation(Player player) { + return player.getRespawnLocation(); + } + + @Override + public boolean isDialogSupported() { + return DIALOG_AVAILABLE; + } + + @Override + public void showLoginDialog(Player player) { + SpigotDialogHelper.showLoginDialog(player); + } + + @Override + public void showRegisterDialog(Player player, RegistrationType type, RegisterSecondaryArgument secondArg) { + SpigotDialogHelper.showRegisterDialog(player, type, secondArg); + } +} diff --git a/authme-spigot-1.21/src/main/resources/META-INF/services/fr.xephi.authme.platform.PlatformAdapter b/authme-spigot-1.21/src/main/resources/META-INF/services/fr.xephi.authme.platform.PlatformAdapter new file mode 100644 index 0000000000..115cd0cfed --- /dev/null +++ b/authme-spigot-1.21/src/main/resources/META-INF/services/fr.xephi.authme.platform.PlatformAdapter @@ -0,0 +1 @@ +fr.xephi.authme.platform.SpigotPlatformAdapter diff --git a/authme-spigot-1.21/src/main/resources/plugin.yml b/authme-spigot-1.21/src/main/resources/plugin.yml new file mode 100644 index 0000000000..e29a4e685f --- /dev/null +++ b/authme-spigot-1.21/src/main/resources/plugin.yml @@ -0,0 +1,340 @@ +# noinspection YAMLSchemaValidation +name: ${pluginDescription.name} +# noinspection YAMLSchemaValidation +authors: [${pluginDescription.authors}] +website: ${project.url} +description: ${project.description} +# noinspection YAMLSchemaValidation +main: ${pluginDescription.main} +version: ${pluginDescription.version} +api-version: ${plugin.api-version} +softdepend: + - Vault + - LuckPerms + - PermissionsEx + - bPermissions + - zPermissions + - Multiverse-Core + - Essentials + - EssentialsSpawn + - ProtocolLib +libraries: + - ch.jalu:injector:${dependencies.injector.version} + - net.ricecode:string-similarity:${dependencies.string-similarity.version} + - com.maxmind.geoip2:geoip2:${dependencies.geoip2.version} + - javatar:javatar:${dependencies.javatar.version} + - org.apache.commons:commons-email:${dependencies.commons-email.version} + - com.zaxxer:HikariCP:${dependencies.hikaricp.version} + - org.slf4j:slf4j-simple:${dependencies.slf4j.version} + - ch.jalu:datasourcecolumns:${dependencies.datasourcecolumns.version} + - com.mysql:mysql-connector-j:${dependencies.mysql-connector-j.version} + - org.mariadb.jdbc:mariadb-java-client:${dependencies.mariadb-java-client.version} + - org.postgresql:postgresql:${dependencies.postgresql.version} + - de.rtner:PBKDF2:${dependencies.pbkdf2.version} + - de.mkammerer:argon2-jvm-nolibs:${dependencies.argon2-jvm-nolibs.version} + - at.favre.lib:bcrypt:${dependencies.bcrypt.version} + - com.warrenstrange:googleauth:${dependencies.googleauth.version} + - ch.jalu:configme:${dependencies.configme.version} + - org.bstats:bstats-bukkit:${dependencies.bstats.version} +commands: + authme: + description: AuthMe op commands + usage: /authme register|unregister|forcelogin|password|lastlogin|accounts|email|setemail|getip|totp|disabletotp|spawn|setspawn|firstspawn|setfirstspawn|purge|purgeplayer|backup|resetpos|purgebannedplayers|switchantibot|reload|version|converter|messages|recent|debug + email: + description: Add email or recover password + usage: /email show|add|change|recover|code|setpassword + login: + description: Login command + usage: /login + aliases: + - l + - log + logout: + description: Logout command + usage: /logout + register: + description: Register an account + usage: /register [password] [verifyPassword] + aliases: + - reg + unregister: + description: Unregister an account + usage: /unregister + aliases: + - unreg + changepassword: + description: Change password of an account + usage: /changepassword + aliases: + - changepass + - cp + totp: + description: TOTP commands + usage: /totp code|add|confirm|remove + aliases: + - 2fa + captcha: + description: Captcha command + usage: /captcha + verification: + description: Verification command + usage: /verification +permissions: + authme.admin.*: + description: Gives access to all admin commands + children: + authme.admin.accounts: true + authme.admin.antibotmessages: true + authme.admin.backup: true + authme.admin.changemail: true + authme.admin.changepassword: true + authme.admin.converter: true + authme.admin.firstspawn: true + authme.admin.forcelogin: true + authme.admin.getemail: true + authme.admin.getip: true + authme.admin.lastlogin: true + authme.admin.purge: true + authme.admin.purgebannedplayers: true + authme.admin.purgelastpos: true + authme.admin.purgeplayer: true + authme.admin.register: true + authme.admin.reload: true + authme.admin.seeotheraccounts: true + authme.admin.seerecent: true + authme.admin.setfirstspawn: true + authme.admin.setspawn: true + authme.admin.spawn: true + authme.admin.switchantibot: true + authme.admin.totpdisable: true + authme.admin.totpviewstatus: true + authme.admin.unregister: true + authme.admin.updatemessages: true + authme.admin.accounts: + description: Administrator command to see all accounts associated with a user. + default: op + authme.admin.antibotmessages: + description: Permission to see Antibot messages. + default: op + authme.admin.backup: + description: Allows to use the backup command. + default: op + authme.admin.changemail: + description: Administrator command to set or change the email address of a user. + default: op + authme.admin.changepassword: + description: Administrator command to change the password of a user. + default: op + authme.admin.converter: + description: Administrator command to convert old or other data to AuthMe data. + default: op + authme.admin.firstspawn: + description: Administrator command to teleport to the first AuthMe spawn. + default: op + authme.admin.forcelogin: + description: Administrator command to force-login an existing user. + default: op + authme.admin.getemail: + description: Administrator command to get the email address of a user, if set. + default: op + authme.admin.getip: + description: Administrator command to get the last known IP of a user. + default: op + authme.admin.lastlogin: + description: Administrator command to see the last login date and time of a user. + default: op + authme.admin.purge: + description: Administrator command to purge old user data. + default: op + authme.admin.purgebannedplayers: + description: Administrator command to purge all data associated with banned players. + default: op + authme.admin.purgelastpos: + description: Administrator command to purge the last position of a user. + default: op + authme.admin.purgeplayer: + description: Administrator command to purge a given player. + default: op + authme.admin.register: + description: Administrator command to register a new user. + default: op + authme.admin.reload: + description: Administrator command to reload the plugin configuration. + default: op + authme.admin.seeotheraccounts: + description: Permission to see the other accounts of the players that log in. + default: op + authme.admin.seerecent: + description: Administrator command to see the last recently logged in players. + default: op + authme.admin.setfirstspawn: + description: Administrator command to set the first AuthMe spawn. + default: op + authme.admin.setspawn: + description: Administrator command to set the AuthMe spawn. + default: op + authme.admin.spawn: + description: Administrator command to teleport to the AuthMe spawn. + default: op + authme.admin.switchantibot: + description: Administrator command to toggle the AntiBot protection status. + default: op + authme.admin.totpdisable: + description: Administrator command to disable the two-factor auth of a user. + default: op + authme.admin.totpviewstatus: + description: Administrator command to see whether a player has enabled two-factor + authentication. + default: op + authme.admin.unregister: + description: Administrator command to unregister an existing user. + default: op + authme.admin.updatemessages: + description: Permission to use the update messages command. + default: op + authme.allowchatbeforelogin: + description: Permission to send chat messages before being logged in. + default: false + authme.allowmultipleaccounts: + description: Permission to be able to register multiple accounts. + default: op + authme.bypassantibot: + description: Permission node to bypass AntiBot protection. + default: op + authme.bypassbungeesend: + description: Permission node to bypass BungeeCord server teleportation. + default: false + authme.bypasscountrycheck: + description: Permission to bypass the GeoIp country code check. + default: false + authme.bypassforcesurvival: + description: Permission for users to bypass force-survival mode. + default: op + authme.bypasspurge: + description: Permission to bypass the purging process. + default: false + authme.debug: + description: Gives access to /authme debug and all its sections + children: + authme.debug.command: true + authme.debug.country: true + authme.debug.db: true + authme.debug.group: true + authme.debug.limbo: true + authme.debug.mail: true + authme.debug.mysqldef: true + authme.debug.perm: true + authme.debug.spawn: true + authme.debug.stats: true + authme.debug.valid: true + authme.debug.command: + description: General permission to use the /authme debug command. + default: op + authme.debug.country: + description: Permission to use the country lookup section. + default: op + authme.debug.db: + description: Permission to view data from the database. + default: op + authme.debug.group: + description: Permission to view permission groups. + default: op + authme.debug.limbo: + description: Permission to use the limbo data viewer. + default: op + authme.debug.mail: + description: Permission to use the test email sender. + default: op + authme.debug.mysqldef: + description: Permission to change nullable status of MySQL columns. + default: op + authme.debug.perm: + description: Permission to use the permission checker. + default: op + authme.debug.spawn: + description: Permission to view spawn information. + default: op + authme.debug.stats: + description: Permission to use the stats section. + default: op + authme.debug.valid: + description: Permission to use sample validation. + default: op + authme.player.*: + description: Gives access to all player commands + children: + authme.player.canbeforced: true + authme.player.captcha: true + authme.player.changepassword: true + authme.player.email.add: true + authme.player.email.change: true + authme.player.email.recover: true + authme.player.email.see: true + authme.player.login: true + authme.player.logout: true + authme.player.protection.quickcommandsprotection: true + authme.player.register: true + authme.player.security.verificationcode: true + authme.player.seeownaccounts: true + authme.player.totpadd: true + authme.player.totpremove: true + authme.player.unregister: true + authme.player.canbeforced: + description: Permission for users a login can be forced to. + default: true + authme.player.captcha: + description: Command permission to use captcha. + default: true + authme.player.changepassword: + description: Command permission to change the password. + default: true + authme.player.email: + description: Gives access to all email commands + children: + authme.player.email.add: true + authme.player.email.change: true + authme.player.email.recover: true + authme.player.email.see: true + authme.player.email.add: + description: Command permission to add an email address. + default: true + authme.player.email.change: + description: Command permission to change the email address. + default: true + authme.player.email.recover: + description: Command permission to recover an account using its email address. + default: true + authme.player.email.see: + description: Command permission to see the own email address. + default: true + authme.player.login: + description: Command permission to login. + default: true + authme.player.logout: + description: Command permission to logout. + default: true + authme.player.protection.quickcommandsprotection: + description: Permission that enables on join quick commands checks for the player. + default: true + authme.player.register: + description: Command permission to register. + default: true + authme.player.security.verificationcode: + description: Permission to use the email verification codes feature. + default: true + authme.player.seeownaccounts: + description: Permission to use to see own other accounts. + default: true + authme.player.totpadd: + description: Permission to enable two-factor authentication. + default: true + authme.player.totpremove: + description: Permission to disable two-factor authentication. + default: true + authme.player.unregister: + description: Command permission to unregister. + default: true + authme.vip: + description: When the server is full and someone with this permission joins the + server, someone will be kicked. + default: false diff --git a/authme-spigot-1.21/src/test/java/fr/xephi/authme/listener/PlayerSignOpenListenerTest.java b/authme-spigot-1.21/src/test/java/fr/xephi/authme/listener/PlayerSignOpenListenerTest.java new file mode 100644 index 0000000000..093fcb6458 --- /dev/null +++ b/authme-spigot-1.21/src/test/java/fr/xephi/authme/listener/PlayerSignOpenListenerTest.java @@ -0,0 +1,64 @@ +package fr.xephi.authme.listener; + +import org.mockito.quality.Strictness; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.extension.ExtendWith; +import org.bukkit.block.Sign; +import org.bukkit.block.sign.Side; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerSignOpenEvent; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.ArgumentMatchers.anyBoolean; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) +public class PlayerSignOpenListenerTest { + + @InjectMocks + private PlayerSignOpenListener listener; + + @Mock + private ListenerService listenerService; + + @Test + public void shouldCancelEventForUnauthenticated() { + // given + // PlayerSignOpenEvent#getPlayer is final (PlayerEvent), so use spy on a real instance + Player player = mock(Player.class); + PlayerSignOpenEvent event = spy(new PlayerSignOpenEvent( + player, mock(Sign.class), Side.FRONT, PlayerSignOpenEvent.Cause.UNKNOWN)); + given(listenerService.shouldCancelEvent(player)).willReturn(true); + + // when + listener.onPlayerSignOpen(event); + + // then + verify(event).setCancelled(true); + } + + @Test + public void shouldNotCancelEventForAuthenticatedPlayer() { + // given + Player player = mock(Player.class); + PlayerSignOpenEvent event = spy(new PlayerSignOpenEvent( + player, mock(Sign.class), Side.FRONT, PlayerSignOpenEvent.Cause.UNKNOWN)); + given(listenerService.shouldCancelEvent(player)).willReturn(false); + + // when + listener.onPlayerSignOpen(event); + + // then + verify(event, never()).setCancelled(anyBoolean()); + } +} + + diff --git a/authme-spigot-1.21/src/test/java/fr/xephi/authme/platform/SpigotDialogHelperTest.java b/authme-spigot-1.21/src/test/java/fr/xephi/authme/platform/SpigotDialogHelperTest.java new file mode 100644 index 0000000000..86c80f441f --- /dev/null +++ b/authme-spigot-1.21/src/test/java/fr/xephi/authme/platform/SpigotDialogHelperTest.java @@ -0,0 +1,102 @@ +package fr.xephi.authme.platform; + +import fr.xephi.authme.process.register.RegisterSecondaryArgument; +import fr.xephi.authme.process.register.RegistrationType; +import net.md_5.bungee.api.dialog.MultiActionDialog; +import net.md_5.bungee.api.dialog.action.RunCommandAction; +import org.bukkit.entity.Player; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class SpigotDialogHelperTest { + + @Test + public void showLoginDialogSendsCorrectCommandTemplate() { + // given + Player player = mock(Player.class); + + // when + SpigotDialogHelper.showLoginDialog(player); + + // then + RunCommandAction action = captureRunCommandAction(player); + assertThat(action.template(), is("login $(password)")); + } + + @Test + public void showRegisterDialogWithPasswordAndNoSecondArgSendsPasswordTemplate() { + // given + Player player = mock(Player.class); + + // when + SpigotDialogHelper.showRegisterDialog(player, RegistrationType.PASSWORD, RegisterSecondaryArgument.NONE); + + // then + RunCommandAction action = captureRunCommandAction(player); + assertThat(action.template(), is("register $(password)")); + } + + @Test + public void showRegisterDialogWithPasswordAndConfirmationSendsPasswordConfirmTemplate() { + // given + Player player = mock(Player.class); + + // when + SpigotDialogHelper.showRegisterDialog(player, RegistrationType.PASSWORD, RegisterSecondaryArgument.CONFIRMATION); + + // then + RunCommandAction action = captureRunCommandAction(player); + assertThat(action.template(), is("register $(password) $(confirm)")); + } + + @Test + public void showRegisterDialogWithPasswordAndEmailMandatorySendsPasswordEmailTemplate() { + // given + Player player = mock(Player.class); + + // when + SpigotDialogHelper.showRegisterDialog(player, RegistrationType.PASSWORD, RegisterSecondaryArgument.EMAIL_MANDATORY); + + // then + RunCommandAction action = captureRunCommandAction(player); + assertThat(action.template(), is("register $(password) $(email)")); + } + + @Test + public void showRegisterDialogWithEmailAndNoSecondArgSendsEmailTemplate() { + // given + Player player = mock(Player.class); + + // when + SpigotDialogHelper.showRegisterDialog(player, RegistrationType.EMAIL, RegisterSecondaryArgument.NONE); + + // then + RunCommandAction action = captureRunCommandAction(player); + assertThat(action.template(), is("register $(email)")); + } + + @Test + public void showRegisterDialogWithEmailAndConfirmationSendsEmailConfirmTemplate() { + // given + Player player = mock(Player.class); + + // when + SpigotDialogHelper.showRegisterDialog(player, RegistrationType.EMAIL, RegisterSecondaryArgument.CONFIRMATION); + + // then + RunCommandAction action = captureRunCommandAction(player); + assertThat(action.template(), is("register $(email) $(confirm)")); + } + + private static RunCommandAction captureRunCommandAction(Player player) { + ArgumentCaptor captor = ArgumentCaptor.forClass(MultiActionDialog.class); + verify(player).showDialog(captor.capture()); + return (RunCommandAction) captor.getValue().actions().get(0).action(); + } +} + diff --git a/authme-spigot-1.21/src/test/java/fr/xephi/authme/platform/SpigotPlatformAdapterTest.java b/authme-spigot-1.21/src/test/java/fr/xephi/authme/platform/SpigotPlatformAdapterTest.java new file mode 100644 index 0000000000..ed2f36cb3d --- /dev/null +++ b/authme-spigot-1.21/src/test/java/fr/xephi/authme/platform/SpigotPlatformAdapterTest.java @@ -0,0 +1,71 @@ +package fr.xephi.authme.platform; + +import fr.xephi.authme.listener.PlayerSignOpenListener; +import fr.xephi.authme.process.register.RegisterSecondaryArgument; +import fr.xephi.authme.process.register.RegistrationType; +import net.md_5.bungee.api.dialog.MultiActionDialog; +import org.bukkit.entity.Player; +import org.bukkit.event.Listener; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.is; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class SpigotPlatformAdapterTest { + + private final SpigotPlatformAdapter adapter = new SpigotPlatformAdapter(); + + @Test + public void getPlatformNameReturnsExpectedValue() { + assertThat(adapter.getPlatformName(), is("spigot-1.21")); + } + + @Test + public void getCompatibilityErrorReturnsNullForCompatibleClasspath() { + assertThat(adapter.getCompatibilityError(), is((String) null)); + } + + @Test + public void isDialogSupportedReturnsTrueWhenApiIsOnClasspath() { + // The BungeeCord dialog API is a test dependency, so DIALOG_AVAILABLE is true + assertThat(adapter.isDialogSupported(), is(true)); + } + + @Test + public void getAdditionalListenersContainsPlayerSignOpenListener() { + List> listeners = adapter.getAdditionalListeners(); + + assertThat(listeners, contains(PlayerSignOpenListener.class)); + } + + @Test + public void showLoginDialogDelegatesToPlayer() { + // given + Player player = mock(Player.class); + + // when + adapter.showLoginDialog(player); + + // then + verify(player).showDialog(any(MultiActionDialog.class)); + } + + @Test + public void showRegisterDialogDelegatesToPlayer() { + // given + Player player = mock(Player.class); + + // when + adapter.showRegisterDialog(player, RegistrationType.PASSWORD, RegisterSecondaryArgument.NONE); + + // then + verify(player).showDialog(any(MultiActionDialog.class)); + } +} + diff --git a/authme-spigot-legacy/pom.xml b/authme-spigot-legacy/pom.xml new file mode 100644 index 0000000000..330ef2003d --- /dev/null +++ b/authme-spigot-legacy/pom.xml @@ -0,0 +1,140 @@ + + + 4.0.0 + + + fr.xephi + authme-parent + ${revision} + + + authme-spigot-legacy + jar + AuthMe Spigot (Legacy) + AuthMe plugin for Spigot 1.16–1.19 + + + 1.16.5-R0.1-SNAPSHOT + 1.16 + Spigot-Legacy + + + + + fr.xephi + authme-core + ${project.version} + compile + + + org.spigotmc + spigot-api + ${spigot.api.version} + provided + + + org.joml + joml + + + net.md-5 + bungeecord-chat + + + + + + + AuthMe-${project.version}-${module.classifier} + + + + src/main/resources + true + + + .. + false + + LICENSE + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + + org.apache.maven.plugins + maven-shade-plugin + + + shade + package + + shade + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + remove-original-shaded-jar + package + + run + + + + + + + + + + + org.apache.maven.plugins + maven-source-plugin + + ${project.build.finalName} + + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + ${project.build.finalName} + ${module.java.version} + + + + attach-javadoc + + jar + + + + + + + diff --git a/authme-spigot-legacy/src/main/java/fr/xephi/authme/platform/SpigotPlatformAdapter.java b/authme-spigot-legacy/src/main/java/fr/xephi/authme/platform/SpigotPlatformAdapter.java new file mode 100644 index 0000000000..9afdf82bcf --- /dev/null +++ b/authme-spigot-legacy/src/main/java/fr/xephi/authme/platform/SpigotPlatformAdapter.java @@ -0,0 +1,18 @@ +package fr.xephi.authme.platform; + +/** + * Platform adapter for Spigot 1.16–1.19 (legacy versions). + */ +public class SpigotPlatformAdapter extends AbstractSpigotPlatformAdapter { + + @Override + public String getPlatformName() { + return "spigot-legacy"; + } + + @Override + public String getCompatibilityError() { + return getCompatibilityError("This AuthMe Spigot Legacy build requires the Spigot 1.16+ API.", + "org.spigotmc.event.player.PlayerSpawnLocationEvent"); + } +} diff --git a/authme-spigot-legacy/src/main/resources/META-INF/services/fr.xephi.authme.platform.PlatformAdapter b/authme-spigot-legacy/src/main/resources/META-INF/services/fr.xephi.authme.platform.PlatformAdapter new file mode 100644 index 0000000000..115cd0cfed --- /dev/null +++ b/authme-spigot-legacy/src/main/resources/META-INF/services/fr.xephi.authme.platform.PlatformAdapter @@ -0,0 +1 @@ +fr.xephi.authme.platform.SpigotPlatformAdapter diff --git a/authme-spigot-legacy/src/main/resources/plugin.yml b/authme-spigot-legacy/src/main/resources/plugin.yml new file mode 100644 index 0000000000..e29a4e685f --- /dev/null +++ b/authme-spigot-legacy/src/main/resources/plugin.yml @@ -0,0 +1,340 @@ +# noinspection YAMLSchemaValidation +name: ${pluginDescription.name} +# noinspection YAMLSchemaValidation +authors: [${pluginDescription.authors}] +website: ${project.url} +description: ${project.description} +# noinspection YAMLSchemaValidation +main: ${pluginDescription.main} +version: ${pluginDescription.version} +api-version: ${plugin.api-version} +softdepend: + - Vault + - LuckPerms + - PermissionsEx + - bPermissions + - zPermissions + - Multiverse-Core + - Essentials + - EssentialsSpawn + - ProtocolLib +libraries: + - ch.jalu:injector:${dependencies.injector.version} + - net.ricecode:string-similarity:${dependencies.string-similarity.version} + - com.maxmind.geoip2:geoip2:${dependencies.geoip2.version} + - javatar:javatar:${dependencies.javatar.version} + - org.apache.commons:commons-email:${dependencies.commons-email.version} + - com.zaxxer:HikariCP:${dependencies.hikaricp.version} + - org.slf4j:slf4j-simple:${dependencies.slf4j.version} + - ch.jalu:datasourcecolumns:${dependencies.datasourcecolumns.version} + - com.mysql:mysql-connector-j:${dependencies.mysql-connector-j.version} + - org.mariadb.jdbc:mariadb-java-client:${dependencies.mariadb-java-client.version} + - org.postgresql:postgresql:${dependencies.postgresql.version} + - de.rtner:PBKDF2:${dependencies.pbkdf2.version} + - de.mkammerer:argon2-jvm-nolibs:${dependencies.argon2-jvm-nolibs.version} + - at.favre.lib:bcrypt:${dependencies.bcrypt.version} + - com.warrenstrange:googleauth:${dependencies.googleauth.version} + - ch.jalu:configme:${dependencies.configme.version} + - org.bstats:bstats-bukkit:${dependencies.bstats.version} +commands: + authme: + description: AuthMe op commands + usage: /authme register|unregister|forcelogin|password|lastlogin|accounts|email|setemail|getip|totp|disabletotp|spawn|setspawn|firstspawn|setfirstspawn|purge|purgeplayer|backup|resetpos|purgebannedplayers|switchantibot|reload|version|converter|messages|recent|debug + email: + description: Add email or recover password + usage: /email show|add|change|recover|code|setpassword + login: + description: Login command + usage: /login + aliases: + - l + - log + logout: + description: Logout command + usage: /logout + register: + description: Register an account + usage: /register [password] [verifyPassword] + aliases: + - reg + unregister: + description: Unregister an account + usage: /unregister + aliases: + - unreg + changepassword: + description: Change password of an account + usage: /changepassword + aliases: + - changepass + - cp + totp: + description: TOTP commands + usage: /totp code|add|confirm|remove + aliases: + - 2fa + captcha: + description: Captcha command + usage: /captcha + verification: + description: Verification command + usage: /verification +permissions: + authme.admin.*: + description: Gives access to all admin commands + children: + authme.admin.accounts: true + authme.admin.antibotmessages: true + authme.admin.backup: true + authme.admin.changemail: true + authme.admin.changepassword: true + authme.admin.converter: true + authme.admin.firstspawn: true + authme.admin.forcelogin: true + authme.admin.getemail: true + authme.admin.getip: true + authme.admin.lastlogin: true + authme.admin.purge: true + authme.admin.purgebannedplayers: true + authme.admin.purgelastpos: true + authme.admin.purgeplayer: true + authme.admin.register: true + authme.admin.reload: true + authme.admin.seeotheraccounts: true + authme.admin.seerecent: true + authme.admin.setfirstspawn: true + authme.admin.setspawn: true + authme.admin.spawn: true + authme.admin.switchantibot: true + authme.admin.totpdisable: true + authme.admin.totpviewstatus: true + authme.admin.unregister: true + authme.admin.updatemessages: true + authme.admin.accounts: + description: Administrator command to see all accounts associated with a user. + default: op + authme.admin.antibotmessages: + description: Permission to see Antibot messages. + default: op + authme.admin.backup: + description: Allows to use the backup command. + default: op + authme.admin.changemail: + description: Administrator command to set or change the email address of a user. + default: op + authme.admin.changepassword: + description: Administrator command to change the password of a user. + default: op + authme.admin.converter: + description: Administrator command to convert old or other data to AuthMe data. + default: op + authme.admin.firstspawn: + description: Administrator command to teleport to the first AuthMe spawn. + default: op + authme.admin.forcelogin: + description: Administrator command to force-login an existing user. + default: op + authme.admin.getemail: + description: Administrator command to get the email address of a user, if set. + default: op + authme.admin.getip: + description: Administrator command to get the last known IP of a user. + default: op + authme.admin.lastlogin: + description: Administrator command to see the last login date and time of a user. + default: op + authme.admin.purge: + description: Administrator command to purge old user data. + default: op + authme.admin.purgebannedplayers: + description: Administrator command to purge all data associated with banned players. + default: op + authme.admin.purgelastpos: + description: Administrator command to purge the last position of a user. + default: op + authme.admin.purgeplayer: + description: Administrator command to purge a given player. + default: op + authme.admin.register: + description: Administrator command to register a new user. + default: op + authme.admin.reload: + description: Administrator command to reload the plugin configuration. + default: op + authme.admin.seeotheraccounts: + description: Permission to see the other accounts of the players that log in. + default: op + authme.admin.seerecent: + description: Administrator command to see the last recently logged in players. + default: op + authme.admin.setfirstspawn: + description: Administrator command to set the first AuthMe spawn. + default: op + authme.admin.setspawn: + description: Administrator command to set the AuthMe spawn. + default: op + authme.admin.spawn: + description: Administrator command to teleport to the AuthMe spawn. + default: op + authme.admin.switchantibot: + description: Administrator command to toggle the AntiBot protection status. + default: op + authme.admin.totpdisable: + description: Administrator command to disable the two-factor auth of a user. + default: op + authme.admin.totpviewstatus: + description: Administrator command to see whether a player has enabled two-factor + authentication. + default: op + authme.admin.unregister: + description: Administrator command to unregister an existing user. + default: op + authme.admin.updatemessages: + description: Permission to use the update messages command. + default: op + authme.allowchatbeforelogin: + description: Permission to send chat messages before being logged in. + default: false + authme.allowmultipleaccounts: + description: Permission to be able to register multiple accounts. + default: op + authme.bypassantibot: + description: Permission node to bypass AntiBot protection. + default: op + authme.bypassbungeesend: + description: Permission node to bypass BungeeCord server teleportation. + default: false + authme.bypasscountrycheck: + description: Permission to bypass the GeoIp country code check. + default: false + authme.bypassforcesurvival: + description: Permission for users to bypass force-survival mode. + default: op + authme.bypasspurge: + description: Permission to bypass the purging process. + default: false + authme.debug: + description: Gives access to /authme debug and all its sections + children: + authme.debug.command: true + authme.debug.country: true + authme.debug.db: true + authme.debug.group: true + authme.debug.limbo: true + authme.debug.mail: true + authme.debug.mysqldef: true + authme.debug.perm: true + authme.debug.spawn: true + authme.debug.stats: true + authme.debug.valid: true + authme.debug.command: + description: General permission to use the /authme debug command. + default: op + authme.debug.country: + description: Permission to use the country lookup section. + default: op + authme.debug.db: + description: Permission to view data from the database. + default: op + authme.debug.group: + description: Permission to view permission groups. + default: op + authme.debug.limbo: + description: Permission to use the limbo data viewer. + default: op + authme.debug.mail: + description: Permission to use the test email sender. + default: op + authme.debug.mysqldef: + description: Permission to change nullable status of MySQL columns. + default: op + authme.debug.perm: + description: Permission to use the permission checker. + default: op + authme.debug.spawn: + description: Permission to view spawn information. + default: op + authme.debug.stats: + description: Permission to use the stats section. + default: op + authme.debug.valid: + description: Permission to use sample validation. + default: op + authme.player.*: + description: Gives access to all player commands + children: + authme.player.canbeforced: true + authme.player.captcha: true + authme.player.changepassword: true + authme.player.email.add: true + authme.player.email.change: true + authme.player.email.recover: true + authme.player.email.see: true + authme.player.login: true + authme.player.logout: true + authme.player.protection.quickcommandsprotection: true + authme.player.register: true + authme.player.security.verificationcode: true + authme.player.seeownaccounts: true + authme.player.totpadd: true + authme.player.totpremove: true + authme.player.unregister: true + authme.player.canbeforced: + description: Permission for users a login can be forced to. + default: true + authme.player.captcha: + description: Command permission to use captcha. + default: true + authme.player.changepassword: + description: Command permission to change the password. + default: true + authme.player.email: + description: Gives access to all email commands + children: + authme.player.email.add: true + authme.player.email.change: true + authme.player.email.recover: true + authme.player.email.see: true + authme.player.email.add: + description: Command permission to add an email address. + default: true + authme.player.email.change: + description: Command permission to change the email address. + default: true + authme.player.email.recover: + description: Command permission to recover an account using its email address. + default: true + authme.player.email.see: + description: Command permission to see the own email address. + default: true + authme.player.login: + description: Command permission to login. + default: true + authme.player.logout: + description: Command permission to logout. + default: true + authme.player.protection.quickcommandsprotection: + description: Permission that enables on join quick commands checks for the player. + default: true + authme.player.register: + description: Command permission to register. + default: true + authme.player.security.verificationcode: + description: Permission to use the email verification codes feature. + default: true + authme.player.seeownaccounts: + description: Permission to use to see own other accounts. + default: true + authme.player.totpadd: + description: Permission to enable two-factor authentication. + default: true + authme.player.totpremove: + description: Permission to disable two-factor authentication. + default: true + authme.player.unregister: + description: Command permission to unregister. + default: true + authme.vip: + description: When the server is full and someone with this permission joins the + server, someone will be kicked. + default: false diff --git a/authme-tools/pom.xml b/authme-tools/pom.xml new file mode 100644 index 0000000000..d698f91d86 --- /dev/null +++ b/authme-tools/pom.xml @@ -0,0 +1,214 @@ + + + 4.0.0 + + + fr.xephi + authme-parent + ${revision} + + + authme-tools + jar + AuthMe Tools + Development tooling for AuthMeReloaded + + + + fr.xephi + authme-core + ${project.version} + + + + net.ricecode + string-similarity + test + + + com.maxmind.geoip2 + geoip2 + test + + + javatar + javatar + test + + + org.apache.commons + commons-email + test + + + com.zaxxer + HikariCP + test + + + org.slf4j + slf4j-simple + test + + + ch.jalu + datasourcecolumns + test + + + com.mysql + mysql-connector-j + test + + + org.mariadb.jdbc + mariadb-java-client + test + + + org.postgresql + postgresql + test + + + de.rtner + PBKDF2 + test + + + de.mkammerer + argon2-jvm-nolibs + test + + + at.favre.lib + bcrypt + test + + + com.warrenstrange + googleauth + test + + + org.spigotmc + spigot-api + ${dependencies.spigot.version} + test + + + org.joml + joml + + + net.md-5 + bungeecord-chat + + + + + ch.jalu + configme + test + + + ch.jalu + injector + test + + + org.bstats + bstats-bukkit + test + + + com.comphenix.protocol + ProtocolLib + test + + + net.luckperms + api + test + + + ru.tehkode + PermissionsEx + test + + + org.tyrannyofheaven.bukkit + zPermissions + test + + + net.milkbowl.vault + VaultAPI + test + + + com.onarandombox.multiversecore + multiverse-core + test + + + net.essentialsx + EssentialsX + test + + + org.apache.logging.log4j + log4j-core + test + + + org.mockito + mockito-core + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.codehaus.mojo + exec-maven-plugin + 3.6.1 + + test + tools.ToolsRunner + + + + + + + + run-tools + + + + org.codehaus.mojo + exec-maven-plugin + + + run-tools + process-test-classes + + java + + + + + + + + + diff --git a/authme-tools/src/test/java/fr/xephi/authme/ClassCollector.java b/authme-tools/src/test/java/fr/xephi/authme/ClassCollector.java new file mode 100644 index 0000000000..23c065e822 --- /dev/null +++ b/authme-tools/src/test/java/fr/xephi/authme/ClassCollector.java @@ -0,0 +1,165 @@ +package fr.xephi.authme; + +import java.io.File; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * Collects available classes by walking through a source directory. + *

+ * This is a naive, zero dependency collector that walks through a file directory + * and loads classes from the class loader based on the .java files it encounters. + * This is a very slow approach and should be avoided for production code. + *

+ * For more performant approaches, see e.g. org.reflections. + */ +public class ClassCollector { + + private final String root; + private final String nonCodePath; + + /** + * Constructor. The arguments make up the path from which the collector will start scanning. + * + * @param nonCodePath beginning of the starting path that are not Java packages, e.g. {@code src/main/java/} + * @param packagePath folders following {@code nonCodePath} that are packages, e.g. {@code com/project/app} + */ + public ClassCollector(String nonCodePath, String packagePath) { + if (!nonCodePath.endsWith("/") && !nonCodePath.endsWith("\\")) { + nonCodePath = nonCodePath.concat(File.separator); + } + this.root = nonCodePath + packagePath; + this.nonCodePath = nonCodePath; + } + + /** + * Collects all classes from the parent folder and below. + * + * @return all classes + */ + public List> collectClasses() { + return collectClasses(x -> true); + } + + /** + * Collects all classes from the parent folder and below which are of type {@link T}. + * + * @param parent the parent which classes need to extend (or be equal to) in order to be collected + * @param the parent type + * @return list of matching classes + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public List> collectClasses(Class parent) { + List> classes = collectClasses(parent::isAssignableFrom); + return new ArrayList<>((List) classes); + } + + /** + * Collects all classes from the parent folder and below which match the given predicate. + * + * @param filter the predicate classes need to satisfy in order to be collected + * @return list of matching classes + */ + public List> collectClasses(Predicate> filter) { + File rootFolder = new File(root); + List> collection = new ArrayList<>(); + gatherClassesFromFile(rootFolder, filter, collection); + return collection; + } + + /** + * Constructs an instance of all classes which are of the provided type {@code clazz}. + * This method assumes that every class has an accessible no-args constructor for creation. + * + * @param parent the parent which classes need to extend (or be equal to) in order to be instantiated + * @param the parent type + * @return collection of created objects + */ + public List getInstancesOfType(Class parent) { + return getInstancesOfType(parent, (clz) -> { + try { + return canInstantiate(clz) ? clz.newInstance() : null; + } catch (InstantiationException | IllegalAccessException e) { + throw new IllegalStateException(e); + } + }); + } + + /** + * Constructs an instance of all classes which are of the provided type {@code clazz} + * with the provided {@code instantiator}. + * + * @param parent the parent which classes need to extend (or be equal to) in order to be instantiated + * @param instantiator function which returns an object of the given class, or null to skip the class + * @param the parent type + * @return collection of created objects + */ + public List getInstancesOfType(Class parent, Function, T> instantiator) { + return collectClasses(parent) + .stream() + .map(instantiator) + .filter(o -> o != null) + .collect(Collectors.toList()); + } + + /** + * Returns whether the given class can be instantiated, i.e. if it is not abstract, an interface, etc. + * + * @param clazz the class to process + * @return true if the class can be instantiated, false otherwise + */ + public static boolean canInstantiate(Class clazz) { + return clazz != null && !clazz.isEnum() && !clazz.isInterface() + && !clazz.isArray() && !Modifier.isAbstract(clazz.getModifiers()); + } + + /** + * Recursively collects the classes based on the files in the directory and in its child directories. + * + * @param folder the folder to scan + * @param filter the class predicate + * @param collection collection to add classes to + */ + private void gatherClassesFromFile(File folder, Predicate> filter, List> collection) { + File[] files = folder.listFiles(); + if (files == null) { + throw new IllegalStateException("Could not read files from '" + folder + "'"); + } + for (File file : files) { + if (file.isDirectory()) { + gatherClassesFromFile(file, filter, collection); + } else if (file.isFile()) { + Class clazz = loadTaskClassFromFile(file); + if (clazz != null && filter.test(clazz)) { + collection.add(clazz); + } + } + } + } + + /** + * Loads a class from the class loader based on the given file. + * + * @param file the file whose corresponding Java class should be retrieved + * @return the corresponding class, or null if not applicable + */ + private Class loadTaskClassFromFile(File file) { + if (!file.getName().endsWith(".java")) { + return null; + } + + String filePath = file.getPath(); + String className = filePath + .substring(nonCodePath.length(), filePath.length() - 5) + .replace(File.separator, "."); + try { + return Class.forName(className); + } catch (ClassNotFoundException e) { + throw new IllegalStateException(e); + } + } +} diff --git a/authme-tools/src/test/java/fr/xephi/authme/TestHelper.java b/authme-tools/src/test/java/fr/xephi/authme/TestHelper.java new file mode 100644 index 0000000000..cddb5572d5 --- /dev/null +++ b/authme-tools/src/test/java/fr/xephi/authme/TestHelper.java @@ -0,0 +1,32 @@ +package fr.xephi.authme; + +import ch.jalu.configme.properties.Property; +import fr.xephi.authme.settings.Settings; +import tools.utils.ToolsConstants; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; + +/** + * Minimal test utilities required by the tooling module. + */ +public final class TestHelper { + + public static final String SOURCES_FOLDER = ToolsConstants.MAIN_SOURCE_ROOT; + public static final String TEST_SOURCES_FOLDER = ToolsConstants.CORE_TEST_SOURCE_ROOT; + public static final String PROJECT_ROOT = "/fr/xephi/authme/"; + + private TestHelper() { + } + + /** + * Configures the Settings mock to return the property's default value for any given property. + * + * @param settings the settings mock + */ + @SuppressWarnings("unchecked") + public static void returnDefaultsForAllProperties(Settings settings) { + given(settings.getProperty(any(Property.class))) + .willAnswer(invocation -> ((Property) invocation.getArgument(0)).getDefaultValue()); + } +} diff --git a/authme-tools/src/test/java/tools/README.md b/authme-tools/src/test/java/tools/README.md new file mode 100644 index 0000000000..00e4463ebc --- /dev/null +++ b/authme-tools/src/test/java/tools/README.md @@ -0,0 +1,7 @@ +# About the _tools_ Folder + +This _tools_ folder provides helpers and extended tests useful during the development of AuthMe. +It now lives in the dedicated `authme-tools` module and does not contain unit tests. + +Run the tools from the repository root with the `run-tools` Maven profile. +See the exact commands in [`docs/build.md`](../../../../../docs/build.md). diff --git a/src/test/java/tools/ToolsRunner.java b/authme-tools/src/test/java/tools/ToolsRunner.java similarity index 94% rename from src/test/java/tools/ToolsRunner.java rename to authme-tools/src/test/java/tools/ToolsRunner.java index 3dfbd36b74..611a6f2f65 100644 --- a/src/test/java/tools/ToolsRunner.java +++ b/authme-tools/src/test/java/tools/ToolsRunner.java @@ -1,9 +1,9 @@ package tools; import fr.xephi.authme.ClassCollector; -import fr.xephi.authme.TestHelper; import tools.utils.AutoToolTask; import tools.utils.ToolTask; +import tools.utils.ToolsConstants; import java.util.Map; import java.util.Scanner; @@ -28,7 +28,7 @@ private ToolsRunner(Map tasks) { public static void main(String... args) { // Note ljacqu 20151212: If the tools folder becomes a lot bigger, it will make sense to restrict the depth // of this recursive collector - ClassCollector collector = new ClassCollector(TestHelper.TEST_SOURCES_FOLDER, "tools"); + ClassCollector collector = new ClassCollector(ToolsConstants.TOOLS_TEST_SOURCE_ROOT, "tools"); Map tasks = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); for (ToolTask task : collector.getInstancesOfType(ToolTask.class)) { tasks.put(task.getTaskName(), task); diff --git a/src/test/java/tools/bathelpers/README.md b/authme-tools/src/test/java/tools/bathelpers/README.md similarity index 100% rename from src/test/java/tools/bathelpers/README.md rename to authme-tools/src/test/java/tools/bathelpers/README.md diff --git a/src/test/java/tools/bathelpers/build_project.bat b/authme-tools/src/test/java/tools/bathelpers/build_project.bat similarity index 100% rename from src/test/java/tools/bathelpers/build_project.bat rename to authme-tools/src/test/java/tools/bathelpers/build_project.bat diff --git a/src/test/java/tools/bathelpers/freshen_jar.bat b/authme-tools/src/test/java/tools/bathelpers/freshen_jar.bat similarity index 100% rename from src/test/java/tools/bathelpers/freshen_jar.bat rename to authme-tools/src/test/java/tools/bathelpers/freshen_jar.bat diff --git a/src/test/java/tools/bathelpers/list_files.bat b/authme-tools/src/test/java/tools/bathelpers/list_files.bat similarity index 100% rename from src/test/java/tools/bathelpers/list_files.bat rename to authme-tools/src/test/java/tools/bathelpers/list_files.bat diff --git a/src/test/java/tools/bathelpers/move_plugin.bat b/authme-tools/src/test/java/tools/bathelpers/move_plugin.bat similarity index 100% rename from src/test/java/tools/bathelpers/move_plugin.bat rename to authme-tools/src/test/java/tools/bathelpers/move_plugin.bat diff --git a/src/test/java/tools/bathelpers/quick_build.bat b/authme-tools/src/test/java/tools/bathelpers/quick_build.bat similarity index 100% rename from src/test/java/tools/bathelpers/quick_build.bat rename to authme-tools/src/test/java/tools/bathelpers/quick_build.bat diff --git a/src/test/java/tools/bathelpers/run_server.bat b/authme-tools/src/test/java/tools/bathelpers/run_server.bat similarity index 100% rename from src/test/java/tools/bathelpers/run_server.bat rename to authme-tools/src/test/java/tools/bathelpers/run_server.bat diff --git a/src/test/java/tools/bathelpers/setvars.bat b/authme-tools/src/test/java/tools/bathelpers/setvars.bat similarity index 100% rename from src/test/java/tools/bathelpers/setvars.bat rename to authme-tools/src/test/java/tools/bathelpers/setvars.bat diff --git a/src/test/java/tools/checktestmocks/CheckTestMocks.java b/authme-tools/src/test/java/tools/checktestmocks/CheckTestMocks.java similarity index 100% rename from src/test/java/tools/checktestmocks/CheckTestMocks.java rename to authme-tools/src/test/java/tools/checktestmocks/CheckTestMocks.java diff --git a/src/test/java/tools/dependencygraph/DrawDependency.java b/authme-tools/src/test/java/tools/dependencygraph/DrawDependency.java similarity index 100% rename from src/test/java/tools/dependencygraph/DrawDependency.java rename to authme-tools/src/test/java/tools/dependencygraph/DrawDependency.java diff --git a/src/test/java/tools/docs/UpdateDocsTask.java b/authme-tools/src/test/java/tools/docs/UpdateDocsTask.java similarity index 91% rename from src/test/java/tools/docs/UpdateDocsTask.java rename to authme-tools/src/test/java/tools/docs/UpdateDocsTask.java index 84f723c648..60886e2fb9 100644 --- a/src/test/java/tools/docs/UpdateDocsTask.java +++ b/authme-tools/src/test/java/tools/docs/UpdateDocsTask.java @@ -1,9 +1,9 @@ package tools.docs; import fr.xephi.authme.ClassCollector; -import fr.xephi.authme.TestHelper; import tools.utils.AutoToolTask; import tools.utils.ToolTask; +import tools.utils.ToolsConstants; import java.util.List; import java.util.Scanner; @@ -43,7 +43,7 @@ private void executeTasks(Consumer taskRunner) { private List getDocTasks() { ClassCollector classCollector = - new ClassCollector(TestHelper.TEST_SOURCES_FOLDER, "tools/docs"); + new ClassCollector(ToolsConstants.TOOLS_TEST_SOURCE_ROOT, "tools/docs"); return classCollector.getInstancesOfType(ToolTask.class).stream() .filter(task -> task.getClass() != getClass()) .collect(Collectors.toList()); diff --git a/src/test/java/tools/docs/commands/CommandPageCreater.java b/authme-tools/src/test/java/tools/docs/commands/CommandPageCreater.java similarity index 100% rename from src/test/java/tools/docs/commands/CommandPageCreater.java rename to authme-tools/src/test/java/tools/docs/commands/CommandPageCreater.java diff --git a/src/test/java/tools/docs/commands/commands.tpl.md b/authme-tools/src/test/java/tools/docs/commands/commands.tpl.md similarity index 63% rename from src/test/java/tools/docs/commands/commands.tpl.md rename to authme-tools/src/test/java/tools/docs/commands/commands.tpl.md index 61018634cb..2571227a4c 100644 --- a/src/test/java/tools/docs/commands/commands.tpl.md +++ b/authme-tools/src/test/java/tools/docs/commands/commands.tpl.md @@ -1,10 +1,12 @@ - + ## AuthMe Commands You can use the following commands to use the features of AuthMe. Mandatory arguments are marked with `< >` brackets; optional arguments are enclosed in square brackets (`[ ]`). +The command tree is shared across the current Spigot Legacy, Spigot 1.21, and Paper 1.21 builds. + [#commands] - **{command}**{arguments}: {description}[permissions]
Requires `{permissions}`[/permissions] diff --git a/src/test/java/tools/docs/config/UpdateConfigPageTask.java b/authme-tools/src/test/java/tools/docs/config/UpdateConfigPageTask.java similarity index 100% rename from src/test/java/tools/docs/config/UpdateConfigPageTask.java rename to authme-tools/src/test/java/tools/docs/config/UpdateConfigPageTask.java diff --git a/src/test/java/tools/docs/config/config.tpl.md b/authme-tools/src/test/java/tools/docs/config/config.tpl.md similarity index 57% rename from src/test/java/tools/docs/config/config.tpl.md rename to authme-tools/src/test/java/tools/docs/config/config.tpl.md index 1de5c99dfb..fba9a0efc6 100644 --- a/src/test/java/tools/docs/config/config.tpl.md +++ b/authme-tools/src/test/java/tools/docs/config/config.tpl.md @@ -1,11 +1,14 @@ - + ## AuthMe Configuration The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder, with which you can configure various settings. The following is the initial contents of the generated config.yml file. +This configuration is generated from the shared `authme-core` module. It applies to the +current Spigot Legacy, Spigot 1.21, and Paper 1.21 builds unless a setting comment says otherwise. + ```yml {config} ``` diff --git a/src/test/java/tools/docs/hashmethods/EncryptionMethodInfoGatherer.java b/authme-tools/src/test/java/tools/docs/hashmethods/EncryptionMethodInfoGatherer.java similarity index 100% rename from src/test/java/tools/docs/hashmethods/EncryptionMethodInfoGatherer.java rename to authme-tools/src/test/java/tools/docs/hashmethods/EncryptionMethodInfoGatherer.java diff --git a/src/test/java/tools/docs/hashmethods/HashAlgorithmsDescriptionTask.java b/authme-tools/src/test/java/tools/docs/hashmethods/HashAlgorithmsDescriptionTask.java similarity index 100% rename from src/test/java/tools/docs/hashmethods/HashAlgorithmsDescriptionTask.java rename to authme-tools/src/test/java/tools/docs/hashmethods/HashAlgorithmsDescriptionTask.java diff --git a/src/test/java/tools/docs/hashmethods/MethodDescription.java b/authme-tools/src/test/java/tools/docs/hashmethods/MethodDescription.java similarity index 100% rename from src/test/java/tools/docs/hashmethods/MethodDescription.java rename to authme-tools/src/test/java/tools/docs/hashmethods/MethodDescription.java diff --git a/src/test/java/tools/docs/hashmethods/hash_algorithms.tpl.md b/authme-tools/src/test/java/tools/docs/hashmethods/hash_algorithms.tpl.md similarity index 95% rename from src/test/java/tools/docs/hashmethods/hash_algorithms.tpl.md rename to authme-tools/src/test/java/tools/docs/hashmethods/hash_algorithms.tpl.md index bb3c8befcf..b728fd2140 100644 --- a/src/test/java/tools/docs/hashmethods/hash_algorithms.tpl.md +++ b/authme-tools/src/test/java/tools/docs/hashmethods/hash_algorithms.tpl.md @@ -1,5 +1,5 @@ - + ## Hash Algorithms AuthMe supports the following hash algorithms for storing your passwords safely. diff --git a/src/test/java/tools/docs/permissions/PermissionNodesGatherer.java b/authme-tools/src/test/java/tools/docs/permissions/PermissionNodesGatherer.java similarity index 100% rename from src/test/java/tools/docs/permissions/PermissionNodesGatherer.java rename to authme-tools/src/test/java/tools/docs/permissions/PermissionNodesGatherer.java diff --git a/src/test/java/tools/docs/permissions/PermissionsListWriter.java b/authme-tools/src/test/java/tools/docs/permissions/PermissionsListWriter.java similarity index 100% rename from src/test/java/tools/docs/permissions/PermissionsListWriter.java rename to authme-tools/src/test/java/tools/docs/permissions/PermissionsListWriter.java diff --git a/authme-tools/src/test/java/tools/docs/permissions/permission_nodes.tpl.md b/authme-tools/src/test/java/tools/docs/permissions/permission_nodes.tpl.md new file mode 100644 index 0000000000..132dbea8bc --- /dev/null +++ b/authme-tools/src/test/java/tools/docs/permissions/permission_nodes.tpl.md @@ -0,0 +1,14 @@ + + + +## AuthMe Permission Nodes +The following are the permission nodes that are currently supported by the latest dev builds. + +Unless noted otherwise, these nodes are available across the current Spigot Legacy, Spigot 1.21, +and Paper 1.21 builds. + +[#nodes] + - **{node}** – {description} +[/#nodes] + +{gen_footer} diff --git a/src/test/java/tools/docs/translations/TranslationPageGenerator.java b/authme-tools/src/test/java/tools/docs/translations/TranslationPageGenerator.java similarity index 100% rename from src/test/java/tools/docs/translations/TranslationPageGenerator.java rename to authme-tools/src/test/java/tools/docs/translations/TranslationPageGenerator.java diff --git a/src/test/java/tools/docs/translations/TranslationsGatherer.java b/authme-tools/src/test/java/tools/docs/translations/TranslationsGatherer.java similarity index 100% rename from src/test/java/tools/docs/translations/TranslationsGatherer.java rename to authme-tools/src/test/java/tools/docs/translations/TranslationsGatherer.java diff --git a/authme-tools/src/test/java/tools/docs/translations/translations.tpl.md b/authme-tools/src/test/java/tools/docs/translations/translations.tpl.md new file mode 100644 index 0000000000..bd469c77d1 --- /dev/null +++ b/authme-tools/src/test/java/tools/docs/translations/translations.tpl.md @@ -0,0 +1,14 @@ + + + +# AuthMe Translations +The following translations are available in AuthMe. Set `messagesLanguage` to the language code +in your config.yml to use the language, or use another language code to start a new translation. + +Code | Language | Translated |   +---- | -------- | ---------: | ------ +[#languages] +[{code}](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_{code}.yml) | {name} | {percentage}% | {percentage} +[/#languages] + +{gen_footer} diff --git a/src/test/java/tools/filegeneration/GenerateCommandsYml.java b/authme-tools/src/test/java/tools/filegeneration/GenerateCommandsYml.java similarity index 100% rename from src/test/java/tools/filegeneration/GenerateCommandsYml.java rename to authme-tools/src/test/java/tools/filegeneration/GenerateCommandsYml.java diff --git a/src/test/java/tools/filegeneration/GeneratePluginYml.java b/authme-tools/src/test/java/tools/filegeneration/GeneratePluginYml.java similarity index 78% rename from src/test/java/tools/filegeneration/GeneratePluginYml.java rename to authme-tools/src/test/java/tools/filegeneration/GeneratePluginYml.java index cf833b007f..ecadb5d1c0 100644 --- a/src/test/java/tools/filegeneration/GeneratePluginYml.java +++ b/authme-tools/src/test/java/tools/filegeneration/GeneratePluginYml.java @@ -14,6 +14,7 @@ import tools.utils.ToolsConstants; import java.io.StringReader; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.Collection; @@ -28,7 +29,13 @@ */ public class GeneratePluginYml implements AutoToolTask { - private static final String PLUGIN_YML_FILE = ToolsConstants.MAIN_RESOURCES_ROOT + "plugin.yml"; + private static final List PLUGIN_YML_FILES = List.of( + Paths.get(ToolsConstants.MAIN_RESOURCES_ROOT, "plugin.yml"), + Paths.get(ToolsConstants.CORE_TEST_RESOURCES_ROOT, "plugin.yml"), + Paths.get(ToolsConstants.REPOSITORY_ROOT_PATH, "authme-spigot-legacy", "src", "main", "resources", "plugin.yml"), + Paths.get(ToolsConstants.REPOSITORY_ROOT_PATH, "authme-spigot-1.21", "src", "main", "resources", "plugin.yml"), + Paths.get(ToolsConstants.REPOSITORY_ROOT_PATH, "authme-paper", "src", "main", "resources", "plugin.yml"), + Paths.get(ToolsConstants.REPOSITORY_ROOT_PATH, "authme-folia", "src", "main", "resources", "plugin.yml")); private static final Map WILDCARD_PERMISSIONS = ImmutableMap.of( "authme.player.*", "Gives access to all player commands", @@ -38,17 +45,20 @@ public class GeneratePluginYml implements AutoToolTask { private List permissionNodes; - private String pluginYmlStart; - @Override public void executeDefault() { - FileConfiguration configuration = loadPartialPluginYmlFile(); + Map commands = generateCommands(); + Map permissions = generatePermissions(); - configuration.set("commands", generateCommands()); - configuration.set("permissions", generatePermissions()); + for (Path pluginYmlFile : PLUGIN_YML_FILES) { + PartialPluginYml pluginYml = loadPartialPluginYmlFile(pluginYmlFile); + pluginYml.configuration().set("commands", commands); + pluginYml.configuration().set("permissions", permissions); - FileIoUtils.writeToFile(PLUGIN_YML_FILE, - pluginYmlStart + "\n" + configuration.saveToString()); + FileIoUtils.writeToFile(pluginYmlFile, + pluginYml.pluginYmlStart() + "\n" + pluginYml.configuration().saveToString()); + System.out.println("Updated " + pluginYmlFile); + } } @Override @@ -61,10 +71,11 @@ public String getTaskName() { * to split the contents into an upper part that we ignore and a lower part we load as YAML. When * saving we prepend the YAML export with the stripped off part of the file again. * - * @return file configuration with the lower part of the plugin.yml file + * @param pluginYmlFile the file to update + * @return plugin.yml contents split into the preserved header and the editable YAML section */ - private FileConfiguration loadPartialPluginYmlFile() { - List pluginYmlLines = FileIoUtils.readLinesFromFile(Paths.get(PLUGIN_YML_FILE)); + private static PartialPluginYml loadPartialPluginYmlFile(Path pluginYmlFile) { + List pluginYmlLines = FileIoUtils.readLinesFromFile(pluginYmlFile); int lineNr = 0; for (String line : pluginYmlLines) { if ("commands:".equals(line)) { @@ -75,9 +86,9 @@ private FileConfiguration loadPartialPluginYmlFile() { if (lineNr == pluginYmlLines.size()) { throw new IllegalStateException("Could not find line starting 'commands:' section"); } - pluginYmlStart = String.join("\n", pluginYmlLines.subList(0, lineNr)); + String pluginYmlStart = String.join("\n", pluginYmlLines.subList(0, lineNr)); String yamlContents = String.join("\n", pluginYmlLines.subList(lineNr, pluginYmlLines.size())); - return YamlConfiguration.loadConfiguration(new StringReader(yamlContents)); + return new PartialPluginYml(YamlConfiguration.loadConfiguration(new StringReader(yamlContents)), pluginYmlStart); } private static Map generateCommands() { @@ -179,4 +190,7 @@ private static Object convertDefaultPermission(DefaultPermission defaultPermissi throw new IllegalArgumentException("Unknown default permission '" + defaultPermission + "'"); } } + + private record PartialPluginYml(FileConfiguration configuration, String pluginYmlStart) { + } } diff --git a/src/test/java/tools/helptranslation/HelpTranslationVerifier.java b/authme-tools/src/test/java/tools/helptranslation/HelpTranslationVerifier.java similarity index 100% rename from src/test/java/tools/helptranslation/HelpTranslationVerifier.java rename to authme-tools/src/test/java/tools/helptranslation/HelpTranslationVerifier.java diff --git a/src/test/java/tools/helptranslation/VerifyHelpTranslations.java b/authme-tools/src/test/java/tools/helptranslation/VerifyHelpTranslations.java similarity index 100% rename from src/test/java/tools/helptranslation/VerifyHelpTranslations.java rename to authme-tools/src/test/java/tools/helptranslation/VerifyHelpTranslations.java diff --git a/src/test/java/tools/messages/AddJavaDocToMessageEnumTask.java b/authme-tools/src/test/java/tools/messages/AddJavaDocToMessageEnumTask.java similarity index 100% rename from src/test/java/tools/messages/AddJavaDocToMessageEnumTask.java rename to authme-tools/src/test/java/tools/messages/AddJavaDocToMessageEnumTask.java diff --git a/src/test/java/tools/messages/CheckMessageKeyUsages.java b/authme-tools/src/test/java/tools/messages/CheckMessageKeyUsages.java similarity index 100% rename from src/test/java/tools/messages/CheckMessageKeyUsages.java rename to authme-tools/src/test/java/tools/messages/CheckMessageKeyUsages.java diff --git a/src/test/java/tools/messages/MessageFileVerifier.java b/authme-tools/src/test/java/tools/messages/MessageFileVerifier.java similarity index 100% rename from src/test/java/tools/messages/MessageFileVerifier.java rename to authme-tools/src/test/java/tools/messages/MessageFileVerifier.java diff --git a/src/test/java/tools/messages/MessagesFileWriter.java b/authme-tools/src/test/java/tools/messages/MessagesFileWriter.java similarity index 100% rename from src/test/java/tools/messages/MessagesFileWriter.java rename to authme-tools/src/test/java/tools/messages/MessagesFileWriter.java diff --git a/src/test/java/tools/messages/README.md b/authme-tools/src/test/java/tools/messages/README.md similarity index 100% rename from src/test/java/tools/messages/README.md rename to authme-tools/src/test/java/tools/messages/README.md diff --git a/src/test/java/tools/messages/VerifyMessagesTask.java b/authme-tools/src/test/java/tools/messages/VerifyMessagesTask.java similarity index 100% rename from src/test/java/tools/messages/VerifyMessagesTask.java rename to authme-tools/src/test/java/tools/messages/VerifyMessagesTask.java diff --git a/src/test/java/tools/shhelpers/build_project.sh b/authme-tools/src/test/java/tools/shhelpers/build_project.sh similarity index 100% rename from src/test/java/tools/shhelpers/build_project.sh rename to authme-tools/src/test/java/tools/shhelpers/build_project.sh diff --git a/src/test/java/tools/shhelpers/list_files.sh b/authme-tools/src/test/java/tools/shhelpers/list_files.sh similarity index 100% rename from src/test/java/tools/shhelpers/list_files.sh rename to authme-tools/src/test/java/tools/shhelpers/list_files.sh diff --git a/src/test/java/tools/shhelpers/move_plugin.sh b/authme-tools/src/test/java/tools/shhelpers/move_plugin.sh similarity index 100% rename from src/test/java/tools/shhelpers/move_plugin.sh rename to authme-tools/src/test/java/tools/shhelpers/move_plugin.sh diff --git a/src/test/java/tools/shhelpers/quick_build.sh b/authme-tools/src/test/java/tools/shhelpers/quick_build.sh similarity index 100% rename from src/test/java/tools/shhelpers/quick_build.sh rename to authme-tools/src/test/java/tools/shhelpers/quick_build.sh diff --git a/src/test/java/tools/shhelpers/run_server.sh b/authme-tools/src/test/java/tools/shhelpers/run_server.sh similarity index 100% rename from src/test/java/tools/shhelpers/run_server.sh rename to authme-tools/src/test/java/tools/shhelpers/run_server.sh diff --git a/src/test/java/tools/shhelpers/setvars.sh b/authme-tools/src/test/java/tools/shhelpers/setvars.sh similarity index 100% rename from src/test/java/tools/shhelpers/setvars.sh rename to authme-tools/src/test/java/tools/shhelpers/setvars.sh diff --git a/src/test/java/tools/shhelpers/sort_file_content.sh b/authme-tools/src/test/java/tools/shhelpers/sort_file_content.sh similarity index 100% rename from src/test/java/tools/shhelpers/sort_file_content.sh rename to authme-tools/src/test/java/tools/shhelpers/sort_file_content.sh diff --git a/src/test/java/tools/utils/AutoToolTask.java b/authme-tools/src/test/java/tools/utils/AutoToolTask.java similarity index 100% rename from src/test/java/tools/utils/AutoToolTask.java rename to authme-tools/src/test/java/tools/utils/AutoToolTask.java diff --git a/src/test/java/tools/utils/FileIoUtils.java b/authme-tools/src/test/java/tools/utils/FileIoUtils.java similarity index 100% rename from src/test/java/tools/utils/FileIoUtils.java rename to authme-tools/src/test/java/tools/utils/FileIoUtils.java diff --git a/src/test/java/tools/utils/InjectorUtils.java b/authme-tools/src/test/java/tools/utils/InjectorUtils.java similarity index 100% rename from src/test/java/tools/utils/InjectorUtils.java rename to authme-tools/src/test/java/tools/utils/InjectorUtils.java diff --git a/src/test/java/tools/utils/TagReplacer.java b/authme-tools/src/test/java/tools/utils/TagReplacer.java similarity index 100% rename from src/test/java/tools/utils/TagReplacer.java rename to authme-tools/src/test/java/tools/utils/TagReplacer.java diff --git a/src/test/java/tools/utils/TagValue.java b/authme-tools/src/test/java/tools/utils/TagValue.java similarity index 100% rename from src/test/java/tools/utils/TagValue.java rename to authme-tools/src/test/java/tools/utils/TagValue.java diff --git a/src/test/java/tools/utils/TagValueHolder.java b/authme-tools/src/test/java/tools/utils/TagValueHolder.java similarity index 100% rename from src/test/java/tools/utils/TagValueHolder.java rename to authme-tools/src/test/java/tools/utils/TagValueHolder.java diff --git a/src/test/java/tools/utils/ToolTask.java b/authme-tools/src/test/java/tools/utils/ToolTask.java similarity index 100% rename from src/test/java/tools/utils/ToolTask.java rename to authme-tools/src/test/java/tools/utils/ToolTask.java diff --git a/authme-tools/src/test/java/tools/utils/ToolsConstants.java b/authme-tools/src/test/java/tools/utils/ToolsConstants.java new file mode 100644 index 0000000000..22fe07d0e4 --- /dev/null +++ b/authme-tools/src/test/java/tools/utils/ToolsConstants.java @@ -0,0 +1,54 @@ +package tools.utils; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * Constants for the src/tools folder. + */ +public final class ToolsConstants { + + private static final Path CURRENT_DIRECTORY = Paths.get("").toAbsolutePath().normalize(); + + private static final Path REPOSITORY_ROOT = + Files.isDirectory(CURRENT_DIRECTORY.resolve("authme-core")) ? CURRENT_DIRECTORY : CURRENT_DIRECTORY.getParent(); + + private static final Path TOOLS_MODULE_ROOT = + Files.isDirectory(CURRENT_DIRECTORY.resolve("src").resolve("test").resolve("java").resolve("tools")) + ? CURRENT_DIRECTORY + : REPOSITORY_ROOT.resolve("authme-tools"); + + public static final String MAIN_SOURCE_ROOT = asDirectory(REPOSITORY_ROOT.resolve("authme-core") + .resolve("src").resolve("main").resolve("java")); + + public static final String REPOSITORY_ROOT_PATH = asDirectory(REPOSITORY_ROOT); + + public static final String MAIN_RESOURCES_ROOT = asDirectory(REPOSITORY_ROOT.resolve("authme-core") + .resolve("src").resolve("main").resolve("resources")); + + public static final String CORE_TEST_SOURCE_ROOT = asDirectory(REPOSITORY_ROOT.resolve("authme-core") + .resolve("src").resolve("test").resolve("java")); + + public static final String CORE_TEST_RESOURCES_ROOT = asDirectory(REPOSITORY_ROOT.resolve("authme-core") + .resolve("src").resolve("test").resolve("resources")); + + public static final String TOOLS_TEST_SOURCE_ROOT = asDirectory(TOOLS_MODULE_ROOT.resolve("src") + .resolve("test").resolve("java")); + + public static final String TOOLS_SOURCE_ROOT = asDirectory(TOOLS_MODULE_ROOT.resolve("src") + .resolve("test").resolve("java").resolve("tools")); + + // Docs are published at the repository root, while the tools run from the authme-tools module. + public static final String DOCS_FOLDER = asDirectory(REPOSITORY_ROOT.resolve("docs")); + + public static final String DOCS_FOLDER_URL = "https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/"; + + private ToolsConstants() { + } + + private static String asDirectory(Path path) { + return path.toString() + File.separator; + } +} diff --git a/docs/build.md b/docs/build.md new file mode 100644 index 0000000000..33b4156826 --- /dev/null +++ b/docs/build.md @@ -0,0 +1,91 @@ +# Building and Development Commands + +This page documents the main build, test, and tooling commands used in this repository. + +## Requirements + +- JDK 17+ for `authme-core`, `authme-tools`, and `authme-spigot-legacy` +- JDK 21+ for the full multi-module build, including: + - `authme-spigot-1.21` + - `authme-paper-common` + - `authme-paper` + - `authme-folia` +- Maven 3.8.8+ + +## Build commands + +From the repository root: + +```bash +# Build everything available for the current JDK +mvn clean package + +# Build without tests +mvn clean package -DskipTests + +# Build while skipping long hash tests +mvn clean package -P skipLongHashTests + +# Build one deliverable with its dependencies +mvn clean package -pl authme-paper-common -am +mvn clean package -pl authme-paper -am +mvn clean package -pl authme-folia -am +mvn clean package -pl authme-spigot-1.21 -am + +# Build without generating Javadoc +mvn clean package -P skipJavadocGeneration +``` + +## Test commands + +```bash +# Run tests in authme-core +mvn test -pl authme-core + +# Run tests in Paper-derived modules +mvn test -am -pl authme-paper-common,authme-paper,authme-folia + +# Run tests in 1.21 version modules +mvn test -am -pl authme-spigot-1.21,authme-paper,authme-folia + +# Run the full test suite +mvn test -P skipLongHashTests + +# Run one authme-core test class or method +mvn test -Dtest=ClassName -pl authme-core +mvn test -Dtest=ClassName#methodName -pl authme-core + +# Generate coverage for authme-core +mvn clean verify -pl authme-core -am +``` + +## Running development tools + +The tooling lives in `authme-tools`, but you can run it directly from the repository root through the Maven reactor. + +```bash +# Show the interactive task runner +mvn -q -pl authme-tools -am -P run-tools process-test-classes + +# Regenerate repository docs +mvn -q -pl authme-tools -am -P run-tools process-test-classes \ + -Dexec.args=updateDocs + +# Regenerate generated command/plugin manifests +mvn -q -pl authme-tools -am -P run-tools process-test-classes \ + "-Dexec.args=generateCommandsYml generatePluginYml" +``` + +## Generated files + +- `docs/commands.md` +- `docs/config.md` +- `docs/hash_algorithms.md` +- `docs/permission_nodes.md` +- `docs/translations.md` +- `authme-core/src/main/resources/commands.yml` +- `authme-core/src/main/resources/plugin.yml` +- `authme-core/src/test/resources/plugin.yml` +- version-module `plugin.yml` files + +If you change a generator or template, commit both the source change and the regenerated output. diff --git a/docs/commands.md b/docs/commands.md index 2a225e1f07..444bbc6f3b 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -1,10 +1,12 @@ - + ## AuthMe Commands You can use the following commands to use the features of AuthMe. Mandatory arguments are marked with `< >` brackets; optional arguments are enclosed in square brackets (`[ ]`). +The command tree is shared across the current Spigot Legacy, Spigot 1.21, and Paper 1.21 builds. + - **/authme**: The main AuthMeReloaded command. The root for all admin commands. - **/authme register** <player> <password>: Register the specified player with the specified password.
Requires `authme.admin.register` @@ -108,4 +110,4 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`). --- -This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Apr 04 21:31:42 CEST 2021 +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Wed Apr 22 20:38:37 CEST 2026 diff --git a/docs/config.md b/docs/config.md index 1c86eaf70c..b3f4fa66ee 100644 --- a/docs/config.md +++ b/docs/config.md @@ -1,11 +1,14 @@ - + ## AuthMe Configuration The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder, with which you can configure various settings. The following is the initial contents of the generated config.yml file. +This configuration is generated from the shared `authme-core` module. It applies to the +current Spigot Legacy, Spigot 1.21, and Paper 1.21 builds unless a setting comment says otherwise. + ```yml DataSource: # What type of database do you want to use? @@ -120,14 +123,24 @@ settings: # Message language, available languages: # https://github.com/AuthMe/AuthMeReloaded/blob/master/docs/translations.md messagesLanguage: en + # When enabled, messages are sent to each player in their client language, + # falling back to messagesLanguage if their locale is unavailable. + # Disable this if you want all players to receive messages in the same language. + perPlayerLocale: true # Forces authme to hook into Vault instead of a specific permission handler system. forceVaultHook: false # Log level: INFO, FINE, DEBUG. Use INFO for general messages, # FINE for some additional detailed ones (like password failed), # and DEBUG for debugging logLevel: FINE - # By default we schedule async tasks when talking to the database. If you want - # typical communication with the database to happen synchronously, set this to false + # Whether to run authentication work (password hashing, database queries) on async threads. + # When true (strongly recommended), all CPU-intensive and I/O-bound operations — including + # password hashing and database access — are offloaded from the main server thread, keeping + # the server responsive during login and registration. + # Setting this to false forces all of that work onto the main thread, which will cause + # noticeable lag spikes on every login or registration attempt, especially with slow + # algorithms (Argon2, BCrypt) or a remote database. Only set this to false if you have + # a specific technical reason — it is not recommended under any normal circumstances. useAsyncTasks: true # The name of the server, used in some placeholders. serverName: Your Minecraft Server @@ -198,9 +211,12 @@ settings: teleportUnAuthedToSpawn: false # Can unregistered players walk around? allowMovement: false - # After how many seconds should players who fail to login or register - # be kicked? Set to 0 to disable. - timeout: 30 + # After how many seconds should players who fail to login be kicked? + # Set to 0 to disable. + loginTimeout: 30 + # After how many seconds should players who fail to register be kicked? + # Set to 0 to disable. + registerTimeout: 30 # Regex pattern of allowed characters in the player name. allowedNicknameCharacters: '[a-zA-Z0-9_]*' # How far can unregistered players walk? @@ -251,7 +267,7 @@ settings: # Maximum length of password passwordMaxLength: 30 # Possible values: SHA256, BCRYPT, BCRYPT2Y, PBKDF2, SALTEDSHA512, - # MYBB, IPB3, PHPBB, PHPFUSION, SMF, XENFORO, XAUTH, JOOMLA, WBB3, WBB4, MD5VB, + # MYBB, IPB3, PHPBB, PHPFUSION, SMF, XENFORO, JOOMLA, WBB3, WBB4, MD5VB, # PBKDF2DJANGO, WORDPRESS, ROYALAUTH, ARGON2, CUSTOM (for developers only). See full list at # https://github.com/AuthMe/AuthMeReloaded/blob/master/docs/hash_algorithms.md # If you use ARGON2, check that you have the argon2 c library on your system @@ -304,6 +320,10 @@ settings: forceKickAfterRegister: false # Does AuthMe need to enforce a /login after a successful registration? forceLoginAfterRegister: false + # Show a graphical dialog UI for login/register instead of chat messages. + # Requires Minecraft 1.21.6+ on Spigot, or 1.21.11+ on Paper. + # On older server versions, this option is automatically ignored. + useDialogUi: true # Enable to display the welcome message (welcome.txt) after a login # You can use colors in this welcome.txt + some replaced strings: # {PLAYER}: player name, {ONLINE}: display number of online players, @@ -355,9 +375,15 @@ GroupOptions: Email: # Email SMTP server host mailSMTP: smtp.gmail.com - # Email SMTP server port + # Email SMTP server port. The port determines the encryption mode: + # 25 -> plain SMTP; optional STARTTLS via 'useTls' (see below) + # 465 -> implicit SSL/TLS (SMTPS); 'useTls' is ignored + # 587 -> STARTTLS required (submission); 'useTls' is ignored + # other -> STARTTLS required; 'useTls' is ignored mailPort: 465 - # Only affects port 25: enable TLS/STARTTLS? + # Only applies to port 25: enable STARTTLS on the plain SMTP connection? + # Has no effect when using port 465 (SSL) or 587 (STARTTLS), which enforce + # their own encryption and cannot be overridden by this setting. useTls: true # Email account which sends the mails mailAccount: '' @@ -386,6 +412,13 @@ Email: generateImage: false # The OAuth2 token emailOauth2Token: '' + # Verify the SSL/TLS server certificate hostname? + # Only applies when an SSL/TLS connection is active (port 465, port 587, + # port 25 with useTls=true, or any other port). + # Set to false only if your SMTP server uses a self-signed certificate. + # Note: if you previously used port 465, this check was not enforced; + # set to false to restore the old behavior with a self-signed certificate. + sslCheckServerIdentity: true Hooks: # Do we need to hook with multiverse for spawn checking? multiverse: true @@ -590,4 +623,4 @@ To change settings on a running server, save your changes to config.yml and use --- -This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Thu Jul 28 18:11:22 CEST 2022 +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Wed Apr 22 20:38:37 CEST 2026 diff --git a/docs/converters.md b/docs/converters.md new file mode 100644 index 0000000000..dbef9d3e2c --- /dev/null +++ b/docs/converters.md @@ -0,0 +1,74 @@ +## Converters + +Converters allow you to migrate player data from another authentication plugin into AuthMe. +Run a converter with `/authme converter ` (requires `authme.admin.converter`). + +--- + +### Auth+ → `authplus` + +Migrates accounts from the **Auth+** plugin. + +**Before running:** +1. Set `passwordHash` to `PBKDF2BASE64` in AuthMe's `config.yml` +2. Set `pbkdf2Rounds` to `120000` under `settings.security` in `config.yml` + +**Source file:** `plugins/Auth/players.yml` + +**Notes:** +- Auth+ stores accounts by UUID only. AuthMe resolves each UUID to a player name via the server's + local cache (`usercache.json`). Players who have never joined the server will be skipped. +- Players already present in AuthMe's database are skipped automatically. + +--- + +### CrazyLogin → `crazylogin` + +Migrates accounts from the **CrazyLogin** plugin. + +**Source file:** configured via `Converter.crazyloginFileName` in AuthMe's `config.yml` (default: `crazylogin.db`). + +--- + +### LoginSecurity → `loginsecurity` + +Migrates accounts from the **LoginSecurity** plugin. Supports both SQLite and MySQL sources, +configured under `Converter.LoginSecurity` in AuthMe's `config.yml`. + +**After running:** add `BCRYPT` to `legacyHashes` in your `config.yml`. + +--- + +### RakamakConverter → `rakamak` + +Migrates accounts from the **Rakamak** plugin. + +**Source file:** configured via `Converter.rakamakFile` in AuthMe's `config.yml`. + +--- + +### RoyalAuth → `royalauth` + +Migrates accounts from the **RoyalAuth** plugin. + +**Source file:** `plugins/RoyalAuth/players.yml` + +--- + +### vAuth → `vauth` + +Migrates accounts from the **vAuth** plugin. + +**Source file:** `plugins/vAuth/passwords.yml` + +--- + +### SQLite → SQL → `sqlitetosql` + +Copies AuthMe data from a SQLite database into the configured SQL database. + +--- + +### MySQL → SQLite → `mysqltosqlite` + +Copies AuthMe data from the configured MySQL database into a SQLite file. diff --git a/docs/hash_algorithms.md b/docs/hash_algorithms.md index 7e85e05c70..c42483af65 100644 --- a/docs/hash_algorithms.md +++ b/docs/hash_algorithms.md @@ -1,5 +1,5 @@ - + ## Hash Algorithms AuthMe supports the following hash algorithms for storing your passwords safely. @@ -18,6 +18,7 @@ JOOMLA | Acceptable | 65 | | | Text | 32 | MD5VB | Acceptable | 56 | | | Text | 16 | MYBB | Acceptable | 32 | | | Text | 8 | Y PBKDF2 | Recommended | 165 | | | Text | 16 | +PBKDF2BASE64 | Recommended | 82 | | | Text | 16 | PBKDF2DJANGO | Acceptable | 77 | Y | | Text | 12 | PHPBB | Acceptable | 60 | | | Text | 22 | PHPFUSION | Do not use | 64 | Y | | | | Y @@ -79,4 +80,4 @@ or bad. --- -This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Apr 04 21:31:44 CEST 2021 +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Wed Apr 22 20:38:38 CEST 2026 diff --git a/docs/permission_nodes.md b/docs/permission_nodes.md index bf76796d5c..30a5cf0932 100644 --- a/docs/permission_nodes.md +++ b/docs/permission_nodes.md @@ -1,9 +1,12 @@ - + ## AuthMe Permission Nodes The following are the permission nodes that are currently supported by the latest dev builds. +Unless noted otherwise, these nodes are available across the current Spigot Legacy, Spigot 1.21, +and Paper 1.21 builds. + - **authme.admin.*** – Give access to all admin commands. - **authme.admin.accounts** – Administrator command to see all accounts associated with a user. - **authme.admin.antibotmessages** – Permission to see Antibot messages. @@ -73,4 +76,4 @@ The following are the permission nodes that are currently supported by the lates --- -This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Apr 04 21:31:44 CEST 2021 +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Wed Apr 22 20:38:39 CEST 2026 diff --git a/docs/translations.md b/docs/translations.md index fc8251e64f..1e88dfbd66 100644 --- a/docs/translations.md +++ b/docs/translations.md @@ -1,5 +1,5 @@ - + # AuthMe Translations The following translations are available in AuthMe. Set `messagesLanguage` to the language code @@ -7,41 +7,41 @@ in your config.yml to use the language, or use another language code to start a Code | Language | Translated |   ---- | -------- | ---------: | ------ -[en](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_en.yml) | English | 100% | 100 -[bg](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_bg.yml) | Bulgarian | 99% | 99 -[br](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_br.yml) | Brazilian | 100% | 100 -[cz](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_cz.yml) | Czech | 100% | 100 -[de](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_de.yml) | German | 99% | 99 -[eo](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eo.yml) | Esperanto | 79% | 79 -[es](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_es.yml) | Spanish | 99% | 99 -[et](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_et.yml) | Estonian | 100% | 100 -[eu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eu.yml) | Basque | 100% | 100 -[fi](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fi.yml) | Finnish | 45% | 45 -[fr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fr.yml) | French | 100% | 100 -[gl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_gl.yml) | Galician | 48% | 48 -[hu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_hu.yml) | Hungarian | 99% | 99 -[id](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_id.yml) | Indonesian | 93% | 93 -[it](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_it.yml) | Italian | 100% | 100 -[ja](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ja.yml) | Japanese | 100% | 100 -[ko](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ko.yml) | Korean | 99% | 99 -[lt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_lt.yml) | Lithuanian | 100% | 100 -[nl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_nl.yml) | Dutch | 100% | 100 -[pl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pl.yml) | Polish | 100% | 100 -[pt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pt.yml) | Portuguese | 100% | 100 -[ro](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ro.yml) | Romanian | 100% | 100 -[ru](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ru.yml) | Russian | 100% | 100 -[si](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_si.yml) | Slovenian | 99% | 99 -[sk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_sk.yml) | Slovakian | 79% | 79 -[sr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_sr.yml) | Serbian | 99% | 99 -[tr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_tr.yml) | Turkish | 100% | 100 -[uk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_uk.yml) | Ukrainian | 100% | 100 -[vn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_vn.yml) | Vietnamese | 100% | 100 -[zhcn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhcn.yml) | Chinese (China) | 100% | 100 -[zhhk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhhk.yml) | Chinese (Hong Kong) | 99% | 99 -[zhmc](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhmc.yml) | Chinese (Macau) | 64% | 64 -[zhtw](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhtw.yml) | Chinese (Taiwan) | 100% | 100 +[en](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_en.yml) | English | 100% | 100 +[bg](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_bg.yml) | Bulgarian | 99% | 99 +[br](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_br.yml) | Brazilian | 100% | 100 +[cz](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_cz.yml) | Czech | 100% | 100 +[de](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_de.yml) | German | 99% | 99 +[eo](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_eo.yml) | Esperanto | 84% | 84 +[es](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_es.yml) | Spanish | 99% | 99 +[et](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_et.yml) | Estonian | 100% | 100 +[eu](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_eu.yml) | Basque | 100% | 100 +[fi](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_fi.yml) | Finnish | 56% | 56 +[fr](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_fr.yml) | French | 100% | 100 +[gl](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_gl.yml) | Galician | 58% | 58 +[hu](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_hu.yml) | Hungarian | 99% | 99 +[id](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_id.yml) | Indonesian | 94% | 94 +[it](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_it.yml) | Italian | 100% | 100 +[ja](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_ja.yml) | Japanese | 100% | 100 +[ko](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_ko.yml) | Korean | 99% | 99 +[lt](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_lt.yml) | Lithuanian | 100% | 100 +[nl](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_nl.yml) | Dutch | 100% | 100 +[pl](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_pl.yml) | Polish | 100% | 100 +[pt](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_pt.yml) | Portuguese | 100% | 100 +[ro](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_ro.yml) | Romanian | 100% | 100 +[ru](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_ru.yml) | Russian | 100% | 100 +[si](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_si.yml) | Slovenian | 99% | 99 +[sk](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_sk.yml) | Slovakian | 84% | 84 +[sr](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_sr.yml) | Serbian | 99% | 99 +[tr](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_tr.yml) | Turkish | 100% | 100 +[uk](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_uk.yml) | Ukrainian | 100% | 100 +[vn](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_vn.yml) | Vietnamese | 100% | 100 +[zhcn](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_zhcn.yml) | Chinese (China) | 100% | 100 +[zhhk](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_zhhk.yml) | Chinese (Hong Kong) | 99% | 99 +[zhmc](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_zhmc.yml) | Chinese (Macau) | 72% | 72 +[zhtw](https://github.com/AuthMe/AuthMeReloaded/blob/master/authme-core/src/main/resources/messages/messages_zhtw.yml) | Chinese (Taiwan) | 100% | 100 --- -This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Wed Jun 21 12:14:29 CEST 2023 +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Wed Apr 22 20:38:39 CEST 2026 diff --git a/pom.xml b/pom.xml index 07eb42cc27..74c9f7edf7 100644 --- a/pom.xml +++ b/pom.xml @@ -5,8 +5,9 @@ 4.0.0 fr.xephi - authme - 5.7.1-SNAPSHOT + authme-parent + ${revision} + pom AuthMeReloaded The first authentication plugin for the Bukkit API! @@ -53,6 +54,12 @@ + + authme-core + authme-tools + authme-spigot-legacy + + false @@ -60,10 +67,8 @@ UTF-8 UTF-8 - 17 - 17 - 17 - 17 + 6.0.0-SNAPSHOT + 17 3.8.8 @@ -75,7 +80,7 @@ ${project.outputName} ${project.versionCode} - ${project.groupId}.${project.artifactId}.${pluginDescription.name} + fr.xephi.authme.AuthMe sgdc3, games647, Hex3l, krusic22 @@ -95,6 +100,11 @@ 0.10.2 1.5.0 1.3.1 + 5.12.2 + 5.23.0 + + ${settings.localRepository}/org/mockito/mockito-core/${dependencies.mockito.version}/mockito-core-${dependencies.mockito.version}.jar + 1.16.5-R0.1-SNAPSHOT 2.8.1 @@ -116,6 +126,18 @@ ${env.BUILD_NUMBER} + + java21-modules + + [21,) + + + authme-spigot-1.21 + authme-paper-common + authme-paper + authme-folia + + skipLongHashTests @@ -147,267 +169,184 @@ clean package - - - - - . - false - - LICENSE - - - - src/main/resources/ - true - - - src/main/resources/messages/ - ./messages/ - false - - + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.6.2 + + + enforce-environment + + enforce + + + + + ${maven.minimumVersion} + + + [${module.java.version},) + + + true + + + + + + org.apache.maven.plugins + maven-clean-plugin + 3.4.0 + + + org.apache.maven.plugins + maven-resources-plugin + 3.3.1 + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + ${module.java.version} + ${module.java.version} + ${module.java.version} + + + + org.jacoco + jacoco-maven-plugin + 0.8.13 + + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.5 + + + org.apache.maven.plugins + maven-jar-plugin + 3.4.2 + org.apache.maven.plugins maven-javadoc-plugin 3.7.0 + + org.apache.maven.plugins + maven-source-plugin + 3.3.1 + + + + org.apache.maven.plugins + maven-shade-plugin + 3.6.0 + + false + false + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + META-INF/*.MF + META-INF/DEPENDENCIES + META-INF/**/module-info.class + + + + + + + false + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 3.1.0 + + + org.apache.maven.plugins + maven-install-plugin + 3.1.2 + + + org.apache.maven.plugins + maven-deploy-plugin + 3.1.2 + + + org.apache.maven.plugins + maven-site-plugin + 3.12.1 + + + org.eluder.coveralls + coveralls-maven-plugin + 4.3.0 + + false + + + + javax.xml.bind + jaxb-api + 2.3.1 + + + - org.apache.maven.plugins maven-enforcer-plugin - 3.6.2 - - - enforce-environment - - enforce - - - - - ${maven.minimumVersion} - - - [${java.compiler.minimumVersion},) - - - true - - - - - - - org.apache.maven.plugins - maven-clean-plugin - 3.4.0 - - - - org.apache.maven.plugins - maven-resources-plugin - 3.3.1 - - org.apache.maven.plugins - maven-compiler-plugin - 3.13.0 + org.codehaus.mojo + flatten-maven-plugin + 1.7.3 - ${java.source} - ${java.target} - ${java.apiVersion} - - - - - org.jacoco - jacoco-maven-plugin - 0.8.13 - - - fr/xephi/authme/* - fr/xephi/authme/**/* - + true + resolveCiFriendliesOnly - pre-unit-test + flatten + process-resources - prepare-agent + flatten - post-unit-test + flatten.clean + clean - report + clean - org.apache.maven.plugins maven-surefire-plugin - 3.5.5 - - - - --add-opens=java.base/java.lang=ALL-UNNAMED -Dfile.encoding=${project.build.sourceEncoding} -Duser.language=en @{argLine} - - ${project.skipExtendedHashTests} - - - false + -Xshare:off -javaagent:${dependencies.mockito.javaagent} -Dfile.encoding=${project.build.sourceEncoding} -Duser.language=en - - - org.apache.maven.plugins - maven-jar-plugin - 3.4.2 - - - - org.apache.maven.plugins - maven-javadoc-plugin - - ${project.finalNameBase} - - ${java.source} - - - - attach-javadoc - - jar - - - - - - - org.apache.maven.plugins - maven-source-plugin - 3.3.1 - - ${project.finalNameBase} - - - - attach-sources - - jar - - - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.6.0 - - - - - - false - - true - - ${project.finalNameBase} - - - - - - - - *:* - - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - META-INF/*.RSA - META-INF/*.MF - META-INF/DEPENDENCIES - META-INF/**/module-info.class - - - - - - - - false - - - - - - - - org.apache.maven.plugins - maven-install-plugin - 3.1.2 - - - - org.apache.maven.plugins - maven-deploy-plugin - 3.1.2 - - - - org.apache.maven.plugins - maven-site-plugin - 3.12.1 - - - - org.eluder.coveralls - coveralls-maven-plugin - 4.3.0 - - - false - - - - javax.xml.bind - jaxb-api - 2.3.1 - - - @@ -503,8 +442,21 @@ true + + + + papermc + https://repo.papermc.io/repository/maven-public/ + + true + + + true + + + @@ -662,23 +614,6 @@ - - - org.spigotmc - spigot-api - ${dependencies.spigot.version} - provided - - - org.joml - joml - - - net.md-5 - bungeecord-chat - - - org.apache.logging.log4j log4j-core @@ -893,18 +828,11 @@ - - junit - junit + org.junit.jupiter + junit-jupiter test - 4.13.2 - - - org.hamcrest - hamcrest-core - - + ${dependencies.junit-jupiter.version} @@ -914,12 +842,17 @@ 2.2 - org.mockito mockito-core test - 4.11.0 + ${dependencies.mockito.version} + + + org.mockito + mockito-junit-jupiter + test + ${dependencies.mockito.version} @@ -950,5 +883,40 @@ test + + + org.spigotmc + spigot-api + ${dependencies.spigot.version} + provided + + + org.joml + joml + + + net.md-5 + bungeecord-chat + + + + + + + io.papermc.paper + paper-api + 1.21.11-R0.1-SNAPSHOT + provided + + + + + dev.folia + folia-api + 1.21.11-R0.1-SNAPSHOT + provided + + + diff --git a/src/main/java/fr/xephi/authme/listener/PlayerListener111.java b/src/main/java/fr/xephi/authme/listener/PlayerListener111.java deleted file mode 100644 index 43b4ca339b..0000000000 --- a/src/main/java/fr/xephi/authme/listener/PlayerListener111.java +++ /dev/null @@ -1,24 +0,0 @@ -package fr.xephi.authme.listener; - -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.entity.EntityAirChangeEvent; - -import javax.inject.Inject; - -/** - * Listener of player events for events introduced in Minecraft 1.11. - */ -public class PlayerListener111 implements Listener { - - @Inject - private ListenerService listenerService; - - @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) - public void onPlayerAirChange(EntityAirChangeEvent event) { - if (listenerService.shouldCancelEvent(event)) { - event.setCancelled(true); - } - } -} diff --git a/src/main/java/fr/xephi/authme/listener/PlayerListener19.java b/src/main/java/fr/xephi/authme/listener/PlayerListener19.java deleted file mode 100644 index aeb116dccb..0000000000 --- a/src/main/java/fr/xephi/authme/listener/PlayerListener19.java +++ /dev/null @@ -1,24 +0,0 @@ -package fr.xephi.authme.listener; - -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerSwapHandItemsEvent; - -import javax.inject.Inject; - -/** - * Listener of player events for events introduced in Minecraft 1.9. - */ -public class PlayerListener19 implements Listener { - - @Inject - private ListenerService listenerService; - - @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) - public void onPlayerSwapHandItems(PlayerSwapHandItemsEvent event) { - if (listenerService.shouldCancelEvent(event)) { - event.setCancelled(true); - } - } -} diff --git a/src/main/java/fr/xephi/authme/listener/PlayerListener19Spigot.java b/src/main/java/fr/xephi/authme/listener/PlayerListener19Spigot.java deleted file mode 100644 index d9988dd5f1..0000000000 --- a/src/main/java/fr/xephi/authme/listener/PlayerListener19Spigot.java +++ /dev/null @@ -1,36 +0,0 @@ -package fr.xephi.authme.listener; - -import fr.xephi.authme.service.TeleportationService; -import org.bukkit.Location; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.spigotmc.event.player.PlayerSpawnLocationEvent; - -import javax.inject.Inject; - -public class PlayerListener19Spigot implements Listener { - - private static boolean isPlayerSpawnLocationEventCalled = false; - - @Inject - private TeleportationService teleportationService; - - public static boolean isPlayerSpawnLocationEventCalled() { - return isPlayerSpawnLocationEventCalled; - } - - // Note: the following event is called since MC1.9, in older versions we have to fallback on the PlayerJoinEvent - @SuppressWarnings("deprecation") - @EventHandler(priority = EventPriority.HIGH) - public void onPlayerSpawn(PlayerSpawnLocationEvent event) { - isPlayerSpawnLocationEventCalled = true; - final Player player = event.getPlayer(); - - Location customSpawnLocation = teleportationService.prepareOnJoinSpawnLocation(player); - if (customSpawnLocation != null) { - event.setSpawnLocation(customSpawnLocation); - } - } -} diff --git a/src/main/java/fr/xephi/authme/service/TeleportationService.java b/src/main/java/fr/xephi/authme/service/TeleportationService.java deleted file mode 100644 index b1e233c5d0..0000000000 --- a/src/main/java/fr/xephi/authme/service/TeleportationService.java +++ /dev/null @@ -1,232 +0,0 @@ -package fr.xephi.authme.service; - -import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.data.auth.PlayerAuth; -import fr.xephi.authme.data.auth.PlayerCache; -import fr.xephi.authme.data.limbo.LimboPlayer; -import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.events.AbstractTeleportEvent; -import fr.xephi.authme.events.AuthMeTeleportEvent; -import fr.xephi.authme.events.FirstSpawnTeleportEvent; -import fr.xephi.authme.events.SpawnTeleportEvent; -import fr.xephi.authme.initialization.Reloadable; -import fr.xephi.authme.output.ConsoleLoggerFactory; -import fr.xephi.authme.settings.Settings; -import fr.xephi.authme.settings.SpawnLoader; -import fr.xephi.authme.settings.properties.RegistrationSettings; -import fr.xephi.authme.settings.properties.RestrictionSettings; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.entity.Player; - -import javax.annotation.PostConstruct; -import javax.inject.Inject; -import java.util.HashSet; -import java.util.Locale; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -import static fr.xephi.authme.settings.properties.RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN; - -/** - * Handles teleportation (placement of player to spawn). - */ -public class TeleportationService implements Reloadable { - - private final ConsoleLogger logger = ConsoleLoggerFactory.get(TeleportationService.class); - - @Inject - private Settings settings; - - @Inject - private BukkitService bukkitService; - - @Inject - private SpawnLoader spawnLoader; - - @Inject - private PlayerCache playerCache; - - @Inject - private DataSource dataSource; - - private Set spawnOnLoginWorlds; - private final ConcurrentHashMap preloadedAuthStatus = new ConcurrentHashMap<>(); - - TeleportationService() { - } - - @PostConstruct - @Override - public void reload() { - // Use a Set for better performance with #contains() - spawnOnLoginWorlds = new HashSet<>(settings.getProperty(RestrictionSettings.FORCE_SPAWN_ON_WORLDS)); - } - - /** - * Teleports the player according to the settings when he joins. - * - * @param player the player to process - */ - public void teleportOnJoin(final Player player) { - if (!settings.getProperty(RestrictionSettings.NO_TELEPORT) - && settings.getProperty(TELEPORT_UNAUTHED_TO_SPAWN) - && !isUnregisteredWithOptionalAuth(player)) { - logger.debug("Teleport on join for player `{0}`", player.getName()); - teleportToSpawn(player, playerCache.isAuthenticated(player.getName())); - } - } - - /** - * Returns the player's custom on join location. - * - * @param player the player to process - * - * @return the custom spawn location, null if the player should spawn at the original location - */ - public Location prepareOnJoinSpawnLocation(final Player player) { - if (!settings.getProperty(RestrictionSettings.NO_TELEPORT) - && settings.getProperty(TELEPORT_UNAUTHED_TO_SPAWN) - && !isUnregisteredWithOptionalAuth(player)) { - final Location location = spawnLoader.getSpawnLocation(player); - - SpawnTeleportEvent event = new SpawnTeleportEvent(player, location, - playerCache.isAuthenticated(player.getName())); - bukkitService.callEvent(event); - if (!isEventValid(event)) { - return null; - } - - logger.debug("Returning custom location for >1.9 join event for player `{0}`", player.getName()); - return location; - } - return null; - } - - /** - * Teleports the player to the first spawn if he is new and the first spawn is configured. - * - * @param player the player to process - */ - public void teleportNewPlayerToFirstSpawn(final Player player) { - if (settings.getProperty(RestrictionSettings.NO_TELEPORT)) { - return; - } - - Location firstSpawn = spawnLoader.getFirstSpawn(); - if (firstSpawn == null) { - return; - } - - if (!player.hasPlayedBefore() || !dataSource.isAuthAvailable(player.getName())) { - logger.debug("Attempting to teleport player `{0}` to first spawn", player.getName()); - performTeleportation(player, new FirstSpawnTeleportEvent(player, firstSpawn)); - } - } - - /** - * Teleports the player according to the settings after having successfully logged in. - * - * @param player the player - * @param auth corresponding PlayerAuth object - * @param limbo corresponding LimboPlayer object - */ - public void teleportOnLogin(final Player player, PlayerAuth auth, LimboPlayer limbo) { - if (settings.getProperty(RestrictionSettings.NO_TELEPORT)) { - return; - } - - // #856: If LimboPlayer comes from a persisted file, the Location might be null - String worldName = (limbo != null && limbo.getLocation() != null) - ? limbo.getLocation().getWorld().getName() - : null; - - // The world in LimboPlayer is from where the player comes, before any teleportation by AuthMe - if (mustForceSpawnAfterLogin(worldName)) { - logger.debug("Teleporting `{0}` to spawn because of 'force-spawn after login'", player.getName()); - teleportToSpawn(player, true); - } else if (settings.getProperty(TELEPORT_UNAUTHED_TO_SPAWN)) { - if (settings.getProperty(RestrictionSettings.SAVE_QUIT_LOCATION)) { - Location location = buildLocationFromAuth(player, auth); - logger.debug("Teleporting `{0}` after login, based on the player auth", player.getName()); - teleportBackFromSpawn(player, location); - } else if (limbo != null && limbo.getLocation() != null) { - logger.debug("Teleporting `{0}` after login, based on the limbo player", player.getName()); - teleportBackFromSpawn(player, limbo.getLocation()); - } - } - } - - /** - * Caches the player's registration status from the async pre-login event to avoid a blocking - * DB call on the main thread later. Must be cleared with {@link #clearPreloadedAuthStatus}. - * - * @param name the player name (case-insensitive) - * @param isRegistered whether the player has an account - */ - public void preloadAuthStatus(String name, boolean isRegistered) { - preloadedAuthStatus.put(name.toLowerCase(Locale.ROOT), isRegistered); - } - - /** - * Clears the pre-loaded registration status for the given player after it has been consumed. - * - * @param name the player name - */ - public void clearPreloadedAuthStatus(String name) { - preloadedAuthStatus.remove(name.toLowerCase(Locale.ROOT)); - } - - private boolean isUnregisteredWithOptionalAuth(Player player) { - if (settings.getProperty(RegistrationSettings.FORCE)) { - return false; - } - String key = player.getName().toLowerCase(Locale.ROOT); - Boolean cached = preloadedAuthStatus.get(key); - boolean isRegistered = cached != null ? cached : dataSource.isAuthAvailable(player.getName()); - return !isRegistered; - } - - private boolean mustForceSpawnAfterLogin(String worldName) { - return worldName != null && settings.getProperty(RestrictionSettings.FORCE_SPAWN_LOCATION_AFTER_LOGIN) - && spawnOnLoginWorlds.contains(worldName); - } - - private Location buildLocationFromAuth(Player player, PlayerAuth auth) { - World world = bukkitService.getWorld(auth.getWorld()); - if (world == null) { - world = player.getWorld(); - } - return new Location(world, auth.getQuitLocX(), auth.getQuitLocY(), auth.getQuitLocZ(), - auth.getYaw(), auth.getPitch()); - } - - private void teleportBackFromSpawn(final Player player, final Location location) { - performTeleportation(player, new AuthMeTeleportEvent(player, location)); - } - - private void teleportToSpawn(final Player player, final boolean isAuthenticated) { - final Location spawnLoc = spawnLoader.getSpawnLocation(player); - performTeleportation(player, new SpawnTeleportEvent(player, spawnLoc, isAuthenticated)); - } - - /** - * Emits the teleportation event and performs teleportation according to it (potentially modified - * by external listeners). Note that no teleportation is performed if the event's location is empty. - * - * @param player the player to teleport - * @param event the event to emit and according to which to teleport - */ - private void performTeleportation(final Player player, final AbstractTeleportEvent event) { - bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> { - bukkitService.callEvent(event); - if (player.isOnline() && isEventValid(event)) { - player.teleport(event.getTo()); - } - }); - } - - private static boolean isEventValid(AbstractTeleportEvent event) { - return !event.isCancelled() && event.getTo() != null && event.getTo().getWorld() != null; - } -} diff --git a/src/test/java/fr/xephi/authme/listener/PlayerListener111Test.java b/src/test/java/fr/xephi/authme/listener/PlayerListener111Test.java deleted file mode 100644 index b7b2c60571..0000000000 --- a/src/test/java/fr/xephi/authme/listener/PlayerListener111Test.java +++ /dev/null @@ -1,30 +0,0 @@ -package fr.xephi.authme.listener; - -import org.bukkit.event.entity.EntityAirChangeEvent; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import static fr.xephi.authme.listener.EventCancelVerifier.withServiceMock; - -/** - * Test for {@link PlayerListener111}. - */ -@RunWith(MockitoJUnitRunner.class) -public class PlayerListener111Test { - - @InjectMocks - private PlayerListener111 listener; - - @Mock - private ListenerService listenerService; - - @Test - public void shouldCancelEvent() { - withServiceMock(listenerService) - .check(listener::onPlayerAirChange, EntityAirChangeEvent.class); - } - -} diff --git a/src/test/java/fr/xephi/authme/listener/PlayerListener19Test.java b/src/test/java/fr/xephi/authme/listener/PlayerListener19Test.java deleted file mode 100644 index b870ac069c..0000000000 --- a/src/test/java/fr/xephi/authme/listener/PlayerListener19Test.java +++ /dev/null @@ -1,30 +0,0 @@ -package fr.xephi.authme.listener; - -import org.bukkit.event.player.PlayerSwapHandItemsEvent; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import static fr.xephi.authme.listener.EventCancelVerifier.withServiceMock; - -/** - * Test for {@link PlayerListener19}. - */ -@RunWith(MockitoJUnitRunner.class) -public class PlayerListener19Test { - - @InjectMocks - private PlayerListener19 listener; - - @Mock - private ListenerService listenerService; - - @Test - public void shouldCancelEvent() { - withServiceMock(listenerService) - .check(listener::onPlayerSwapHandItems, PlayerSwapHandItemsEvent.class); - } - -} diff --git a/src/test/java/fr/xephi/authme/settings/SpawnLoaderTest.java b/src/test/java/fr/xephi/authme/settings/SpawnLoaderTest.java deleted file mode 100644 index 9644403be6..0000000000 --- a/src/test/java/fr/xephi/authme/settings/SpawnLoaderTest.java +++ /dev/null @@ -1,81 +0,0 @@ -package fr.xephi.authme.settings; - -import ch.jalu.injector.testing.BeforeInjecting; -import ch.jalu.injector.testing.DelayedInjectionRunner; -import ch.jalu.injector.testing.InjectDelayed; -import com.google.common.io.Files; -import fr.xephi.authme.TestHelper; -import fr.xephi.authme.initialization.DataFolder; -import fr.xephi.authme.service.PluginHookService; -import fr.xephi.authme.settings.properties.RestrictionSettings; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.configuration.file.YamlConfiguration; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; -import org.mockito.Mock; - -import java.io.File; -import java.io.IOException; - -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -/** - * Test for {@link SpawnLoader}. - */ -@RunWith(DelayedInjectionRunner.class) -public class SpawnLoaderTest { - - @InjectDelayed - private SpawnLoader spawnLoader; - - @Mock - private Settings settings; - - @Mock - private PluginHookService pluginHookService; - - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); - - @DataFolder - private File testFolder; - - @BeforeInjecting - public void setup() throws IOException { - // Copy test config into a new temporary folder - testFolder = temporaryFolder.newFolder(); - File source = TestHelper.getJarFile(TestHelper.PROJECT_ROOT + "settings/spawn-firstspawn.yml"); - File destination = new File(testFolder, "spawn.yml"); - Files.copy(source, destination); - - // Create a settings mock with default values - given(settings.getProperty(RestrictionSettings.SPAWN_PRIORITY)) - .willReturn("authme, essentials, multiverse, default"); - } - - @Test - public void shouldSetSpawn() { - // given - World world = mock(World.class); - given(world.getName()).willReturn("new_world"); - Location newSpawn = new Location(world, 123, 45.0, -67.89); - - // when - boolean result = spawnLoader.setSpawn(newSpawn); - - // then - assertThat(result, equalTo(true)); - YamlConfiguration configuration = YamlConfiguration.loadConfiguration(new File(testFolder, "spawn.yml")); - assertThat(configuration.getDouble("spawn.x"), equalTo(123.0)); - assertThat(configuration.getDouble("spawn.y"), equalTo(45.0)); - assertThat(configuration.getDouble("spawn.z"), equalTo(-67.89)); - assertThat(configuration.getString("spawn.world"), equalTo("new_world")); - } - -} diff --git a/src/test/java/tools/README.md b/src/test/java/tools/README.md deleted file mode 100644 index 012405cf19..0000000000 --- a/src/test/java/tools/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# About the _tools_ Folder - -This _tools_ folder provides helpers and extended tests useful during the development of AuthMe. -This folder is not included during the build of AuthMe and does not contain unit tests. - -Run the `ToolsRunner` class to perform a task. diff --git a/src/test/java/tools/docs/permissions/permission_nodes.tpl.md b/src/test/java/tools/docs/permissions/permission_nodes.tpl.md deleted file mode 100644 index 8987f08bb0..0000000000 --- a/src/test/java/tools/docs/permissions/permission_nodes.tpl.md +++ /dev/null @@ -1,11 +0,0 @@ - - - -## AuthMe Permission Nodes -The following are the permission nodes that are currently supported by the latest dev builds. - -[#nodes] - - **{node}** – {description} -[/#nodes] - -{gen_footer} diff --git a/src/test/java/tools/docs/translations/translations.tpl.md b/src/test/java/tools/docs/translations/translations.tpl.md deleted file mode 100644 index 51c5aeadcc..0000000000 --- a/src/test/java/tools/docs/translations/translations.tpl.md +++ /dev/null @@ -1,14 +0,0 @@ - - - -# AuthMe Translations -The following translations are available in AuthMe. Set `messagesLanguage` to the language code -in your config.yml to use the language, or use another language code to start a new translation. - -Code | Language | Translated |   ----- | -------- | ---------: | ------ -[#languages] -[{code}](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_{code}.yml) | {name} | {percentage}% | {percentage} -[/#languages] - -{gen_footer} diff --git a/src/test/java/tools/utils/ToolsConstants.java b/src/test/java/tools/utils/ToolsConstants.java deleted file mode 100644 index e7ba3e05a6..0000000000 --- a/src/test/java/tools/utils/ToolsConstants.java +++ /dev/null @@ -1,21 +0,0 @@ -package tools.utils; - -/** - * Constants for the src/tools folder. - */ -public final class ToolsConstants { - - public static final String MAIN_SOURCE_ROOT = "src/main/java/"; - - public static final String MAIN_RESOURCES_ROOT = "src/main/resources/"; - - public static final String TOOLS_SOURCE_ROOT = "src/test/java/tools/"; - - public static final String DOCS_FOLDER = "docs/"; - - public static final String DOCS_FOLDER_URL = "https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/"; - - private ToolsConstants() { - } - -}