diff --git a/src/main/java/fr/xephi/authme/process/SyncProcessManager.java b/src/main/java/fr/xephi/authme/process/SyncProcessManager.java index e634302f4..136c6b353 100644 --- a/src/main/java/fr/xephi/authme/process/SyncProcessManager.java +++ b/src/main/java/fr/xephi/authme/process/SyncProcessManager.java @@ -47,8 +47,8 @@ public class SyncProcessManager { runTask(() -> processSyncPlayerLogout.processSyncLogout(player)); } - public void processSyncPlayerLogin(Player player) { - runTask(() -> processSyncPlayerLogin.processPlayerLogin(player)); + public void processSyncPlayerLogin(Player player, boolean isFirstLogin) { + runTask(() -> processSyncPlayerLogin.processPlayerLogin(player, isFirstLogin)); } public void processSyncPlayerQuit(Player player, boolean wasLoggedIn) { diff --git a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java b/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java index e76fea783..75219b451 100644 --- a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java @@ -220,6 +220,8 @@ public class AsynchronousLogin implements AsynchronousProcess { */ private void performLogin(Player player, PlayerAuth auth) { if (player.isOnline()) { + final boolean isFirstLogin = (auth.getLastLogin() == null); + // Update auth to reflect this new login final String ip = PlayerUtils.getPlayerIp(player); auth.setRealName(player.getName()); @@ -258,7 +260,7 @@ public class AsynchronousLogin implements AsynchronousProcess { // task, we schedule it in the end // so that we can be sure, and have not to care if it might be // processed in other order. - syncProcessManager.processSyncPlayerLogin(player); + syncProcessManager.processSyncPlayerLogin(player, isFirstLogin); } else { ConsoleLogger.warning("Player '" + player.getName() + "' wasn't online during login process, aborted..."); } 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 359aca033..f47ccdbff 100644 --- a/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java @@ -62,7 +62,13 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess { } } - public void processPlayerLogin(Player player) { + /** + * Performs operations in sync mode for a player that has just logged in. + * + * @param player the player that was logged in + * @param isFirstLogin true if this is the first time the player logged in + */ + public void processPlayerLogin(Player player, boolean isFirstLogin) { final String name = player.getName().toLowerCase(); final LimboPlayer limbo = limboService.getLimboPlayer(name); @@ -93,6 +99,9 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess { welcomeMessageConfiguration.sendWelcomeMessage(player); // Login is now finished; we can force all commands + if (isFirstLogin) { + commandManager.runCommandsOnFirstLogin(player); + } commandManager.runCommandsOnLogin(player); // Send Bungee stuff. The service will check if it is enabled or not. diff --git a/src/main/java/fr/xephi/authme/settings/commandconfig/CommandConfig.java b/src/main/java/fr/xephi/authme/settings/commandconfig/CommandConfig.java index f70230209..e210f597c 100644 --- a/src/main/java/fr/xephi/authme/settings/commandconfig/CommandConfig.java +++ b/src/main/java/fr/xephi/authme/settings/commandconfig/CommandConfig.java @@ -13,6 +13,7 @@ public class CommandConfig { private Map onJoin = new LinkedHashMap<>(); private Map onLogin = new LinkedHashMap<>(); private Map onSessionLogin = new LinkedHashMap<>(); + private Map onFirstLogin = new LinkedHashMap<>(); private Map onRegister = new LinkedHashMap<>(); private Map onUnregister = new LinkedHashMap<>(); private Map onLogout = new LinkedHashMap<>(); @@ -41,6 +42,14 @@ public class CommandConfig { this.onSessionLogin = onSessionLogin; } + public Map getOnFirstLogin() { + return onFirstLogin; + } + + public void setOnFirstLogin(Map onFirstLogin) { + this.onFirstLogin = onFirstLogin; + } + public Map getOnRegister() { return onRegister; } 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 f6fbd4c78..6fdb5fdee 100644 --- a/src/main/java/fr/xephi/authme/settings/commandconfig/CommandManager.java +++ b/src/main/java/fr/xephi/authme/settings/commandconfig/CommandManager.java @@ -34,6 +34,7 @@ public class CommandManager implements Reloadable { private WrappedTagReplacer onJoinCommands; private WrappedTagReplacer onLoginCommands; private WrappedTagReplacer onSessionLoginCommands; + private WrappedTagReplacer onFirstLoginCommands; private WrappedTagReplacer onRegisterCommands; private WrappedTagReplacer onUnregisterCommands; private WrappedTagReplacer onLogoutCommands; @@ -75,7 +76,6 @@ public class CommandManager implements Reloadable { executeCommands(player, onLoginCommands.getAdaptedItems(player)); } - /** * Runs the configured commands for when a player has logged in successfully due to session. * @@ -85,6 +85,15 @@ public class CommandManager implements Reloadable { executeCommands(player, onSessionLoginCommands.getAdaptedItems(player)); } + /** + * Runs the configured commands for when a player logs in the first time. + * + * @param player the player that has logged in for the first time + */ + public void runCommandsOnFirstLogin(Player player) { + executeCommands(player, onFirstLoginCommands.getAdaptedItems(player)); + } + /** * Runs the configured commands for when a player has been unregistered. * @@ -124,6 +133,7 @@ public class CommandManager implements Reloadable { CommandConfig commandConfig = settingsManager.getProperty(CommandSettingsHolder.COMMANDS); onJoinCommands = newReplacer(commandConfig.getOnJoin()); onLoginCommands = newReplacer(commandConfig.getOnLogin()); + onFirstLoginCommands = newReplacer(commandConfig.getOnFirstLogin()); onSessionLoginCommands = newReplacer(commandConfig.getOnSessionLogin()); onRegisterCommands = newReplacer(commandConfig.getOnRegister()); onUnregisterCommands = newReplacer(commandConfig.getOnUnregister()); diff --git a/src/main/java/fr/xephi/authme/settings/commandconfig/CommandMigrationService.java b/src/main/java/fr/xephi/authme/settings/commandconfig/CommandMigrationService.java index e1d1085a6..acd39cc64 100644 --- a/src/main/java/fr/xephi/authme/settings/commandconfig/CommandMigrationService.java +++ b/src/main/java/fr/xephi/authme/settings/commandconfig/CommandMigrationService.java @@ -23,7 +23,7 @@ class CommandMigrationService implements MigrationService { /** List of all properties in {@link CommandConfig}. */ @VisibleForTesting static final List COMMAND_CONFIG_PROPERTIES = ImmutableList.of( - "onJoin", "onLogin", "onSessionLogin", "onRegister", "onUnregister", "onLogout"); + "onJoin", "onLogin", "onSessionLogin", "onFirstLogin", "onRegister", "onUnregister", "onLogout"); @Inject private SettingsMigrationService settingsMigrationService; diff --git a/src/main/java/fr/xephi/authme/settings/commandconfig/CommandSettingsHolder.java b/src/main/java/fr/xephi/authme/settings/commandconfig/CommandSettingsHolder.java index 29e9b0c94..87530d22f 100644 --- a/src/main/java/fr/xephi/authme/settings/commandconfig/CommandSettingsHolder.java +++ b/src/main/java/fr/xephi/authme/settings/commandconfig/CommandSettingsHolder.java @@ -22,7 +22,7 @@ public final class CommandSettingsHolder implements SettingsHolder { @SectionComments public static Map sectionComments() { - String[] comments = { + String[] rootComments = { "This configuration file allows you to execute commands on various events.", "Supported placeholders in commands:", " %p is replaced with the player name.", @@ -48,10 +48,14 @@ public final class CommandSettingsHolder implements SettingsHolder { " command: 'broadcast %p has joined, welcome back!'", " executor: CONSOLE", "", - "Supported command events: onLogin, onSessionLogin, onJoin, onLogout, onRegister, onUnregister" + "Supported command events: onLogin, onSessionLogin, onFirstLogin, onJoin, onLogout, onRegister, " + + "onUnregister" }; Map commentMap = new HashMap<>(); - commentMap.put("", comments); + commentMap.put("", rootComments); + commentMap.put("onFirstLogin", new String[]{ + "Commands to run for players logging in whose 'last login date' was empty" + }); commentMap.put("onUnregister", new String[]{ "Commands to run whenever a player is unregistered (by himself, or by an admin)" }); diff --git a/src/main/resources/commands.yml b/src/main/resources/commands.yml index fecccd595..5e7717ac9 100644 --- a/src/main/resources/commands.yml +++ b/src/main/resources/commands.yml @@ -24,7 +24,9 @@ # command: 'broadcast %p has joined, welcome back!' # executor: CONSOLE # -# Supported command events: onLogin, onSessionLogin, onJoin, onLogout, onRegister, onUnregister +# Supported command events: onLogin, onSessionLogin, onFirstLogin, onJoin, onLogout, onRegister, onUnregister +# Commands to run for players logging in whose 'last login date' was empty +onFirstLogin: {} onJoin: {} onLogin: {} # These commands are called whenever a logged in player uses /logout or quits. 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 ebf92726f..b4183d700 100644 --- a/src/test/java/fr/xephi/authme/settings/commandconfig/CommandManagerTest.java +++ b/src/test/java/fr/xephi/authme/settings/commandconfig/CommandManagerTest.java @@ -107,6 +107,21 @@ public class CommandManagerTest { verifyZeroInteractions(geoIpService); } + @Test + public void shouldExecuteCommandsOnFirstLogin() { + // given + copyJarFileAsCommandsYml(TEST_FILES_FOLDER + "commands.complete.yml"); + initManager(); + + // when + manager.runCommandsOnFirstLogin(player); + + // then + verify(bukkitService).dispatchConsoleCommand("pay Bobby 30"); + verifyNoMoreInteractions(bukkitService); + verifyZeroInteractions(geoIpService); + } + @Test public void shouldExecuteCommandsOnJoin() { // given 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 57154c7fa..7bcf0b770 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 @@ -25,6 +25,10 @@ onSessionLogin: welcome: command: 'msg %p Session login!' executor: CONSOLE +onFirstLogin: + give_money: + command: 'pay %p 30' + executor: CONSOLE onUnregister: {} onLogout: announce: