From 75421fd15619f2a641a09694c22875ec315b78ae Mon Sep 17 00:00:00 2001 From: Gabriele C Date: Wed, 3 Aug 2016 01:02:25 +0200 Subject: [PATCH] Listeners name refactor + Fix #831 for both Spigot and CraftBukkit --- src/main/java/fr/xephi/authme/AuthMe.java | 35 ++++++++++------ .../listener/AsyncSingleSessionListener.java | 39 ++++++++++++++++++ ...eBlockListener.java => BlockListener.java} | 2 +- ...ntityListener.java => EntityListener.java} | 5 ++- ...layerListener.java => PlayerListener.java} | 28 +++++++------ ...rListener16.java => PlayerListener16.java} | 2 +- ...rListener18.java => PlayerListener18.java} | 2 +- ...erverListener.java => ServerListener.java} | 2 +- .../listener/SyncSingleSessionListener.java | 41 +++++++++++++++++++ .../process/login/ProcessSyncPlayerLogin.java | 6 +-- .../authme/AuthMeInitializationTest.java | 4 +- .../listener/AuthMeBlockListenerTest.java | 7 +++- .../listener/ListenerConsistencyTest.java | 14 +++++-- .../authme/listener/ListenerServiceTest.java | 1 + .../authme/listener/OnJoinVerifierTest.java | 2 + src/test/java/tools/utils/ServerUtils.java | 18 ++++++++ 16 files changed, 167 insertions(+), 41 deletions(-) create mode 100644 src/main/java/fr/xephi/authme/listener/AsyncSingleSessionListener.java rename src/main/java/fr/xephi/authme/listener/{AuthMeBlockListener.java => BlockListener.java} (92%) rename src/main/java/fr/xephi/authme/listener/{AuthMeEntityListener.java => EntityListener.java} (97%) rename src/main/java/fr/xephi/authme/listener/{AuthMePlayerListener.java => PlayerListener.java} (94%) rename src/main/java/fr/xephi/authme/listener/{AuthMePlayerListener16.java => PlayerListener16.java} (91%) rename src/main/java/fr/xephi/authme/listener/{AuthMePlayerListener18.java => PlayerListener18.java} (91%) rename src/main/java/fr/xephi/authme/listener/{AuthMeServerListener.java => ServerListener.java} (98%) create mode 100644 src/main/java/fr/xephi/authme/listener/SyncSingleSessionListener.java create mode 100644 src/test/java/tools/utils/ServerUtils.java diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index 88972acea..1818f736a 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -20,12 +20,14 @@ import fr.xephi.authme.hooks.BungeeCordMessage; import fr.xephi.authme.hooks.PluginHooks; import fr.xephi.authme.initialization.DataFolder; import fr.xephi.authme.initialization.MetricsStarter; -import fr.xephi.authme.listener.AuthMeBlockListener; -import fr.xephi.authme.listener.AuthMeEntityListener; -import fr.xephi.authme.listener.AuthMePlayerListener; -import fr.xephi.authme.listener.AuthMePlayerListener16; -import fr.xephi.authme.listener.AuthMePlayerListener18; -import fr.xephi.authme.listener.AuthMeServerListener; +import fr.xephi.authme.listener.AsyncSingleSessionListener; +import fr.xephi.authme.listener.BlockListener; +import fr.xephi.authme.listener.EntityListener; +import fr.xephi.authme.listener.PlayerListener; +import fr.xephi.authme.listener.PlayerListener16; +import fr.xephi.authme.listener.PlayerListener18; +import fr.xephi.authme.listener.ServerListener; +import fr.xephi.authme.listener.SyncSingleSessionListener; import fr.xephi.authme.output.ConsoleFilter; import fr.xephi.authme.output.Log4JFilter; import fr.xephi.authme.output.MessageKey; @@ -53,6 +55,8 @@ import fr.xephi.authme.util.MigrationService; import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.Utils; import fr.xephi.authme.util.ValidationService; +import tools.utils.ServerUtils; + import org.apache.logging.log4j.LogManager; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -316,24 +320,31 @@ public class AuthMe extends JavaPlugin { PluginManager pluginManager = getServer().getPluginManager(); // Register event listeners - pluginManager.registerEvents(injector.getSingleton(AuthMePlayerListener.class), this); - pluginManager.registerEvents(injector.getSingleton(AuthMeBlockListener.class), this); - pluginManager.registerEvents(injector.getSingleton(AuthMeEntityListener.class), this); - pluginManager.registerEvents(injector.getSingleton(AuthMeServerListener.class), this); + pluginManager.registerEvents(injector.getSingleton(PlayerListener.class), this); + pluginManager.registerEvents(injector.getSingleton(BlockListener.class), this); + pluginManager.registerEvents(injector.getSingleton(EntityListener.class), this); + pluginManager.registerEvents(injector.getSingleton(ServerListener.class), this); // Try to register 1.6 player listeners try { Class.forName("org.bukkit.event.player.PlayerEditBookEvent"); - pluginManager.registerEvents(injector.getSingleton(AuthMePlayerListener16.class), this); + pluginManager.registerEvents(injector.getSingleton(PlayerListener16.class), this); } catch (ClassNotFoundException ignore) { } // Try to register 1.8 player listeners try { Class.forName("org.bukkit.event.player.PlayerInteractAtEntityEvent"); - pluginManager.registerEvents(injector.getSingleton(AuthMePlayerListener18.class), this); + pluginManager.registerEvents(injector.getSingleton(PlayerListener18.class), this); } catch (ClassNotFoundException ignore) { } + + // Choose the right SingleSessionListener + if(Bukkit.getOnlineMode() || ServerUtils.isSpigot()) { + pluginManager.registerEvents(injector.getSingleton(AsyncSingleSessionListener.class), this); + } else { + pluginManager.registerEvents(injector.getSingleton(SyncSingleSessionListener.class), this); + } } private void reloadSupportHook() { diff --git a/src/main/java/fr/xephi/authme/listener/AsyncSingleSessionListener.java b/src/main/java/fr/xephi/authme/listener/AsyncSingleSessionListener.java new file mode 100644 index 000000000..14d81332a --- /dev/null +++ b/src/main/java/fr/xephi/authme/listener/AsyncSingleSessionListener.java @@ -0,0 +1,39 @@ +package fr.xephi.authme.listener; + +import javax.inject.Inject; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.AsyncPlayerPreLoginEvent; + +import fr.xephi.authme.output.Messages; + +/* + * This listener is registered only if the server is in online-mode or the implementation is Spigot. + * The reason is that only Spigot fires the Async version of the event on an offline-mode server! + */ +public class AsyncSingleSessionListener implements Listener { + + @Inject + private Messages m; + @Inject + private OnJoinVerifier onJoinVerifier; + + @EventHandler(priority = EventPriority.LOWEST) + public void onSyncPreLogin(AsyncPlayerPreLoginEvent event) { + if(event.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) { + return; + } + + final String name = event.getName(); + + try { + onJoinVerifier.checkSingleSession(name); + } catch (FailedVerificationException e) { + event.setKickMessage(m.retrieveSingle(e.getReason(), e.getArgs())); + event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER); + return; + } + } +} diff --git a/src/main/java/fr/xephi/authme/listener/AuthMeBlockListener.java b/src/main/java/fr/xephi/authme/listener/BlockListener.java similarity index 92% rename from src/main/java/fr/xephi/authme/listener/AuthMeBlockListener.java rename to src/main/java/fr/xephi/authme/listener/BlockListener.java index 7fe23dd96..64ef09ea0 100644 --- a/src/main/java/fr/xephi/authme/listener/AuthMeBlockListener.java +++ b/src/main/java/fr/xephi/authme/listener/BlockListener.java @@ -7,7 +7,7 @@ import org.bukkit.event.block.BlockPlaceEvent; import javax.inject.Inject; -public class AuthMeBlockListener implements Listener { +public class BlockListener implements Listener { @Inject private ListenerService listenerService; diff --git a/src/main/java/fr/xephi/authme/listener/AuthMeEntityListener.java b/src/main/java/fr/xephi/authme/listener/EntityListener.java similarity index 97% rename from src/main/java/fr/xephi/authme/listener/AuthMeEntityListener.java rename to src/main/java/fr/xephi/authme/listener/EntityListener.java index 4fa59a07a..649f51533 100644 --- a/src/main/java/fr/xephi/authme/listener/AuthMeEntityListener.java +++ b/src/main/java/fr/xephi/authme/listener/EntityListener.java @@ -1,6 +1,7 @@ package fr.xephi.authme.listener; import fr.xephi.authme.ConsoleLogger; + import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.entity.Projectile; @@ -20,14 +21,14 @@ import javax.inject.Inject; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -public class AuthMeEntityListener implements Listener { +public class EntityListener implements Listener { private final ListenerService listenerService; private Method getShooter; private boolean shooterIsLivingEntity; @Inject - AuthMeEntityListener(ListenerService listenerService) { + EntityListener(ListenerService listenerService) { this.listenerService = listenerService; try { getShooter = Projectile.class.getDeclaredMethod("getShooter"); diff --git a/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java b/src/main/java/fr/xephi/authme/listener/PlayerListener.java similarity index 94% rename from src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java rename to src/main/java/fr/xephi/authme/listener/PlayerListener.java index 4a3775bb7..f7b253178 100644 --- a/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java +++ b/src/main/java/fr/xephi/authme/listener/PlayerListener.java @@ -1,7 +1,6 @@ package fr.xephi.authme.listener; import fr.xephi.authme.AntiBot; -import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; @@ -15,6 +14,7 @@ import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.util.BukkitService; import fr.xephi.authme.util.TeleportationService; import fr.xephi.authme.util.ValidationService; + import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -25,6 +25,7 @@ import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryOpenEvent; import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.player.AsyncPlayerPreLoginEvent; import org.bukkit.event.player.PlayerBedEnterEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerDropItemEvent; @@ -52,7 +53,7 @@ import static fr.xephi.authme.settings.properties.RestrictionSettings.ALLOW_UNAU /** * Listener class for player events. */ -public class AuthMePlayerListener implements Listener { +public class PlayerListener implements Listener { public static final ConcurrentHashMap joinMessage = new ConcurrentHashMap<>(); @@ -201,26 +202,27 @@ public class AuthMePlayerListener implements Listener { management.performJoin(player); } - // Note: AsyncPlayerPreLoginEvent is not fired by all servers in offline mode - // e.g. CraftBukkit does not. So we need to run crucial things in onPlayerLogin, too - // We have no performance improvements if we do the same thing on two different events - // Important: the single session feature works if we use the low priority to the sync handler - @EventHandler(priority = EventPriority.LOWEST) - public void onLoginSingleSession(PlayerLoginEvent event) { - if(event.getResult() != PlayerLoginEvent.Result.ALLOWED) { + public void onAsyncPreLogin(AsyncPlayerPreLoginEvent event) { + + // Spigot only + try { + Class.forName("org.spigotmc.CustomTimingsHandler"); + } catch(Exception e) { + return; + } + + if(event.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) { return; } - final Player player = event.getPlayer(); - final String name = player.getName(); + final String name = event.getName(); try { onJoinVerifier.checkSingleSession(name); } catch (FailedVerificationException e) { - ConsoleLogger.warning("DEBUG: " + name + " tried to join the game but an user with the same name was already online!" ); event.setKickMessage(m.retrieveSingle(e.getReason(), e.getArgs())); - event.setResult(PlayerLoginEvent.Result.KICK_OTHER); + event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER); return; } } diff --git a/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener16.java b/src/main/java/fr/xephi/authme/listener/PlayerListener16.java similarity index 91% rename from src/main/java/fr/xephi/authme/listener/AuthMePlayerListener16.java rename to src/main/java/fr/xephi/authme/listener/PlayerListener16.java index 871757f4e..0fe167566 100644 --- a/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener16.java +++ b/src/main/java/fr/xephi/authme/listener/PlayerListener16.java @@ -10,7 +10,7 @@ import javax.inject.Inject; /** * Listener of player events for events introduced in Minecraft 1.6. */ -public class AuthMePlayerListener16 implements Listener { +public class PlayerListener16 implements Listener { @Inject private ListenerService listenerService; diff --git a/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener18.java b/src/main/java/fr/xephi/authme/listener/PlayerListener18.java similarity index 91% rename from src/main/java/fr/xephi/authme/listener/AuthMePlayerListener18.java rename to src/main/java/fr/xephi/authme/listener/PlayerListener18.java index b6cbf2a7c..891a9b94f 100644 --- a/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener18.java +++ b/src/main/java/fr/xephi/authme/listener/PlayerListener18.java @@ -10,7 +10,7 @@ import javax.inject.Inject; /** * Listener of player events for events introduced in Minecraft 1.8. */ -public class AuthMePlayerListener18 implements Listener { +public class PlayerListener18 implements Listener { @Inject private ListenerService listenerService; diff --git a/src/main/java/fr/xephi/authme/listener/AuthMeServerListener.java b/src/main/java/fr/xephi/authme/listener/ServerListener.java similarity index 98% rename from src/main/java/fr/xephi/authme/listener/AuthMeServerListener.java rename to src/main/java/fr/xephi/authme/listener/ServerListener.java index 3dd46296c..80081b90a 100644 --- a/src/main/java/fr/xephi/authme/listener/AuthMeServerListener.java +++ b/src/main/java/fr/xephi/authme/listener/ServerListener.java @@ -14,7 +14,7 @@ import javax.inject.Inject; /** */ -public class AuthMeServerListener implements Listener { +public class ServerListener implements Listener { @Inject private PluginHooks pluginHooks; diff --git a/src/main/java/fr/xephi/authme/listener/SyncSingleSessionListener.java b/src/main/java/fr/xephi/authme/listener/SyncSingleSessionListener.java new file mode 100644 index 000000000..57f78ff64 --- /dev/null +++ b/src/main/java/fr/xephi/authme/listener/SyncSingleSessionListener.java @@ -0,0 +1,41 @@ +package fr.xephi.authme.listener; + +import javax.inject.Inject; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerPreLoginEvent; + +import fr.xephi.authme.output.Messages; + +/* + * This listener is registered only if the server is in offline-mode and the implementation is not Spigot. + * The reason is that only Spigot fires the Async version of this event on an offline-mode server! + */ +public class SyncSingleSessionListener implements Listener { + + @Inject + private Messages m; + @Inject + private OnJoinVerifier onJoinVerifier; + + // Note: the PlayerPreLoginEvent causes the login thread to synchronize with the main thread + @SuppressWarnings("deprecation") + @EventHandler(priority = EventPriority.LOWEST) + public void onSyncPreLogin(PlayerPreLoginEvent event) { + if(event.getResult() != PlayerPreLoginEvent.Result.ALLOWED) { + return; + } + + final String name = event.getName(); + + try { + onJoinVerifier.checkSingleSession(name); + } catch (FailedVerificationException e) { + event.setKickMessage(m.retrieveSingle(e.getReason(), e.getArgs())); + event.setResult(PlayerPreLoginEvent.Result.KICK_OTHER); + 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 9de55223b..ebbcbc995 100644 --- a/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java @@ -7,7 +7,7 @@ import fr.xephi.authme.cache.limbo.PlayerData; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.events.LoginEvent; import fr.xephi.authme.events.RestoreInventoryEvent; -import fr.xephi.authme.listener.AuthMePlayerListener; +import fr.xephi.authme.listener.PlayerListener; import fr.xephi.authme.process.ProcessService; import fr.xephi.authme.process.SynchronousProcess; import fr.xephi.authme.service.BungeeService; @@ -100,7 +100,7 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess { teleportationService.teleportOnLogin(player, auth, limbo); // We can now display the join message (if delayed) - String jm = AuthMePlayerListener.joinMessage.get(name); + String jm = PlayerListener.joinMessage.get(name); if (jm != null) { if (!jm.isEmpty()) { for (Player p : bukkitService.getOnlinePlayers()) { @@ -109,7 +109,7 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess { } } } - AuthMePlayerListener.joinMessage.remove(name); + PlayerListener.joinMessage.remove(name); } if (service.getProperty(RegistrationSettings.APPLY_BLIND_EFFECT)) { diff --git a/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java b/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java index 9c0036f90..fe4c80176 100644 --- a/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java +++ b/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java @@ -7,7 +7,7 @@ import fr.xephi.authme.api.NewAPI; import fr.xephi.authme.command.CommandHandler; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.initialization.DataFolder; -import fr.xephi.authme.listener.AuthMeBlockListener; +import fr.xephi.authme.listener.BlockListener; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.process.Management; import fr.xephi.authme.process.login.ProcessSyncPlayerLogin; @@ -113,7 +113,7 @@ public class AuthMeInitializationTest { // then // Take a few samples and ensure that they are not null - assertThat(injector.getIfAvailable(AuthMeBlockListener.class), not(nullValue())); + assertThat(injector.getIfAvailable(BlockListener.class), not(nullValue())); assertThat(injector.getIfAvailable(CommandHandler.class), not(nullValue())); assertThat(injector.getIfAvailable(Management.class), not(nullValue())); assertThat(injector.getIfAvailable(NewAPI.class), not(nullValue())); diff --git a/src/test/java/fr/xephi/authme/listener/AuthMeBlockListenerTest.java b/src/test/java/fr/xephi/authme/listener/AuthMeBlockListenerTest.java index e96a30223..91147cdeb 100644 --- a/src/test/java/fr/xephi/authme/listener/AuthMeBlockListenerTest.java +++ b/src/test/java/fr/xephi/authme/listener/AuthMeBlockListenerTest.java @@ -9,19 +9,22 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import fr.xephi.authme.listener.BlockListener; +import fr.xephi.authme.listener.ListenerService; + import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; /** - * Test for {@link AuthMeBlockListener}. + * Test for {@link BlockListener}. */ @RunWith(MockitoJUnitRunner.class) public class AuthMeBlockListenerTest { @InjectMocks - private AuthMeBlockListener listener; + private BlockListener listener; @Mock private ListenerService listenerService; diff --git a/src/test/java/fr/xephi/authme/listener/ListenerConsistencyTest.java b/src/test/java/fr/xephi/authme/listener/ListenerConsistencyTest.java index 39b7b8d0b..e81b42a05 100644 --- a/src/test/java/fr/xephi/authme/listener/ListenerConsistencyTest.java +++ b/src/test/java/fr/xephi/authme/listener/ListenerConsistencyTest.java @@ -1,6 +1,14 @@ package fr.xephi.authme.listener; import com.google.common.collect.Sets; + +import fr.xephi.authme.listener.BlockListener; +import fr.xephi.authme.listener.EntityListener; +import fr.xephi.authme.listener.PlayerListener; +import fr.xephi.authme.listener.PlayerListener16; +import fr.xephi.authme.listener.PlayerListener18; +import fr.xephi.authme.listener.ServerListener; + import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.junit.Test; @@ -20,9 +28,9 @@ import static org.junit.Assert.fail; */ public final class ListenerConsistencyTest { - private static final Class[] LISTENERS = { AuthMeBlockListener.class, AuthMeEntityListener.class, - AuthMePlayerListener.class, AuthMePlayerListener16.class, AuthMePlayerListener18.class, - AuthMeServerListener.class }; + private static final Class[] LISTENERS = { BlockListener.class, EntityListener.class, + PlayerListener.class, PlayerListener16.class, PlayerListener18.class, + ServerListener.class }; private static final Set CANCELED_EXCEPTIONS = Sets.newHashSet("AuthMePlayerListener#onPlayerJoin", "AuthMePlayerListener#onPreLogin", "AuthMePlayerListener#onPlayerLogin", diff --git a/src/test/java/fr/xephi/authme/listener/ListenerServiceTest.java b/src/test/java/fr/xephi/authme/listener/ListenerServiceTest.java index 172768582..01805a347 100644 --- a/src/test/java/fr/xephi/authme/listener/ListenerServiceTest.java +++ b/src/test/java/fr/xephi/authme/listener/ListenerServiceTest.java @@ -6,6 +6,7 @@ import ch.jalu.injector.testing.InjectDelayed; import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.hooks.PluginHooks; +import fr.xephi.authme.listener.ListenerService; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.util.ValidationService; diff --git a/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java b/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java index f2e9ee9eb..3861b4dff 100644 --- a/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java +++ b/src/test/java/fr/xephi/authme/listener/OnJoinVerifierTest.java @@ -4,6 +4,8 @@ import fr.xephi.authme.AntiBot; import fr.xephi.authme.TestHelper; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.listener.FailedVerificationException; +import fr.xephi.authme.listener.OnJoinVerifier; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.Messages; import fr.xephi.authme.permission.PermissionsManager; diff --git a/src/test/java/tools/utils/ServerUtils.java b/src/test/java/tools/utils/ServerUtils.java new file mode 100644 index 000000000..aa78eef44 --- /dev/null +++ b/src/test/java/tools/utils/ServerUtils.java @@ -0,0 +1,18 @@ +package tools.utils; + +public class ServerUtils { + + /** + * Check if the server implementation is based on Spigot + * + * @return true if the implementation is based on Spigot + */ + public static boolean isSpigot() { + try { + Class.forName("org.spigotmc.CustomTimingsHandler"); + return true; + } catch (ClassNotFoundException ignored) { + } + return false; + } +}