package fr.xephi.authme.util; import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; import org.bukkit.BanEntry; import org.bukkit.BanList; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.plugin.Plugin; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitTask; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.Set; /** * Service for operations requiring server entities, such as for scheduling. */ public class BukkitService { /** 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; private final AuthMe authMe; private final boolean getOnlinePlayersIsCollection; private Method getOnlinePlayers; public BukkitService(AuthMe authMe) { this.authMe = authMe; getOnlinePlayersIsCollection = initializeOnlinePlayersIsCollectionField(); } /** * 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 * @return Task id number (-1 if scheduling failed) */ public int scheduleSyncDelayedTask(Runnable task) { return Bukkit.getScheduler().scheduleSyncDelayedTask(authMe, 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 * @return Task id number (-1 if scheduling failed) */ public int scheduleSyncDelayedTask(Runnable task, long delay) { return Bukkit.getScheduler().scheduleSyncDelayedTask(authMe, task, delay); } /** * Returns a task that will run on the next server tick. * * @param task the task to be run * @return a BukkitTask that contains the id number * @throws IllegalArgumentException if plugin is null * @throws IllegalArgumentException if task is null */ public BukkitTask runTask(Runnable task) { return Bukkit.getScheduler().runTask(authMe, task); } /** * 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 BukkitTask runTaskLater(Runnable task, long delay) { return Bukkit.getScheduler().runTaskLater(authMe, task, delay); } /** * 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 * @return a BukkitTask that contains the id number * @throws IllegalArgumentException if plugin is null * @throws IllegalArgumentException if task is null */ public BukkitTask runTaskAsynchronously(Runnable task) { return 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 */ public BukkitTask runTaskTimerAsynchronously(Runnable task, long delay, long period) { return Bukkit.getScheduler().runTaskTimerAsynchronously(authMe, task, 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 * @see BukkitScheduler#runTaskTimer(Plugin, Runnable, long, long) */ public BukkitTask runTaskTimer(BukkitRunnable task, long delay, long period) { return task.runTaskTimer(authMe, period, delay); } /** * 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 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(); } /** * Safe way to retrieve the list of online players from the server. Depending on the * implementation of the server, either an array of {@link Player} instances is being returned, * or a Collection. Always use this wrapper to retrieve online players instead of {@link * Bukkit#getOnlinePlayers()} directly. * * @return collection of online players * * @see SpigotMC * forum * @see StackOverflow */ @SuppressWarnings("unchecked") public Collection getOnlinePlayers() { if (getOnlinePlayersIsCollection) { return Bukkit.getOnlinePlayers(); } try { // The lookup of a method via Reflections is rather expensive, so we keep a reference to it if (getOnlinePlayers == null) { getOnlinePlayers = Bukkit.class.getDeclaredMethod("getOnlinePlayers"); } Object obj = getOnlinePlayers.invoke(null); if (obj instanceof Collection) { return (Collection) obj; } else if (obj instanceof Player[]) { return Arrays.asList((Player[]) obj); } else { String type = (obj == null) ? "null" : obj.getClass().getName(); ConsoleLogger.warning("Unknown list of online players of type " + type); } } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { ConsoleLogger.logException("Could not retrieve list of online players:", e); } return Collections.emptyList(); } /** * 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); } /** * 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); } /** * Method run upon initialization to verify whether or not the Bukkit implementation * returns the online players as a Collection. * * @see #getOnlinePlayers() */ private static boolean initializeOnlinePlayersIsCollectionField() { try { Method method = Bukkit.class.getDeclaredMethod("getOnlinePlayers"); return method.getReturnType() == Collection.class; } catch (NoSuchMethodException e) { ConsoleLogger.warning("Error verifying if getOnlinePlayers is a collection! Method doesn't exist"); } return false; } /** * Adds a ban to the this 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); } }