About to round off the content

This commit is contained in:
Paldiu 2023-01-28 20:54:14 -06:00
parent 7a47aebcf1
commit b82ede0b9e
24 changed files with 448 additions and 60 deletions

View File

@ -22,6 +22,9 @@ dependencies {
implementation 'org.projectlombok:lombok:1.18.20'
implementation 'org.postgresql:postgresql:42.2.20'
implementation 'org.apache.commons:commons-lang3:3.12.0'
implementation 'com.github.MilkBowl:VaultAPI:1.7'
implementation 'com.google.code.gson:gson:2.8.7'
implementation 'org.jetbrains:annotations:22.0.0'
shadow 'io.projectreactor:reactor-core:3.4.10'
compileOnly 'io.papermc.paper:paper-api:1.19.3-R0.1-SNAPSHOT'
}

View File

@ -1,7 +1,9 @@
package mc.unraveled.reforged.api;
import mc.unraveled.reforged.service.base.ServicePool;
import org.apache.commons.lang3.ArrayUtils;
import reactor.core.publisher.Mono;
import reactor.core.publisher.MonoSink;
public interface IService extends Runnable {
Mono<ServicePool> getParentPool();

View File

@ -13,17 +13,17 @@ public abstract class AbstractBan implements Serializable<AbstractBan> {
private final String ip;
private final String source;
private final String reason;
private final long propogated;
private final long propagated;
private final long expiry;
private final List<Pair<String, String>> contentPairs;
private boolean active;
public AbstractBan(String uuid, String ip, String source, String reason, long propogated, long expiry, boolean active) {
public AbstractBan(String uuid, String ip, String source, String reason, long propagated, long expiry, boolean active) {
this.uuid = uuid;
this.ip = ip;
this.source = source;
this.reason = reason;
this.propogated = propogated;
this.propagated = propagated;
this.expiry = expiry;
this.active = active;
@ -32,7 +32,7 @@ public abstract class AbstractBan implements Serializable<AbstractBan> {
new Pair<>("ip", ip),
new Pair<>("source", source),
new Pair<>("reason", reason),
new Pair<>("propogated", String.valueOf(propogated)),
new Pair<>("propagated", String.valueOf(propagated)),
new Pair<>("expiry", String.valueOf(expiry)),
new Pair<>("active", String.valueOf(active))
);
@ -55,7 +55,7 @@ public abstract class AbstractBan implements Serializable<AbstractBan> {
char ip = 'i';
char reason = 'r';
char source = 's';
char propogated = 'p';
char propagated = 'p';
char expiry = 'e';
char active = 'a';
@ -63,7 +63,7 @@ public abstract class AbstractBan implements Serializable<AbstractBan> {
String ipString = formatted.substring(formatted.indexOf(ip) + 1, formatted.indexOf(end));
String reasonString = formatted.substring(formatted.indexOf(reason) + 1, formatted.indexOf(end));
String sourceString = formatted.substring(formatted.indexOf(source) + 1, formatted.indexOf(end));
String propogatedString = formatted.substring(formatted.indexOf(propogated) + 1, formatted.indexOf(end));
String propagatedString = formatted.substring(formatted.indexOf(propagated) + 1, formatted.indexOf(end));
String expiryString = formatted.substring(formatted.indexOf(expiry) + 1, formatted.indexOf(end));
String activeString = formatted.substring(formatted.indexOf(active) + 1, formatted.indexOf(end));
@ -71,7 +71,7 @@ public abstract class AbstractBan implements Serializable<AbstractBan> {
ipString,
reasonString,
sourceString,
Long.parseLong(propogatedString),
Long.parseLong(propagatedString),
Long.parseLong(expiryString),
Boolean.parseBoolean(activeString));
}

View File

@ -6,6 +6,8 @@ import mc.unraveled.reforged.api.Baker;
import mc.unraveled.reforged.api.Locker;
import mc.unraveled.reforged.plugin.Traverse;
import mc.unraveled.reforged.storage.DBBan;
import mc.unraveled.reforged.storage.DBUser;
import org.bukkit.OfflinePlayer;
import java.util.HashSet;
import java.util.Set;
@ -40,13 +42,32 @@ public final class BanManager implements Locker, Baker {
lock().notify();
storedBans.add(ban);
DBBan db = new DBBan(plugin.getSQLManager().establish());
db.insert(ban);
db.close();
}
public void eject(AbstractBan ban) {
if (baked) throw new IllegalStateException("Cannot eject from a baked list.");
lock().notify();
storedBans.remove(ban);
DBBan db = new DBBan(plugin.getSQLManager().establish());
db.delete(ban);
db.close();
}
public AbstractBan getBan(OfflinePlayer player) {
DBBan db = new DBBan(plugin.getSQLManager().establish());
AbstractBan ban = db.getBan(player.getUniqueId());
db.close();
return ban;
}
public boolean isBanned(OfflinePlayer player) {
return storedBans.stream()
.anyMatch(ban -> ban.getUuid().equalsIgnoreCase(
player.getUniqueId().toString()));
}
public void save() {
@ -56,7 +77,6 @@ public final class BanManager implements Locker, Baker {
DBBan banHandler = new DBBan(plugin.getSQLManager().establish());
storedBans.forEach(banHandler::insert);
banHandler.close();
}
@Override

View File

@ -18,8 +18,9 @@ import org.jetbrains.annotations.NotNull;
import java.util.Date;
@CommandInfo(name = "ban",
description = "Ban a player",
usage = "/ban <player> <reason> [duration]")
description = "Ban a player. Use -n as the second parameter for the default ban reason.",
usage = "/ban <player> <duration> <reason>",
aliases = {"b", "tempban", "tb"})
public class BanCMD extends AbstractCommandBase {
public BanCMD(@NotNull Traverse plugin) {
super(plugin, "ban");
@ -28,13 +29,19 @@ public class BanCMD extends AbstractCommandBase {
@Override
public Component cmd(CommandSender sender, String[] args) {
if (args.length < 2) {
return Component.text("Usage: /ban <player> <duration> <reason>");
return Component.text("Usage: /ban <player> <duration> <reason | -n>");
}
BanManager manager = getPlugin().getBanManager();
OfflinePlayer target = (getPlugin().getServer().getPlayer(args[0]) != null) ? getPlugin().getServer().getPlayer(args[0]) : getPlugin().getServer().getOfflinePlayer(args[0]);
String duration = args[1];
String reason = StringUtils.join(ArrayUtils.subarray(args, 2, args.length - 1), " ");
String reason;
if (args[2].equalsIgnoreCase("-n")) {
reason = MessageDefaults.BANNED.toString();
} else {
reason = StringUtils.join(ArrayUtils.subarray(args, 2, args.length - 1), " ");
}
Date expiry = Utilities.parseDate(duration);
String expiryString = Utilities.parseDateToString(expiry);

View File

@ -0,0 +1,37 @@
package mc.unraveled.reforged.command;
import mc.unraveled.reforged.api.annotations.CommandInfo;
import mc.unraveled.reforged.command.base.AbstractCommandBase;
import mc.unraveled.reforged.plugin.Traverse;
import net.kyori.adventure.text.Component;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.*;
@CommandInfo(name = "entitypurge",
usage = "/entitypurge",
description = "Purge entities",
aliases = {"ep", "ew", "mp", "mw", "mobpurge", "entitywipe", "mobwipe"})
public class EntityPurgeCMD extends AbstractCommandBase {
public EntityPurgeCMD(Traverse plugin) {
super(plugin, "entitypurge", false);
}
@Override
public Component cmd(CommandSender sender, String[] args) {
Player player = (Player) sender;
World world = player.getWorld();
int count = 0;
for (Entity entity : world.getEntities()) {
if ((entity instanceof Player) ||
(entity instanceof Tameable) ||
(entity instanceof Hanging) ||
(entity instanceof Sittable) ||
(entity instanceof Steerable)) continue;
entity.remove();
count++;
}
return Component.text("Removed " + count + " entities");
}
}

View File

@ -17,7 +17,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
@CommandInfo(name = "group",
description = "Manages groups for all players.",
usage = "/group <add | del> <player> <group>")
usage = "/group <add | del> <player> <group>",
aliases = {"g"})
public class GroupCMD extends AbstractCommandBase {
private final Rank[] ranks = Rank.values();

View File

@ -5,13 +5,18 @@ import mc.unraveled.reforged.command.base.AbstractCommandBase;
import mc.unraveled.reforged.data.InfractionData;
import mc.unraveled.reforged.data.PlayerData;
import mc.unraveled.reforged.plugin.Traverse;
import mc.unraveled.reforged.service.MuteService;
import mc.unraveled.reforged.util.TimeUtil;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@CommandInfo(name = "mute", description = "Mute a player.", usage = "/mute <player> <duration>")
@CommandInfo(name = "mute",
description = "Mute a player.",
usage = "/mute <player> <duration>",
aliases = {"m", "silence"})
public class MuteCMD extends AbstractCommandBase {
public MuteCMD(@NotNull Traverse plugin) {
super(plugin, "mute");
@ -19,14 +24,10 @@ public class MuteCMD extends AbstractCommandBase {
@Override
public Component cmd(CommandSender sender, String[] args) {
if (args.length != 2) {
return Component.text("Usage: /mute <player> <duration>");
}
if (args.length != 2) return Component.text("Usage: /mute <player> <duration>");
Player target = Bukkit.getPlayer(args[0]);
if (target == null) {
return MessageDefaults.MSG_NOT_FOUND;
}
if (target == null) return MessageDefaults.MSG_NOT_FOUND;
PlayerData pData = getPlugin().getDataManager().getPlayerData(target.getUniqueId());
if (pData == null) throw new IllegalStateException("PlayerData is null!");
@ -34,11 +35,15 @@ public class MuteCMD extends AbstractCommandBase {
InfractionData infData = pData.getInfractionData();
if (infData == null) throw new IllegalStateException("InfractionData is null!");
long duration = TimeUtil.parse(args[1]);
if (duration == 0) return Component.text("Invalid duration.");
MuteService service = new MuteService(getPlugin().getPIPELINE(), "MuteService", duration);
if (!infData.isMuted()) {
infData.setMuted(true);
getPlugin().getScheduler().queue(service).subscribe();
return Component.text("You have muted " + target.getName() + " for " + args[1] + " seconds.");
} else {
return Component.text("Target is already muted.");
}
} else return Component.text("Target is already muted.");
}
}

View File

@ -0,0 +1,46 @@
package mc.unraveled.reforged.command;
import mc.unraveled.reforged.api.annotations.CommandInfo;
import mc.unraveled.reforged.banning.AbstractBan;
import mc.unraveled.reforged.command.base.AbstractCommandBase;
import mc.unraveled.reforged.plugin.Traverse;
import mc.unraveled.reforged.storage.DBBan;
import mc.unraveled.reforged.util.Utilities;
import net.kyori.adventure.text.Component;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
@CommandInfo(name = "pardon", description = "Unban a player", usage = "/pardon <player>", aliases = {"unban", "ub"})
public class PardonCMD extends AbstractCommandBase {
public PardonCMD(@NotNull Traverse plugin) {
super(plugin, "pardon");
}
@Override
public Component cmd(CommandSender sender, String[] args) {
if (args.length != 1) return Component.text("Usage: /pardon <player>");
String name = args[0];
OfflinePlayer player = Utilities.getOfflinePlayer(name).block();
if (player == null) return MessageDefaults.MSG_NOT_FOUND;
if (getPlugin().getBanManager().isBanned(player)) {
getPlugin().getBanManager().unbake();
DBBan ban = new DBBan(getPlugin().getSQLManager().establish());
AbstractBan inst = ban.getBan(player.getUniqueId());
if (inst == null) return Component.text("Failed to find ban for " + name);
ban.delete(inst);
getPlugin().getBanManager().eject(inst);
getPlugin().getBanManager().bake();
ban.close();
return Component.text("Unbanned " + name);
} else return Component.text(name + " is not banned");
}
}

View File

@ -0,0 +1,41 @@
package mc.unraveled.reforged.command;
import mc.unraveled.reforged.api.annotations.CommandInfo;
import mc.unraveled.reforged.command.base.AbstractCommandBase;
import mc.unraveled.reforged.data.InfractionData;
import mc.unraveled.reforged.data.PlayerData;
import mc.unraveled.reforged.plugin.Traverse;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@CommandInfo(name = "unmute", description = "Unmute a player", usage = "/unmute <player>", aliases = {"um"})
public class UnmuteCMD extends AbstractCommandBase {
public UnmuteCMD(@NotNull Traverse plugin) {
super(plugin, "unmute");
}
@Override
public Component cmd(CommandSender sender, String[] args) {
if (args.length != 1) return Component.text("Usage: /unmute <player>");
String name = args[0];
Player player = Bukkit.getPlayer(args[0]);
if (player == null) return MessageDefaults.MSG_NOT_FOUND;
PlayerData data = getPlugin().getDataManager().getPlayerData(player.getUniqueId());
if (data == null) return Component.text("Failed to find data for " + name);
InfractionData inf = data.getInfractionData();
if (inf == null) return Component.text("Failed to find infraction data for " + name);
if (inf.isMuted()) {
inf.setMuted(false);
return Component.text("Unmuted " + name);
}
else return Component.text(name + " is not muted");
}
}

View File

@ -146,5 +146,7 @@ public abstract class AbstractCommandBase extends TPermission implements IComman
public static Component MSG_NOT_FOUND = Component.text("Player not found.").color(NamedTextColor.RED);
public static Component MSG_NOT_CONSOLE = Component.text("This command can only be run by a player.").color(NamedTextColor.RED);
public static Component MSG_NOT_ENOUGH_ARGS = Component.text("Not enough arguments.").color(NamedTextColor.RED);
public static Component MUTED = Component.text("You are muted!").color(NamedTextColor.RED);
public static Component BANNED = Component.text("You are banned!").color(NamedTextColor.RED);
}
}

View File

@ -72,6 +72,12 @@ public final class DataManager implements Baker, Locker {
user.close();
}
public void saveCacheToDB() {
DBUser user = new DBUser(plugin.getSQLManager().establish());
playerDataCache.forEach(user::insert);
user.close();
}
@SneakyThrows
@Override
public void bake() {

View File

@ -0,0 +1,25 @@
package mc.unraveled.reforged.economy;
public class Coin {
private final String name;
private final String symbol;
private final double value;
public Coin(String name, String symbol, double value) {
this.name = name;
this.symbol = symbol;
this.value = value;
}
public String getName() {
return name;
}
public String getSymbol() {
return symbol;
}
public double getValue() {
return value;
}
}

View File

@ -0,0 +1,5 @@
package mc.unraveled.reforged.economy;
public class EconomyManager {
// TODO: Implement economy
}

View File

@ -0,0 +1,12 @@
package mc.unraveled.reforged.listening;
import lombok.AllArgsConstructor;
import lombok.Data;
import mc.unraveled.reforged.plugin.Traverse;
import org.bukkit.event.Listener;
@AllArgsConstructor
@Data
public class AbstractListener implements Listener {
private final Traverse plugin;
}

View File

@ -0,0 +1,40 @@
package mc.unraveled.reforged.listening;
import io.papermc.paper.event.player.AsyncChatEvent;
import mc.unraveled.reforged.banning.BanManager;
import mc.unraveled.reforged.data.PlayerData;
import mc.unraveled.reforged.plugin.Traverse;
import net.kyori.adventure.text.Component;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.player.PlayerLoginEvent;
public class InfractionListener extends AbstractListener {
public InfractionListener(Traverse plugin) {
super(plugin);
}
@EventHandler(priority = EventPriority.HIGHEST)
public void banCheck(PlayerLoginEvent event) {
Player player = event.getPlayer();
BanManager manager = getPlugin().getBanManager();
if (manager.isBanned(player)) {
Component reason = Component.text(manager.getBan(player).getReason());
event.disallow(PlayerLoginEvent.Result.KICK_BANNED, reason);
}
}
@EventHandler
public void muteCheck(AsyncChatEvent event) {
Player player = event.getPlayer();
PlayerData data = getPlugin().getDataManager().getPlayerData(player.getUniqueId());
if (data == null) return;
if (data.getInfractionData().isMuted()) {
event.setCancelled(true);
player.sendMessage(Component.text("You are muted!"));
}
}
}

View File

@ -10,6 +10,8 @@ import mc.unraveled.reforged.command.TraverseCMD;
import mc.unraveled.reforged.command.base.CommandLoader;
import mc.unraveled.reforged.data.DataManager;
import mc.unraveled.reforged.permission.RankManager;
import mc.unraveled.reforged.service.base.Scheduling;
import mc.unraveled.reforged.service.base.ServicePool;
import mc.unraveled.reforged.storage.DBConnectionHandler;
import mc.unraveled.reforged.storage.DBProperties;
import org.bukkit.plugin.java.JavaPlugin;
@ -27,6 +29,10 @@ public final class Traverse extends JavaPlugin implements Locker {
private BanManager banManager;
@Getter
private RankManager rankManager;
@Getter
private Scheduling scheduler;
@Getter
private ServicePool PIPELINE;
@Override
@SneakyThrows
@ -36,10 +42,16 @@ public final class Traverse extends JavaPlugin implements Locker {
this.commandLoader = new CommandLoader(this, "TRAVERSE");
this.banManager = new BanManager(this);
this.rankManager = new RankManager(this);
this.scheduler = new Scheduling(this);
this.PIPELINE = new ServicePool("PIPELINE", this);
}
@Override
public void onDisable() {
this.banManager.save();
this.dataManager.saveCacheToDB();
this.PIPELINE.recycle();
this.rankManager.save();
// Plugin shutdown logic
}

View File

@ -0,0 +1,41 @@
package mc.unraveled.reforged.service;
import lombok.Getter;
import lombok.Setter;
import mc.unraveled.reforged.data.InfractionData;
import mc.unraveled.reforged.service.base.AbstractService;
import mc.unraveled.reforged.service.base.ServicePool;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import reactor.core.publisher.Mono;
public class MuteService extends AbstractService {
@Setter
private InfractionData infractionData = null;
public MuteService(@Nullable ServicePool parentPool, @NotNull String service_name, long delay) {
super(parentPool, service_name, delay);
}
@Override
public int getServiceId() {
return 0;
}
@Override
public Mono<Void> start() {
if (infractionData == null) return Mono.empty();
if (infractionData.isMuted()) return Mono.empty();
return Mono.create(sink -> {
infractionData.setMuted(false);
sink.success();
});
}
@Override
public Mono<Void> stop() {
return Mono.empty();
}
}

View File

@ -1,28 +0,0 @@
package mc.unraveled.reforged.service;
import mc.unraveled.reforged.service.base.AbstractService;
import mc.unraveled.reforged.service.base.ServicePool;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import reactor.core.publisher.Mono;
public class SimpleService extends AbstractService {
public SimpleService(@Nullable ServicePool parentPool, @NotNull String service_name) {
super(parentPool, service_name);
}
@Override
public int getServiceId() {
return 0;
}
@Override
public Mono<Void> start() {
return null;
}
@Override
public Mono<Void> stop() {
return null;
}
}

View File

@ -20,7 +20,7 @@ public final class ReactorBukkitScheduler
*/
private final BukkitScheduler scheduler;
public ReactorBukkitScheduler(JavaPlugin plugin) {
public ReactorBukkitScheduler(@NotNull JavaPlugin plugin) {
this.plugin = plugin;
this.scheduler = plugin.getServer().getScheduler();
}

View File

@ -40,13 +40,13 @@ public class DBBan {
statement.setString(3, ban.getIp());
statement.setString(4, ban.getReason());
statement.setString(5, ban.getSource());
statement.setLong(6, ban.getPropogated());
statement.setLong(6, ban.getPropagated());
statement.setLong(7, ban.getExpiry());
statement.setBoolean(8, ban.isActive());
statement.setString(9, ban.getIp());
statement.setString(10, ban.getReason());
statement.setString(11, ban.getSource());
statement.setLong(12, ban.getPropogated());
statement.setLong(12, ban.getPropagated());
statement.setLong(13, ban.getExpiry());
statement.setBoolean(14, ban.isActive());
statement.setString(15, ban.getUuid());

View File

@ -0,0 +1,37 @@
package mc.unraveled.reforged.util;
public class TimeUtil {
public static final long SECOND = 1000L;
public static final long MINUTE = SECOND * 60L;
public static final long HOUR = MINUTE * 60L;
public static final long DAY = HOUR * 24L;
public static final long WEEK = DAY * 7L;
public static final long MONTH = DAY * 30L;
public static final long YEAR = DAY * 365L;
public static final long TICK = SECOND / 20L;
public static long parse(String input) {
long duration = 0L;
StringBuilder number = new StringBuilder();
for (char c : input.toCharArray()) {
if (Character.isDigit(c)) {
number.append(c);
} else {
String str = number.toString();
if (str.isEmpty()) continue;
long parsed = Long.parseLong(str);
number = new StringBuilder();
switch (c) {
case 's' -> duration += parsed * SECOND;
case 'm' -> duration += parsed * MINUTE;
case 'h' -> duration += parsed * HOUR;
case 'd' -> duration += parsed * DAY;
case 'w' -> duration += parsed * WEEK;
case 'M' -> duration += parsed * MONTH;
case 'y' -> duration += parsed * YEAR;
}
}
}
return duration;
}
}

View File

@ -1,15 +1,36 @@
package mc.unraveled.reforged.util;
import org.jetbrains.annotations.Contract;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
import reactor.core.publisher.Mono;
import java.lang.reflect.Array;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public final class Utilities {
/**
* A simple method to serialize a list of pairs into a string.
* The second object in the pair can be of any type. However,
* the second object MUST be serializable. The first object is a denotation device,
* which will only use the first letter of your string. This is to save space in the final output.
* For Example:
* <p></p>
* If you were to have a Pair of "name" and "Bob", a pair of "age" and 20, and a pair of "isAlive" and true,
* the output would be
* <p>
* <b>n:Bob;a:20;i:true;</b></p>
* The separator between each string and object is a colon, and the separator between each pair is a semicolon.
* <p></p>
*
* @param objectPairs The list of pairs to serialize.
* @param <T> The type of the second object in the pair.
* @return The serialized string.
*/
public static <T> @NotNull String serialize(@NotNull List<Pair<String, T>> objectPairs) {
char delimiter = ':';
char end = ';';
@ -23,6 +44,15 @@ public final class Utilities {
return builder.toString();
}
/**
* Convenient method to parse a date from a String which contains a numerical value.
* It's important to note that the contents of the string is converted to a LONG, and thus it is
* subject to the same limitations as a long, i.e. {@link Long#MAX_VALUE} and {@link Long#MIN_VALUE}.
* <p>
*
* @param duration The duration to parse.
* @return The parsed date.
*/
public static Date parseDate(String duration) {
TimeUnit unit;
long amount;
@ -39,8 +69,50 @@ public final class Utilities {
return new Date(System.currentTimeMillis() + unit.toMillis(amount));
}
/**
* Converts the date into a readable string, with the format <b>dd/MM/yyyy HH:mm:ss</b>.
* <p>
*
* @param date The date to convert.
* @return The converted date.
*/
public static String parseDateToString(Date date) {
SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
return format.format(date);
}
/**
* Converts seconds into ticks. One second = 20 ticks.
* 1 tick = 1/20th of a second.
* <p>
*
* @param seconds The amount in seconds to convert.
* @return The amount in ticks.
*/
public static long parseSecondsToTicks(long seconds) {
return seconds * 20;
}
/**
* Converts ticks into seconds. One second = 20 ticks.
* 1 tick = 1/20th of a second.
*
* @param ticks The amount in ticks to convert.
* @return The amount in seconds.
*/
public static long parseTicksToSeconds(long ticks) {
return ticks / 20;
}
/**
* This method is necessary because Bukkit's {@link Bukkit#getOfflinePlayer(String)} method
* returns an {@link OfflinePlayer} object, but may execute a blocking web request to retrieve
* the UUID in question. To avoid this, we are wrapping this call in a {@link Mono} object.
*
* @param name The name of the player to retrieve.
* @return A {@link Mono} object containing the {@link OfflinePlayer} object.
*/
public static Mono<OfflinePlayer> getOfflinePlayer(String name) {
return Mono.create(sink -> sink.success(Bukkit.getOfflinePlayer(name)));
}
}

View File

@ -3,6 +3,8 @@ version: '${version}'
main: mc.unraveled.reforged.plugin.Traverse
api-version: 1.19
authors: [ SimplexDevelopment ]
description:
A plugin designed for the Unraveled: Reforged server.
website: https://github.com/SimplexDevelopment
description: "A plugin designed for the Unraveled: Reforged server."
website: https://github.com/SimplexDevelopment/Traverse
depend: [ Vault ]
softdepend: [ EssentialsX, EssentialsXSpawn ]