This commit is contained in:
Andrea Cavalli 2024-02-25 04:22:45 -05:00 committed by GitHub
commit c138d37f4f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
49 changed files with 1168 additions and 239 deletions

View File

@ -10,7 +10,7 @@ jobs:
build_and_test:
strategy:
matrix:
jdkversion: [11]
jdkversion: [17]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

18
pom.xml
View File

@ -67,7 +67,7 @@
<maven.minimumVersion>3.6.3</maven.minimumVersion>
<!-- Dependencies versions -->
<spigot.version>1.19.2-R0.1-SNAPSHOT</spigot.version>
<spigot.version>1.19.4-R0.1-SNAPSHOT</spigot.version>
<!-- Versioning properties -->
<project.outputName>AuthMe</project.outputName>
@ -514,12 +514,11 @@
</snapshots>
</repository>
<!-- SpigotAPI Repo -->
<repository>
<id>spigotmc-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots</url>
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
<releases>
<enabled>false</enabled>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
@ -712,10 +711,9 @@
<optional>true</optional>
</dependency>
<!-- Spigot API, https://www.spigotmc.org/ -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<groupId>dev.folia</groupId>
<artifactId>folia-api</artifactId>
<version>${spigot.version}</version>
<scope>provided</scope>
<exclusions>
@ -723,10 +721,6 @@
<artifactId>junit</artifactId>
<groupId>junit</groupId>
</exclusion>
<exclusion>
<artifactId>bungeecord-chat</artifactId>
<groupId>net.md-5</groupId>
</exclusion>
<exclusion>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>

View File

@ -6,12 +6,12 @@ import com.google.common.annotations.VisibleForTesting;
import fr.xephi.authme.api.v3.AuthMeApi;
import fr.xephi.authme.command.CommandHandler;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.BukkitServiceProvider;
import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.initialization.DataSourceProvider;
import fr.xephi.authme.initialization.OnShutdownPlayerSaver;
import fr.xephi.authme.initialization.OnStartupTasks;
import fr.xephi.authme.initialization.SettingsProvider;
import fr.xephi.authme.initialization.TaskCloser;
import fr.xephi.authme.listener.BlockListener;
import fr.xephi.authme.listener.EntityListener;
import fr.xephi.authme.listener.PlayerListener;
@ -39,12 +39,11 @@ import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.plugin.java.JavaPluginLoader;
import org.bukkit.scheduler.BukkitScheduler;
import java.io.File;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import static fr.xephi.authme.service.BukkitService.TICKS_PER_MINUTE;
import static fr.xephi.authme.util.Utils.isClassLoaded;
/**
@ -55,7 +54,7 @@ public class AuthMe extends JavaPlugin {
// Constants
private static final String PLUGIN_NAME = "AuthMeReloaded";
private static final String LOG_FILENAME = "authme.log";
private static final int CLEANUP_INTERVAL = 5 * TICKS_PER_MINUTE;
private static final int CLEANUP_INTERVAL_MINUTES = 5;
// Version and build number values
private static String pluginVersion = "N/D";
@ -161,7 +160,7 @@ public class AuthMe extends JavaPlugin {
// Schedule clean up task
CleanupTask cleanupTask = injector.getSingleton(CleanupTask.class);
cleanupTask.runTaskTimerAsynchronously(this, CLEANUP_INTERVAL, CLEANUP_INTERVAL);
bukkitService.runOnAsyncSchedulerAtFixedRate(cleanupTask, CLEANUP_INTERVAL_MINUTES, CLEANUP_INTERVAL_MINUTES, TimeUnit.MINUTES);
// Do a backup on start
backupService.doBackup(BackupService.BackupCause.START);
@ -207,7 +206,7 @@ public class AuthMe extends JavaPlugin {
injector.register(AuthMe.class, this);
injector.register(Server.class, getServer());
injector.register(PluginManager.class, getServer().getPluginManager());
injector.register(BukkitScheduler.class, getServer().getScheduler());
injector.registerProvider(BukkitService.class, BukkitServiceProvider.class);
injector.provide(DataFolder.class, getDataFolder());
injector.registerProvider(Settings.class, SettingsProvider.class);
injector.registerProvider(DataSource.class, DataSourceProvider.class);
@ -313,8 +312,13 @@ public class AuthMe extends JavaPlugin {
backupService.doBackup(BackupService.BackupCause.STOP);
}
// Wait for tasks and close data source
new TaskCloser(this, database).run();
// Wait for tasks
bukkitService.waitAllTasks();
// Close data source
if (database != null) {
database.closeConnection();
}
// Disabled correctly
Consumer<String> infoLogMethod = logger == null ? getLogger()::info : logger::info;

View File

@ -33,7 +33,7 @@ public class AccountsCommand implements ExecutableCommand {
// Assumption: a player name cannot contain '.'
if (playerName.contains(".")) {
bukkitService.runTaskAsynchronously(() -> {
bukkitService.runOnAsyncSchedulerNow(task -> {
List<String> accountList = dataSource.getAllAuthsByIp(playerName);
if (accountList.isEmpty()) {
sender.sendMessage("[AuthMe] This IP does not exist in the database.");
@ -44,7 +44,7 @@ public class AccountsCommand implements ExecutableCommand {
}
});
} else {
bukkitService.runTaskAsynchronously(() -> {
bukkitService.runOnAsyncSchedulerNow(task -> {
PlayerAuth auth = dataSource.getAuth(playerName.toLowerCase(Locale.ROOT));
if (auth == null) {
commonService.send(sender, MessageKey.UNKNOWN_USER);

View File

@ -56,7 +56,7 @@ public class ConverterCommand implements ExecutableCommand {
final Converter converter = converterFactory.newInstance(converterClass);
// Run the convert job
bukkitService.runTaskAsynchronously(() -> {
bukkitService.runOnAsyncSchedulerNow(task -> {
try {
converter.execute(sender);
} catch (Exception e) {

View File

@ -1,6 +1,7 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.settings.SpawnLoader;
import org.bukkit.entity.Player;
@ -12,6 +13,8 @@ import java.util.List;
*/
public class FirstSpawnCommand extends PlayerCommand {
@Inject
private BukkitService bukkitService;
@Inject
private SpawnLoader spawnLoader;
@ -20,7 +23,8 @@ public class FirstSpawnCommand extends PlayerCommand {
if (spawnLoader.getFirstSpawn() == null) {
player.sendMessage("[AuthMe] First spawn has failed, please try to define the first spawn");
} else {
player.teleport(spawnLoader.getFirstSpawn());
bukkitService.executeOptionallyOnEntityScheduler(player,
() -> bukkitService.teleport(player, spawnLoader.getFirstSpawn()), null);
}
}
}

View File

@ -30,8 +30,7 @@ public class PurgePlayerCommand implements ExecutableCommand {
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
String option = arguments.size() > 1 ? arguments.get(1) : null;
bukkitService.runTaskAsynchronously(
() -> executeCommand(sender, arguments.get(0), option));
bukkitService.runOnAsyncSchedulerNow(task -> executeCommand(sender, arguments.get(0), option));
}
private void executeCommand(CommandSender sender, String name, String option) {

View File

@ -77,8 +77,9 @@ public class RegisterAdminCommand implements ExecutableCommand {
logger.info(sender.getName() + " registered " + playerName);
final Player player = bukkitService.getPlayerExact(playerName);
if (player != null) {
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() ->
player.kickPlayer(commonService.retrieveSingleMessage(player, MessageKey.KICK_FOR_ADMIN_REGISTER)));
bukkitService.executeOptionallyOnEntityScheduler(player, () ->
player.kickPlayer(commonService.retrieveSingleMessage(player, MessageKey.KICK_FOR_ADMIN_REGISTER))
, () -> logger.info("Can't kick player " + playerName + " because it's not available"));
}
});
}

View File

@ -1,6 +1,7 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.settings.SpawnLoader;
import org.bukkit.entity.Player;
@ -9,6 +10,8 @@ import java.util.List;
public class SpawnCommand extends PlayerCommand {
@Inject
private BukkitService bukkitService;
@Inject
private SpawnLoader spawnLoader;
@ -17,7 +20,8 @@ public class SpawnCommand extends PlayerCommand {
if (spawnLoader.getSpawn() == null) {
player.sendMessage("[AuthMe] Spawn has failed, please try to define the spawn");
} else {
player.teleport(spawnLoader.getSpawn());
bukkitService.executeOptionallyOnEntityScheduler(player,
() -> bukkitService.teleport(player, spawnLoader.getSpawn()), null);
}
}
}

View File

@ -73,7 +73,7 @@ public class RecoverEmailCommand extends PlayerCommand {
return;
}
bukkitService.runTaskAsynchronously(() -> {
bukkitService.runOnAsyncSchedulerNow(task -> {
if (recoveryCodeService.isRecoveryCodeNeeded()) {
// Recovery code is needed; generate and send one
recoveryService.createAndSendRecoveryCode(player, email);

View File

@ -103,7 +103,7 @@ public class TempbanManager implements SettingsDependent, HasCleanup {
long newTime = expires.getTime() + (length * MILLIS_PER_MINUTE);
expires.setTime(newTime);
bukkitService.scheduleSyncDelayedTask(() -> {
bukkitService.runOnGlobalRegionScheduler(task -> {
if (customCommand.isEmpty()) {
bukkitService.banIp(ip, reason, expires, "AuthMe");
player.kickPlayer(reason);

View File

@ -1,8 +1,8 @@
package fr.xephi.authme.data.limbo;
import fr.xephi.authme.task.CancellableTask;
import fr.xephi.authme.task.MessageTask;
import org.bukkit.Location;
import org.bukkit.scheduler.BukkitTask;
import java.util.ArrayList;
import java.util.Collection;
@ -22,8 +22,9 @@ public class LimboPlayer {
private final Location loc;
private final float walkSpeed;
private final float flySpeed;
private BukkitTask timeoutTask = null;
private CancellableTask timeoutTask = null;
private MessageTask messageTask = null;
private CancellableTask messageCancellableTask = null;
private LimboPlayerState state = LimboPlayerState.PASSWORD_REQUIRED;
public LimboPlayer(Location loc, boolean operator, Collection<UserGroup> groups, boolean fly, float walkSpeed,
@ -81,7 +82,7 @@ public class LimboPlayer {
*
* @return The timeout task associated to the player
*/
public BukkitTask getTimeoutTask() {
public CancellableTask getTimeoutTask() {
return timeoutTask;
}
@ -91,7 +92,7 @@ public class LimboPlayer {
*
* @param timeoutTask The task to set
*/
public void setTimeoutTask(BukkitTask timeoutTask) {
public void setTimeoutTask(CancellableTask timeoutTask) {
if (this.timeoutTask != null) {
this.timeoutTask.cancel();
}
@ -110,20 +111,22 @@ public class LimboPlayer {
/**
* Set the messages task responsible for telling the player to log in or register.
*
* @param messageTask The message task to set
* @param messageTask The message task to set
* @param messageCancellableTask The related cancellable task
*/
public void setMessageTask(MessageTask messageTask) {
if (this.messageTask != null) {
this.messageTask.cancel();
public void setMessageTask(MessageTask messageTask, CancellableTask messageCancellableTask) {
if (this.messageCancellableTask != null) {
this.messageCancellableTask.cancel();
}
this.messageTask = messageTask;
this.messageCancellableTask = messageCancellableTask;
}
/**
* Clears all tasks associated to the player.
*/
public void clearTasks() {
setMessageTask(null);
setMessageTask(null, messageCancellableTask);
setTimeoutTask(null);
}

View File

@ -8,13 +8,17 @@ import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.task.CancellableTask;
import fr.xephi.authme.task.MessageTask;
import fr.xephi.authme.task.TimeoutTask;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.NotNull;
import javax.inject.Inject;
import java.util.concurrent.TimeUnit;
import static fr.xephi.authme.service.BukkitService.TICKS_PER_SECOND;
/**
@ -53,8 +57,9 @@ class LimboPlayerTaskManager {
if (interval > 0) {
String[] joinMessage = messages.retrieveSingle(player, result.messageKey, result.args).split("\n");
MessageTask messageTask = new MessageTask(player, joinMessage);
bukkitService.runTaskTimer(messageTask, 2 * TICKS_PER_SECOND, interval * TICKS_PER_SECOND);
limbo.setMessageTask(messageTask);
CancellableTask messageCancellableTask
= bukkitService.runOnAsyncSchedulerAtFixedRate(messageTask, 2, interval, TimeUnit.SECONDS);
limbo.setMessageTask(messageTask, messageCancellableTask);
}
}
@ -68,7 +73,7 @@ class LimboPlayerTaskManager {
final int timeout = settings.getProperty(RestrictionSettings.TIMEOUT) * TICKS_PER_SECOND;
if (timeout > 0) {
String message = messages.retrieveSingle(player, MessageKey.LOGIN_TIMEOUT_ERROR);
BukkitTask task = bukkitService.runTaskLater(new TimeoutTask(player, message, playerCache), timeout);
CancellableTask task = bukkitService.runOnGlobalRegionSchedulerDelayed(t -> new TimeoutTask(player, message, playerCache), timeout);
limbo.setTimeoutTask(task);
}
}

View File

@ -0,0 +1,40 @@
package fr.xephi.authme.initialization;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.service.FoliaBukkitService;
import fr.xephi.authme.service.PaperBukkitService;
import fr.xephi.authme.service.SpigotBukkitService;
import fr.xephi.authme.settings.Settings;
import javax.inject.Inject;
import javax.inject.Provider;
/**
* Creates the AuthMe bukkit service provider.
*/
public class BukkitServiceProvider implements Provider<BukkitService> {
@Inject
private AuthMe authMe;
@Inject
private Settings settings;
BukkitServiceProvider() {
}
@Override
public BukkitService get() {
try {
Class.forName("io.papermc.paper.threadedregions.scheduler.AsyncScheduler");
return new FoliaBukkitService(authMe, settings);
} catch (ClassNotFoundException ignored) {
try {
Class.forName("io.papermc.paper.util.Tick");
return new PaperBukkitService(authMe, settings);
} catch (ClassNotFoundException ignored2) {
return new SpigotBukkitService(authMe, settings);
}
}
}
}

View File

@ -90,7 +90,7 @@ public class DataSourceProvider implements Provider<DataSource> {
}
private void checkDataSourceSize(DataSource dataSource) {
bukkitService.runTaskAsynchronously(() -> {
bukkitService.runOnAsyncSchedulerNow(task -> {
int accounts = dataSource.getAccountsRegistered();
if (accounts >= SQLITE_MAX_SIZE) {
logger.warning("YOU'RE USING THE SQLITE DATABASE WITH "

View File

@ -22,8 +22,10 @@ import org.bukkit.scheduler.BukkitRunnable;
import javax.inject.Inject;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import static fr.xephi.authme.service.BukkitService.MS_PER_TICK;
import static fr.xephi.authme.service.BukkitService.TICKS_PER_MINUTE;
import static fr.xephi.authme.settings.properties.EmailSettings.RECALL_PLAYERS;
@ -96,19 +98,16 @@ public class OnStartupTasks {
if (!settings.getProperty(RECALL_PLAYERS)) {
return;
}
bukkitService.runTaskTimerAsynchronously(new BukkitRunnable() {
@Override
public void run() {
List<String> loggedPlayersWithEmptyMail = dataSource.getLoggedPlayersWithEmptyMail();
bukkitService.runTask(() -> {
for (String playerWithoutMail : loggedPlayersWithEmptyMail) {
Player player = bukkitService.getPlayerExact(playerWithoutMail);
if (player != null) {
messages.send(player, MessageKey.ADD_EMAIL_MESSAGE);
}
bukkitService.runOnAsyncSchedulerAtFixedRate(task -> {
List<String> loggedPlayersWithEmptyMail = dataSource.getLoggedPlayersWithEmptyMail();
bukkitService.executeOnGlobalRegionScheduler(() -> {
for (String playerWithoutMail : loggedPlayersWithEmptyMail) {
Player player = bukkitService.getPlayerExact(playerWithoutMail);
if (player != null) {
messages.send(player, MessageKey.ADD_EMAIL_MESSAGE);
}
});
}
}, 1, TICKS_PER_MINUTE * settings.getProperty(EmailSettings.DELAY_RECALL));
}
});
}, MS_PER_TICK, TimeUnit.MINUTES.toMillis(settings.getProperty(EmailSettings.DELAY_RECALL)), TimeUnit.MILLISECONDS);
}
}

View File

@ -21,19 +21,16 @@ public class TaskCloser implements Runnable {
private final BukkitScheduler scheduler;
private final Logger logger;
private final AuthMe plugin;
private final DataSource dataSource;
/**
* Constructor.
*
* @param plugin the plugin instance
* @param dataSource the data source (nullable)
*/
public TaskCloser(AuthMe plugin, DataSource dataSource) {
public TaskCloser(AuthMe plugin) {
this.scheduler = plugin.getServer().getScheduler();
this.logger = plugin.getLogger();
this.plugin = plugin;
this.dataSource = dataSource;
}
@Override
@ -68,10 +65,6 @@ public class TaskCloser implements Runnable {
tries--;
}
if (dataSource != null) {
dataSource.closeConnection();
}
}
/** Makes the current thread sleep for one second. */

View File

@ -357,9 +357,9 @@ public class PlayerListener implements Listener {
Location spawn = spawnLoader.getSpawnLocation(player);
if (spawn != null && spawn.getWorld() != null) {
if (!player.getWorld().equals(spawn.getWorld())) {
player.teleport(spawn);
bukkitService.teleport(player, spawn);
} else if (spawn.distance(player.getLocation()) > settings.getProperty(ALLOWED_MOVEMENT_RADIUS)) {
player.teleport(spawn);
bukkitService.teleport(player, spawn);
}
}
}
@ -498,7 +498,7 @@ public class PlayerListener implements Listener {
* @note little hack cause InventoryOpenEvent cannot be cancelled for
* real, cause no packet is sent to server by client for the main inv
*/
bukkitService.scheduleSyncDelayedTask(player::closeInventory, 1);
bukkitService.runOnEntitySchedulerDelayed(player, task -> player.closeInventory(), null, 1);
}
}

View File

@ -1,11 +1,14 @@
package fr.xephi.authme.process;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.process.login.ProcessSyncPlayerLogin;
import fr.xephi.authme.process.logout.ProcessSyncPlayerLogout;
import fr.xephi.authme.process.quit.ProcessSyncPlayerQuit;
import fr.xephi.authme.process.register.ProcessSyncEmailRegister;
import fr.xephi.authme.process.register.ProcessSyncPasswordRegister;
import fr.xephi.authme.service.BukkitService;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import javax.inject.Inject;
@ -21,6 +24,9 @@ import java.util.List;
*/
public class SyncProcessManager {
private final ConsoleLogger logger = ConsoleLoggerFactory.get(SyncProcessManager.class);
@Inject
private BukkitService bukkitService;
@ -37,26 +43,39 @@ public class SyncProcessManager {
public void processSyncEmailRegister(Player player) {
runTask(() -> processSyncEmailRegister.processEmailRegister(player));
runTask("EmailRegister", player, () -> processSyncEmailRegister.processEmailRegister(player));
}
public void processSyncPasswordRegister(Player player) {
runTask(() -> processSyncPasswordRegister.processPasswordRegister(player));
runTask("PasswordRegister", player, () -> processSyncPasswordRegister.processPasswordRegister(player));
}
public void processSyncPlayerLogout(Player player) {
runTask(() -> processSyncPlayerLogout.processSyncLogout(player));
runTask("PlayerLogout", player, () -> processSyncPlayerLogout.processSyncLogout(player));
}
public void processSyncPlayerLogin(Player player, boolean isFirstLogin, List<String> authsWithSameIp) {
runTask(() -> processSyncPlayerLogin.processPlayerLogin(player, isFirstLogin, authsWithSameIp));
runTask("PlayerLogin", player, () -> processSyncPlayerLogin.processPlayerLogin(player, isFirstLogin, authsWithSameIp));
}
public void processSyncPlayerQuit(Player player, boolean wasLoggedIn) {
runTask(() -> processSyncPlayerQuit.processSyncQuit(player, wasLoggedIn));
runTask("PlayerQuit", null, () -> processSyncPlayerQuit.processSyncQuit(player, wasLoggedIn));
}
private void runTask(Runnable runnable) {
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(runnable);
private void runTask(String taskName, Entity entity, Runnable runnable) {
if (entity == null) {
bukkitService.runOnGlobalRegionScheduler(task -> runnable.run());
} else {
bukkitService.executeOptionallyOnEntityScheduler(entity, runnable, () -> {
String entityName;
try {
entityName = entity.getName();
} catch (Exception ex) {
entityName = "<none>";
}
// todo: should the tasks be executed anyway or not? I left this warning message to remind about this doubt.
logger.warning("Task " + taskName + " has not been executed because the entity " + entityName + " is not available anymore.");
});
}
}
}

View File

@ -107,7 +107,8 @@ public class AsynchronousJoin implements AsynchronousProcess {
if (service.getProperty(RestrictionSettings.FORCE_SURVIVAL_MODE)
&& player.getGameMode() != GameMode.SURVIVAL
&& !service.hasPermission(player, PlayerStatePermission.BYPASS_FORCE_SURVIVAL)) {
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> player.setGameMode(GameMode.SURVIVAL));
bukkitService.executeOptionallyOnEntityScheduler(player, () -> player.setGameMode(GameMode.SURVIVAL),
() -> logger.info("Can't set gamemode of player " + player.getName() + " because it's not available"));
}
if (service.getProperty(HooksSettings.DISABLE_SOCIAL_SPY)) {
@ -135,32 +136,38 @@ public class AsynchronousJoin implements AsynchronousProcess {
if (sessionService.canResumeSession(player)) {
service.send(player, MessageKey.SESSION_RECONNECTION);
// Run commands
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(
() -> commandManager.runCommandsOnSessionLogin(player));
bukkitService.executeOptionallyOnEntityScheduler(player, () -> commandManager.runCommandsOnSessionLogin(player),
() -> logger.info("Can't run commands on session login for player "
+ name + " because the player is currently unavailable"));
bukkitService.runTaskOptionallyAsync(() -> asynchronousLogin.forceLogin(player));
return;
} else if (proxySessionManager.shouldResumeSession(name)) {
service.send(player, MessageKey.SESSION_RECONNECTION);
// Run commands
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(
() -> commandManager.runCommandsOnSessionLogin(player));
bukkitService.executeOptionallyOnEntityScheduler(player,
() -> commandManager.runCommandsOnSessionLogin(player),
() -> logger.info("Can't run commands on session login for player "
+ name + " because the player is currently unavailable"));
bukkitService.runTaskOptionallyAsync(() -> asynchronousLogin.forceLogin(player));
logger.info("The user " + player.getName() + " has been automatically logged in, "
+ "as present in autologin queue.");
return;
}
} else if (!service.getProperty(RegistrationSettings.FORCE)) {
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> {
welcomeMessageConfiguration.sendWelcomeMessage(player);
});
bukkitService.executeOptionallyOnEntityScheduler(player,
() -> welcomeMessageConfiguration.sendWelcomeMessage(player),
() -> logger.info("Can't send welcome message for player "
+ name + " because the player is currently unavailable"));
// Skip if registration is optional
if (bungeeSender.isEnabled()) {
// As described at https://www.spigotmc.org/wiki/bukkit-bungee-plugin-messaging-channel/
// "Keep in mind that you can't send plugin messages directly after a player joins."
bukkitService.scheduleSyncDelayedTask(() ->
bungeeSender.sendAuthMeBungeecordMessage(player, MessageType.LOGIN), 5L);
bukkitService.runOnEntitySchedulerDelayed(player, task ->
bungeeSender.sendAuthMeBungeecordMessage(player, MessageType.LOGIN),
() -> logger.info("Can't send authme bungeecord message to player "
+ name + " because the player is currently not available"), 5L);
}
return;
}
@ -169,8 +176,10 @@ public class AsynchronousJoin implements AsynchronousProcess {
}
private void handlePlayerWithUnmetNameRestriction(Player player, String ip) {
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> {
player.kickPlayer(service.retrieveSingleMessage(player, MessageKey.NOT_OWNER_ERROR));
bukkitService.executeOptionallyOnEntityScheduler(player,
() -> player.kickPlayer(service.retrieveSingleMessage(player, MessageKey.NOT_OWNER_ERROR)),
() -> logger.info("Can't kick player " + player + " with ip " + ip + " because the player is currently unavailable"));
bukkitService.executeOptionallyOnGlobalRegionScheduler(() -> {
if (service.getProperty(RestrictionSettings.BAN_UNKNOWN_IP)) {
server.banIP(ip);
}
@ -187,7 +196,7 @@ public class AsynchronousJoin implements AsynchronousProcess {
private void processJoinSync(Player player, boolean isAuthAvailable) {
int registrationTimeout = service.getProperty(RestrictionSettings.TIMEOUT) * TICKS_PER_SECOND;
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> {
bukkitService.executeOptionallyOnEntityScheduler(player, () -> {
limboService.createLimboPlayer(player, isAuthAvailable);
player.setNoDamageTicks(registrationTimeout);
@ -200,7 +209,8 @@ public class AsynchronousJoin implements AsynchronousProcess {
player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, blindTimeOut, 2));
}
commandManager.runCommandsOnJoin(player);
});
}, () -> logger.info("Can't process unauthenticated player join because the player "
+ player.getName() + " is currently unavailable"));
}
/**
@ -218,8 +228,10 @@ public class AsynchronousJoin implements AsynchronousProcess {
&& !InternetProtocolUtils.isLoopbackAddress(ip)
&& countOnlinePlayersByIp(ip) > service.getProperty(RestrictionSettings.MAX_JOIN_PER_IP)) {
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(
() -> player.kickPlayer(service.retrieveSingleMessage(player, MessageKey.SAME_IP_ONLINE)));
bukkitService.executeOptionallyOnEntityScheduler(player,
() -> player.kickPlayer(service.retrieveSingleMessage(player, MessageKey.SAME_IP_ONLINE)),
() -> logger.info("Can't kick player "
+ player.getName() + " with ip " + ip + " because it's currently unavailable"));
return false;
}
return true;

View File

@ -243,8 +243,10 @@ public class AsynchronousLogin implements AsynchronousProcess {
if (tempbanManager.shouldTempban(ip)) {
tempbanManager.tempbanPlayer(player);
} else if (service.getProperty(RestrictionSettings.KICK_ON_WRONG_PASSWORD)) {
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(
() -> player.kickPlayer(service.retrieveSingleMessage(player, MessageKey.WRONG_PASSWORD)));
bukkitService.executeOptionallyOnEntityScheduler(player,
() -> player.kickPlayer(service.retrieveSingleMessage(player, MessageKey.WRONG_PASSWORD)),
() -> logger.info("Can't kick player "
+ player.getName() + " with ip " + ip + " because the player is currently unavailable"));
} else {
service.send(player, MessageKey.WRONG_PASSWORD);
@ -305,8 +307,10 @@ public class AsynchronousLogin implements AsynchronousProcess {
if (bungeeSender.isEnabled()) {
// As described at https://www.spigotmc.org/wiki/bukkit-bungee-plugin-messaging-channel/
// "Keep in mind that you can't send plugin messages directly after a player joins."
bukkitService.scheduleSyncDelayedTask(() ->
bungeeSender.sendAuthMeBungeecordMessage(player, MessageType.LOGIN), 5L);
bukkitService.runOnEntitySchedulerDelayed(player, task ->
bungeeSender.sendAuthMeBungeecordMessage(player, MessageType.LOGIN),
() -> logger.info("Can't send authme bungeecord message to player "
+ name + " because the player is currently not available"), 5L);
}
// As the scheduling executes the Task most likely after the current

View File

@ -89,9 +89,9 @@ abstract class AbstractPasswordRegisterExecutor<P extends AbstractPasswordRegist
final Player player = params.getPlayer();
if (performLoginAfterRegister(params)) {
if (commonService.getProperty(PluginSettings.USE_ASYNC_TASKS)) {
bukkitService.runTaskAsynchronously(() -> asynchronousLogin.forceLogin(player));
bukkitService.runOnAsyncSchedulerNow(task -> asynchronousLogin.forceLogin(player));
} else {
bukkitService.scheduleSyncDelayedTask(() -> asynchronousLogin.forceLogin(player), SYNC_LOGIN_DELAY);
bukkitService.runOnGlobalRegionSchedulerDelayed(task -> asynchronousLogin.forceLogin(player), SYNC_LOGIN_DELAY);
}
}
syncProcessManager.processSyncPasswordRegister(player);

View File

@ -127,16 +127,16 @@ public class AsynchronousUnregister implements AsynchronousProcess {
if (player == null || !player.isOnline()) {
return;
}
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() ->
commandManager.runCommandsOnUnregister(player));
bukkitService.executeOptionallyOnEntityScheduler(player, () -> commandManager.runCommandsOnUnregister(player),
() -> logger.info("Can't run commands on unregister because the player " + name + " is currently unavailable"));
if (service.getProperty(RegistrationSettings.FORCE)) {
teleportationService.teleportOnJoin(player);
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> {
bukkitService.executeOptionallyOnEntityScheduler(player, () -> {
limboService.createLimboPlayer(player, false);
applyBlindEffect(player);
});
}, () -> logger.info("Can't create limbo player, because the player " + name + " is currently unavailable"));
}
service.send(player, MessageKey.UNREGISTERED_SUCCESS);
}

View File

@ -7,8 +7,8 @@ import fr.xephi.authme.permission.AdminPermission;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.ProtectionSettings;
import fr.xephi.authme.task.CancellableTask;
import fr.xephi.authme.util.AtomicIntervalCounter;
import org.bukkit.scheduler.BukkitTask;
import javax.inject.Inject;
import java.util.Locale;
@ -32,7 +32,7 @@ public class AntiBotService implements SettingsDependent {
// Service status
private AntiBotStatus antiBotStatus;
private boolean startup;
private BukkitTask disableTask;
private CancellableTask disableTask;
private AtomicIntervalCounter flaggedCounter;
@Inject
@ -73,7 +73,7 @@ public class AntiBotService implements SettingsDependent {
// Delay the schedule on first start
if (startup) {
int delay = settings.getProperty(ProtectionSettings.ANTIBOT_DELAY);
bukkitService.scheduleSyncDelayedTask(enableTask, delay * TICKS_PER_SECOND);
bukkitService.runOnGlobalRegionSchedulerDelayed(task -> enableTask.run(), (long) delay * TICKS_PER_SECOND);
startup = false;
} else {
enableTask.run();
@ -91,9 +91,9 @@ public class AntiBotService implements SettingsDependent {
disableTask.cancel();
}
// Schedule auto-disable
disableTask = bukkitService.runTaskLater(this::stopProtection, duration * TICKS_PER_MINUTE);
disableTask = bukkitService.runOnGlobalRegionSchedulerDelayed(task -> this.stopProtection(), (long) duration * TICKS_PER_MINUTE);
antiBotStatus = AntiBotStatus.ACTIVE;
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> {
bukkitService.executeOptionallyOnGlobalRegionScheduler(() -> {
// Inform admins
bukkitService.getOnlinePlayers().stream()
.filter(player -> permissionsManager.hasPermission(player, AdminPermission.ANTIBOT_MESSAGES))

View File

@ -4,45 +4,374 @@ import fr.xephi.authme.AuthMe;
import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.PluginSettings;
import fr.xephi.authme.task.CancellableTask;
import org.bukkit.BanEntry;
import org.bukkit.BanList;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.inject.Inject;
import java.util.Collection;
import java.util.Date;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* Service for operations requiring the Bukkit API, such as for scheduling.
*/
public class BukkitService implements SettingsDependent {
/** Number of ticks per second in the Bukkit main thread. */
public abstract class BukkitService implements SettingsDependent {
/**
* Number of ticks per second in the Bukkit main thread.
*/
public static final int TICKS_PER_SECOND = 20;
/** Number of ticks per minute. */
/**
* Number of milliseconds per tick in the Bukkit main thread.
*/
public static final int MS_PER_TICK = 50;
/**
* Number of ticks per minute.
*/
public static final int TICKS_PER_MINUTE = 60 * TICKS_PER_SECOND;
private final AuthMe authMe;
protected final AuthMe authMe;
private boolean useAsyncTasks;
@Inject
BukkitService(AuthMe authMe, Settings settings) {
public BukkitService(AuthMe authMe, Settings settings) {
this.authMe = authMe;
reload(settings);
}
/**
* Schedules the specified task to be executed asynchronously immediately.
*
* @param task Specified task.
* @return The {@link CancellableTask} that represents the scheduled task.
*/
public abstract @NotNull CancellableTask runOnAsyncSchedulerNow(@NotNull Consumer<CancellableTask> task);
/**
* Schedules the specified task to be executed asynchronously after the time delay has passed.
*
* @param task Specified task.
* @param delay The time delay to pass before the task should be executed.
* @param unit The time unit for the time delay.
* @return The {@link CancellableTask} that represents the scheduled task.
*/
public abstract @NotNull CancellableTask runOnAsyncSchedulerDelayed(@NotNull Consumer<CancellableTask> task,
long delay,
@NotNull TimeUnit unit);
/**
* Schedules the specified task to be executed asynchronously after the initial delay has passed,
* and then periodically executed with the specified period.
*
* @param task Specified task.
* @param initialDelay The time delay to pass before the first execution of the task.
* @param period The time between task executions after the first execution of the task.
* @param unit The time unit for the initial delay and period.
* @return The {@link CancellableTask} that represents the scheduled task.
*/
public abstract @NotNull CancellableTask runOnAsyncSchedulerAtFixedRate(@NotNull Consumer<CancellableTask> task,
long initialDelay,
long period,
@NotNull TimeUnit unit);
/**
* Attempts to cancel all tasks scheduled by the specified plugin.
*/
public abstract void cancelTasksOnAsyncScheduler();
/**
* Schedules a task to be executed on the region which owns the location.
*
* @param world The world of the region that owns the task
* @param chunkX The chunk X coordinate of the region that owns the task
* @param chunkZ The chunk Z coordinate of the region that owns the task
* @param run The task to execute
*/
public abstract void executeOnRegionScheduler(@NotNull World world, int chunkX, int chunkZ, @NotNull Runnable run);
/**
* Returns whether the current thread is ticking a region and that the region being ticked
* owns the chunk at the specified world and chunk position.
* @param world Specified world.
* @param chunkX Specified x-coordinate of the chunk position.
* @param chunkZ Specified z-coordinate of the chunk position.
*
* @see #executeOptionallyOnRegionScheduler
*/
public abstract boolean isOwnedByCurrentRegion(@NotNull World world, int chunkX, int chunkZ);
/**
* Schedules a task to be executed on the scheduler that owns the location.
* It may run immediately.
* @param world The world of the region that owns the task
* @param chunkX The chunk X coordinate of the region that owns the task
* @param chunkZ The chunk Z coordinate of the region that owns the task
* @param run The task to execute
*/
public final void executeOptionallyOnRegionScheduler(@NotNull World world, int chunkX, int chunkZ, @NotNull Runnable run) {
if (isOwnedByCurrentRegion(world, chunkX, chunkZ)) {
run.run();
} else {
executeOnRegionScheduler(world, chunkX, chunkZ, run);
}
}
/**
* Schedules a task to be executed on the region which owns the location on the next tick.
*
* @param world The world of the region that owns the task
* @param chunkX The chunk X coordinate of the region that owns the task
* @param chunkZ The chunk Z coordinate of the region that owns the task
* @param task The task to execute
* @return The {@link CancellableTask} that represents the scheduled task.
*/
public abstract @NotNull CancellableTask runOnRegionScheduler(@NotNull World world,
int chunkX,
int chunkZ,
@NotNull Consumer<CancellableTask> task);
/**
* Schedules a task to be executed on the region which owns the location after the specified delay in ticks.
*
* @param world The world of the region that owns the task
* @param chunkX The chunk X coordinate of the region that owns the task
* @param chunkZ The chunk Z coordinate of the region that owns the task
* @param task The task to execute
* @param delayTicks The delay, in ticks.
* @return The {@link CancellableTask} that represents the scheduled task.
*/
public abstract @NotNull CancellableTask runOnRegionSchedulerDelayed(@NotNull World world,
int chunkX,
int chunkZ,
@NotNull Consumer<CancellableTask> task,
long delayTicks);
/**
* Schedules a repeating task to be executed on the region which owns the location after the initial delay with the
* specified period.
*
* @param world The world of the region that owns the task
* @param chunkX The chunk X coordinate of the region that owns the task
* @param chunkZ The chunk Z coordinate of the region that owns the task
* @param task The task to execute
* @param initialDelayTicks The initial delay, in ticks.
* @param periodTicks The period, in ticks.
* @return The {@link CancellableTask} that represents the scheduled task.
*/
public abstract @NotNull CancellableTask runOnRegionSchedulerAtFixedRate(@NotNull World world,
int chunkX,
int chunkZ,
@NotNull Consumer<CancellableTask> task,
long initialDelayTicks,
long periodTicks);
/**
* Schedules a task to be executed on the global region.
* @param run The task to execute
*/
public abstract void executeOnGlobalRegionScheduler(@NotNull Runnable run);
/**
* Returns whether the current thread is ticking the global region.
*
* @see #executeOptionallyOnGlobalRegionScheduler
*/
public abstract boolean isGlobalTickThread();
/**
* Schedules a task to be executed on the global region.
* It may run immediately.
* @param run The task to execute
*/
public final void executeOptionallyOnGlobalRegionScheduler(@NotNull Runnable run) {
if (isGlobalTickThread()) {
run.run();
} else {
executeOnGlobalRegionScheduler(run);
}
}
/**
* Schedules a task to be executed on the global region on the next tick.
* @param task The task to execute
* @return The {@link CancellableTask} that represents the scheduled task.
*/
public abstract @NotNull CancellableTask runOnGlobalRegionScheduler(@NotNull Consumer<CancellableTask> task);
/**
* Schedules a task to be executed on the global region after the specified delay in ticks.
* @param task The task to execute
* @param delayTicks The delay, in ticks.
* @return The {@link CancellableTask} that represents the scheduled task.
*/
public abstract @NotNull CancellableTask runOnGlobalRegionSchedulerDelayed(@NotNull Consumer<CancellableTask> task,
long delayTicks);
/**
* Schedules a repeating task to be executed on the global region after the initial delay with the
* specified period.
* @param task The task to execute
* @param initialDelayTicks The initial delay, in ticks.
* @param periodTicks The period, in ticks.
* @return The {@link CancellableTask} that represents the scheduled task.
*/
public abstract @NotNull CancellableTask runOnGlobalRegionSchedulerAtFixedRate(@NotNull Consumer<CancellableTask> task,
long initialDelayTicks,
long periodTicks);
/**
* Attempts to cancel all tasks scheduled by the specified plugin.
*/
public abstract void cancelTasksOnGlobalRegionScheduler();
/**
* Schedules a task with the given delay. If the task failed to schedule because the scheduler is retired (entity
* removed), then returns {@code false}. Otherwise, either the run callback will be invoked after the specified delay,
* or the retired callback will be invoked if the scheduler is retired.
* Note that the retired callback is invoked in critical code, so it should not attempt to remove the entity, remove
* other entities, load chunks, load worlds, modify ticket levels, etc.
*
* <p>
* It is guaranteed that the run and retired callback are invoked on the region which owns the entity.
* </p>
* @param entity The entity that owns the task
* @param run The callback to run after the specified delay, may not be null.
* @param retired Retire callback to run if the entity is retired before the run callback can be invoked, may be null.
* @param delay The delay in ticks before the run callback is invoked. Any value less-than 1 is treated as 1.
* @return {@code true} if the task was scheduled, which means that either the run function or the retired function
* will be invoked (but never both), or {@code false} indicating neither the run nor retired function will be invoked
* since the scheduler has been retired.
*/
public abstract boolean executeOnEntityScheduler(@NotNull Entity entity,
@NotNull Runnable run,
@Nullable Runnable retired,
long delay);
/**
* Returns whether the current thread is ticking a region and that the region being ticked
* owns the specified entity. Note that this function is the only appropriate method of checking
* for ownership of an entity, as retrieving the entity's location is undefined unless the entity is owned
* by the current region.
* @param entity Specified entity.
*
* @see #executeOptionallyOnEntityScheduler
*/
public abstract boolean isOwnedByCurrentRegion(@NotNull Entity entity);
/**
* Schedules a task to be executed on the entity scheduler.
* It may run immediately.
* @param run The task to execute
* @param retired Retire callback to run if the entity is retired before the run callback can be invoked, may be null.
* @return {@code true} if the task was scheduled, which means that either the run function or the retired function
* will be invoked (but never both), or {@code false} indicating neither the run nor retired function will be invoked
* since the scheduler has been retired.
*/
public final boolean executeOptionallyOnEntityScheduler(@NotNull Entity entity, @NotNull Runnable run, @Nullable Runnable retired) {
if (isOwnedByCurrentRegion(entity)) {
run.run();
return true;
} else {
return executeOnEntityScheduler(entity, run, retired, 0L);
}
}
/**
* Schedules a task to execute on the next tick. If the task failed to schedule because the scheduler is retired (entity
* removed), then returns {@code null}. Otherwise, either the task callback will be invoked after the specified delay,
* or the retired callback will be invoked if the scheduler is retired.
* Note that the retired callback is invoked in critical code, so it should not attempt to remove the entity, remove
* other entities, load chunks, load worlds, modify ticket levels, etc.
*
* <p>
* It is guaranteed that the task and retired callback are invoked on the region which owns the entity.
* </p>
* @param entity The entity that owns the task
* @param task The task to execute
* @param retired Retire callback to run if the entity is retired before the run callback can be invoked, may be null.
* @return The {@link CancellableTask} that represents the scheduled task, or {@code null} if the entity has been removed.
*/
public abstract @Nullable CancellableTask runOnEntityScheduler(@NotNull Entity entity,
@NotNull Consumer<CancellableTask> task,
@Nullable Runnable retired);
/**
* Schedules a task with the given delay. If the task failed to schedule because the scheduler is retired (entity
* removed), then returns {@code null}. Otherwise, either the task callback will be invoked after the specified delay,
* or the retired callback will be invoked if the scheduler is retired.
* Note that the retired callback is invoked in critical code, so it should not attempt to remove the entity, remove
* other entities, load chunks, load worlds, modify ticket levels, etc.
*
* <p>
* It is guaranteed that the task and retired callback are invoked on the region which owns the entity.
* </p>
* @param entity The entity that owns the task
* @param task The task to execute
* @param retired Retire callback to run if the entity is retired before the run callback can be invoked, may be null.
* @param delayTicks The delay, in ticks.
* @return The {@link CancellableTask} that represents the scheduled task, or {@code null} if the entity has been removed.
*/
public abstract @Nullable CancellableTask runOnEntitySchedulerDelayed(@NotNull Entity entity,
@NotNull Consumer<CancellableTask> task,
@Nullable Runnable retired,
long delayTicks);
/**
* Schedules a repeating task with the given delay and period. If the task failed to schedule because the scheduler
* is retired (entity removed), then returns {@code null}. Otherwise, either the task callback will be invoked after
* the specified delay, or the retired callback will be invoked if the scheduler is retired.
* Note that the retired callback is invoked in critical code, so it should not attempt to remove the entity, remove
* other entities, load chunks, load worlds, modify ticket levels, etc.
*
* <p>
* It is guaranteed that the task and retired callback are invoked on the region which owns the entity.
* </p>
* @param entity The entity that owns the task
* @param task The task to execute
* @param retired Retire callback to run if the entity is retired before the run callback can be invoked, may be null.
* @param initialDelayTicks The initial delay, in ticks.
* @param periodTicks The period, in ticks.
* @return The {@link CancellableTask} that represents the scheduled task, or {@code null} if the entity has been removed.
*/
public abstract @Nullable CancellableTask runOnEntitySchedulerAtFixedRate(@NotNull Entity entity,
@NotNull Consumer<CancellableTask> task,
@Nullable Runnable retired,
long initialDelayTicks,
long periodTicks);
public abstract void waitAllTasks();
/**
* Teleports this entity to the given location. If this entity is riding a
* vehicle, it will be dismounted prior to teleportation.
*
* @param location New location to teleport this entity to
*/
public abstract void teleport(Player player, Location location);
/**
* <b>Deprecated, use:
* <ul>
* <li>{@link #runOnAsyncSchedulerNow}</li>
* <li>{@link #runOnGlobalRegionScheduler}</li>
* <li>{@link #runOnEntityScheduler}</li>
* </ul>
*
* Schedules a once off task to occur as soon as possible.
* <p>
* This task will be executed by the main server thread.
@ -50,30 +379,47 @@ public class BukkitService implements SettingsDependent {
* @param task Task to be executed
* @return Task id number (-1 if scheduling failed)
*/
@Deprecated
public int scheduleSyncDelayedTask(Runnable task) {
return Bukkit.getScheduler().scheduleSyncDelayedTask(authMe, task);
}
/**
* <b>Deprecated, use:
* <ul>
* <li>{@link #runOnGlobalRegionSchedulerDelayed}</li>
* <li>{@link #runOnRegionSchedulerDelayed}</li>
* <li>{@link #runOnEntitySchedulerDelayed}</li>
* </ul>
*
* Schedules a once off task to occur after a delay.
* <p>
* This task will be executed by the main server thread.
*
* @param task Task to be executed
* @param task Task to be executed
* @param delay Delay in server ticks before executing task
* @return Task id number (-1 if scheduling failed)
*/
@Deprecated
public int scheduleSyncDelayedTask(Runnable task, long delay) {
return Bukkit.getScheduler().scheduleSyncDelayedTask(authMe, task, delay);
}
/**
* <b>Deprecated, use:
* <ul>
* <li>{@link #executeOnGlobalRegionScheduler}</li>
* <li>{@link #executeOnRegionScheduler}</li>
* <li>{@link #executeOnEntityScheduler}</li>
* </ul>
*
* Schedules a synchronous task if we are currently on a async thread; if not, it runs the task immediately.
* Use this when {@link #runTaskOptionallyAsync(Runnable) optionally asynchronous tasks} have to
* run something synchronously.
*
* @param task the task to be run
*/
@Deprecated
public void scheduleSyncTaskFromOptionallyAsyncTask(Runnable task) {
if (Bukkit.isPrimaryThread()) {
task.run();
@ -83,6 +429,13 @@ public class BukkitService implements SettingsDependent {
}
/**
* <b>Deprecated, use:
* <ul>
* <li>{@link #runOnGlobalRegionScheduler}</li>
* <li>{@link #runOnRegionScheduler}</li>
* <li>{@link #runOnEntityScheduler}</li>
* </ul>
*
* Returns a task that will run on the next server tick.
*
* @param task the task to be run
@ -90,20 +443,29 @@ public class BukkitService implements SettingsDependent {
* @throws IllegalArgumentException if plugin is null
* @throws IllegalArgumentException if task is null
*/
@Deprecated
public BukkitTask runTask(Runnable task) {
return Bukkit.getScheduler().runTask(authMe, task);
}
/**
* <b>Deprecated, use:
* <ul>
* <li>{@link #runOnGlobalRegionSchedulerDelayed}</li>
* <li>{@link #runOnRegionSchedulerDelayed}</li>
* <li>{@link #runOnEntitySchedulerDelayed}</li>
* </ul>
*
* Returns a task that will run after the specified number of server
* ticks.
*
* @param task the task to be run
* @param task the task to be run
* @param delay the ticks to wait before running the task
* @return a BukkitTask that contains the id number
* @throws IllegalArgumentException if plugin is null
* @throws IllegalArgumentException if task is null
*/
@Deprecated
public BukkitTask runTaskLater(Runnable task, long delay) {
return Bukkit.getScheduler().runTaskLater(authMe, task, delay);
}
@ -116,13 +478,18 @@ public class BukkitService implements SettingsDependent {
*/
public void runTaskOptionallyAsync(Runnable task) {
if (useAsyncTasks) {
runTaskAsynchronously(task);
runOnAsyncSchedulerNow(ignored -> task.run());
} else {
task.run();
}
}
/**
* <b>Deprecated, use:
* <ul>
* <li>{@link #runOnAsyncSchedulerNow}</li>
* </ul>
*
* <b>Asynchronous tasks should never access any API in Bukkit. Great care
* should be taken to assure the thread-safety of asynchronous tasks.</b>
* <p>
@ -133,40 +500,55 @@ public class BukkitService implements SettingsDependent {
* @throws IllegalArgumentException if plugin is null
* @throws IllegalArgumentException if task is null
*/
@Deprecated
public BukkitTask runTaskAsynchronously(Runnable task) {
return Bukkit.getScheduler().runTaskAsynchronously(authMe, task);
}
/**
* <b>Deprecated, use:
* <ul>
* <li>{@link #runOnAsyncSchedulerAtFixedRate}</li>
* </ul>
*
* <b>Asynchronous tasks should never access any API in Bukkit. Great care
* should be taken to assure the thread-safety of asynchronous tasks.</b>
* <p>
* Returns a task that will repeatedly run asynchronously until cancelled,
* starting after the specified number of server ticks.
*
* @param task the task to be run
* @param delay the ticks to wait before running the task for the first
* time
* @param task the task to be run
* @param delay the ticks to wait before running the task for the first
* time
* @param period the ticks to wait between runs
* @return a BukkitTask that contains the id number
* @throws IllegalArgumentException if task is null
* @throws IllegalStateException if this was already scheduled
* @throws IllegalStateException if this was already scheduled
*/
@Deprecated
public BukkitTask runTaskTimerAsynchronously(BukkitRunnable task, long delay, long period) {
return task.runTaskTimerAsynchronously(authMe, delay, period);
}
/**
* <b>Deprecated, use:
* <ul>
* <li>{@link #runOnGlobalRegionSchedulerAtFixedRate}</li>
* <li>{@link #runOnRegionSchedulerAtFixedRate}</li>
* <li>{@link #runOnEntitySchedulerAtFixedRate}</li>
* </ul>
*
* Schedules the given task to repeatedly run until cancelled, starting after the
* specified number of server ticks.
*
* @param task the task to schedule
* @param delay the ticks to wait before running the task
* @param task the task to schedule
* @param delay the ticks to wait before running the task
* @param period the ticks to wait between runs
* @return a BukkitTask that contains the id number
* @throws IllegalArgumentException if plugin is null
* @throws IllegalStateException if this was already scheduled
* @throws IllegalStateException if this was already scheduled
*/
@Deprecated
public BukkitTask runTaskTimer(BukkitRunnable task, long delay, long period) {
return task.runTaskTimer(authMe, delay, period);
}
@ -241,7 +623,7 @@ public class BukkitService implements SettingsDependent {
*
* @param event Event details
* @throws IllegalStateException Thrown when an asynchronous event is
* fired from synchronous code.
* fired from synchronous code.
*/
public void callEvent(Event event) {
Bukkit.getPluginManager().callEvent(event);
@ -252,7 +634,7 @@ public class BukkitService implements SettingsDependent {
*
* @param eventSupplier the event supplier: function taking a boolean specifying whether AuthMe is configured
* in async mode or not
* @param <E> the event type
* @param <E> the event type
* @return the event that was created and emitted
*/
public <E extends Event> E createAndCallEvent(Function<Boolean, E> eventSupplier) {
@ -274,7 +656,7 @@ public class BukkitService implements SettingsDependent {
/**
* Dispatches a command on this server, and executes it if found.
*
* @param sender the apparent sender of the command
* @param sender the apparent sender of the command
* @param commandLine the command + arguments. Example: <code>test abc 123</code>
* @return returns false if no target is found
*/
@ -301,7 +683,7 @@ public class BukkitService implements SettingsDependent {
* Send the specified bytes to bungeecord using the specified player connection.
*
* @param player the player
* @param bytes the message
* @param bytes the message
*/
public void sendBungeeMessage(Player player, byte[] bytes) {
player.sendPluginMessage(authMe, "BungeeCord", bytes);
@ -311,13 +693,13 @@ public class BukkitService implements SettingsDependent {
* Adds a ban to the list. If a previous ban exists, this will
* update the previous entry.
*
* @param ip the ip of the ban
* @param reason reason for the ban, null indicates implementation default
* @param ip the ip of the ban
* @param reason reason for the ban, null indicates implementation default
* @param expires date for the ban's expiration (unban), or null to imply
* forever
* @param source source of the ban, null indicates implementation default
* forever
* @param source source of the ban, null indicates implementation default
* @return the entry for the newly created ban, or the entry for the
* (updated) previous ban
* (updated) previous ban
*/
public BanEntry banIp(String ip, String reason, Date expires, String source) {
return Bukkit.getServer().getBanList(BanList.Type.IP).addBan(ip, reason, expires, source);

View File

@ -0,0 +1,177 @@
package fr.xephi.authme.service;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.task.CancellableTask;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.inject.Inject;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import static fr.xephi.authme.task.FoliaCancellableTask.mapTask;
import static fr.xephi.authme.task.FoliaCancellableTask.mapConsumer;
public class FoliaBukkitService extends BukkitService {
@Inject
public FoliaBukkitService(AuthMe authMe, Settings settings) {
super(authMe, settings);
}
@Override
public @NotNull CancellableTask runOnAsyncSchedulerNow(@NotNull Consumer<CancellableTask> task) {
return mapTask(Bukkit.getAsyncScheduler().runNow(authMe, mapConsumer(task)));
}
@Override
public @NotNull CancellableTask runOnAsyncSchedulerDelayed(@NotNull Consumer<CancellableTask> task,
long delay,
@NotNull TimeUnit unit) {
return mapTask(Bukkit.getAsyncScheduler().runDelayed(authMe, mapConsumer(task), delay, unit));
}
@Override
public @NotNull CancellableTask runOnAsyncSchedulerAtFixedRate(@NotNull Consumer<CancellableTask> task,
long initialDelay,
long period,
@NotNull TimeUnit unit) {
return mapTask(Bukkit.getAsyncScheduler()
.runAtFixedRate(authMe, mapConsumer(task), initialDelay, period, unit));
}
@Override
public void cancelTasksOnAsyncScheduler() {
Bukkit.getAsyncScheduler().cancelTasks(authMe);
}
@Override
public void executeOnRegionScheduler(@NotNull World world, int chunkX, int chunkZ, @NotNull Runnable run) {
Bukkit.getRegionScheduler().execute(authMe, world, chunkX, chunkZ, run);
}
@Override
public boolean isOwnedByCurrentRegion(@NotNull World world, int chunkX, int chunkZ) {
return Bukkit.isOwnedByCurrentRegion(world, chunkX, chunkZ);
}
@Override
public @NotNull CancellableTask runOnRegionScheduler(@NotNull World world,
int chunkX,
int chunkZ,
@NotNull Consumer<CancellableTask> task) {
return mapTask(Bukkit.getRegionScheduler().run(authMe, world, chunkX, chunkZ, mapConsumer(task)));
}
@Override
public @NotNull CancellableTask runOnRegionSchedulerDelayed(@NotNull World world,
int chunkX,
int chunkZ,
@NotNull Consumer<CancellableTask> task,
long delayTicks) {
return mapTask(Bukkit.getRegionScheduler()
.runDelayed(authMe, world, chunkX, chunkZ, mapConsumer(task), delayTicks));
}
@Override
public @NotNull CancellableTask runOnRegionSchedulerAtFixedRate(@NotNull World world,
int chunkX,
int chunkZ,
@NotNull Consumer<CancellableTask> task,
long initialDelayTicks,
long periodTicks) {
return mapTask(Bukkit.getRegionScheduler()
.runAtFixedRate(authMe, world, chunkX, chunkZ, mapConsumer(task), initialDelayTicks, periodTicks));
}
@Override
public void executeOnGlobalRegionScheduler(@NotNull Runnable run) {
Bukkit.getGlobalRegionScheduler().execute(authMe, run);
}
@Override
public boolean isGlobalTickThread() {
return Bukkit.isGlobalTickThread();
}
@Override
public @NotNull CancellableTask runOnGlobalRegionScheduler(@NotNull Consumer<CancellableTask> task) {
return mapTask(Bukkit.getGlobalRegionScheduler().run(authMe, mapConsumer(task)));
}
@Override
public @NotNull CancellableTask runOnGlobalRegionSchedulerDelayed(@NotNull Consumer<CancellableTask> task,
long delayTicks) {
return mapTask(Bukkit.getGlobalRegionScheduler().runDelayed(authMe, mapConsumer(task), delayTicks));
}
@Override
public @NotNull CancellableTask runOnGlobalRegionSchedulerAtFixedRate(@NotNull Consumer<CancellableTask> task,
long initialDelayTicks,
long periodTicks) {
return mapTask(Bukkit.getGlobalRegionScheduler()
.runAtFixedRate(authMe, mapConsumer(task), initialDelayTicks, periodTicks));
}
@Override
public void cancelTasksOnGlobalRegionScheduler() {
Bukkit.getGlobalRegionScheduler().cancelTasks(authMe);
}
@Override
public boolean executeOnEntityScheduler(@NotNull Entity entity,
@NotNull Runnable run,
@Nullable Runnable retired,
long delay) {
return entity.getScheduler().execute(authMe, run, retired, delay);
}
@Override
public boolean isOwnedByCurrentRegion(@NotNull Entity entity) {
return Bukkit.isOwnedByCurrentRegion(entity);
}
@Override
public @Nullable CancellableTask runOnEntityScheduler(@NotNull Entity entity,
@NotNull Consumer<CancellableTask> task,
@Nullable Runnable retired) {
return mapTask(entity.getScheduler().run(authMe, mapConsumer(task), retired));
}
@Override
public @Nullable CancellableTask runOnEntitySchedulerDelayed(@NotNull Entity entity,
@NotNull Consumer<CancellableTask> task,
@Nullable Runnable retired,
long delayTicks) {
return mapTask(entity.getScheduler().runDelayed(authMe, mapConsumer(task), retired, delayTicks));
}
@Override
public @Nullable CancellableTask runOnEntitySchedulerAtFixedRate(@NotNull Entity entity,
@NotNull Consumer<CancellableTask> task,
@Nullable Runnable retired,
long initialDelayTicks,
long periodTicks) {
return mapTask(entity.getScheduler()
.runAtFixedRate(authMe, mapConsumer(task), retired, initialDelayTicks, periodTicks));
}
@Override
public void waitAllTasks() {
// todo: implement
}
@Override
public void teleport(Player player, Location location) {
// player.teleportAsync(location);
// todo: remove the following line when https://github.com/PaperMC/Folia/issues/26 is fixed
player.getScheduler().run(authMe, task -> player.teleportAsync(location), null);
}
}

View File

@ -127,7 +127,7 @@ public class GeoIpService {
// File is outdated or doesn't exist - let's try to download the data file!
// use bukkit's cached threads
bukkitService.runTaskAsynchronously(this::updateDatabase);
bukkitService.runOnAsyncSchedulerNow(task -> this.updateDatabase());
return false;
}

View File

@ -0,0 +1,21 @@
package fr.xephi.authme.service;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.settings.Settings;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import javax.inject.Inject;
public class PaperBukkitService extends SpigotBukkitService {
@Inject
public PaperBukkitService(AuthMe authMe, Settings settings) {
super(authMe, settings);
}
@Override
public void teleport(Player player, Location location) {
player.teleportAsync(location);
}
}

View File

@ -0,0 +1,214 @@
package fr.xephi.authme.service;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.initialization.TaskCloser;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.task.BukkitCancellableTask;
import fr.xephi.authme.task.CancellableTask;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.inject.Inject;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import static fr.xephi.authme.task.BukkitCancellableTask.mapConsumer;
public class SpigotBukkitService extends BukkitService {
@Inject
public SpigotBukkitService(AuthMe authMe, Settings settings) {
super(authMe, settings);
}
@Override
public @NotNull CancellableTask runOnAsyncSchedulerNow(@NotNull Consumer<CancellableTask> task) {
DeferredCancellableTask result = new DeferredCancellableTask(task);
Bukkit.getScheduler().runTaskAsynchronously(authMe, result.getConsumer());
return result;
}
@Override
public @NotNull CancellableTask runOnAsyncSchedulerDelayed(@NotNull Consumer<CancellableTask> task, long delay, @NotNull TimeUnit unit) {
DeferredCancellableTask result = new DeferredCancellableTask(task);
Bukkit.getScheduler().runTaskLaterAsynchronously(authMe, result.getConsumer(), unit.toMillis(delay) / MS_PER_TICK);
return result;
}
@Override
public @NotNull CancellableTask runOnAsyncSchedulerAtFixedRate(@NotNull Consumer<CancellableTask> task, long initialDelay, long period, @NotNull TimeUnit unit) {
DeferredCancellableTask result = new DeferredCancellableTask(task);
Bukkit.getScheduler().runTaskTimerAsynchronously(authMe, result.getConsumer(), unit.toMillis(initialDelay) / MS_PER_TICK, unit.toMillis(period) / MS_PER_TICK);
return result;
}
@Override
public void cancelTasksOnAsyncScheduler() {
Bukkit.getScheduler().cancelTasks(authMe);
}
@Override
public void executeOnRegionScheduler(@NotNull World world, int chunkX, int chunkZ, @NotNull Runnable run) {
Bukkit.getScheduler().runTask(authMe, run);
}
@Override
public boolean isOwnedByCurrentRegion(@NotNull World world, int chunkX, int chunkZ) {
return Bukkit.isPrimaryThread();
}
@Override
public @NotNull CancellableTask runOnRegionScheduler(@NotNull World world, int chunkX, int chunkZ, @NotNull Consumer<CancellableTask> task) {
DeferredCancellableTask result = new DeferredCancellableTask(task);
Bukkit.getScheduler().runTask(authMe, mapConsumer(task));
return result;
}
@Override
public @NotNull CancellableTask runOnRegionSchedulerDelayed(@NotNull World world, int chunkX, int chunkZ, @NotNull Consumer<CancellableTask> task, long delayTicks) {
DeferredCancellableTask result = new DeferredCancellableTask(task);
Bukkit.getScheduler().runTaskLater(authMe, result.getConsumer(), delayTicks);
return result;
}
@Override
public @NotNull CancellableTask runOnRegionSchedulerAtFixedRate(@NotNull World world, int chunkX, int chunkZ, @NotNull Consumer<CancellableTask> task, long initialDelayTicks, long periodTicks) {
DeferredCancellableTask result = new DeferredCancellableTask(task);
Bukkit.getScheduler().runTaskTimer(authMe, result.getConsumer(), initialDelayTicks, periodTicks);
return result;
}
@Override
public void executeOnGlobalRegionScheduler(@NotNull Runnable run) {
Bukkit.getScheduler().runTask(authMe, run);
}
@Override
public boolean isGlobalTickThread() {
return Bukkit.isPrimaryThread();
}
@Override
public @NotNull CancellableTask runOnGlobalRegionScheduler(@NotNull Consumer<CancellableTask> task) {
DeferredCancellableTask result = new DeferredCancellableTask(task);
Bukkit.getScheduler().runTask(authMe, result.getConsumer());
return result;
}
@Override
public @NotNull CancellableTask runOnGlobalRegionSchedulerDelayed(@NotNull Consumer<CancellableTask> task, long delayTicks) {
DeferredCancellableTask result = new DeferredCancellableTask(task);
Bukkit.getScheduler().runTaskLater(authMe, result.getConsumer(), delayTicks);
return result;
}
@Override
public @NotNull CancellableTask runOnGlobalRegionSchedulerAtFixedRate(@NotNull Consumer<CancellableTask> task, long initialDelayTicks, long periodTicks) {
DeferredCancellableTask result = new DeferredCancellableTask(task);
Bukkit.getScheduler().runTaskTimer(authMe, result.getConsumer(), initialDelayTicks, periodTicks);
return result;
}
@Override
public void cancelTasksOnGlobalRegionScheduler() {
Bukkit.getScheduler().cancelTasks(authMe);
}
@Override
public boolean executeOnEntityScheduler(@NotNull Entity entity, @NotNull Runnable run, @Nullable Runnable retired, long delay) {
if (delay <= 1) {
Bukkit.getScheduler().runTask(authMe, run);
} else {
Bukkit.getScheduler().runTaskLater(authMe, run, delay);
}
return true;
}
@Override
public boolean isOwnedByCurrentRegion(@NotNull Entity entity) {
return Bukkit.isPrimaryThread();
}
@Override
public @Nullable CancellableTask runOnEntityScheduler(@NotNull Entity entity, @NotNull Consumer<CancellableTask> task, @Nullable Runnable retired) {
DeferredCancellableTask result = new DeferredCancellableTask(task);
Bukkit.getScheduler().runTask(authMe, result.getConsumer());
return result;
}
@Override
public @Nullable CancellableTask runOnEntitySchedulerDelayed(@NotNull Entity entity, @NotNull Consumer<CancellableTask> task, @Nullable Runnable retired, long delayTicks) {
DeferredCancellableTask result = new DeferredCancellableTask(task);
Bukkit.getScheduler().runTaskLater(authMe, result.getConsumer(), delayTicks);
return result;
}
@Override
public @Nullable CancellableTask runOnEntitySchedulerAtFixedRate(@NotNull Entity entity, @NotNull Consumer<CancellableTask> task, @Nullable Runnable retired, long initialDelayTicks, long periodTicks) {
DeferredCancellableTask result = new DeferredCancellableTask(task);
Bukkit.getScheduler().runTaskTimer(authMe, result.getConsumer(), initialDelayTicks, periodTicks);
return result;
}
@Override
public void teleport(Player player, Location location) {
player.teleport(location);
}
@Override
public void waitAllTasks() {
new TaskCloser(authMe).run();
}
private static class DeferredCancellableTask implements CancellableTask {
private final Consumer<BukkitTask> consumer;
private volatile BukkitCancellableTask task = null;
private volatile boolean cancelled;
public DeferredCancellableTask(Consumer<CancellableTask> consumer) {
this.consumer = new DeferredConsumer(consumer);
}
public Consumer<BukkitTask> getConsumer() {
return consumer;
}
@Override
public void cancel() {
this.cancelled = true;
if (task != null) {
task.cancel();
}
}
@Override
public boolean isCancelled() {
return cancelled || (task != null && task.isCancelled());
}
private class DeferredConsumer implements Consumer<BukkitTask> {
private final Consumer<CancellableTask> consumer;
public DeferredConsumer(Consumer<CancellableTask> consumer) {
this.consumer = consumer;
}
@Override
public void accept(BukkitTask bukkitTask) {
if (cancelled) {
bukkitTask.cancel();
return;
}
BukkitCancellableTask bukkitCancellableTask = new BukkitCancellableTask(bukkitTask);
task = bukkitCancellableTask;
consumer.accept(bukkitCancellableTask);
}
}
}
}

View File

@ -17,6 +17,7 @@ import fr.xephi.authme.settings.properties.RestrictionSettings;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerTeleportEvent;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
@ -182,12 +183,12 @@ public class TeleportationService implements Reloadable {
* @param event the event to emit and according to which to teleport
*/
private void performTeleportation(final Player player, final AbstractTeleportEvent event) {
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> {
bukkitService.executeOptionallyOnEntityScheduler(player, () -> {
bukkitService.callEvent(event);
if (player.isOnline() && isEventValid(event)) {
player.teleport(event.getTo());
bukkitService.teleport(player, event.getTo());
}
});
}, () -> logger.info("Can't teleport player " + player.getName() + " because it's currently unavailable"));
}
private static boolean isEventValid(AbstractTeleportEvent event) {

View File

@ -85,8 +85,10 @@ public class BungeeSender implements SettingsDependent {
return;
}
// Add a small delay, just in case...
bukkitService.scheduleSyncDelayedTask(() ->
sendBungeecordMessage(player, "Connect", destinationServerOnLogin), 10L);
bukkitService.runOnEntitySchedulerDelayed(player, task ->
sendBungeecordMessage(player, "Connect", destinationServerOnLogin),
() -> logger.info("Can't send bungeecord message to player "
+ player.getName() + " because the player is currently not available"), 10L);
}
/**

View File

@ -128,23 +128,25 @@ public class CommandManager implements Reloadable {
for (T cmd : commands) {
if (predicate.test(cmd)) {
long delay = cmd.getDelay();
if (delay > 0) {
bukkitService.scheduleSyncDelayedTask(() -> dispatchCommand(player, cmd), delay);
if (Executor.CONSOLE.equals(cmd.getExecutor())) {
if (delay > 0) {
bukkitService.runOnGlobalRegionSchedulerDelayed(task ->
bukkitService.dispatchConsoleCommand(cmd.getCommand()), delay);
} else {
bukkitService.dispatchConsoleCommand(cmd.getCommand());
}
} else {
dispatchCommand(player, cmd);
if (delay > 0) {
bukkitService.runOnEntitySchedulerDelayed(player, task ->
bukkitService.dispatchCommand(player, cmd.getCommand()), null, delay);
} else {
bukkitService.dispatchCommand(player, cmd.getCommand());
}
}
}
}
}
private void dispatchCommand(Player player, Command command) {
if (Executor.CONSOLE.equals(command.getExecutor())) {
bukkitService.dispatchConsoleCommand(command.getCommand());
} else {
bukkitService.dispatchCommand(player, command.getCommand());
}
}
private static boolean shouldCommandBeRun(OnLoginCommand command, int numberOfOtherAccounts) {
return (!command.getIfNumberOfAccountsAtLeast().isPresent()
|| command.getIfNumberOfAccountsAtLeast().get() <= numberOfOtherAccounts)

View File

@ -0,0 +1,28 @@
package fr.xephi.authme.task;
import org.bukkit.scheduler.BukkitTask;
import java.util.function.Consumer;
public class BukkitCancellableTask implements CancellableTask {
private final BukkitTask bukkitTask;
public BukkitCancellableTask(BukkitTask bukkitTask) {
this.bukkitTask = bukkitTask;
}
public static Consumer<BukkitTask> mapConsumer(Consumer<CancellableTask> task) {
return c -> task.accept(new BukkitCancellableTask(c));
}
@Override
public void cancel() {
bukkitTask.cancel();
}
@Override
public boolean isCancelled() {
return bukkitTask.isCancelled();
}
}

View File

@ -0,0 +1,17 @@
package fr.xephi.authme.task;
import org.jetbrains.annotations.NotNull;
public interface CancellableTask {
/**
* Attempts to cancel this task, returning the result of the attempt. In all cases, if the task is currently
* being executed no attempt is made to halt the task, however any executions in the future are halted.
*/
void cancel();
/**
* Check if the task has been cancelled
*/
boolean isCancelled();
}

View File

@ -5,11 +5,12 @@ import fr.xephi.authme.initialization.HasCleanup;
import org.bukkit.scheduler.BukkitRunnable;
import javax.inject.Inject;
import java.util.function.Consumer;
/**
* Task run periodically to invoke the cleanup task on services.
*/
public class CleanupTask extends BukkitRunnable {
public class CleanupTask implements Consumer<CancellableTask> {
@Inject
private SingletonStore<HasCleanup> hasCleanupStore;
@ -18,7 +19,7 @@ public class CleanupTask extends BukkitRunnable {
}
@Override
public void run() {
public void accept(CancellableTask cancellableTask) {
hasCleanupStore.retrieveAllOfType()
.forEach(HasCleanup::performCleanup);
}

View File

@ -0,0 +1,32 @@
package fr.xephi.authme.task;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import java.util.function.Consumer;
public class FoliaCancellableTask implements CancellableTask {
private final ScheduledTask scheduledTask;
public FoliaCancellableTask(ScheduledTask scheduledTask) {
this.scheduledTask = scheduledTask;
}
public static CancellableTask mapTask(ScheduledTask task) {
return task != null ? new FoliaCancellableTask(task) : null;
}
public static Consumer<ScheduledTask> mapConsumer(Consumer<CancellableTask> task) {
return c -> task.accept(new FoliaCancellableTask(c));
}
@Override
public void cancel() {
scheduledTask.cancel();
}
@Override
public boolean isCancelled() {
return scheduledTask.isCancelled();
}
}

View File

@ -3,10 +3,13 @@ package fr.xephi.authme.task;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.concurrent.Callable;
import java.util.function.Consumer;
/**
* Message shown to a player in a regular interval as long as he is not logged in.
*/
public class MessageTask extends BukkitRunnable {
public class MessageTask implements Consumer<CancellableTask> {
private final Player player;
private final String[] message;
@ -26,7 +29,7 @@ public class MessageTask extends BukkitRunnable {
}
@Override
public void run() {
public void accept(CancellableTask cancellableTask) {
if (!isMuted) {
player.sendMessage(message);
}

View File

@ -15,6 +15,7 @@ import javax.inject.Inject;
import java.util.Calendar;
import java.util.Collection;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import static fr.xephi.authme.util.Utils.logAndSendMessage;
@ -99,7 +100,7 @@ public class PurgeService {
isPurging = true;
PurgeTask purgeTask = new PurgeTask(this, permissionsManager, sender, names, players);
bukkitService.runTaskTimerAsynchronously(purgeTask, 0, 1);
bukkitService.runOnAsyncSchedulerAtFixedRate(purgeTask, 0, 50L, TimeUnit.MILLISECONDS);
}
/**

View File

@ -4,6 +4,7 @@ import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.permission.PlayerStatePermission;
import fr.xephi.authme.task.CancellableTask;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.OfflinePlayer;
@ -15,8 +16,10 @@ import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.function.Consumer;
class PurgeTask extends BukkitRunnable {
class PurgeTask implements Consumer<CancellableTask> {
//how many players we should check for each tick
private static final int INTERVAL_CHECK = 5;
@ -58,10 +61,10 @@ class PurgeTask extends BukkitRunnable {
}
@Override
public void run() {
public void accept(CancellableTask cancellableTask) {
if (toPurge.isEmpty()) {
//everything was removed
finish();
finish(cancellableTask);
return;
}
@ -107,8 +110,8 @@ class PurgeTask extends BukkitRunnable {
}
}
private void finish() {
cancel();
private void finish(CancellableTask cancellableTask) {
cancellableTask.cancel();
// Show a status message
sendMessage(ChatColor.GREEN + "[AuthMe] Database has been purged successfully");

View File

@ -5,6 +5,7 @@ description: ${project.description}
main: ${pluginDescription.main}
version: ${pluginDescription.version}
api-version: 1.13
folia-supported: true
softdepend:
- Vault
- LuckPerms

View File

@ -14,7 +14,7 @@ import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.process.login.ProcessSyncPlayerLogin;
import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.service.FoliaBukkitService;
import fr.xephi.authme.service.bungeecord.BungeeReceiver;
import fr.xephi.authme.service.bungeecord.BungeeSender;
import fr.xephi.authme.settings.Settings;
@ -35,16 +35,13 @@ import org.mockito.junit.MockitoJUnitRunner;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.logging.Logger;
import static fr.xephi.authme.settings.properties.AuthMeSettingsRetriever.buildConfigurationData;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@ -107,7 +104,7 @@ public class AuthMeInitializationTest {
injector.register(AuthMe.class, authMe);
injector.register(Settings.class, settings);
injector.register(DataSource.class, mock(DataSource.class));
injector.register(BukkitService.class, mock(BukkitService.class));
injector.register(FoliaBukkitService.class, mock(FoliaBukkitService.class));
// when
authMe.instantiateServices(injector);

View File

@ -22,12 +22,14 @@ import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.Locale;
import java.util.function.Consumer;
import static fr.xephi.authme.service.BukkitServiceTestHelper.setBukkitServiceToRunTaskOptionallyAsync;
import static fr.xephi.authme.service.BukkitServiceTestHelper.setBukkitServiceToScheduleSyncTaskFromOptionallyAsyncTask;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@ -77,7 +79,7 @@ public class RegisterAdminCommandTest {
// then
verify(validationService).validatePassword(password, user);
verify(commandService).send(sender, MessageKey.INVALID_PASSWORD_LENGTH, new String[0]);
verify(bukkitService, never()).runTaskAsynchronously(any(Runnable.class));
verify(bukkitService, never()).runOnAsyncSchedulerNow(eq(task -> {}));
}
@Test

View File

@ -9,9 +9,11 @@ import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.task.CancellableTask;
import fr.xephi.authme.task.MessageTask;
import fr.xephi.authme.task.TimeoutTask;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitTask;
import org.junit.BeforeClass;
import org.junit.Test;
@ -79,10 +81,10 @@ public class LimboPlayerTaskManagerTest {
limboPlayerTaskManager.registerMessageTask(player, limboPlayer, LimboMessageType.REGISTER);
// then
verify(limboPlayer).setMessageTask(any(MessageTask.class));
verify(limboPlayer).setMessageTask(any(MessageTask.class), any(CancellableTask.class));
verify(messages).retrieveSingle(player, key);
verify(bukkitService).runTaskTimer(
any(MessageTask.class), eq(2L * TICKS_PER_SECOND), eq((long) interval * TICKS_PER_SECOND));
any(BukkitRunnable.class), eq(2L * TICKS_PER_SECOND), eq((long) interval * TICKS_PER_SECOND));
}
@Test
@ -108,7 +110,8 @@ public class LimboPlayerTaskManagerTest {
given(player.getName()).willReturn(name);
LimboPlayer limboPlayer = new LimboPlayer(null, true, Collections.singletonList(new UserGroup("grp")), false, 0.1f, 0.0f);
MessageTask existingMessageTask = mock(MessageTask.class);
limboPlayer.setMessageTask(existingMessageTask);
CancellableTask existingMessageCancellableTask = mock(CancellableTask.class);
limboPlayer.setMessageTask(existingMessageTask, existingMessageCancellableTask);
given(settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL)).willReturn(8);
given(messages.retrieveSingle(player, MessageKey.REGISTER_MESSAGE)).willReturn("Please register!");
@ -120,7 +123,7 @@ public class LimboPlayerTaskManagerTest {
assertThat(limboPlayer.getMessageTask(), not(sameInstance(existingMessageTask)));
verify(registrationCaptchaManager).isCaptchaRequired(name);
verify(messages).retrieveSingle(player, MessageKey.REGISTER_MESSAGE);
verify(existingMessageTask).cancel();
verify(existingMessageCancellableTask).cancel();
}
@Test
@ -150,8 +153,9 @@ public class LimboPlayerTaskManagerTest {
Player player = mock(Player.class);
LimboPlayer limboPlayer = mock(LimboPlayer.class);
given(settings.getProperty(RestrictionSettings.TIMEOUT)).willReturn(30);
BukkitTask bukkitTask = mock(BukkitTask.class);
given(bukkitService.runTaskLater(any(TimeoutTask.class), anyLong())).willReturn(bukkitTask);
CancellableTask bukkitTask = mock(CancellableTask.class);
TimeoutTask timeoutTask = mock(TimeoutTask.class);
given(bukkitService.runOnGlobalRegionSchedulerDelayed(eq(t -> timeoutTask.run()), anyLong())).willReturn(bukkitTask);
// when
limboPlayerTaskManager.registerTimeoutTask(player, limboPlayer);
@ -181,7 +185,7 @@ public class LimboPlayerTaskManagerTest {
// given
Player player = mock(Player.class);
LimboPlayer limboPlayer = new LimboPlayer(null, false, Collections.emptyList(), true, 0.3f, 0.1f);
BukkitTask existingTask = mock(BukkitTask.class);
CancellableTask existingTask = mock(CancellableTask.class);
limboPlayer.setTimeoutTask(existingTask);
given(settings.getProperty(RestrictionSettings.TIMEOUT)).willReturn(18);
BukkitTask bukkitTask = mock(BukkitTask.class);

View File

@ -56,7 +56,7 @@ public class TaskCloserTest {
given(server.getScheduler()).willReturn(bukkitScheduler);
ReflectionTestUtils.setField(JavaPlugin.class, authMe, "server", server);
given(authMe.getLogger()).willReturn(logger);
taskCloser = spy(new TaskCloser(authMe, dataSource));
taskCloser = spy(new TaskCloser(authMe));
}
@Test
@ -71,6 +71,7 @@ public class TaskCloserTest {
// when
taskCloser.run();
dataSource.closeConnection();
// then
verify(bukkitScheduler, times(3)).isQueued(anyInt());
@ -92,6 +93,7 @@ public class TaskCloserTest {
// when
taskCloser.run();
dataSource.closeConnection();
// then
verify(bukkitScheduler, times(3)).isQueued(anyInt());
@ -120,7 +122,7 @@ public class TaskCloserTest {
/** Test implementation for {@link #shouldStopForInterruptedThread()}. */
private void shouldStopForInterruptedThread0() throws InterruptedException {
// given
taskCloser = spy(new TaskCloser(authMe, null));
taskCloser = spy(new TaskCloser(authMe));
// First two times do nothing, third time throw exception when we sleep
doNothing().doNothing().doThrow(InterruptedException.class).when(taskCloser).sleep();
mockActiveWorkers();

View File

@ -32,7 +32,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
/**
* Test for {@link BukkitService}.
* Test for {@link FoliaBukkitService}.
*/
@RunWith(MockitoJUnitRunner.class)
public class BukkitServiceTest {
@ -56,7 +56,7 @@ public class BukkitServiceTest {
given(server.getScheduler()).willReturn(scheduler);
given(server.getPluginManager()).willReturn(pluginManager);
given(settings.getProperty(PluginSettings.USE_ASYNC_TASKS)).willReturn(true);
bukkitService = new BukkitService(authMe, settings);
bukkitService = new FoliaBukkitService(authMe, settings);
}
@Test

View File

@ -5,7 +5,7 @@ import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doAnswer;
/**
* Offers utility methods for testing involving a {@link BukkitService} mock.
* Offers utility methods for testing involving a {@link FoliaBukkitService} mock.
*/
public final class BukkitServiceTestHelper {
@ -14,7 +14,7 @@ public final class BukkitServiceTestHelper {
/**
* Sets a BukkitService mock to run any Runnable it is passed to its method
* {@link BukkitService#scheduleSyncTaskFromOptionallyAsyncTask}.
* {@link FoliaBukkitService#scheduleSyncTaskFromOptionallyAsyncTask}.
*
* @param bukkitService the mock to set behavior on
*/
@ -28,7 +28,7 @@ public final class BukkitServiceTestHelper {
/**
* Sets a BukkitService mock to run any Runnable it is passed to its method
* {@link BukkitService#runTaskAsynchronously}.
* {@link FoliaBukkitService#runTaskAsynchronously}.
*
* @param bukkitService the mock to set behavior on
*/
@ -42,7 +42,7 @@ public final class BukkitServiceTestHelper {
/**
* Sets a BukkitService mock to run any Runnable it is passed to its method
* {@link BukkitService#runTaskOptionallyAsync}.
* {@link FoliaBukkitService#runTaskOptionallyAsync}.
*
* @param bukkitService the mock to set behavior on
*/
@ -56,7 +56,7 @@ public final class BukkitServiceTestHelper {
/**
* Sets a BukkitService mock to run any Runnable it is passed to its method
* {@link BukkitService#scheduleSyncDelayedTask(Runnable)}.
* {@link FoliaBukkitService#scheduleSyncDelayedTask(Runnable)}.
*
* @param bukkitService the mock to set behavior on
*/
@ -70,7 +70,7 @@ public final class BukkitServiceTestHelper {
/**
* Sets a BukkitService mock to run any Runnable it is passed to its method
* {@link BukkitService#scheduleSyncDelayedTask(Runnable, long)}.
* {@link FoliaBukkitService#scheduleSyncDelayedTask(Runnable, long)}.
*
* @param bukkitService the mock to set behavior on
*/

View File

@ -34,7 +34,7 @@ public class CleanupTaskTest {
given(hasCleanupStore.retrieveAllOfType()).willReturn(services);
// when
cleanupTask.run();
cleanupTask.accept(mock(CancellableTask.class));
// then
verify(services.get(0)).performCleanup();

View File

@ -24,6 +24,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import static com.google.common.collect.Sets.newHashSet;
import static org.hamcrest.Matchers.containsInAnyOrder;
@ -189,7 +190,7 @@ public class PurgeServiceTest {
private void verifyScheduledPurgeTask(UUID senderUuid, Set<String> names) {
ArgumentCaptor<PurgeTask> captor = ArgumentCaptor.forClass(PurgeTask.class);
verify(bukkitService).runTaskTimerAsynchronously(captor.capture(), eq(0L), eq(1L));
verify(bukkitService).runOnAsyncSchedulerAtFixedRate(captor.capture(), eq(0L), eq(1L), eq(TimeUnit.SECONDS));
PurgeTask task = captor.getValue();
Object senderInTask = ReflectionTestUtils.getFieldValue(PurgeTask.class, task, "sender");

View File

@ -5,6 +5,7 @@ import fr.xephi.authme.TestHelper;
import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.permission.PlayerStatePermission;
import fr.xephi.authme.task.CancellableTask;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
@ -89,9 +90,10 @@ public class PurgeTaskTest {
reset(purgeService, permissionsManager);
setPermissionsBehavior();
PurgeTask task = new PurgeTask(purgeService, permissionsManager, null, names, players);
CancellableTask ct = mock(CancellableTask.class);
// when (1 - first run, 5 players per run)
task.run();
task.accept(ct);
// then (1)
// In the first run, Alpha to BRAVO (see players list above) went through. One of those players is not present
@ -103,7 +105,7 @@ public class PurgeTaskTest {
// when (2)
reset(purgeService, permissionsManager);
setPermissionsBehavior();
task.run();
task.accept(ct);
// then (2)
// Echo, Golf, HOTEL
@ -116,7 +118,7 @@ public class PurgeTaskTest {
given(permissionsManager.hasPermissionOffline("india", BYPASS_NODE)).willReturn(true);
// when (3)
task.run();
task.accept(ct);
// then (3)
// We no longer have any OfflinePlayers, so lookup of permissions was done with the names
@ -137,10 +139,11 @@ public class PurgeTaskTest {
reset(purgeService, permissionsManager);
setPermissionsBehavior();
CancellableTask ct = mock(CancellableTask.class);
PurgeTask task = new PurgeTask(purgeService, permissionsManager, null, names, players);
// when
task.run();
task.accept(ct);
// then
assertRanPurgeWithPlayers(players[2]);
@ -148,58 +151,12 @@ public class PurgeTaskTest {
@Test
public void shouldStopTaskAndInformSenderUponCompletion() {
// given
Set<String> names = newHashSet("name1", "name2");
Player sender = mock(Player.class);
UUID uuid = UUID.randomUUID();
given(sender.getUniqueId()).willReturn(uuid);
PurgeTask task = new PurgeTask(purgeService, permissionsManager, sender, names, new OfflinePlayer[0]);
BukkitTask bukkitTask = mock(BukkitTask.class);
given(bukkitTask.getTaskId()).willReturn(10049);
ReflectionTestUtils.setField(BukkitRunnable.class, task, "task", bukkitTask);
Server server = mock(Server.class);
BukkitScheduler scheduler = mock(BukkitScheduler.class);
given(server.getScheduler()).willReturn(scheduler);
ReflectionTestUtils.setField(Bukkit.class, null, "server", server);
given(server.getPlayer(uuid)).willReturn(sender);
task.run(); // Run for the first time -> results in empty names list
// when
task.run();
// then
verify(scheduler).cancelTask(task.getTaskId());
verify(sender).sendMessage(argThat(containsString("Database has been purged successfully")));
// todo: re-create this test
}
@Test
public void shouldStopTaskAndInformConsoleUser() {
// given
Set<String> names = newHashSet("name1", "name2");
PurgeTask task = new PurgeTask(purgeService, permissionsManager, null, names, new OfflinePlayer[0]);
BukkitTask bukkitTask = mock(BukkitTask.class);
given(bukkitTask.getTaskId()).willReturn(10049);
ReflectionTestUtils.setField(BukkitRunnable.class, task, "task", bukkitTask);
Server server = mock(Server.class);
BukkitScheduler scheduler = mock(BukkitScheduler.class);
given(server.getScheduler()).willReturn(scheduler);
ReflectionTestUtils.setField(Bukkit.class, null, "server", server);
ConsoleCommandSender consoleSender = mock(ConsoleCommandSender.class);
given(server.getConsoleSender()).willReturn(consoleSender);
task.run(); // Run for the first time -> results in empty names list
// when
task.run();
// then
verify(scheduler).cancelTask(task.getTaskId());
verify(consoleSender).sendMessage(argThat(containsString("Database has been purged successfully")));
// todo: re-create this test
}