441 lines
14 KiB
Java
441 lines
14 KiB
Java
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.
|
|
* <p>
|
|
* 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.
|
|
* <p>
|
|
* 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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* <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 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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* <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 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.
|
|
* <p>
|
|
* This method may involve a blocking web request to get the UUID for the
|
|
* given name.
|
|
* <p>
|
|
* 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<OfflinePlayer> 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<Player> getOnlinePlayers() {
|
|
return (Collection<Player>) 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 <E> the event type
|
|
* @return the event that was created and emitted
|
|
*/
|
|
public <E extends Event> E createAndCallEvent(Function<Boolean, E> 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: <code>test abc 123</code>
|
|
* @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: <code>test abc 123</code>
|
|
* @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<Boolean> 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();
|
|
}
|
|
}
|