diff --git a/pom.xml b/pom.xml
index a79f3a9d..37e5f505 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
fr.xephi
authme
- 5.2
+ 5.3-SNAPSHOT
AuthMeReloaded
The first authentication plugin for the Bukkit API!
diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java
index 74a98654..1b987080 100644
--- a/src/main/java/fr/xephi/authme/AuthMe.java
+++ b/src/main/java/fr/xephi/authme/AuthMe.java
@@ -27,7 +27,6 @@ import fr.xephi.authme.permission.PermissionsSystemType;
import fr.xephi.authme.security.crypts.SHA256;
import fr.xephi.authme.service.BackupService;
import fr.xephi.authme.service.BukkitService;
-import fr.xephi.authme.service.GeoIpService;
import fr.xephi.authme.service.MigrationService;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.PluginSettings;
@@ -35,11 +34,9 @@ import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.task.CleanupTask;
import fr.xephi.authme.task.purge.PurgeService;
-import fr.xephi.authme.util.PlayerUtils;
import org.bukkit.Server;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
-import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
@@ -72,8 +69,6 @@ public class AuthMe extends JavaPlugin {
private DataSource database;
private BukkitService bukkitService;
private Injector injector;
- private GeoIpService geoIpService;
- private PlayerCache playerCache;
/**
* Constructor.
@@ -242,14 +237,13 @@ public class AuthMe extends JavaPlugin {
*/
protected void instantiateServices(Injector injector) {
// PlayerCache is still injected statically sometimes
- playerCache = PlayerCache.getInstance();
+ PlayerCache playerCache = PlayerCache.getInstance();
injector.register(PlayerCache.class, playerCache);
database = injector.getSingleton(DataSource.class);
permsMan = injector.getSingleton(PermissionsManager.class);
bukkitService = injector.getSingleton(BukkitService.class);
commandHandler = injector.getSingleton(CommandHandler.class);
- geoIpService = injector.getSingleton(GeoIpService.class);
// Trigger construction of API classes; they will keep track of the singleton
injector.getSingleton(NewAPI.class);
@@ -344,24 +338,6 @@ public class AuthMe extends JavaPlugin {
ConsoleLogger.close();
}
- public String replaceAllInfo(String message, Player player) {
- String playersOnline = Integer.toString(bukkitService.getOnlinePlayers().size());
- String ipAddress = PlayerUtils.getPlayerIp(player);
- Server server = getServer();
- return message
- .replace("&", "\u00a7")
- .replace("{PLAYER}", player.getName())
- .replace("{ONLINE}", playersOnline)
- .replace("{MAXPLAYERS}", Integer.toString(server.getMaxPlayers()))
- .replace("{IP}", ipAddress)
- .replace("{LOGINS}", Integer.toString(playerCache.getLogged()))
- .replace("{WORLD}", player.getWorld().getName())
- .replace("{SERVER}", server.getServerName())
- .replace("{VERSION}", server.getBukkitVersion())
- // TODO: We should cache info like this, maybe with a class that extends Player?
- .replace("{COUNTRY}", geoIpService.getCountryName(ipAddress));
- }
-
/**
* Handle Bukkit commands.
*
diff --git a/src/main/java/fr/xephi/authme/command/CommandDescription.java b/src/main/java/fr/xephi/authme/command/CommandDescription.java
index b6c006f9..18463bca 100644
--- a/src/main/java/fr/xephi/authme/command/CommandDescription.java
+++ b/src/main/java/fr/xephi/authme/command/CommandDescription.java
@@ -1,8 +1,8 @@
package fr.xephi.authme.command;
import fr.xephi.authme.permission.PermissionNode;
-import fr.xephi.authme.util.CollectionUtils;
import fr.xephi.authme.util.StringUtils;
+import fr.xephi.authme.util.Utils;
import java.util.ArrayList;
import java.util.List;
@@ -224,7 +224,7 @@ public class CommandDescription {
* @return The generated CommandDescription object
*/
public CommandDescription build() {
- checkArgument(!CollectionUtils.isEmpty(labels), "Labels may not be empty");
+ checkArgument(!Utils.isCollectionEmpty(labels), "Labels may not be empty");
checkArgument(!StringUtils.isEmpty(description), "Description may not be empty");
checkArgument(!StringUtils.isEmpty(detailedDescription), "Detailed description may not be empty");
checkArgument(executableCommand != null, "Executable command must be set");
diff --git a/src/main/java/fr/xephi/authme/command/CommandMapper.java b/src/main/java/fr/xephi/authme/command/CommandMapper.java
index 8e2b1bdc..c5cf91c0 100644
--- a/src/main/java/fr/xephi/authme/command/CommandMapper.java
+++ b/src/main/java/fr/xephi/authme/command/CommandMapper.java
@@ -2,8 +2,8 @@ package fr.xephi.authme.command;
import fr.xephi.authme.command.executable.HelpCommand;
import fr.xephi.authme.permission.PermissionsManager;
-import fr.xephi.authme.util.CollectionUtils;
import fr.xephi.authme.util.StringUtils;
+import fr.xephi.authme.util.Utils;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
@@ -45,7 +45,7 @@ public class CommandMapper {
* @return The generated {@link FoundCommandResult}
*/
public FoundCommandResult mapPartsToCommand(CommandSender sender, final List parts) {
- if (CollectionUtils.isEmpty(parts)) {
+ if (Utils.isCollectionEmpty(parts)) {
return new FoundCommandResult(null, parts, null, 0.0, MISSING_BASE_COMMAND);
}
@@ -142,7 +142,7 @@ public class CommandMapper {
* @return A command if there was a complete match (including proper argument count), null otherwise
*/
private static CommandDescription getSuitableChild(CommandDescription baseCommand, List parts) {
- if (CollectionUtils.isEmpty(parts)) {
+ if (Utils.isCollectionEmpty(parts)) {
return null;
}
diff --git a/src/main/java/fr/xephi/authme/datasource/converter/xAuthConverter.java b/src/main/java/fr/xephi/authme/datasource/converter/xAuthConverter.java
index 27fa6c2e..af4d5beb 100644
--- a/src/main/java/fr/xephi/authme/datasource/converter/xAuthConverter.java
+++ b/src/main/java/fr/xephi/authme/datasource/converter/xAuthConverter.java
@@ -6,7 +6,7 @@ import de.luricos.bukkit.xAuth.xAuth;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.DataFolder;
-import fr.xephi.authme.util.CollectionUtils;
+import fr.xephi.authme.util.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.PluginManager;
@@ -55,7 +55,7 @@ public class xAuthConverter implements Converter {
sender.sendMessage("[AuthMe] xAuth H2 database not found, checking for MySQL or SQLite data...");
}
List players = getXAuthPlayers();
- if (CollectionUtils.isEmpty(players)) {
+ if (Utils.isCollectionEmpty(players)) {
sender.sendMessage("[AuthMe] Error while importing xAuthPlayers: did not find any players");
return;
}
diff --git a/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java b/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java
index d64bda77..6fb5fa8b 100644
--- a/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java
+++ b/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java
@@ -13,15 +13,16 @@ import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.service.BungeeService;
import fr.xephi.authme.service.TeleportationService;
import fr.xephi.authme.settings.Settings;
+import fr.xephi.authme.settings.WelcomeMessageConfiguration;
import fr.xephi.authme.settings.commandconfig.CommandManager;
import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.util.StringUtils;
-import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginManager;
import org.bukkit.potion.PotionEffectType;
import javax.inject.Inject;
+import java.util.List;
import static fr.xephi.authme.settings.properties.RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN;
@@ -54,6 +55,9 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess {
@Inject
private Settings settings;
+ @Inject
+ private WelcomeMessageConfiguration welcomeMessageConfiguration;
+
ProcessSyncPlayerLogin() {
}
@@ -103,15 +107,12 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess {
player.saveData();
// Login is done, display welcome message
+ List welcomeMessage = welcomeMessageConfiguration.getWelcomeMessage(player);
if (settings.getProperty(RegistrationSettings.USE_WELCOME_MESSAGE)) {
if (settings.getProperty(RegistrationSettings.BROADCAST_WELCOME_MESSAGE)) {
- for (String s : settings.getWelcomeMessage()) {
- Bukkit.getServer().broadcastMessage(plugin.replaceAllInfo(s, player));
- }
+ welcomeMessage.forEach(bukkitService::broadcastMessage);
} else {
- for (String s : settings.getWelcomeMessage()) {
- player.sendMessage(plugin.replaceAllInfo(s, player));
- }
+ welcomeMessage.forEach(player::sendMessage);
}
}
diff --git a/src/main/java/fr/xephi/authme/service/ValidationService.java b/src/main/java/fr/xephi/authme/service/ValidationService.java
index 7905b367..7c7abf78 100644
--- a/src/main/java/fr/xephi/authme/service/ValidationService.java
+++ b/src/main/java/fr/xephi/authme/service/ValidationService.java
@@ -11,7 +11,6 @@ import fr.xephi.authme.settings.properties.EmailSettings;
import fr.xephi.authme.settings.properties.ProtectionSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.settings.properties.SecuritySettings;
-import fr.xephi.authme.util.CollectionUtils;
import fr.xephi.authme.util.Utils;
import org.bukkit.command.CommandSender;
@@ -144,11 +143,11 @@ public class ValidationService implements Reloadable {
private boolean validateWhitelistAndBlacklist(String value, Property> whitelist,
Property> blacklist) {
List whitelistValue = settings.getProperty(whitelist);
- if (!CollectionUtils.isEmpty(whitelistValue)) {
+ if (!Utils.isCollectionEmpty(whitelistValue)) {
return containsIgnoreCase(whitelistValue, value);
}
List blacklistValue = settings.getProperty(blacklist);
- return CollectionUtils.isEmpty(blacklistValue) || !containsIgnoreCase(blacklistValue, value);
+ return Utils.isCollectionEmpty(blacklistValue) || !containsIgnoreCase(blacklistValue, value);
}
private static boolean containsIgnoreCase(Collection coll, String needle) {
diff --git a/src/main/java/fr/xephi/authme/settings/Settings.java b/src/main/java/fr/xephi/authme/settings/Settings.java
index d287e140..980f2add 100644
--- a/src/main/java/fr/xephi/authme/settings/Settings.java
+++ b/src/main/java/fr/xephi/authme/settings/Settings.java
@@ -19,7 +19,6 @@ import static fr.xephi.authme.util.FileUtils.copyFileFromResource;
public class Settings extends SettingsManager {
private final File pluginFolder;
- private String[] welcomeMessage;
private String passwordEmailMessage;
private String recoveryCodeEmailMessage;
@@ -56,19 +55,9 @@ public class Settings extends SettingsManager {
return recoveryCodeEmailMessage;
}
- /**
- * Return the lines to output after an in-game registration.
- *
- * @return The welcome message
- */
- public String[] getWelcomeMessage() {
- return welcomeMessage;
- }
-
private void loadSettingsFromFiles() {
passwordEmailMessage = readFile("email.html");
recoveryCodeEmailMessage = readFile("recovery_code_email.html");
- welcomeMessage = readFile("welcome.txt").split("\\n");
}
@Override
diff --git a/src/main/java/fr/xephi/authme/settings/WelcomeMessageConfiguration.java b/src/main/java/fr/xephi/authme/settings/WelcomeMessageConfiguration.java
new file mode 100644
index 00000000..60666f7a
--- /dev/null
+++ b/src/main/java/fr/xephi/authme/settings/WelcomeMessageConfiguration.java
@@ -0,0 +1,97 @@
+package fr.xephi.authme.settings;
+
+import fr.xephi.authme.ConsoleLogger;
+import fr.xephi.authme.data.auth.PlayerCache;
+import fr.xephi.authme.initialization.DataFolder;
+import fr.xephi.authme.initialization.Reloadable;
+import fr.xephi.authme.service.BukkitService;
+import fr.xephi.authme.service.GeoIpService;
+import fr.xephi.authme.util.PlayerUtils;
+import fr.xephi.authme.util.lazytags.Tag;
+import fr.xephi.authme.util.lazytags.TagReplacer;
+import org.bukkit.Server;
+import org.bukkit.entity.Player;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static fr.xephi.authme.util.FileUtils.copyFileFromResource;
+import static fr.xephi.authme.util.lazytags.TagBuilder.createTag;
+
+/**
+ * Configuration for the welcome message (welcome.txt).
+ */
+public class WelcomeMessageConfiguration implements Reloadable {
+
+ @DataFolder
+ @Inject
+ private File pluginFolder;
+
+ @Inject
+ private Server server;
+
+ @Inject
+ private GeoIpService geoIpService;
+
+ @Inject
+ private BukkitService bukkitService;
+
+ @Inject
+ private PlayerCache playerCache;
+
+ /** List of all supported tags for the welcome message. */
+ private final List> availableTags = Arrays.asList(
+ createTag("&", () -> "\u00a7"),
+ createTag("{PLAYER}", pl -> pl.getName()),
+ createTag("{ONLINE}", () -> Integer.toString(bukkitService.getOnlinePlayers().size())),
+ createTag("{MAXPLAYERS}", () -> Integer.toString(server.getMaxPlayers())),
+ createTag("{IP}", pl -> PlayerUtils.getPlayerIp(pl)),
+ createTag("{LOGINS}", () -> Integer.toString(playerCache.getLogged())),
+ createTag("{WORLD}", pl -> pl.getWorld().getName()),
+ createTag("{SERVER}", () -> server.getServerName()),
+ createTag("{VERSION}", () -> server.getBukkitVersion()),
+ createTag("{COUNTRY}", pl -> geoIpService.getCountryName(PlayerUtils.getPlayerIp(pl))));
+
+ private TagReplacer messageSupplier;
+
+ @PostConstruct
+ @Override
+ public void reload() {
+ List welcomeMessage = readWelcomeFile();
+ messageSupplier = TagReplacer.newReplacer(availableTags, welcomeMessage);
+ }
+
+ /**
+ * Returns the welcome message for the given player.
+ *
+ * @param player the player for whom the welcome message should be prepared
+ * @return the welcome message
+ */
+ public List getWelcomeMessage(Player player) {
+ return messageSupplier.getAdaptedMessages(player);
+ }
+
+ /**
+ * @return the lines of the welcome message file
+ */
+ private List readWelcomeFile() {
+ File welcomeFile = new File(pluginFolder, "welcome.txt");
+ if (copyFileFromResource(welcomeFile, "welcome.txt")) {
+ try {
+ return Files.readAllLines(welcomeFile.toPath(), StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ ConsoleLogger.logException("Failed to read welcome.txt file:", e);
+ }
+ } else {
+ ConsoleLogger.warning("Failed to copy welcome.txt from JAR");
+ }
+ return Collections.emptyList();
+ }
+}
diff --git a/src/main/java/fr/xephi/authme/settings/commandconfig/CommandManager.java b/src/main/java/fr/xephi/authme/settings/commandconfig/CommandManager.java
index bc0abf38..d87ebd5e 100644
--- a/src/main/java/fr/xephi/authme/settings/commandconfig/CommandManager.java
+++ b/src/main/java/fr/xephi/authme/settings/commandconfig/CommandManager.java
@@ -5,13 +5,21 @@ import ch.jalu.configme.resource.YamlFileResource;
import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.initialization.Reloadable;
import fr.xephi.authme.service.BukkitService;
+import fr.xephi.authme.service.GeoIpService;
import fr.xephi.authme.util.FileUtils;
+import fr.xephi.authme.util.PlayerUtils;
+import fr.xephi.authme.util.lazytags.Tag;
+import fr.xephi.authme.util.lazytags.WrappedTagReplacer;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.io.File;
+import java.util.Arrays;
+import java.util.List;
import java.util.Map;
+import static fr.xephi.authme.util.lazytags.TagBuilder.createTag;
+
/**
* Manages configurable commands to be run when various events occur.
*/
@@ -19,15 +27,20 @@ public class CommandManager implements Reloadable {
private final File dataFolder;
private final BukkitService bukkitService;
+ private final GeoIpService geoIpService;
private final CommandMigrationService commandMigrationService;
+ private final List> availableTags = buildAvailableTags();
- private CommandConfig commandConfig;
+ private WrappedTagReplacer onJoinCommands;
+ private WrappedTagReplacer onLoginCommands;
+ private WrappedTagReplacer onRegisterCommands;
@Inject
- CommandManager(@DataFolder File dataFolder, BukkitService bukkitService,
+ CommandManager(@DataFolder File dataFolder, BukkitService bukkitService, GeoIpService geoIpService,
CommandMigrationService commandMigrationService) {
this.dataFolder = dataFolder;
this.bukkitService = bukkitService;
+ this.geoIpService = geoIpService;
this.commandMigrationService = commandMigrationService;
reload();
}
@@ -38,7 +51,7 @@ public class CommandManager implements Reloadable {
* @param player the joining player
*/
public void runCommandsOnJoin(Player player) {
- executeCommands(player, commandConfig.getOnJoin());
+ executeCommands(player, onJoinCommands.getAdaptedItems(player));
}
/**
@@ -47,7 +60,7 @@ public class CommandManager implements Reloadable {
* @param player the player who has registered
*/
public void runCommandsOnRegister(Player player) {
- executeCommands(player, commandConfig.getOnRegister());
+ executeCommands(player, onRegisterCommands.getAdaptedItems(player));
}
/**
@@ -56,11 +69,11 @@ public class CommandManager implements Reloadable {
* @param player the player that logged in
*/
public void runCommandsOnLogin(Player player) {
- executeCommands(player, commandConfig.getOnLogin());
+ executeCommands(player, onLoginCommands.getAdaptedItems(player));
}
- private void executeCommands(Player player, Map commands) {
- for (Command command : commands.values()) {
+ private void executeCommands(Player player, List commands) {
+ for (Command command : commands) {
final String execution = command.getCommand().replace("%p", player.getName());
if (Executor.CONSOLE.equals(command.getExecutor())) {
bukkitService.dispatchConsoleCommand(execution);
@@ -77,8 +90,22 @@ public class CommandManager implements Reloadable {
SettingsManager settingsManager = new SettingsManager(
new YamlFileResource(file), commandMigrationService, CommandSettingsHolder.class);
- commandConfig = settingsManager.getProperty(CommandSettingsHolder.COMMANDS);
+ CommandConfig commandConfig = settingsManager.getProperty(CommandSettingsHolder.COMMANDS);
+ onJoinCommands = newReplacer(commandConfig.getOnJoin());
+ onLoginCommands = newReplacer(commandConfig.getOnLogin());
+ onRegisterCommands = newReplacer(commandConfig.getOnRegister());
}
+ private WrappedTagReplacer newReplacer(Map commands) {
+ return new WrappedTagReplacer<>(availableTags, commands.values(), Command::getCommand,
+ (cmd, text) -> new Command(text, cmd.getExecutor()));
+ }
+ private List> buildAvailableTags() {
+ return Arrays.asList(
+ createTag("%p", pl -> pl.getName()),
+ createTag("%nick", pl -> pl.getDisplayName()),
+ createTag("%ip", pl -> PlayerUtils.getPlayerIp(pl)),
+ createTag("%country", pl -> geoIpService.getCountryName(PlayerUtils.getPlayerIp(pl))));
+ }
}
diff --git a/src/main/java/fr/xephi/authme/task/purge/PurgeService.java b/src/main/java/fr/xephi/authme/task/purge/PurgeService.java
index 7a1d19fa..fed07768 100644
--- a/src/main/java/fr/xephi/authme/task/purge/PurgeService.java
+++ b/src/main/java/fr/xephi/authme/task/purge/PurgeService.java
@@ -3,10 +3,10 @@ package fr.xephi.authme.task.purge;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.permission.PermissionsManager;
+import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.PurgeSettings;
-import fr.xephi.authme.service.BukkitService;
-import fr.xephi.authme.util.CollectionUtils;
+import fr.xephi.authme.util.Utils;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
@@ -75,7 +75,7 @@ public class PurgeService {
public void runPurge(CommandSender sender, long until, boolean includeEntriesWithLastLoginZero) {
//todo: note this should may run async because it may executes a SQL-Query
Set toPurge = dataSource.getRecordsToPurge(until, includeEntriesWithLastLoginZero);
- if (CollectionUtils.isEmpty(toPurge)) {
+ if (Utils.isCollectionEmpty(toPurge)) {
logAndSendMessage(sender, "No players to purge");
return;
}
diff --git a/src/main/java/fr/xephi/authme/util/CollectionUtils.java b/src/main/java/fr/xephi/authme/util/CollectionUtils.java
deleted file mode 100644
index 2388fc60..00000000
--- a/src/main/java/fr/xephi/authme/util/CollectionUtils.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package fr.xephi.authme.util;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * Utils class for collections.
- */
-public final class CollectionUtils {
-
- // Utility class
- private CollectionUtils() {
- }
-
- /**
- * Get a range from a list based on start and count parameters in a safe way.
- *
- * @param element
- * @param list The List
- * @param start The start index
- * @param count The number of elements to add
- *
- * @return The sublist consisting at most of {@code count} elements (less if the parameters
- * exceed the size of the list)
- */
- public static List getRange(List list, int start, int count) {
- if (start >= list.size() || count <= 0) {
- return new ArrayList<>();
- } else if (start < 0) {
- start = 0;
- }
- int end = Math.min(list.size(), start + count);
- return list.subList(start, end);
- }
-
- /**
- * Get all elements from a list starting from the given index.
- *
- * @param element
- * @param list The List
- * @param start The start index
- *
- * @return The sublist of all elements from index {@code start} and on; empty list
- * if the start index exceeds the list's size
- */
- public static List getRange(List list, int start) {
- if (start >= list.size()) {
- return new ArrayList<>();
- }
- return getRange(list, start, list.size() - start);
- }
-
- /**
- * Null-safe way to check whether a collection is empty or not.
- *
- * @param coll The collection to verify
- * @return True if the collection is null or empty, false otherwise
- */
- public static boolean isEmpty(Collection> coll) {
- return coll == null || coll.isEmpty();
- }
-}
diff --git a/src/main/java/fr/xephi/authme/util/Utils.java b/src/main/java/fr/xephi/authme/util/Utils.java
index b3ec53c7..b7628147 100644
--- a/src/main/java/fr/xephi/authme/util/Utils.java
+++ b/src/main/java/fr/xephi/authme/util/Utils.java
@@ -2,6 +2,7 @@ package fr.xephi.authme.util;
import fr.xephi.authme.ConsoleLogger;
+import java.util.Collection;
import java.util.regex.Pattern;
/**
@@ -50,6 +51,16 @@ public final class Utils {
}
}
+ /**
+ * Null-safe way to check whether a collection is empty or not.
+ *
+ * @param coll The collection to verify
+ * @return True if the collection is null or empty, false otherwise
+ */
+ public static boolean isCollectionEmpty(Collection> coll) {
+ return coll == null || coll.isEmpty();
+ }
+
/**
* Return the available core count of the JVM.
*
diff --git a/src/main/java/fr/xephi/authme/util/lazytags/DependentTag.java b/src/main/java/fr/xephi/authme/util/lazytags/DependentTag.java
new file mode 100644
index 00000000..5fb0cd3d
--- /dev/null
+++ b/src/main/java/fr/xephi/authme/util/lazytags/DependentTag.java
@@ -0,0 +1,35 @@
+package fr.xephi.authme.util.lazytags;
+
+import java.util.function.Function;
+
+/**
+ * Replaceable tag whose value depends on an argument.
+ *
+ * @param the argument type
+ */
+public class DependentTag implements Tag {
+
+ private final String name;
+ private final Function replacementFunction;
+
+ /**
+ * Constructor.
+ *
+ * @param name the tag (placeholder) that will be replaced
+ * @param replacementFunction the function producing the replacement
+ */
+ public DependentTag(String name, Function replacementFunction) {
+ this.name = name;
+ this.replacementFunction = replacementFunction;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getValue(A argument) {
+ return replacementFunction.apply(argument);
+ }
+}
diff --git a/src/main/java/fr/xephi/authme/util/lazytags/SimpleTag.java b/src/main/java/fr/xephi/authme/util/lazytags/SimpleTag.java
new file mode 100644
index 00000000..a5bb58a2
--- /dev/null
+++ b/src/main/java/fr/xephi/authme/util/lazytags/SimpleTag.java
@@ -0,0 +1,29 @@
+package fr.xephi.authme.util.lazytags;
+
+import java.util.function.Supplier;
+
+/**
+ * Tag to be replaced that does not depend on an argument.
+ *
+ * @param type of the argument (not used in this implementation)
+ */
+public class SimpleTag implements Tag {
+
+ private final String name;
+ private final Supplier replacementFunction;
+
+ public SimpleTag(String name, Supplier replacementFunction) {
+ this.name = name;
+ this.replacementFunction = replacementFunction;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getValue(A argument) {
+ return replacementFunction.get();
+ }
+}
diff --git a/src/main/java/fr/xephi/authme/util/lazytags/Tag.java b/src/main/java/fr/xephi/authme/util/lazytags/Tag.java
new file mode 100644
index 00000000..2c7c6ba5
--- /dev/null
+++ b/src/main/java/fr/xephi/authme/util/lazytags/Tag.java
@@ -0,0 +1,22 @@
+package fr.xephi.authme.util.lazytags;
+
+/**
+ * Represents a tag in a text to be replaced with a value (which may depend on some argument).
+ *
+ * @param argument type the replacement may depend on
+ */
+public interface Tag {
+
+ /**
+ * @return the tag to replace
+ */
+ String getName();
+
+ /**
+ * Returns the value to replace the tag with for the given argument.
+ *
+ * @param argument the argument to evaluate the replacement for
+ * @return the replacement
+ */
+ String getValue(A argument);
+}
diff --git a/src/main/java/fr/xephi/authme/util/lazytags/TagBuilder.java b/src/main/java/fr/xephi/authme/util/lazytags/TagBuilder.java
new file mode 100644
index 00000000..677b30e2
--- /dev/null
+++ b/src/main/java/fr/xephi/authme/util/lazytags/TagBuilder.java
@@ -0,0 +1,21 @@
+package fr.xephi.authme.util.lazytags;
+
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+/**
+ * Utility class for creating tags.
+ */
+public final class TagBuilder {
+
+ private TagBuilder() {
+ }
+
+ public static Tag createTag(String name, Function replacementFunction) {
+ return new DependentTag<>(name, replacementFunction);
+ }
+
+ public static Tag createTag(String name, Supplier replacementFunction) {
+ return new SimpleTag<>(name, replacementFunction);
+ }
+}
diff --git a/src/main/java/fr/xephi/authme/util/lazytags/TagReplacer.java b/src/main/java/fr/xephi/authme/util/lazytags/TagReplacer.java
new file mode 100644
index 00000000..660132fb
--- /dev/null
+++ b/src/main/java/fr/xephi/authme/util/lazytags/TagReplacer.java
@@ -0,0 +1,102 @@
+package fr.xephi.authme.util.lazytags;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Replaces tags lazily by first determining which tags are being used
+ * and only applying those replacements afterwards.
+ *
+ * @param the argument type
+ */
+public class TagReplacer {
+
+ private final List> tags;
+ private final Collection messages;
+
+ /**
+ * Private constructor. Use {@link #newReplacer(Collection, Collection)}.
+ *
+ * @param tags the tags that are being used in the messages
+ * @param messages the messages
+ */
+ private TagReplacer(List> tags, Collection messages) {
+ this.tags = tags;
+ this.messages = messages;
+ }
+
+ /**
+ * Creates a new instance of this class, which will provide the given
+ * messages adapted with the provided tags.
+ *
+ * @param allTags all available tags
+ * @param messages the messages to use
+ * @param the argument type
+ * @return new tag replacer instance
+ */
+ public static TagReplacer newReplacer(Collection> allTags, Collection messages) {
+ List> usedTags = determineUsedTags(allTags, messages);
+ return new TagReplacer<>(usedTags, messages);
+ }
+
+ /**
+ * Returns the messages with the tags applied for the given argument.
+ *
+ * @param argument the argument to get the messages for
+ * @return the adapted messages
+ */
+ public List getAdaptedMessages(A argument) {
+ // Note ljacqu 20170121: Using a Map might seem more natural here but we avoid doing so for performance
+ // Although the performance gain here is probably minimal...
+ List tagValues = new LinkedList<>();
+ for (Tag tag : tags) {
+ tagValues.add(new TagValue(tag.getName(), tag.getValue(argument)));
+ }
+
+ List adaptedMessages = new LinkedList<>();
+ for (String line : messages) {
+ String adaptedLine = line;
+ for (TagValue tagValue : tagValues) {
+ adaptedLine = adaptedLine.replace(tagValue.tag, tagValue.value);
+ }
+ adaptedMessages.add(adaptedLine);
+ }
+ return adaptedMessages;
+ }
+
+ /**
+ * Determines which tags are used somewhere in the given list of messages.
+ *
+ * @param allTags all available tags
+ * @param messages the messages
+ * @param argument type
+ * @return tags used at least once
+ */
+ private static List> determineUsedTags(Collection> allTags, Collection messages) {
+ return allTags.stream()
+ .filter(tag -> messages.stream().anyMatch(msg -> msg.contains(tag.getName())))
+ .collect(Collectors.toList());
+ }
+
+ /** (Tag, value) pair. */
+ private static final class TagValue {
+
+ /** The tag to replace. */
+ private final String tag;
+ /** The value to replace with. */
+ private final String value;
+
+ TagValue(String tag, String value) {
+ this.tag = tag;
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return "TagValue[tag='" + tag + "', value='" + value + "']";
+ }
+ }
+
+}
diff --git a/src/main/java/fr/xephi/authme/util/lazytags/WrappedTagReplacer.java b/src/main/java/fr/xephi/authme/util/lazytags/WrappedTagReplacer.java
new file mode 100644
index 00000000..92c3f70d
--- /dev/null
+++ b/src/main/java/fr/xephi/authme/util/lazytags/WrappedTagReplacer.java
@@ -0,0 +1,61 @@
+package fr.xephi.authme.util.lazytags;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * Applies tags lazily to the String property of an item. This class wraps
+ * a {@link TagReplacer} with the extraction of the String property and
+ * the creation of new items with the adapted string property.
+ *
+ * @param the item type
+ * @param the argument type to evaluate the replacements
+ */
+public class WrappedTagReplacer {
+
+ private final Collection items;
+ private final BiFunction itemCreator;
+ private final TagReplacer tagReplacer;
+
+ /**
+ * Constructor.
+ *
+ * @param allTags all available tags
+ * @param items the items to apply the replacements on
+ * @param stringGetter getter of the String property to adapt on the items
+ * @param itemCreator a function of signature (T, String) -> T: the original item and the adapted String are passed
+ */
+ public WrappedTagReplacer(Collection> allTags,
+ Collection items,
+ Function super T, String> stringGetter,
+ BiFunction itemCreator) {
+ this.items = items;
+ this.itemCreator = itemCreator;
+
+ List stringItems = items.stream().map(stringGetter).collect(Collectors.toList());
+ tagReplacer = TagReplacer.newReplacer(allTags, stringItems);
+ }
+
+ /**
+ * Creates adapted items for the given argument.
+ *
+ * @param argument the argument to adapt the items for
+ * @return the adapted items
+ */
+ public List getAdaptedItems(A argument) {
+ List adaptedStrings = tagReplacer.getAdaptedMessages(argument);
+ List adaptedItems = new LinkedList<>();
+
+ Iterator originalItemsIter = items.iterator();
+ Iterator newStringsIter = adaptedStrings.iterator();
+ while (originalItemsIter.hasNext() && newStringsIter.hasNext()) {
+ adaptedItems.add(itemCreator.apply(originalItemsIter.next(), newStringsIter.next()));
+ }
+ return adaptedItems;
+ }
+}
diff --git a/src/test/java/fr/xephi/authme/service/BukkitServiceTest.java b/src/test/java/fr/xephi/authme/service/BukkitServiceTest.java
index 597f89a7..6f869564 100644
--- a/src/test/java/fr/xephi/authme/service/BukkitServiceTest.java
+++ b/src/test/java/fr/xephi/authme/service/BukkitServiceTest.java
@@ -9,10 +9,14 @@ import org.bukkit.Server;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player;
+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.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Collection;
@@ -20,9 +24,13 @@ import java.util.Collection;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.only;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
/**
* Test for {@link BukkitService}.
@@ -38,10 +46,13 @@ public class BukkitServiceTest {
private Settings settings;
@Mock
private Server server;
+ @Mock
+ private BukkitScheduler scheduler;
@Before
public void constructBukkitService() {
ReflectionTestUtils.setField(Bukkit.class, null, "server", server);
+ given(server.getScheduler()).willReturn(scheduler);
given(settings.getProperty(PluginSettings.USE_ASYNC_TASKS)).willReturn(true);
bukkitService = new BukkitService(authMe, settings);
}
@@ -101,6 +112,191 @@ public class BukkitServiceTest {
verify(server).dispatchCommand(consoleSender, command);
}
+ @Test
+ public void shouldScheduleSyncDelayedTask() {
+ // given
+ Runnable task = () -> {/* noop */};
+ given(scheduler.scheduleSyncDelayedTask(authMe, task)).willReturn(123);
+
+ // when
+ int taskId = bukkitService.scheduleSyncDelayedTask(task);
+
+ // then
+ verify(scheduler, only()).scheduleSyncDelayedTask(authMe, task);
+ assertThat(taskId, equalTo(123));
+ }
+
+ @Test
+ public void shouldScheduleSyncDelayedTaskWithDelay() {
+ // given
+ Runnable task = () -> {/* noop */};
+ int delay = 3;
+ given(scheduler.scheduleSyncDelayedTask(authMe, task, delay)).willReturn(44);
+
+ // when
+ int taskId = bukkitService.scheduleSyncDelayedTask(task, delay);
+
+ // then
+ verify(scheduler, only()).scheduleSyncDelayedTask(authMe, task, delay);
+ assertThat(taskId, equalTo(44));
+ }
+
+ @Test
+ public void shouldScheduleSyncTask() {
+ // given
+ BukkitService spy = Mockito.spy(bukkitService);
+ doReturn(1).when(spy).scheduleSyncDelayedTask(any(Runnable.class));
+ Runnable task = mock(Runnable.class);
+
+ // when
+ spy.scheduleSyncTaskFromOptionallyAsyncTask(task);
+
+ // then
+ verify(spy).scheduleSyncDelayedTask(task);
+ verifyZeroInteractions(task);
+ }
+
+ @Test
+ public void shouldRunTaskDirectly() {
+ // given
+ given(settings.getProperty(PluginSettings.USE_ASYNC_TASKS)).willReturn(false);
+ bukkitService.reload(settings);
+ BukkitService spy = Mockito.spy(bukkitService);
+ Runnable task = mock(Runnable.class);
+
+ // when
+ spy.scheduleSyncTaskFromOptionallyAsyncTask(task);
+
+ // then
+ verify(task).run();
+ verify(spy, only()).scheduleSyncTaskFromOptionallyAsyncTask(task);
+ }
+
+ @Test
+ public void shouldRunTask() {
+ // given
+ Runnable task = () -> {/* noop */};
+ BukkitTask bukkitTask = mock(BukkitTask.class);
+ given(scheduler.runTask(authMe, task)).willReturn(bukkitTask);
+
+ // when
+ BukkitTask resultingTask = bukkitService.runTask(task);
+
+ // then
+ assertThat(resultingTask, equalTo(bukkitTask));
+ verify(scheduler, only()).runTask(authMe, task);
+ }
+
+ @Test
+ public void shouldRunTaskLater() {
+ // given
+ Runnable task = () -> {/* noop */};
+ BukkitTask bukkitTask = mock(BukkitTask.class);
+ long delay = 400;
+ given(scheduler.runTaskLater(authMe, task, delay)).willReturn(bukkitTask);
+
+ // when
+ BukkitTask resultingTask = bukkitService.runTaskLater(task, delay);
+
+ // then
+ assertThat(resultingTask, equalTo(bukkitTask));
+ verify(scheduler, only()).runTaskLater(authMe, task, delay);
+ }
+
+ @Test
+ public void shouldRunTaskInAsync() {
+ // given
+ Runnable task = mock(Runnable.class);
+ BukkitService spy = Mockito.spy(bukkitService);
+ doReturn(null).when(spy).runTaskAsynchronously(task);
+
+ // when
+ spy.runTaskOptionallyAsync(task);
+
+ // then
+ verifyZeroInteractions(task);
+ verify(spy).runTaskAsynchronously(task);
+ }
+
+ @Test
+ public void shouldRunTaskDirectlyIfConfigured() {
+ // given
+ given(settings.getProperty(PluginSettings.USE_ASYNC_TASKS)).willReturn(false);
+ bukkitService.reload(settings);
+ BukkitService spy = Mockito.spy(bukkitService);
+ Runnable task = mock(Runnable.class);
+
+ // when
+ spy.runTaskOptionallyAsync(task);
+
+ // then
+ verify(task).run();
+ verify(spy, only()).runTaskOptionallyAsync(task);
+ }
+
+ @Test
+ public void shouldRunTaskAsynchronously() {
+ // given
+ Runnable task = () -> {/* noop */};
+ BukkitTask bukkitTask = mock(BukkitTask.class);
+ given(scheduler.runTaskAsynchronously(authMe, task)).willReturn(bukkitTask);
+
+ // when
+ BukkitTask resultingTask = bukkitService.runTaskAsynchronously(task);
+
+ // then
+ assertThat(resultingTask, equalTo(bukkitTask));
+ verify(scheduler, only()).runTaskAsynchronously(authMe, task);
+ }
+
+ @Test
+ public void shouldRunTaskTimerAsynchronously() {
+ // given
+ Runnable task = () -> {/* */};
+ long delay = 20L;
+ long period = 4000L;
+ BukkitTask bukkitTask = mock(BukkitTask.class);
+ given(scheduler.runTaskTimerAsynchronously(authMe, task, delay, period)).willReturn(bukkitTask);
+
+ // when
+ BukkitTask resultingTask = bukkitService.runTaskTimerAsynchronously(task, delay, period);
+
+ // then
+ assertThat(resultingTask, equalTo(bukkitTask));
+ verify(scheduler).runTaskTimerAsynchronously(authMe, task, delay, period);
+ }
+
+ @Test
+ public void shouldRunTaskTimer() {
+ // given
+ BukkitRunnable bukkitRunnable = mock(BukkitRunnable.class);
+ long delay = 20;
+ long period = 80;
+ BukkitTask bukkitTask = mock(BukkitTask.class);
+ given(bukkitRunnable.runTaskTimer(authMe, delay, period)).willReturn(bukkitTask);
+
+ // when
+ BukkitTask result = bukkitService.runTaskTimer(bukkitRunnable, delay, period);
+
+ // then
+ assertThat(result, equalTo(bukkitTask));
+ verify(bukkitRunnable).runTaskTimer(authMe, delay, period);
+ }
+
+ @Test
+ public void shouldBroadcastMessage() {
+ // given
+ String message = "Important message to all";
+ given(server.broadcastMessage(message)).willReturn(24);
+
+ // when
+ int result = bukkitService.broadcastMessage(message);
+
+ // then
+ assertThat(result, equalTo(24));
+ verify(server).broadcastMessage(message);
+ }
+
// Note: This method is used through reflections
public static Player[] onlinePlayersImpl() {
return new Player[]{
diff --git a/src/test/java/fr/xephi/authme/settings/SettingsTest.java b/src/test/java/fr/xephi/authme/settings/SettingsTest.java
index 42326771..f7d2604c 100644
--- a/src/test/java/fr/xephi/authme/settings/SettingsTest.java
+++ b/src/test/java/fr/xephi/authme/settings/SettingsTest.java
@@ -4,7 +4,6 @@ import ch.jalu.configme.configurationdata.ConfigurationData;
import ch.jalu.configme.configurationdata.ConfigurationDataBuilder;
import ch.jalu.configme.resource.PropertyResource;
import fr.xephi.authme.TestHelper;
-import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.TestConfiguration;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -16,11 +15,8 @@ import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
-import static org.hamcrest.Matchers.arrayContaining;
-import static org.hamcrest.Matchers.arrayWithSize;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
-import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
@@ -45,26 +41,6 @@ public class SettingsTest {
testPluginFolder = temporaryFolder.newFolder();
}
- @Test
- public void shouldLoadWelcomeMessage() throws IOException {
- // given
- String welcomeMessage = "This is my welcome message for testing\nBye!";
- File welcomeFile = new File(testPluginFolder, "welcome.txt");
- createFile(welcomeFile);
- Files.write(welcomeFile.toPath(), welcomeMessage.getBytes());
-
- PropertyResource resource = mock(PropertyResource.class);
- given(resource.getBoolean(RegistrationSettings.USE_WELCOME_MESSAGE.getPath())).willReturn(true);
- Settings settings = new Settings(testPluginFolder, resource, null, CONFIG_DATA);
-
- // when
- String[] result = settings.getWelcomeMessage();
-
- // then
- assertThat(result, arrayWithSize(2));
- assertThat(result, arrayContaining(welcomeMessage.split("\\n")));
- }
-
@Test
public void shouldLoadEmailMessage() throws IOException {
// given
diff --git a/src/test/java/fr/xephi/authme/settings/WelcomeMessageConfigurationTest.java b/src/test/java/fr/xephi/authme/settings/WelcomeMessageConfigurationTest.java
new file mode 100644
index 00000000..37a3386a
--- /dev/null
+++ b/src/test/java/fr/xephi/authme/settings/WelcomeMessageConfigurationTest.java
@@ -0,0 +1,141 @@
+package fr.xephi.authme.settings;
+
+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;
+import fr.xephi.authme.initialization.DataFolder;
+import fr.xephi.authme.service.BukkitService;
+import fr.xephi.authme.service.GeoIpService;
+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.mockito.Mock;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
+import static org.junit.Assert.assertThat;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.only;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+/**
+ * Test for {@link WelcomeMessageConfiguration}.
+ */
+@RunWith(DelayedInjectionRunner.class)
+public class WelcomeMessageConfigurationTest {
+
+ @InjectDelayed
+ private WelcomeMessageConfiguration welcomeMessageConfiguration;
+ @Mock
+ private Server server;
+ @Mock
+ private BukkitService bukkitService;
+ @Mock
+ private GeoIpService geoIpService;
+ @Mock
+ private PlayerCache playerCache;
+ @DataFolder
+ private File testPluginFolder;
+
+ private File welcomeFile;
+
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ @BeforeInjecting
+ public void createPluginFolder() throws IOException {
+ testPluginFolder = temporaryFolder.newFolder();
+ welcomeFile = new File(testPluginFolder, "welcome.txt");
+ welcomeFile.createNewFile();
+ }
+
+ @Test
+ public void shouldLoadWelcomeMessage() throws IOException {
+ // given
+ String welcomeMessage = "This is my welcome message for testing\nBye!";
+ setWelcomeMessageAndReload(welcomeMessage);
+ Player player = mock(Player.class);
+
+ // when
+ List result = welcomeMessageConfiguration.getWelcomeMessage(player);
+
+ // then
+ assertThat(result, hasSize(2));
+ assertThat(result, contains(welcomeMessage.split("\\n")));
+ verifyZeroInteractions(player, playerCache, geoIpService, bukkitService, server);
+ }
+
+ @Test
+ public void shouldReplaceNameAndIpAndCountry() throws IOException {
+ // given
+ String welcomeMessage = "Hello {PLAYER}, your IP is {IP}\nYour country is {COUNTRY}.\nWelcome to {SERVER}!";
+ setWelcomeMessageAndReload(welcomeMessage);
+
+ Player player = mock(Player.class);
+ given(player.getName()).willReturn("Bobby");
+ TestHelper.mockPlayerIp(player, "123.45.66.77");
+ given(geoIpService.getCountryName("123.45.66.77")).willReturn("Syldavia");
+ given(server.getServerName()).willReturn("CrazyServer");
+
+ // when
+ List result = welcomeMessageConfiguration.getWelcomeMessage(player);
+
+ // then
+ assertThat(result, hasSize(3));
+ assertThat(result.get(0), equalTo("Hello Bobby, your IP is 123.45.66.77"));
+ assertThat(result.get(1), equalTo("Your country is Syldavia."));
+ assertThat(result.get(2), equalTo("Welcome to CrazyServer!"));
+ verify(server, only()).getServerName();
+ verifyZeroInteractions(playerCache);
+ }
+
+ @Test
+ public void shouldApplyOtherReplacements() throws IOException {
+ // given
+ String welcomeMessage = "{ONLINE}/{MAXPLAYERS} online\n{LOGINS} logged in\nYour world is {WORLD}\nServer: {VERSION}";
+ setWelcomeMessageAndReload(welcomeMessage);
+ given(bukkitService.getOnlinePlayers()).willReturn((List) Arrays.asList(mock(Player.class), mock(Player.class)));
+ given(server.getMaxPlayers()).willReturn(20);
+ given(playerCache.getLogged()).willReturn(1);
+ given(server.getBukkitVersion()).willReturn("Bukkit-456.77.8");
+
+ World world = mock(World.class);
+ given(world.getName()).willReturn("Hub");
+ Player player = mock(Player.class);
+ given(player.getWorld()).willReturn(world);
+
+ // when
+ List result = welcomeMessageConfiguration.getWelcomeMessage(player);
+
+ // then
+ assertThat(result, hasSize(4));
+ assertThat(result.get(0), equalTo("2/20 online"));
+ assertThat(result.get(1), equalTo("1 logged in"));
+ assertThat(result.get(2), equalTo("Your world is Hub"));
+ assertThat(result.get(3), equalTo("Server: Bukkit-456.77.8"));
+ }
+
+ private void setWelcomeMessageAndReload(String welcomeMessage) {
+ try {
+ Files.write(welcomeFile.toPath(), welcomeMessage.getBytes());
+ } catch (IOException e) {
+ throw new IllegalStateException("Could not write to '" + welcomeFile + "'", e);
+ }
+ welcomeMessageConfiguration.reload();
+ }
+}
diff --git a/src/test/java/fr/xephi/authme/settings/commandconfig/CommandManagerTest.java b/src/test/java/fr/xephi/authme/settings/commandconfig/CommandManagerTest.java
index fb8e4e40..3ec3b447 100644
--- a/src/test/java/fr/xephi/authme/settings/commandconfig/CommandManagerTest.java
+++ b/src/test/java/fr/xephi/authme/settings/commandconfig/CommandManagerTest.java
@@ -1,9 +1,9 @@
package fr.xephi.authme.settings.commandconfig;
import com.google.common.io.Files;
-import fr.xephi.authme.ReflectionTestUtils;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.service.BukkitService;
+import fr.xephi.authme.service.GeoIpService;
import fr.xephi.authme.settings.SettingsMigrationService;
import org.bukkit.entity.Player;
import org.junit.Before;
@@ -17,13 +17,7 @@ import org.mockito.junit.MockitoJUnitRunner;
import java.io.File;
import java.io.IOException;
-import java.util.function.BiConsumer;
-import static fr.xephi.authme.settings.commandconfig.CommandConfigTestHelper.isCommand;
-import static java.lang.String.format;
-import static org.hamcrest.Matchers.anEmptyMap;
-import static org.hamcrest.Matchers.contains;
-import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
@@ -31,6 +25,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.only;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
/**
* Test for {@link CommandManager}.
@@ -41,104 +36,116 @@ public class CommandManagerTest {
private static final String TEST_FILES_FOLDER = "/fr/xephi/authme/settings/commandconfig/";
private CommandManager manager;
+ private Player player;
+
@InjectMocks
private CommandMigrationService commandMigrationService;
- @Rule
- public TemporaryFolder temporaryFolder = new TemporaryFolder();
-
@Mock
private BukkitService bukkitService;
@Mock
+ private GeoIpService geoIpService;
+ @Mock
private SettingsMigrationService settingsMigrationService;
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
private File testFolder;
@Before
public void setup() throws IOException {
testFolder = temporaryFolder.newFolder();
- }
-
- @Test
- @SuppressWarnings("unchecked")
- public void shouldLoadCompleteFile() {
- // given
- copyJarFileAsCommandsYml(TEST_FILES_FOLDER + "commands.complete.yml");
-
- // when
- initManager();
-
- // then
- CommandConfig commandConfig = ReflectionTestUtils.getFieldValue(CommandManager.class, manager, "commandConfig");
- assertThat(commandConfig.getOnJoin().keySet(), contains("broadcast"));
- assertThat(commandConfig.getOnJoin().values(), contains(isCommand("broadcast %p has joined", Executor.CONSOLE)));
- assertThat(commandConfig.getOnRegister().keySet(), contains("announce", "notify"));
- assertThat(commandConfig.getOnRegister().values(), contains(
- isCommand("me I just registered", Executor.PLAYER),
- isCommand("log %p registered", Executor.CONSOLE)));
- assertThat(commandConfig.getOnLogin().keySet(), contains("welcome", "show_motd", "display_list"));
- assertThat(commandConfig.getOnLogin().values(), contains(
- isCommand("msg %p Welcome back", Executor.CONSOLE),
- isCommand("motd", Executor.PLAYER),
- isCommand("list", Executor.PLAYER)));
- }
-
- @Test
- public void shouldLoadIncompleteFile() {
- // given
- copyJarFileAsCommandsYml(TEST_FILES_FOLDER + "commands.incomplete.yml");
-
- // when
- initManager();
-
- // then
- CommandConfig commandConfig = ReflectionTestUtils.getFieldValue(CommandManager.class, manager, "commandConfig");
- assertThat(commandConfig.getOnJoin().values(), contains(isCommand("broadcast %p has joined", Executor.CONSOLE)));
- assertThat(commandConfig.getOnLogin().values(), contains(
- isCommand("msg %p Welcome back", Executor.CONSOLE),
- isCommand("list", Executor.PLAYER)));
- assertThat(commandConfig.getOnRegister(), anEmptyMap());
- }
-
- @Test
- public void shouldExecuteCommandsOnJoin() {
- // given
- String name = "Bobby1";
-
- // when
- testCommandExecution(name, CommandManager::runCommandsOnJoin);
-
- // then
- verify(bukkitService, only()).dispatchConsoleCommand(format("broadcast %s has joined", name));
- }
-
- @Test
- public void shouldExecuteCommandsOnRegister() {
- // given
- String name = "luis";
-
- // when
- testCommandExecution(name, CommandManager::runCommandsOnRegister);
-
- // then
- verify(bukkitService).dispatchCommand(any(Player.class), eq("me I just registered"));
- verify(bukkitService).dispatchConsoleCommand(format("log %s registered", name));
- verifyNoMoreInteractions(bukkitService);
+ player = mockPlayer();
}
@Test
public void shouldExecuteCommandsOnLogin() {
// given
- String name = "plaYer01";
+ copyJarFileAsCommandsYml(TEST_FILES_FOLDER + "commands.complete.yml");
+ initManager();
// when
- testCommandExecution(name, CommandManager::runCommandsOnLogin);
+ manager.runCommandsOnLogin(player);
// then
- verify(bukkitService).dispatchConsoleCommand(format("msg %s Welcome back", name));
+ verify(bukkitService).dispatchConsoleCommand("msg Bobby Welcome back");
verify(bukkitService).dispatchCommand(any(Player.class), eq("motd"));
verify(bukkitService).dispatchCommand(any(Player.class), eq("list"));
verifyNoMoreInteractions(bukkitService);
+ verifyZeroInteractions(geoIpService);
+ }
+
+ @Test
+ public void shouldExecuteCommandsOnLoginWithIncompleteConfig() {
+ // given
+ copyJarFileAsCommandsYml(TEST_FILES_FOLDER + "commands.incomplete.yml");
+ initManager();
+
+ // when
+ manager.runCommandsOnLogin(player);
+
+ // then
+ verify(bukkitService).dispatchConsoleCommand("msg Bobby Welcome back, bob");
+ verify(bukkitService).dispatchCommand(any(Player.class), eq("list"));
+ verifyNoMoreInteractions(bukkitService);
+ verifyZeroInteractions(geoIpService);
+ }
+
+ @Test
+ public void shouldExecuteCommandsOnJoin() {
+ // given
+ copyJarFileAsCommandsYml(TEST_FILES_FOLDER + "commands.complete.yml");
+ initManager();
+
+ // when
+ manager.runCommandsOnJoin(player);
+
+ // then
+ verify(bukkitService, only()).dispatchConsoleCommand("broadcast bob has joined");
+ verifyZeroInteractions(geoIpService);
+ }
+
+ @Test
+ public void shouldExecuteCommandsOnJoinWithIncompleteConfig() {
+ // given
+ copyJarFileAsCommandsYml(TEST_FILES_FOLDER + "commands.incomplete.yml");
+ initManager();
+
+ // when
+ manager.runCommandsOnJoin(player);
+
+ // then
+ verify(bukkitService, only()).dispatchConsoleCommand("broadcast Bobby has joined");
+ verifyZeroInteractions(geoIpService);
+ }
+
+ @Test
+ public void shouldExecuteCommandsOnRegister() {
+ // given
+ copyJarFileAsCommandsYml(TEST_FILES_FOLDER + "commands.complete.yml");
+ initManager();
+
+ // when
+ manager.runCommandsOnRegister(player);
+
+ // then
+ verify(bukkitService).dispatchCommand(any(Player.class), eq("me I just registered"));
+ verify(bukkitService).dispatchConsoleCommand("log Bobby (127.0.0.3, Syldavia) registered");
+ verifyNoMoreInteractions(bukkitService);
+ }
+
+ @Test
+ public void shouldExecuteCommandsOnRegisterWithIncompleteConfig() {
+ // given
+ copyJarFileAsCommandsYml(TEST_FILES_FOLDER + "commands.incomplete.yml");
+ initManager();
+
+ // when
+ manager.runCommandsOnRegister(player);
+
+ // then
+ verifyZeroInteractions(bukkitService, geoIpService);
}
@Test
@@ -147,18 +154,8 @@ public class CommandManagerTest {
TestHelper.validateHasOnlyPrivateEmptyConstructor(CommandSettingsHolder.class);
}
-
- private void testCommandExecution(String playerName, BiConsumer testMethod) {
- copyJarFileAsCommandsYml(TEST_FILES_FOLDER + "commands.complete.yml");
- initManager();
- Player player = mock(Player.class);
- given(player.getName()).willReturn(playerName);
-
- testMethod.accept(manager, player);
- }
-
private void initManager() {
- manager = new CommandManager(testFolder, bukkitService, commandMigrationService);
+ manager = new CommandManager(testFolder, bukkitService, geoIpService, commandMigrationService);
}
private void copyJarFileAsCommandsYml(String path) {
@@ -171,4 +168,13 @@ public class CommandManagerTest {
}
}
+ private Player mockPlayer() {
+ Player player = mock(Player.class);
+ given(player.getName()).willReturn("Bobby");
+ given(player.getDisplayName()).willReturn("bob");
+ String ip = "127.0.0.3";
+ TestHelper.mockPlayerIp(player, ip);
+ given(geoIpService.getCountryName(ip)).willReturn("Syldavia");
+ return player;
+ }
}
diff --git a/src/test/java/fr/xephi/authme/util/CollectionUtilsTest.java b/src/test/java/fr/xephi/authme/util/CollectionUtilsTest.java
deleted file mode 100644
index f359f088..00000000
--- a/src/test/java/fr/xephi/authme/util/CollectionUtilsTest.java
+++ /dev/null
@@ -1,102 +0,0 @@
-package fr.xephi.authme.util;
-
-import org.junit.Test;
-
-import java.util.Arrays;
-import java.util.List;
-
-import static org.hamcrest.Matchers.contains;
-import static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.assertThat;
-import static org.hamcrest.Matchers.empty;
-
-/**
- * Test for {@link CollectionUtils}.
- */
-public class CollectionUtilsTest {
-
- @Test
- public void shouldGetFullList() {
- // given
- List list = Arrays.asList("test", "1", "2", "3", "4");
-
- // when
- List result = CollectionUtils.getRange(list, 0, 24);
-
- // then
- assertThat(result, equalTo(list));
- }
-
- @Test
- public void shouldReturnEmptyListForZeroCount() {
- // given
- List list = Arrays.asList("test", "1", "2", "3", "4");
-
- // when
- List result = CollectionUtils.getRange(list, 2, 0);
-
- // then
- assertThat(result, empty());
- }
-
-
- @Test
- public void shouldReturnEmptyListForTooHighStart() {
- // given
- List list = Arrays.asList("test", "1", "2", "3", "4");
-
- // when
- List result = CollectionUtils.getRange(list, 12, 2);
-
- // then
- assertThat(result, empty());
- }
-
- @Test
- public void shouldReturnSubList() {
- // given
- List list = Arrays.asList("test", "1", "2", "3", "4");
-
- // when
- List result = CollectionUtils.getRange(list, 1, 3);
-
- // then
- assertThat(result, contains("1", "2", "3"));
- }
-
- @Test
- public void shouldReturnTillEnd() {
- // given
- List list = Arrays.asList("test", "1", "2", "3", "4");
-
- // when
- List result = CollectionUtils.getRange(list, 2, 3);
-
- // then
- assertThat(result, contains("2", "3", "4"));
- }
-
- @Test
- public void shouldRemoveFirstTwo() {
- // given
- List list = Arrays.asList("test", "1", "2", "3", "4");
-
- // when
- List result = CollectionUtils.getRange(list, 2);
-
- // then
- assertThat(result, contains("2", "3", "4"));
- }
-
- @Test
- public void shouldHandleNegativeStart() {
- // given
- List list = Arrays.asList("test", "1", "2", "3", "4");
-
- // when
- List result = CollectionUtils.getRange(list, -4);
-
- // then
- assertThat(result, equalTo(list));
- }
-}
diff --git a/src/test/java/fr/xephi/authme/util/FileUtilsTest.java b/src/test/java/fr/xephi/authme/util/FileUtilsTest.java
index fa8ee9c6..9d65047c 100644
--- a/src/test/java/fr/xephi/authme/util/FileUtilsTest.java
+++ b/src/test/java/fr/xephi/authme/util/FileUtilsTest.java
@@ -137,6 +137,11 @@ public class FileUtilsTest {
assertThat(result, equalTo("path" + File.separator + "to" + File.separator + "test-file.txt"));
}
+ @Test
+ public void shouldHaveHiddenConstructor() {
+ TestHelper.validateHasOnlyPrivateEmptyConstructor(FileUtils.class);
+ }
+
private static void createFiles(File... files) throws IOException {
for (File file : files) {
boolean result = file.getParentFile().mkdirs() & file.createNewFile();
diff --git a/src/test/java/tools/filegeneration/GenerateCommandsYml.java b/src/test/java/tools/filegeneration/GenerateCommandsYml.java
index 73113ccc..b99eef52 100644
--- a/src/test/java/tools/filegeneration/GenerateCommandsYml.java
+++ b/src/test/java/tools/filegeneration/GenerateCommandsYml.java
@@ -26,7 +26,7 @@ public class GenerateCommandsYml implements AutoToolTask {
// Get default and add sample entry
CommandConfig commandConfig = CommandSettingsHolder.COMMANDS.getDefaultValue();
commandConfig.setOnLogin(
- ImmutableMap.of("welcome", newCommand("msg %p Welcome back!", Executor.PLAYER)));
+ ImmutableMap.of("welcome", new Command("msg %p Welcome back!", Executor.PLAYER)));
// Export the value to the file
SettingsManager settingsManager = new SettingsManager(
@@ -41,11 +41,4 @@ public class GenerateCommandsYml implements AutoToolTask {
public String getTaskName() {
return "generateCommandsYml";
}
-
- private static Command newCommand(String commandLine, Executor executor) {
- Command command = new Command();
- command.setCommand(commandLine);
- command.setExecutor(executor);
- return command;
- }
}
diff --git a/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.complete.yml b/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.complete.yml
index 8c6bf79d..757f09ca 100644
--- a/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.complete.yml
+++ b/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.complete.yml
@@ -2,14 +2,14 @@
onJoin:
broadcast:
- command: 'broadcast %p has joined'
+ command: 'broadcast %nick has joined'
executor: CONSOLE
onRegister:
announce:
command: 'me I just registered'
executor: PLAYER
notify:
- command: 'log %p registered'
+ command: 'log %p (%ip, %country) registered'
executor: CONSOLE
onLogin:
welcome:
diff --git a/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.incomplete.yml b/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.incomplete.yml
index 2eef86b0..a1e9060f 100644
--- a/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.incomplete.yml
+++ b/src/test/resources/fr/xephi/authme/settings/commandconfig/commands.incomplete.yml
@@ -6,7 +6,7 @@ onJoin:
executor: CONSOLE
onLogin:
welcome:
- command: 'msg %p Welcome back'
+ command: 'msg %p Welcome back, %nick'
executor: CONSOLE
show_motd:
# command: 'motd' <-- mandatory property, so entry should be ignored