package fr.xephi.authme.service; import com.github.Anon8281.universalScheduler.UniversalRunnable; import com.github.Anon8281.universalScheduler.UniversalScheduler; import com.github.Anon8281.universalScheduler.scheduling.tasks.MyScheduledTask; 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 org.bukkit.BanEntry; import org.bukkit.BanList; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.OfflinePlayer; import org.bukkit.World; import org.bukkit.command.CommandSender; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import javax.inject.Inject; import java.util.Collection; import java.util.Date; import java.util.Optional; import java.util.Set; import java.util.function.Function; import static fr.xephi.authme.AuthMe.getScheduler; /** * 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 static final int TICKS_PER_SECOND = 20; /** Number of ticks per minute. */ public static final int TICKS_PER_MINUTE = 60 * TICKS_PER_SECOND; /** Whether the server is running Folia. */ private static final boolean isFolia = UniversalScheduler.isFolia; private final AuthMe authMe; private boolean useAsyncTasks; @Inject BukkitService(AuthMe authMe, Settings settings) { this.authMe = authMe; reload(settings); } /** * Schedules a once off task to occur as soon as possible. *

* This task will be executed by the main server thread. * * @param task Task to be executed */ public void scheduleSyncDelayedTask(Runnable task) { runTask(task); } /** * Schedules a once off task to occur after a delay. *

* This task will be executed by the main server thread. * * @param task Task to be executed * @param delay Delay in server ticks before executing task */ public void scheduleSyncDelayedTask(Runnable task, long delay) { if (isFolia) { runTaskLater(task, delay); } else { Bukkit.getScheduler().runTaskLater(authMe, task, delay); // We must do this to keep compatibility } } /** * 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 */ public void scheduleSyncTaskFromOptionallyAsyncTask(Runnable task) { if (Bukkit.isPrimaryThread()) { runTask(task); } else { scheduleSyncDelayedTask(task); } } /** * Returns a task that will run on the next server tick. * * @param task the task to be run * @throws IllegalArgumentException if plugin is null * @throws IllegalArgumentException if task is null */ public void runTask(Runnable task) { if (isFolia) { getScheduler().runTask(task); } else { Bukkit.getScheduler().runTask(authMe, task); } } public void runTask(Entity entity, Runnable task) { if (isFolia) { getScheduler().runTask(entity, task); } else { Bukkit.getScheduler().runTask(authMe, task); } } public void runTask(Location location, Runnable task) { getScheduler().runTask(location, task); } /** * Runs the task synchronously if we are running Folia, else do nothing but run it. * @param task the task to be run */ public void runTaskIfFolia(Runnable task) { if (isFolia) { runTask(task); } else { task.run(); } } /** * Runs the task synchronously if we are running Folia, else do nothing but run it. * @param task the task to be run */ public void runTaskIfFolia(Entity entity, Runnable task) { if (isFolia) { runTask(entity, task); } else { task.run(); } } /** * Runs the task synchronously if we are running Folia, else do nothing but run it. * @param task the task to be run */ public void runTaskIfFolia(Location location, Runnable task) { if (isFolia) { runTask(location, task); } else { task.run(); } } /** * Returns a task that will run after the specified number of server * ticks. * * @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 */ public MyScheduledTask runTaskLater(Runnable task, long delay) { return getScheduler().runTaskLater(task, delay); } public MyScheduledTask runTaskLater(Entity entity, Runnable task, long delay) { return getScheduler().runTaskLater(entity, task, delay); } /** * Schedules this task to run asynchronously or immediately executes it based on * AuthMe's configuration. * * @param task the task to run */ public void runTaskOptionallyAsync(Runnable task) { if (useAsyncTasks) { runTaskAsynchronously(task); } else { runTask(task); } } /** * Asynchronous tasks should never access any API in Bukkit. Great care * should be taken to assure the thread-safety of asynchronous tasks. *

* Returns a task that will run asynchronously. * * @param task the task to be run * @throws IllegalArgumentException if plugin is null * @throws IllegalArgumentException if task is null */ public void runTaskAsynchronously(Runnable task) { if (isFolia) { getScheduler().runTaskAsynchronously(task); } else { Bukkit.getScheduler().runTaskAsynchronously(authMe, task); } } /** * Asynchronous tasks should never access any API in Bukkit. Great care * should be taken to assure the thread-safety of asynchronous tasks. *

* 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 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 */ public MyScheduledTask runTaskTimerAsynchronously(UniversalRunnable task, long delay, long period) { return task.runTaskTimerAsynchronously(authMe, delay, period); } /** * 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 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 */ public MyScheduledTask runTaskTimer(UniversalRunnable task, long delay, long period) { return task.runTaskTimer(authMe, delay, period); } /** * Broadcast a message to all players. * * @param message the message * @return the number of players */ public int broadcastMessage(String message) { return Bukkit.broadcastMessage(message); } /** * Gets the player with the exact given name, case insensitive. * * @param name Exact name of the player to retrieve * @return a player object if one was found, null otherwise */ public Player getPlayerExact(String name) { return authMe.getServer().getPlayerExact(name); } /** * Gets the player by the given name, regardless if they are offline or * online. *

* This method may involve a blocking web request to get the UUID for the * given name. *

* This will return an object even if the player does not exist. To this * method, all players will exist. * * @param name the name the player to retrieve * @return an offline player */ public OfflinePlayer getOfflinePlayer(String name) { return authMe.getServer().getOfflinePlayer(name); } /** * Gets a set containing all banned players. * * @return a set containing banned players */ public Set getBannedPlayers() { return Bukkit.getBannedPlayers(); } /** * Gets every player that has ever played on this server. * * @return an array containing all previous players */ public OfflinePlayer[] getOfflinePlayers() { return Bukkit.getOfflinePlayers(); } /** * Gets a view of all currently online players. * * @return collection of online players */ @SuppressWarnings("unchecked") public Collection getOnlinePlayers() { return (Collection) Bukkit.getOnlinePlayers(); } /** * Calls an event with the given details. * * @param event Event details * @throws IllegalStateException Thrown when an asynchronous event is * fired from synchronous code. */ public void callEvent(Event event) { Bukkit.getPluginManager().callEvent(event); } /** * Creates an event with the provided function and emits it. * * @param eventSupplier the event supplier: function taking a boolean specifying whether AuthMe is configured * in async mode or not * @param the event type * @return the event that was created and emitted */ public E createAndCallEvent(Function eventSupplier) { E event = eventSupplier.apply(useAsyncTasks); callEvent(event); return event; } /** * Creates a PotionEffect with blindness for the given duration in ticks. * * @param timeoutInTicks duration of the effect in ticks * @return blindness potion effect */ public PotionEffect createBlindnessEffect(int timeoutInTicks) { return new PotionEffect(PotionEffectType.BLINDNESS, timeoutInTicks, 2); } /** * Gets the world with the given name. * * @param name the name of the world to retrieve * @return a world with the given name, or null if none exists */ public World getWorld(String name) { return Bukkit.getWorld(name); } /** * Dispatches a command on this server, and executes it if found. * * @param sender the apparent sender of the command * @param commandLine the command + arguments. Example: test abc 123 * @return returns false if no target is found */ public boolean dispatchCommand(CommandSender sender, String commandLine) { return Bukkit.dispatchCommand(sender, commandLine); } /** * Dispatches a command to be run as console user on this server, and executes it if found. * * @param commandLine the command + arguments. Example: test abc 123 * @return returns false if no target is found */ public boolean dispatchConsoleCommand(String commandLine) { return Bukkit.dispatchCommand(Bukkit.getConsoleSender(), commandLine); } @Override public void reload(Settings settings) { useAsyncTasks = settings.getProperty(PluginSettings.USE_ASYNC_TASKS); } /** * Send the specified bytes to bungeecord using the specified player connection. * * @param player the player * @param bytes the message */ public void sendBungeeMessage(Player player, byte[] bytes) { player.sendPluginMessage(authMe, "BungeeCord", bytes); } /** * Send the specified bytes to bungeecord using the specified player connection. * * @param player the player * @param bytes the message */ public void sendVelocityMessage(Player player, byte[] bytes) { if (player != null) { player.sendPluginMessage(authMe, "authmevelocity:main", bytes); } else { Bukkit.getServer().sendPluginMessage(authMe, "authmevelocity:main", bytes); } } /** * 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 expires date for the ban's expiration (unban), or null to imply * 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 */ public BanEntry banIp(String ip, String reason, Date expires, String source) { return Bukkit.getServer().getBanList(BanList.Type.IP).addBan(ip, reason, expires, source); } /** * Returns an optional with a boolean indicating whether bungeecord is enabled or not if the * server implementation is Spigot. Otherwise returns an empty optional. * * @return Optional with configuration value for Spigot, empty optional otherwise */ public Optional isBungeeCordConfiguredForSpigot() { try { YamlConfiguration spigotConfig = Bukkit.spigot().getConfig(); return Optional.of(spigotConfig.getBoolean("settings.bungeecord")); } catch (NoSuchMethodError e) { return Optional.empty(); } } /** * @return the IP string that this server is bound to, otherwise empty string */ public String getIp() { return Bukkit.getServer().getIp(); } }