diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/compat/ReflectionUtil.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/compat/ReflectionUtil.java index 862dbec5..4f0c23e9 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/compat/ReflectionUtil.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/compat/ReflectionUtil.java @@ -28,10 +28,10 @@ package me.lucko.luckperms.bukkit.compat; import org.bukkit.Bukkit; public final class ReflectionUtil { - private static final String SERVER_VERSION = _getServerVersion(); + private static final String SERVER_VERSION = getServerVersion(); private static final boolean CHAT_COMPATIBLE = !SERVER_VERSION.startsWith(".v1_7_"); - private static String _getServerVersion() { + private static String getServerVersion() { Class server = Bukkit.getServer().getClass(); if (!server.getSimpleName().equals("CraftServer")) { return "."; @@ -45,16 +45,12 @@ public final class ReflectionUtil { } } - private static String getServerVersion() { - return SERVER_VERSION; - } - public static boolean isChatCompatible() { return CHAT_COMPATIBLE; } public static String nms(String className) { - return "net.minecraft.server" + getServerVersion() + className; + return "net.minecraft.server" + SERVER_VERSION + className; } public static Class nmsClass(String className) throws ClassNotFoundException { @@ -62,7 +58,7 @@ public final class ReflectionUtil { } public static String obc(String className) { - return "org.bukkit.craftbukkit" + getServerVersion() + className; + return "org.bukkit.craftbukkit" + SERVER_VERSION + className; } public static Class obcClass(String className) throws ClassNotFoundException { diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/BukkitMigrationUtils.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/BukkitUuids.java similarity index 94% rename from bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/BukkitMigrationUtils.java rename to bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/BukkitUuids.java index 8cf8c9bc..d242eed9 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/BukkitMigrationUtils.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/BukkitUuids.java @@ -32,13 +32,13 @@ import org.bukkit.Bukkit; import java.util.UUID; -public final class BukkitMigrationUtils { +public final class BukkitUuids { - @SuppressWarnings("deprecation") public static UUID lookupUuid(ProgressLogger log, String s) { UUID uuid = Uuids.parseNullable(s); if (uuid == null) { try { + //noinspection deprecation uuid = Bukkit.getOfflinePlayer(s).getUniqueId(); } catch (Exception ex) { ex.printStackTrace(); @@ -50,6 +50,6 @@ public final class BukkitMigrationUtils { return uuid; } - private BukkitMigrationUtils() {} + private BukkitUuids() {} } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationBPermissions.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationBPermissions.java index 1542678b..7669851a 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationBPermissions.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationBPermissions.java @@ -155,7 +155,7 @@ public class MigrationBPermissions extends SubCommand { AtomicInteger userCount = new AtomicInteger(0); Iterators.iterate(world.getAll(CalculableType.USER), user -> { // There is no mention of UUIDs in the API. I assume that name = uuid. idk? - UUID uuid = BukkitMigrationUtils.lookupUuid(log, user.getName()); + UUID uuid = BukkitUuids.lookupUuid(log, user.getName()); if (uuid == null) { return; } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationGroupManager.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationGroupManager.java index 019c8997..cb584a3c 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationGroupManager.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationGroupManager.java @@ -163,7 +163,7 @@ public class MigrationGroupManager extends SubCommand { AtomicInteger userWorldCount = new AtomicInteger(0); Iterators.iterate(wdh.getUserList(), user -> { - UUID uuid = BukkitMigrationUtils.lookupUuid(log, user.getUUID()); + UUID uuid = BukkitUuids.lookupUuid(log, user.getUUID()); if (uuid == null) { return; } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationPermissionsBukkit.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationPermissionsBukkit.java index ed3eec46..ef888f6b 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationPermissionsBukkit.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationPermissionsBukkit.java @@ -100,7 +100,7 @@ public class MigrationPermissionsBukkit extends SubCommand { ConfigurationSection usersSection = config.getConfigurationSection("users"); Iterators.iterate(usersSection.getKeys(false), key -> { - UUID uuid = BukkitMigrationUtils.lookupUuid(log, key); + UUID uuid = BukkitUuids.lookupUuid(log, key); if (uuid == null) { return; } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationPermissionsEx.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationPermissionsEx.java index 86e0241d..63d0e4f2 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationPermissionsEx.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationPermissionsEx.java @@ -162,7 +162,7 @@ public class MigrationPermissionsEx extends SubCommand { int userWeight = maxWeight + 5; Iterators.iterate(manager.getUsers(), user -> { - UUID u = BukkitMigrationUtils.lookupUuid(log, user.getIdentifier()); + UUID u = BukkitUuids.lookupUuid(log, user.getIdentifier()); if (u == null) { return; } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationZPermissions.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationZPermissions.java index c9a0689c..c6f42e4f 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationZPermissions.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/migration/MigrationZPermissions.java @@ -116,7 +116,7 @@ public class MigrationZPermissions extends SubCommand { // store user data for later Set members = entity.getMemberships(); for (Membership membership : members) { - UUID uuid = BukkitMigrationUtils.lookupUuid(log, membership.getMember()); + UUID uuid = BukkitUuids.lookupUuid(log, membership.getMember()); if (uuid == null) { continue; } diff --git a/common/src/main/java/me/lucko/luckperms/common/actionlog/ExtendedLogEntry.java b/common/src/main/java/me/lucko/luckperms/common/actionlog/ExtendedLogEntry.java index 72ca6110..a16a9c55 100644 --- a/common/src/main/java/me/lucko/luckperms/common/actionlog/ExtendedLogEntry.java +++ b/common/src/main/java/me/lucko/luckperms/common/actionlog/ExtendedLogEntry.java @@ -36,7 +36,6 @@ import me.lucko.luckperms.common.model.Track; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; -import me.lucko.luckperms.common.utils.DateUtil; import java.util.ArrayList; import java.util.Comparator; @@ -353,7 +352,7 @@ public class ExtendedLogEntry implements LogEntry { @Override public ExtendedLogEntry build() { if (this.timestamp == 0L) { - timestamp(DateUtil.unixSecondsNow()); + timestamp(System.currentTimeMillis() / 1000L); } Objects.requireNonNull(this.actor, "actor"); diff --git a/common/src/main/java/me/lucko/luckperms/common/backup/Exporter.java b/common/src/main/java/me/lucko/luckperms/common/backup/Exporter.java index a2fb6ab5..9036ad68 100644 --- a/common/src/main/java/me/lucko/luckperms/common/backup/Exporter.java +++ b/common/src/main/java/me/lucko/luckperms/common/backup/Exporter.java @@ -36,7 +36,6 @@ import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.references.HolderType; import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.storage.Storage; -import me.lucko.luckperms.common.utils.Cycle; import java.io.BufferedWriter; import java.io.IOException; @@ -52,10 +51,14 @@ import java.util.List; import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; -import java.util.function.Supplier; import java.util.stream.Collectors; /** @@ -180,13 +183,8 @@ public class Exporter implements Runnable { write(writer, "# Export users"); - // divide into 16 pools. - Cycle> userPools = new Cycle<>(nInstances(32, ArrayList::new)); - for (UUID uuid : users) { - userPools.next().add(uuid); - } - - this.log.log("Split users into " + userPools.getBacking().size() + " threads for export."); + // create a threadpool to process the users concurrently + ExecutorService executor = Executors.newFixedThreadPool(32); // Setup a file writing lock. We don't want multiple threads writing at the same time. // The write function accepts a list of strings, as we want a user's data to be grouped together. @@ -203,61 +201,69 @@ public class Exporter implements Runnable { } }; - // A set of futures, which are really just the threads we need to wait for. + // A set of futures, which are really just the processes we need to wait for. Set> futures = new HashSet<>(); AtomicInteger userCount = new AtomicInteger(0); - // iterate through each user sublist. - for (List subList : userPools.getBacking()) { - - // register and start a new thread to process the sublist + // iterate through each user. + for (UUID uuid : users) { + // register a task for the user, and schedule it's execution with the pool futures.add(CompletableFuture.runAsync(() -> { + // actually export the user. this output will be fed to the writing function when we have all of the user's data. + List output = new ArrayList<>(); - // iterate through each user in the sublist, and grab their data. - for (UUID uuid : subList) { - try { - // actually export the user. this output will be fed to the writing function when we have all of the user's data. - List output = new ArrayList<>(); + User user = this.plugin.getStorage().loadUser(uuid, null).join(); + output.add("# Export user: " + user.getUuid().toString() + " - " + user.getName().orElse("unknown username")); - User user = this.plugin.getStorage().loadUser(uuid, null).join(); - output.add("# Export user: " + user.getUuid().toString() + " - " + user.getName().orElse("unknown username")); - - boolean inDefault = false; - for (Node node : user.getEnduringNodes().values()) { - if (node.isGroupNode() && node.getGroupName().equalsIgnoreCase(NodeFactory.DEFAULT_GROUP_NAME)) { - inDefault = true; - continue; - } - - output.add("/lp " + NodeFactory.nodeAsCommand(node, user.getUuid().toString(), HolderType.USER, true)); - } - - if (!user.getPrimaryGroup().getStoredValue().orElse(NodeFactory.DEFAULT_GROUP_NAME).equalsIgnoreCase(NodeFactory.DEFAULT_GROUP_NAME)) { - output.add("/lp user " + user.getUuid().toString() + " switchprimarygroup " + user.getPrimaryGroup().getStoredValue().get()); - } - - if (!inDefault) { - output.add("/lp user " + user.getUuid().toString() + " parent remove default"); - } - - this.plugin.getUserManager().cleanup(user); - writeFunction.accept(output); - - this.log.logProgress("Exported {} users so far.", userCount.incrementAndGet()); - - } catch (Exception e) { - e.printStackTrace(); + boolean inDefault = false; + for (Node node : user.getEnduringNodes().values()) { + if (node.isGroupNode() && node.getGroupName().equalsIgnoreCase(NodeFactory.DEFAULT_GROUP_NAME)) { + inDefault = true; + continue; } + + output.add("/lp " + NodeFactory.nodeAsCommand(node, user.getUuid().toString(), HolderType.USER, true)); } - }, this.plugin.getBootstrap().getScheduler().async())); + + if (!user.getPrimaryGroup().getStoredValue().orElse(NodeFactory.DEFAULT_GROUP_NAME).equalsIgnoreCase(NodeFactory.DEFAULT_GROUP_NAME)) { + output.add("/lp user " + user.getUuid().toString() + " switchprimarygroup " + user.getPrimaryGroup().getStoredValue().get()); + } + + if (!inDefault) { + output.add("/lp user " + user.getUuid().toString() + " parent remove default"); + } + + this.plugin.getUserManager().cleanup(user); + writeFunction.accept(output); + + userCount.incrementAndGet(); + }, executor)); } // all of the threads have been scheduled now and are running. we just need to wait for them all to complete - CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).join(); + CompletableFuture overallFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])); + + while (true) { + try { + overallFuture.get(5, TimeUnit.SECONDS); + } catch (InterruptedException | ExecutionException e) { + // abnormal error - just break + e.printStackTrace(); + break; + } catch (TimeoutException e) { + // still executing - send a progress report and continue waiting + this.log.logAllProgress("Exported {} users so far.", userCount.get()); + continue; + } + + // process is complete + break; + } + + executor.shutdown(); this.log.log("Exported " + userCount.get() + " users."); - writer.flush(); this.log.getListeners().forEach(l -> Message.LOG_EXPORT_SUCCESS.send(l, this.filePath.toFile().getAbsolutePath())); @@ -265,12 +271,4 @@ public class Exporter implements Runnable { e.printStackTrace(); } } - - private static List nInstances(int count, Supplier supplier) { - List ret = new ArrayList<>(count); - for (int i = 0; i < count; i++) { - ret.add(supplier.get()); - } - return ret; - } } diff --git a/common/src/main/java/me/lucko/luckperms/common/backup/Importer.java b/common/src/main/java/me/lucko/luckperms/common/backup/Importer.java index 3ee360dc..17f9cd3e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/backup/Importer.java +++ b/common/src/main/java/me/lucko/luckperms/common/backup/Importer.java @@ -113,10 +113,10 @@ public class Importer implements Runnable { // join the update task future before scheduling command executions updateTask.join(); - // build a list of commands to be executed by each thread + // create a threadpool for the processing ExecutorService executor = Executors.newFixedThreadPool(128); - // A set of futures, which are really just the threads we need to wait for. + // A set of futures, which are really just the processes we need to wait for. Set> futures = new HashSet<>(); AtomicInteger processedCount = new AtomicInteger(0); diff --git a/common/src/main/java/me/lucko/luckperms/common/command/utils/ArgumentParser.java b/common/src/main/java/me/lucko/luckperms/common/command/utils/ArgumentParser.java index 6f9d591f..5b2920ca 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/utils/ArgumentParser.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/utils/ArgumentParser.java @@ -32,7 +32,7 @@ import me.lucko.luckperms.common.command.abstraction.CommandException; import me.lucko.luckperms.common.model.TemporaryModifier; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.storage.DataConstraints; -import me.lucko.luckperms.common.utils.DateUtil; +import me.lucko.luckperms.common.utils.DateParser; import java.util.ArrayList; import java.util.List; @@ -101,19 +101,23 @@ public class ArgumentParser { duration = Long.parseLong(args.get(index)); } catch (NumberFormatException e) { try { - duration = DateUtil.parseDateDiff(args.get(index), true); - } catch (DateUtil.IllegalDateException e1) { + duration = DateParser.parseDate(args.get(index), true); + } catch (IllegalArgumentException e1) { throw new InvalidDateException(args.get(index)); } } - if (DateUtil.shouldExpire(duration)) { + if (shouldExpire(duration)) { throw new PastDateException(); } return duration; } + private static boolean shouldExpire(long unixTime) { + return unixTime < (System.currentTimeMillis() / 1000L); + } + public static Optional parseTemporaryModifier(int index, List args) { if (index < 0 || index >= args.size()) { return Optional.empty(); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaAddTempChatMeta.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaAddTempChatMeta.java index b125ba32..681c0943 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaAddTempChatMeta.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaAddTempChatMeta.java @@ -48,7 +48,7 @@ import me.lucko.luckperms.common.model.TemporaryModifier; import me.lucko.luckperms.common.node.NodeFactory; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; -import me.lucko.luckperms.common.utils.DateUtil; +import me.lucko.luckperms.common.utils.DurationFormatter; import me.lucko.luckperms.common.utils.Predicates; import me.lucko.luckperms.common.utils.TextUtils; @@ -95,7 +95,7 @@ public class MetaAddTempChatMeta extends SharedSubCommand { if (ret.getKey().asBoolean()) { duration = ret.getValue().getExpiryUnixTime(); - TextComponent.Builder builder = TextUtils.fromLegacy(Message.ADD_TEMP_CHATMETA_SUCCESS.asString(plugin.getLocaleManager(), holder.getFriendlyName(), this.type.name().toLowerCase(), meta, priority, DateUtil.formatDateDiff(duration), MessageUtils.contextSetToString(context)), CommandManager.SECTION_CHAR).toBuilder(); + TextComponent.Builder builder = TextUtils.fromLegacy(Message.ADD_TEMP_CHATMETA_SUCCESS.asString(plugin.getLocaleManager(), holder.getFriendlyName(), this.type.name().toLowerCase(), meta, priority, DurationFormatter.LONG.formatDateDiff(duration), MessageUtils.contextSetToString(context)), CommandManager.SECTION_CHAR).toBuilder(); HoverEvent event = new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextUtils.fromLegacy( "¥3Raw " + this.type.name().toLowerCase() + ": ¥r" + meta, '¥' diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaSetTemp.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaSetTemp.java index 32096d6e..47a6e2fb 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaSetTemp.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaSetTemp.java @@ -46,7 +46,7 @@ import me.lucko.luckperms.common.model.TemporaryModifier; import me.lucko.luckperms.common.node.NodeFactory; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; -import me.lucko.luckperms.common.utils.DateUtil; +import me.lucko.luckperms.common.utils.DurationFormatter; import me.lucko.luckperms.common.utils.Predicates; import me.lucko.luckperms.common.utils.TextUtils; @@ -93,7 +93,7 @@ public class MetaSetTemp extends SharedSubCommand { holder.clearMetaKeys(key, context, true); duration = holder.setPermission(n, modifier).getValue().getExpiryUnixTime(); - TextComponent.Builder builder = TextUtils.fromLegacy(Message.SET_META_TEMP_SUCCESS.asString(plugin.getLocaleManager(), key, value, holder.getFriendlyName(), DateUtil.formatDateDiff(duration), MessageUtils.contextSetToString(context)), CommandManager.SECTION_CHAR).toBuilder(); + TextComponent.Builder builder = TextUtils.fromLegacy(Message.SET_META_TEMP_SUCCESS.asString(plugin.getLocaleManager(), key, value, holder.getFriendlyName(), DurationFormatter.LONG.formatDateDiff(duration), MessageUtils.contextSetToString(context)), CommandManager.SECTION_CHAR).toBuilder(); HoverEvent event = new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextUtils.fromLegacy( TextUtils.joinNewline("¥3Raw key: ¥r" + key, "¥3Raw value: ¥r" + value), '¥' diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentAddTemp.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentAddTemp.java index 22056e50..fce90431 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentAddTemp.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentAddTemp.java @@ -47,7 +47,7 @@ import me.lucko.luckperms.common.model.TemporaryModifier; import me.lucko.luckperms.common.node.NodeFactory; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; -import me.lucko.luckperms.common.utils.DateUtil; +import me.lucko.luckperms.common.utils.DurationFormatter; import me.lucko.luckperms.common.utils.Predicates; import java.util.List; @@ -96,7 +96,7 @@ public class ParentAddTemp extends SharedSubCommand { if (ret.getKey().asBoolean()) { duration = ret.getValue().getExpiryUnixTime(); - Message.SET_TEMP_INHERIT_SUCCESS.send(sender, holder.getFriendlyName(), group.getFriendlyName(), DateUtil.formatDateDiff(duration), MessageUtils.contextSetToString(context)); + Message.SET_TEMP_INHERIT_SUCCESS.send(sender, holder.getFriendlyName(), group.getFriendlyName(), DurationFormatter.LONG.formatDateDiff(duration), MessageUtils.contextSetToString(context)); ExtendedLogEntry.build().actor(sender).acted(holder) .action("parent", "addtemp", group.getName(), duration, context) diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentInfo.java index a38a6c07..32c46481 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentInfo.java @@ -45,7 +45,7 @@ import me.lucko.luckperms.common.node.NodeWithContextComparator; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.utils.CollationKeyCache; -import me.lucko.luckperms.common.utils.DateUtil; +import me.lucko.luckperms.common.utils.DurationFormatter; import me.lucko.luckperms.common.utils.Iterators; import me.lucko.luckperms.common.utils.Predicates; import me.lucko.luckperms.common.utils.TextUtils; @@ -115,7 +115,7 @@ public class ParentInfo extends SharedSubCommand { for (LocalizedNode node : content) { String s = "&3> &a" + node.getGroupName() + MessageUtils.getAppendableNodeContextString(node); if (node.isTemporary()) { - s += "\n&2 expires in " + DateUtil.formatDateDiff(node.getExpiryUnixTime()); + s += "\n&2 expires in " + DurationFormatter.LONG.formatDateDiff(node.getExpiryUnixTime()); } TextComponent message = TextUtils.fromLegacy(s, CommandManager.AMPERSAND_CHAR).toBuilder().applyDeep(makeFancy(holder, label, node)).build(); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionInfo.java index 20d126e2..1b56f965 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionInfo.java @@ -45,7 +45,7 @@ import me.lucko.luckperms.common.node.NodeWithContextComparator; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.utils.CollationKeyCache; -import me.lucko.luckperms.common.utils.DateUtil; +import me.lucko.luckperms.common.utils.DurationFormatter; import me.lucko.luckperms.common.utils.Iterators; import me.lucko.luckperms.common.utils.Predicates; import me.lucko.luckperms.common.utils.TextUtils; @@ -120,7 +120,7 @@ public class PermissionInfo extends SharedSubCommand { for (LocalizedNode node : content) { String s = "&3> " + (node.getValuePrimitive() ? "&a" : "&c") + node.getPermission() + (sender.isConsole() ? " &7(" + node.getValuePrimitive() + "&7)" : "") + MessageUtils.getAppendableNodeContextString(node); if (node.isTemporary()) { - s += "\n&2- expires in " + DateUtil.formatDateDiff(node.getExpiryUnixTime()); + s += "\n&2- expires in " + DurationFormatter.LONG.formatDateDiff(node.getExpiryUnixTime()); } TextComponent message = TextUtils.fromLegacy(s, CommandManager.AMPERSAND_CHAR).toBuilder().applyDeep(makeFancy(holder, label, node)).build(); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionSetTemp.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionSetTemp.java index 03e9e30b..23f0fb65 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionSetTemp.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionSetTemp.java @@ -46,7 +46,7 @@ import me.lucko.luckperms.common.model.TemporaryModifier; import me.lucko.luckperms.common.node.NodeFactory; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; -import me.lucko.luckperms.common.utils.DateUtil; +import me.lucko.luckperms.common.utils.DurationFormatter; import me.lucko.luckperms.common.utils.Predicates; import java.util.List; @@ -87,7 +87,7 @@ public class PermissionSetTemp extends SharedSubCommand { if (result.getKey().asBoolean()) { duration = result.getValue().getExpiryUnixTime(); - Message.SETPERMISSION_TEMP_SUCCESS.send(sender, node, value, holder.getFriendlyName(), DateUtil.formatDateDiff(duration), MessageUtils.contextSetToString(context)); + Message.SETPERMISSION_TEMP_SUCCESS.send(sender, node, value, holder.getFriendlyName(), DurationFormatter.LONG.formatDateDiff(duration), MessageUtils.contextSetToString(context)); ExtendedLogEntry.build().actor(sender).acted(holder) .action("permission", "settemp", node, value, duration, context) diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupInfo.java index 6feda446..f5b1d7eb 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupInfo.java @@ -37,7 +37,7 @@ import me.lucko.luckperms.common.locale.message.Message; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; -import me.lucko.luckperms.common.utils.DateUtil; +import me.lucko.luckperms.common.utils.DurationFormatter; import me.lucko.luckperms.common.utils.Predicates; import java.util.List; @@ -83,7 +83,7 @@ public class GroupInfo extends SubCommand { Message.INFO_TEMP_PARENT_HEADER.send(sender); for (Node node : tempParents) { Message.EMPTY.send(sender, "&f- &3> &f" + node.getGroupName() + MessageUtils.getAppendableNodeContextString(node)); - Message.EMPTY.send(sender, "&f- &2- expires in " + DateUtil.formatDateDiff(node.getExpiryUnixTime())); + Message.EMPTY.send(sender, "&f- &2- expires in " + DurationFormatter.LONG.formatDateDiff(node.getExpiryUnixTime())); } } return CommandResult.SUCCESS; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupListMembers.java b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupListMembers.java index dd9a640b..7ccaab0a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupListMembers.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupListMembers.java @@ -46,7 +46,7 @@ import me.lucko.luckperms.common.node.NodeFactory; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.references.HolderType; import me.lucko.luckperms.common.sender.Sender; -import me.lucko.luckperms.common.utils.DateUtil; +import me.lucko.luckperms.common.utils.DurationFormatter; import me.lucko.luckperms.common.utils.Iterators; import me.lucko.luckperms.common.utils.Predicates; import me.lucko.luckperms.common.utils.TextUtils; @@ -150,7 +150,7 @@ public class GroupListMembers extends SubCommand { return ""; } - return " &8(&7expires in " + DateUtil.formatDateDiff(node.getExpiryUnixTime()) + "&8)"; + return " &8(&7expires in " + DurationFormatter.LONG.formatDateDiff(node.getExpiryUnixTime()) + "&8)"; } private static Consumer> makeFancy(String holderName, HolderType holderType, String label, HeldPermission perm) { diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogGroupHistory.java b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogGroupHistory.java index 65669cae..331b7003 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogGroupHistory.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogGroupHistory.java @@ -37,7 +37,7 @@ import me.lucko.luckperms.common.locale.message.Message; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.storage.DataConstraints; -import me.lucko.luckperms.common.utils.DateUtil; +import me.lucko.luckperms.common.utils.DurationFormatter; import me.lucko.luckperms.common.utils.Predicates; import java.util.List; @@ -90,12 +90,12 @@ public class LogGroupHistory extends SubCommand { String name = entries.values().stream().findAny().get().getActedName(); Message.LOG_HISTORY_GROUP_HEADER.send(sender, name, page, maxPage); - long now = DateUtil.unixSecondsNow(); + long now = System.currentTimeMillis() / 1000L; for (Map.Entry e : entries.entrySet()) { long time = e.getValue().getTimestamp(); Message.LOG_ENTRY.send(sender, e.getKey(), - DateUtil.formatTimeBrief(now - time), + DurationFormatter.CONCISE_LOW_ACCURACY.format(now - time), e.getValue().getActorFriendlyString(), Character.toString(e.getValue().getType().getCode()), e.getValue().getActedFriendlyString(), diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogRecent.java b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogRecent.java index ed97ed21..8552d8d5 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogRecent.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogRecent.java @@ -37,7 +37,7 @@ import me.lucko.luckperms.common.locale.message.Message; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.storage.DataConstraints; -import me.lucko.luckperms.common.utils.DateUtil; +import me.lucko.luckperms.common.utils.DurationFormatter; import me.lucko.luckperms.common.utils.Predicates; import me.lucko.luckperms.common.utils.Uuids; @@ -139,12 +139,12 @@ public class LogRecent extends SubCommand { Message.LOG_RECENT_HEADER.send(sender, page, maxPage); } - long now = DateUtil.unixSecondsNow(); + long now = System.currentTimeMillis() / 1000L; for (Map.Entry e : entries.entrySet()) { long time = e.getValue().getTimestamp(); Message.LOG_ENTRY.send(sender, e.getKey(), - DateUtil.formatTimeBrief(now - time), + DurationFormatter.CONCISE_LOW_ACCURACY.format(now - time), e.getValue().getActorFriendlyString(), Character.toString(e.getValue().getType().getCode()), e.getValue().getActedFriendlyString(), diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogSearch.java b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogSearch.java index 540854c5..411568c2 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogSearch.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogSearch.java @@ -35,7 +35,7 @@ import me.lucko.luckperms.common.locale.command.CommandSpec; import me.lucko.luckperms.common.locale.message.Message; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; -import me.lucko.luckperms.common.utils.DateUtil; +import me.lucko.luckperms.common.utils.DurationFormatter; import me.lucko.luckperms.common.utils.Predicates; import java.util.List; @@ -81,12 +81,12 @@ public class LogSearch extends SubCommand { SortedMap entries = log.getSearch(page, query, ENTRIES_PER_PAGE); Message.LOG_SEARCH_HEADER.send(sender, query, page, maxPage); - long now = DateUtil.unixSecondsNow(); + long now = System.currentTimeMillis() / 1000L; for (Map.Entry e : entries.entrySet()) { long time = e.getValue().getTimestamp(); Message.LOG_ENTRY.send(sender, e.getKey(), - DateUtil.formatTimeBrief(now - time), + DurationFormatter.CONCISE_LOW_ACCURACY.format(now - time), e.getValue().getActorFriendlyString(), Character.toString(e.getValue().getType().getCode()), e.getValue().getActedFriendlyString(), diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogTrackHistory.java b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogTrackHistory.java index 692638b8..526bb1a7 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogTrackHistory.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogTrackHistory.java @@ -37,7 +37,7 @@ import me.lucko.luckperms.common.locale.message.Message; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.storage.DataConstraints; -import me.lucko.luckperms.common.utils.DateUtil; +import me.lucko.luckperms.common.utils.DurationFormatter; import me.lucko.luckperms.common.utils.Predicates; import java.util.List; @@ -90,12 +90,12 @@ public class LogTrackHistory extends SubCommand { String name = entries.values().stream().findAny().get().getActedName(); Message.LOG_HISTORY_TRACK_HEADER.send(sender, name, page, maxPage); - long now = DateUtil.unixSecondsNow(); + long now = System.currentTimeMillis() / 1000L; for (Map.Entry e : entries.entrySet()) { long time = e.getValue().getTimestamp(); Message.LOG_ENTRY.send(sender, e.getKey(), - DateUtil.formatTimeBrief(now - time), + DurationFormatter.CONCISE_LOW_ACCURACY.format(now - time), e.getValue().getActorFriendlyString(), Character.toString(e.getValue().getType().getCode()), e.getValue().getActedFriendlyString(), diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogUserHistory.java b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogUserHistory.java index b60cbce6..3442df45 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogUserHistory.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogUserHistory.java @@ -37,7 +37,7 @@ import me.lucko.luckperms.common.locale.message.Message; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.storage.DataConstraints; -import me.lucko.luckperms.common.utils.DateUtil; +import me.lucko.luckperms.common.utils.DurationFormatter; import me.lucko.luckperms.common.utils.Predicates; import me.lucko.luckperms.common.utils.Uuids; @@ -119,12 +119,12 @@ public class LogUserHistory extends SubCommand { String name = entries.values().stream().findAny().get().getActedName(); Message.LOG_HISTORY_USER_HEADER.send(sender, name, page, maxPage); - long now = DateUtil.unixSecondsNow(); + long now = System.currentTimeMillis() / 1000L; for (Map.Entry e : entries.entrySet()) { long time = e.getValue().getTimestamp(); Message.LOG_ENTRY.send(sender, e.getKey(), - DateUtil.formatTimeBrief(now - time), + DurationFormatter.CONCISE_LOW_ACCURACY.format(now - time), e.getValue().getActorFriendlyString(), Character.toString(e.getValue().getType().getCode()), e.getValue().getActedFriendlyString(), diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/ApplyEditsCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/ApplyEditsCommand.java index 26faa785..f5d9dca4 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/ApplyEditsCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/ApplyEditsCommand.java @@ -45,7 +45,7 @@ import me.lucko.luckperms.common.model.PermissionHolder; import me.lucko.luckperms.common.node.NodeModel; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; -import me.lucko.luckperms.common.utils.DateUtil; +import me.lucko.luckperms.common.utils.DurationFormatter; import me.lucko.luckperms.common.utils.Predicates; import me.lucko.luckperms.common.web.WebEditor; @@ -141,7 +141,7 @@ public class ApplyEditsCommand extends SingleCommand { private static String formatNode(Node n) { return n.getPermission() + " &7(" + (n.getValuePrimitive() ? "&a" : "&c") + n.getValuePrimitive() + "&7)" + MessageUtils.getAppendableNodeContextString(n) + - (n.isTemporary() ? " &7(" + DateUtil.formatDateDiffShort(n.getExpiryUnixTime()) + ")" : ""); + (n.isTemporary() ? " &7(" + DurationFormatter.CONCISE.formatDateDiff(n.getExpiryUnixTime()) + ")" : ""); } private static Map.Entry, Set> diff(Set before, Set after) { diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/InfoCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/InfoCommand.java index dab5352d..9c6dad1f 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/InfoCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/InfoCommand.java @@ -35,7 +35,7 @@ import me.lucko.luckperms.common.locale.message.Message; import me.lucko.luckperms.common.messaging.InternalMessagingService; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; -import me.lucko.luckperms.common.utils.DateUtil; +import me.lucko.luckperms.common.utils.DurationFormatter; import me.lucko.luckperms.common.utils.Predicates; import java.util.List; @@ -66,7 +66,7 @@ public class InfoCommand extends SingleCommand { plugin.getContextManager().getStaticContextString().orElse("None"), plugin.getBootstrap().getPlayerCount(), plugin.getConnectionListener().getUniqueConnections().size(), - DateUtil.formatTimeBrief((System.currentTimeMillis() - plugin.getBootstrap().getStartupTime()) / 1000L), + DurationFormatter.CONCISE_LOW_ACCURACY.format((System.currentTimeMillis() - plugin.getBootstrap().getStartupTime()) / 1000L), plugin.getUserManager().getAll().size(), plugin.getGroupManager().getAll().size(), plugin.getTrackManager().getAll().size() diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/SearchCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/SearchCommand.java index 898000d3..e80a5502 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/SearchCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/SearchCommand.java @@ -45,7 +45,7 @@ import me.lucko.luckperms.common.node.NodeFactory; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.references.HolderType; import me.lucko.luckperms.common.sender.Sender; -import me.lucko.luckperms.common.utils.DateUtil; +import me.lucko.luckperms.common.utils.DurationFormatter; import me.lucko.luckperms.common.utils.Iterators; import me.lucko.luckperms.common.utils.Predicates; import me.lucko.luckperms.common.utils.TextUtils; @@ -149,7 +149,7 @@ public class SearchCommand extends SingleCommand { return ""; } - return " &8(&7expires in " + DateUtil.formatDateDiff(node.getExpiryUnixTime()) + "&8)"; + return " &8(&7expires in " + DurationFormatter.LONG.formatDateDiff(node.getExpiryUnixTime()) + "&8)"; } private static Consumer> makeFancy(String holderName, HolderType holderType, String label, HeldPermission perm) { diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/user/UserInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/user/UserInfo.java index 402b3ad4..a1db7835 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/user/UserInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/user/UserInfo.java @@ -42,7 +42,7 @@ import me.lucko.luckperms.common.locale.message.Message; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; -import me.lucko.luckperms.common.utils.DateUtil; +import me.lucko.luckperms.common.utils.DurationFormatter; import me.lucko.luckperms.common.utils.Predicates; import java.util.List; @@ -93,7 +93,7 @@ public class UserInfo extends SubCommand { Message.INFO_TEMP_PARENT_HEADER.send(sender); for (Node node : tempParents) { Message.EMPTY.send(sender, "&f- &3> &f" + node.getGroupName() + MessageUtils.getAppendableNodeContextString(node)); - Message.EMPTY.send(sender, "&f- &2- expires in " + DateUtil.formatDateDiff(node.getExpiryUnixTime())); + Message.EMPTY.send(sender, "&f- &2- expires in " + DurationFormatter.LONG.formatDateDiff(node.getExpiryUnixTime())); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/event/LuckPermsEventBus.java b/common/src/main/java/me/lucko/luckperms/common/event/LuckPermsEventBus.java index 7a9fc82a..bd79f389 100644 --- a/common/src/main/java/me/lucko/luckperms/common/event/LuckPermsEventBus.java +++ b/common/src/main/java/me/lucko/luckperms/common/event/LuckPermsEventBus.java @@ -25,7 +25,12 @@ package me.lucko.luckperms.common.event; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; import me.lucko.luckperms.api.event.Cancellable; import me.lucko.luckperms.api.event.EventBus; @@ -34,7 +39,8 @@ import me.lucko.luckperms.api.event.LuckPermsEvent; import me.lucko.luckperms.common.api.LuckPermsApiProvider; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; -import java.util.Map; +import java.util.Collection; +import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -42,13 +48,42 @@ import java.util.function.Consumer; import javax.annotation.Nonnull; +/** + * Simple implementation of EventBus. + */ public class LuckPermsEventBus implements EventBus { + /** + * The plugin instance + */ private final LuckPermsPlugin plugin; + /** + * The api provider instance + */ private final LuckPermsApiProvider apiProvider; - private final Map, Set>> handlerMap = new ConcurrentHashMap<>(); + /** + * The registered handlers in this event bus + */ + private final Multimap, LuckPermsEventHandler> handlerMap = Multimaps.newSetMultimap(new ConcurrentHashMap<>(), ConcurrentHashMap::newKeySet); + + /** + * A cache of event class --> applicable handlers. + * + * A registered "handler" will be passed all possible events it can handle, according to + * {@link Class#isAssignableFrom(Class)}. + */ + private final LoadingCache, List>> handlerCache = Caffeine.newBuilder() + .build(eventClass -> { + ImmutableList.Builder> matched = ImmutableList.builder(); + LuckPermsEventBus.this.handlerMap.asMap().forEach((clazz, handlers) -> { + if (clazz.isAssignableFrom(eventClass)) { + matched.addAll(handlers); + } + }); + return matched.build(); + }); public LuckPermsEventBus(LuckPermsPlugin plugin, LuckPermsApiProvider apiProvider) { this.plugin = plugin; @@ -68,36 +103,28 @@ public class LuckPermsEventBus implements EventBus { throw new IllegalArgumentException("class " + eventClass.getName() + " does not implement LuckPermsEvent"); } - Set> handlers = this.handlerMap.computeIfAbsent(eventClass, c -> ConcurrentHashMap.newKeySet()); - LuckPermsEventHandler eventHandler = new LuckPermsEventHandler<>(this, eventClass, handler); - handlers.add(eventHandler); + this.handlerMap.put(eventClass, eventHandler); + this.handlerCache.invalidateAll(); return eventHandler; } @Nonnull @Override - @SuppressWarnings("unchecked") public Set> getHandlers(@Nonnull Class eventClass) { - Set> handlers = this.handlerMap.get(eventClass); - if (handlers == null) { - return ImmutableSet.of(); - } else { - ImmutableSet.Builder> ret = ImmutableSet.builder(); - for (LuckPermsEventHandler handler : handlers) { - ret.add((EventHandler) handler); - } - - return ret.build(); + Collection> handlers = this.handlerMap.asMap().get(eventClass); + ImmutableSet.Builder> ret = ImmutableSet.builder(); + for (LuckPermsEventHandler handler : handlers) { + //noinspection unchecked + ret.add((EventHandler) handler); } + return ret.build(); } public void unregisterHandler(LuckPermsEventHandler handler) { - Set> handlers = this.handlerMap.get(handler.getEventClass()); - if (handlers != null) { - handlers.remove(handler); - } + this.handlerMap.remove(handler.getEventClass(), handler); + this.handlerCache.invalidateAll(); } public void fireEvent(LuckPermsEvent event) { @@ -105,12 +132,13 @@ public class LuckPermsEventBus implements EventBus { ((AbstractEvent) event).setApi(this.apiProvider); } - for (Map.Entry, Set>> ent : this.handlerMap.entrySet()) { - if (!ent.getKey().isAssignableFrom(event.getClass())) { - continue; - } + List> handlers = this.handlerCache.get(event.getClass()); + if (handlers == null) { + return; + } - ent.getValue().forEach(h -> h.handle(event)); + for (LuckPermsEventHandler handler : handlers) { + handler.handle(event); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/node/ImmutableNode.java b/common/src/main/java/me/lucko/luckperms/common/node/ImmutableNode.java index cb4764c8..34c0087c 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/ImmutableNode.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/ImmutableNode.java @@ -34,7 +34,6 @@ import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.api.context.MutableContextSet; import me.lucko.luckperms.common.processors.WildcardProcessor; -import me.lucko.luckperms.common.utils.DateUtil; import java.util.Date; import java.util.List; @@ -248,12 +247,12 @@ public final class ImmutableNode implements Node { @Override public long getSecondsTilExpiry() { checkState(isTemporary(), "Node does not have an expiry time."); - return this.expireAt - DateUtil.unixSecondsNow(); + return this.expireAt - System.currentTimeMillis() / 1000L; } @Override public boolean hasExpired() { - return isTemporary() && this.expireAt < DateUtil.unixSecondsNow(); + return isTemporary() && this.expireAt < System.currentTimeMillis() / 1000L; } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/node/LegacyNodeFactory.java b/common/src/main/java/me/lucko/luckperms/common/node/LegacyNodeFactory.java index a8b223f6..4bf22b3c 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/LegacyNodeFactory.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/LegacyNodeFactory.java @@ -58,16 +58,16 @@ public final class LegacyNodeFactory { private static final String[] GENERIC_DELIMITERS = new String[]{".", "/", "-", "$"}; // legacy node format delimiters - private static final Pattern LEGACY_SERVER_DELIM = PatternCache.compileDelimitedMatcher("/", "\\"); + private static final Pattern LEGACY_SERVER_DELIM = PatternCache.compileDelimiterPattern("/", "\\"); private static final Splitter LEGACY_SERVER_SPLITTER = Splitter.on(LEGACY_SERVER_DELIM).limit(2); - private static final Pattern LEGACY_WORLD_DELIM = PatternCache.compileDelimitedMatcher("-", "\\"); + private static final Pattern LEGACY_WORLD_DELIM = PatternCache.compileDelimiterPattern("-", "\\"); private static final Splitter LEGACY_WORLD_SPLITTER = Splitter.on(LEGACY_WORLD_DELIM).limit(2); - private static final Pattern LEGACY_EXPIRY_DELIM = PatternCache.compileDelimitedMatcher("$", "\\"); + private static final Pattern LEGACY_EXPIRY_DELIM = PatternCache.compileDelimiterPattern("$", "\\"); private static final Splitter LEGACY_EXPIRY_SPLITTER = Splitter.on(LEGACY_EXPIRY_DELIM).limit(2); - private static final Pattern LEGACY_CONTEXT_DELIM = PatternCache.compileDelimitedMatcher(")", "\\"); + private static final Pattern LEGACY_CONTEXT_DELIM = PatternCache.compileDelimiterPattern(")", "\\"); private static final Splitter CONTEXT_SPLITTER = Splitter.on(LEGACY_CONTEXT_DELIM).limit(2); - private static final Pattern LEGACY_CONTEXT_PAIR_DELIM = PatternCache.compileDelimitedMatcher(",", "\\"); - private static final Pattern LEGACY_CONTEXT_PAIR_PART_DELIM = PatternCache.compileDelimitedMatcher("=", "\\"); + private static final Pattern LEGACY_CONTEXT_PAIR_DELIM = PatternCache.compileDelimiterPattern(",", "\\"); + private static final Pattern LEGACY_CONTEXT_PAIR_PART_DELIM = PatternCache.compileDelimiterPattern("=", "\\"); private static final Splitter.MapSplitter LEGACY_CONTEXT_PART_SPLITTER = Splitter.on(LEGACY_CONTEXT_PAIR_DELIM) .withKeyValueSeparator(Splitter.on(LEGACY_CONTEXT_PAIR_PART_DELIM)); diff --git a/common/src/main/java/me/lucko/luckperms/common/node/NodeFactory.java b/common/src/main/java/me/lucko/luckperms/common/node/NodeFactory.java index a47888bc..ba148643 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/NodeFactory.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/NodeFactory.java @@ -56,7 +56,7 @@ public final class NodeFactory { private static final String WEIGHT_NODE_MARKER = WEIGHT_KEY + "."; // used to split prefix/suffix/meta nodes - private static final Splitter META_SPLITTER = Splitter.on(PatternCache.compileDelimitedMatcher(".", "\\")).limit(2); + private static final Splitter META_SPLITTER = Splitter.on(PatternCache.compileDelimiterPattern(".", "\\")).limit(2); public static Node.Builder builder(String s) { return new NodeBuilder(s); diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/DataConstraints.java b/common/src/main/java/me/lucko/luckperms/common/storage/DataConstraints.java index e5ab999e..4a976eb4 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/DataConstraints.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/DataConstraints.java @@ -25,8 +25,6 @@ package me.lucko.luckperms.common.storage; -import me.lucko.luckperms.common.utils.DateUtil; - import java.util.function.Predicate; import java.util.regex.Pattern; @@ -57,8 +55,6 @@ public final class DataConstraints { public static final Predicate TRACK_NAME_TEST_ALLOW_SPACE = s -> !s.isEmpty() && s.length() <= MAX_TRACK_NAME_LENGTH; - public static final Predicate TIME_TEST = unixTime -> !DateUtil.shouldExpire(unixTime); - public static final Predicate SERVER_NAME_TEST = s -> !s.isEmpty() && s.length() <= MAX_SERVER_LENGTH && !s.contains(" "); public static final Predicate WORLD_NAME_TEST = s -> !s.isEmpty() && s.length() <= MAX_WORLD_LENGTH; diff --git a/common/src/main/java/me/lucko/luckperms/common/utils/Cycle.java b/common/src/main/java/me/lucko/luckperms/common/utils/Cycle.java deleted file mode 100644 index 48563f22..00000000 --- a/common/src/main/java/me/lucko/luckperms/common/utils/Cycle.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * This file is part of LuckPerms, licensed under the MIT License. - * - * Copyright (c) lucko (Luck) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package me.lucko.luckperms.common.utils; - -import com.google.common.collect.ImmutableList; - -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * A cycle of elements, backed by a list. All operations are thread safe. - * - * @param the element type - */ -public class Cycle { - - /** - * The list that backs this instance - */ - private final List objects; - - /** - * The number of elements in the cycle - */ - private final int size; - - /** - * The current position of the cursor - */ - private final AtomicInteger cursor = new AtomicInteger(0); - - public Cycle(List objects) { - if (objects == null || objects.isEmpty()) { - throw new IllegalArgumentException("List of objects cannot be null/empty."); - } - this.objects = ImmutableList.copyOf(objects); - this.size = this.objects.size(); - } - - public int cursor() { - return this.cursor.get(); - } - - public E current() { - return this.objects.get(cursor()); - } - - public E next() { - return this.objects.get(this.cursor.updateAndGet(i -> { - int n = i + 1; - if (n >= this.size) { - return 0; - } - return n; - })); - } - - public E previous() { - return this.objects.get(this.cursor.updateAndGet(i -> { - if (i == 0) { - return this.size - 1; - } - return i - 1; - })); - } - - public List getBacking() { - return this.objects; - } -} \ No newline at end of file diff --git a/common/src/main/java/me/lucko/luckperms/common/utils/DateParser.java b/common/src/main/java/me/lucko/luckperms/common/utils/DateParser.java new file mode 100644 index 00000000..e09194bd --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/utils/DateParser.java @@ -0,0 +1,133 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.utils; + +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Translates unix timestamps / durations into a readable format + * + * @author khobbits, drtshock, vemacs + * see: https://github.com/drtshock/Essentials/blob/2.x/Essentials/src/com/earth2me/essentials/utils/DateUtil.java + */ +public final class DateParser { + private static final Pattern TIME_PATTERN = Pattern.compile("(?:([0-9]+)\\s*y[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*mo[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*w[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*d[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*h[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*m[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*(?:s[a-z]*)?)?", Pattern.CASE_INSENSITIVE); + private static final int MAX_YEARS = 100000; + + /** + * Converts a time string to a unix timestamp + * + * @param time the time string + * @param future if the date is in the future, as opposed to the past + * @return a unix timestamp + * @throws IllegalArgumentException if the date input was invalid + */ + public static long parseDate(String time, boolean future) throws IllegalArgumentException { + Matcher matcher = TIME_PATTERN.matcher(time); + int years = 0, months = 0, weeks = 0, days = 0, hours = 0, minutes = 0, seconds = 0; + + boolean found = false; + while (matcher.find()) { + if (matcher.group() == null || matcher.group().isEmpty()) { + continue; + } + for (int i = 0; i < matcher.groupCount(); i++) { + if (matcher.group(i) != null && !matcher.group(i).isEmpty()) { + found = true; + break; + } + } + if (found) { + if (matcher.group(1) != null && !matcher.group(1).isEmpty()) { + years = Integer.parseInt(matcher.group(1)); + } + if (matcher.group(2) != null && !matcher.group(2).isEmpty()) { + months = Integer.parseInt(matcher.group(2)); + } + if (matcher.group(3) != null && !matcher.group(3).isEmpty()) { + weeks = Integer.parseInt(matcher.group(3)); + } + if (matcher.group(4) != null && !matcher.group(4).isEmpty()) { + days = Integer.parseInt(matcher.group(4)); + } + if (matcher.group(5) != null && !matcher.group(5).isEmpty()) { + hours = Integer.parseInt(matcher.group(5)); + } + if (matcher.group(6) != null && !matcher.group(6).isEmpty()) { + minutes = Integer.parseInt(matcher.group(6)); + } + if (matcher.group(7) != null && !matcher.group(7).isEmpty()) { + seconds = Integer.parseInt(matcher.group(7)); + } + break; + } + } + + if (!found) { + throw new IllegalArgumentException(); + } + + Calendar c = new GregorianCalendar(); + if (years > 0) { + if (years > MAX_YEARS) { + years = MAX_YEARS; + } + c.add(Calendar.YEAR, years * (future ? 1 : -1)); + } + if (months > 0) { + c.add(Calendar.MONTH, months * (future ? 1 : -1)); + } + if (weeks > 0) { + c.add(Calendar.WEEK_OF_YEAR, weeks * (future ? 1 : -1)); + } + if (days > 0) { + c.add(Calendar.DAY_OF_MONTH, days * (future ? 1 : -1)); + } + if (hours > 0) { + c.add(Calendar.HOUR_OF_DAY, hours * (future ? 1 : -1)); + } + if (minutes > 0) { + c.add(Calendar.MINUTE, minutes * (future ? 1 : -1)); + } + if (seconds > 0) { + c.add(Calendar.SECOND, seconds * (future ? 1 : -1)); + } + + Calendar max = new GregorianCalendar(); + max.add(Calendar.YEAR, 10); + + if (c.after(max)) { + return (max.getTimeInMillis() / 1000) + 1; + } + return (c.getTimeInMillis() / 1000) + 1; + } + + private DateParser() {} + +} \ No newline at end of file diff --git a/common/src/main/java/me/lucko/luckperms/common/utils/DateUtil.java b/common/src/main/java/me/lucko/luckperms/common/utils/DateUtil.java deleted file mode 100644 index f4a0891e..00000000 --- a/common/src/main/java/me/lucko/luckperms/common/utils/DateUtil.java +++ /dev/null @@ -1,262 +0,0 @@ -/* - * This file is part of LuckPerms, licensed under the MIT License. - * - * Copyright (c) lucko (Luck) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* - * All credit to Essentials / EssentialsX for this class - * https://github.com/drtshock/Essentials/blob/2.x/Essentials/src/com/earth2me/essentials/utils/DateUtil.java - * https://github.com/essentials/Essentials/blob/2.x/Essentials/src/com/earth2me/essentials/utils/DateUtil.java - */ - -package me.lucko.luckperms.common.utils; - -import java.util.Calendar; -import java.util.GregorianCalendar; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Translates unix timestamps / durations into a readable format - */ -public final class DateUtil { - private static final Pattern TIME_PATTERN = Pattern.compile("(?:([0-9]+)\\s*y[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*mo[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*w[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*d[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*h[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*m[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*(?:s[a-z]*)?)?", Pattern.CASE_INSENSITIVE); - private static final int MAX_YEARS = 100000; - - public static long unixSecondsNow() { - return System.currentTimeMillis() / 1000L; - } - - public static boolean shouldExpire(long unixTime) { - return unixTime < (unixSecondsNow()); - } - - /** - * Converts a time string to a unix timestamp - * - * @param time the time string - * @param future if the date is in the future, as opposed to the past - * @return a unix timestamp - * @throws IllegalDateException if the date input was invalid - */ - public static long parseDateDiff(String time, boolean future) throws IllegalDateException { - Matcher m = TIME_PATTERN.matcher(time); - int years = 0, months = 0, weeks = 0, days = 0, hours = 0, minutes = 0, seconds = 0; - boolean found = false; - while (m.find()) { - if (m.group() == null || m.group().isEmpty()) { - continue; - } - for (int i = 0; i < m.groupCount(); i++) { - if (m.group(i) != null && !m.group(i).isEmpty()) { - found = true; - break; - } - } - if (found) { - if (m.group(1) != null && !m.group(1).isEmpty()) { - years = Integer.parseInt(m.group(1)); - } - if (m.group(2) != null && !m.group(2).isEmpty()) { - months = Integer.parseInt(m.group(2)); - } - if (m.group(3) != null && !m.group(3).isEmpty()) { - weeks = Integer.parseInt(m.group(3)); - } - if (m.group(4) != null && !m.group(4).isEmpty()) { - days = Integer.parseInt(m.group(4)); - } - if (m.group(5) != null && !m.group(5).isEmpty()) { - hours = Integer.parseInt(m.group(5)); - } - if (m.group(6) != null && !m.group(6).isEmpty()) { - minutes = Integer.parseInt(m.group(6)); - } - if (m.group(7) != null && !m.group(7).isEmpty()) { - seconds = Integer.parseInt(m.group(7)); - } - break; - } - } - if (!found) { - throw new IllegalDateException(); - } - Calendar c = new GregorianCalendar(); - if (years > 0) { - if (years > MAX_YEARS) { - years = MAX_YEARS; - } - c.add(Calendar.YEAR, years * (future ? 1 : -1)); - } - if (months > 0) { - c.add(Calendar.MONTH, months * (future ? 1 : -1)); - } - if (weeks > 0) { - c.add(Calendar.WEEK_OF_YEAR, weeks * (future ? 1 : -1)); - } - if (days > 0) { - c.add(Calendar.DAY_OF_MONTH, days * (future ? 1 : -1)); - } - if (hours > 0) { - c.add(Calendar.HOUR_OF_DAY, hours * (future ? 1 : -1)); - } - if (minutes > 0) { - c.add(Calendar.MINUTE, minutes * (future ? 1 : -1)); - } - if (seconds > 0) { - c.add(Calendar.SECOND, seconds * (future ? 1 : -1)); - } - Calendar max = new GregorianCalendar(); - max.add(Calendar.YEAR, 10); - if (c.after(max)) { - return (max.getTimeInMillis() / 1000) + 1; - } - return (c.getTimeInMillis() / 1000) + 1; - } - - private static int dateDiff(int type, Calendar fromDate, Calendar toDate, boolean future) { - int year = Calendar.YEAR; - - int fromYear = fromDate.get(year); - int toYear = toDate.get(year); - if (Math.abs(fromYear - toYear) > MAX_YEARS) { - toDate.set(year, fromYear + (future ? MAX_YEARS : -MAX_YEARS)); - } - - int diff = 0; - long savedDate = fromDate.getTimeInMillis(); - while ((future && !fromDate.after(toDate)) || (!future && !fromDate.before(toDate))) { - savedDate = fromDate.getTimeInMillis(); - fromDate.add(type, future ? 1 : -1); - diff++; - } - diff--; - fromDate.setTimeInMillis(savedDate); - return diff; - } - - public static String formatDateDiff(long seconds) { - Calendar now = new GregorianCalendar(); - Calendar then = new GregorianCalendar(); - then.setTimeInMillis(seconds * 1000L); - return DateUtil.formatDateDiff(now, then); - } - - public static String formatDateDiffShort(long seconds) { - long now = unixSecondsNow(); - return formatTimeShort(seconds - now); - } - - public static String formatTimeShort(long seconds) { - if (seconds <= 0) { - return "0s"; - } - - long minute = seconds / 60; - seconds = seconds % 60; - long hour = minute / 60; - minute = minute % 60; - long day = hour / 24; - hour = hour % 24; - - StringBuilder time = new StringBuilder(); - if (day != 0) { - time.append(day).append("d "); - } - if (hour != 0) { - time.append(hour).append("h "); - } - if (minute != 0) { - time.append(minute).append("m "); - } - if (seconds != 0) { - time.append(seconds).append("s"); - } - - return time.toString().trim(); - } - - public static String formatTimeBrief(long seconds) { - if (seconds <= 0) { - return "0s"; - } - - long minute = seconds / 60; - seconds = seconds % 60; - long hour = minute / 60; - minute = minute % 60; - long day = hour / 24; - hour = hour % 24; - - StringBuilder time = new StringBuilder(); - if (day != 0) { - time.append(day).append("d "); - time.append(hour).append("h "); - } else if (hour != 0) { - time.append(hour).append("h "); - time.append(minute).append("m "); - } else if (minute != 0) { - time.append(minute).append("m "); - time.append(seconds).append("s"); - } else if (seconds != 0) { - time.append(seconds).append("s"); - } - - return time.toString().trim(); - } - - private static String formatDateDiff(Calendar fromDate, Calendar toDate) { - boolean future = false; - if (toDate.equals(fromDate)) { - return "now"; - } - if (toDate.after(fromDate)) { - future = true; - } - StringBuilder sb = new StringBuilder(); - int[] types = new int[]{Calendar.YEAR, Calendar.MONTH, Calendar.DAY_OF_MONTH, Calendar.HOUR_OF_DAY, Calendar.MINUTE, Calendar.SECOND}; - String[] names = new String[]{"year", "years", "month", "months", "day", "days", "hour", "hours", "minute", "minutes", "second", "seconds"}; - int accuracy = 0; - for (int i = 0; i < types.length; i++) { - if (accuracy > 2) { - break; - } - int diff = dateDiff(types[i], fromDate, toDate, future); - if (diff > 0) { - accuracy++; - sb.append(" ").append(diff).append(" ").append(names[i * 2 + (diff > 1 ? 1 : 0)]); - } - } - if (sb.length() == 0) { - return "now"; - } - return sb.toString().trim(); - } - - public static class IllegalDateException extends Exception { - - } - - private DateUtil() {} - -} \ No newline at end of file diff --git a/common/src/main/java/me/lucko/luckperms/common/utils/DurationFormatter.java b/common/src/main/java/me/lucko/luckperms/common/utils/DurationFormatter.java new file mode 100644 index 00000000..bf47a6ec --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/utils/DurationFormatter.java @@ -0,0 +1,176 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.utils; + +import java.util.Calendar; +import java.util.GregorianCalendar; + +/** + * Formats durations to a readable form + * + * @author khobbits, drtshock, vemacs + * see: https://github.com/drtshock/Essentials/blob/2.x/Essentials/src/com/earth2me/essentials/utils/DateUtil.java + */ +public enum DurationFormatter { + + CONCISE { + private final String[] names = new String[]{"y", "y", "m", "m", "d", "d", "h", "h", "m", "m", "s", "s"}; + + @Override + public String format(Calendar from, Calendar to) { + return dateDiff(from, to, 4, this.names); + } + }, + + CONCISE_LOW_ACCURACY { + private final String[] names = new String[]{"y", "y", "m", "m", "d", "d", "h", "h", "m", "m", "s", "s"}; + + @Override + public String format(Calendar from, Calendar to) { + return dateDiff(from, to, 2, this.names); + } + }, + + LONG { + private final String[] names = new String[]{"year", "years", "month", "months", "day", "days", "hour", "hours", "minute", "minutes", "second", "seconds"}; + + @Override + public String format(Calendar from, Calendar to) { + return dateDiff(from, to, 4, this.names); + } + }; + + /** + * The calender type magic numbers to use when formatting + */ + private static final int[] CALENDAR_TYPES = new int[] { + Calendar.YEAR, + Calendar.MONTH, + Calendar.DAY_OF_MONTH, + Calendar.HOUR_OF_DAY, + Calendar.MINUTE, + Calendar.SECOND + }; + + private static final int MAX_YEARS = 100000; + + /** + * Formats the difference between two dates + * + * @param from the start date + * @param to the end date + * @param maxAccuracy how accurate the output should be (how many sections it'll have) + * @param names the names to use to format each of the corresponding {@link #CALENDAR_TYPES} + * @return a formatted string + */ + private static String dateDiff(Calendar from, Calendar to, int maxAccuracy, String[] names) { + boolean future = false; + if (to.equals(from)) { + return "now"; + } + if (to.after(from)) { + future = true; + } + + StringBuilder sb = new StringBuilder(); + int accuracy = 0; + for (int i = 0; i < CALENDAR_TYPES.length; i++) { + if (accuracy > maxAccuracy) { + break; + } + + int diff = dateDiff(CALENDAR_TYPES[i], from, to, future); + if (diff > 0) { + accuracy++; + sb.append(" ").append(diff).append(" ").append(names[i * 2 + (diff > 1 ? 1 : 0)]); + } + } + + if (sb.length() == 0) { + return "now"; + } + + return sb.toString().trim(); + } + + private static int dateDiff(int type, Calendar fromDate, Calendar toDate, boolean future) { + int year = Calendar.YEAR; + + int fromYear = fromDate.get(year); + int toYear = toDate.get(year); + if (Math.abs(fromYear - toYear) > MAX_YEARS) { + toDate.set(year, fromYear + (future ? MAX_YEARS : -MAX_YEARS)); + } + + int diff = 0; + long savedDate = fromDate.getTimeInMillis(); + while ((future && !fromDate.after(toDate)) || (!future && !fromDate.before(toDate))) { + savedDate = fromDate.getTimeInMillis(); + fromDate.add(type, future ? 1 : -1); + diff++; + } + + diff--; + fromDate.setTimeInMillis(savedDate); + return diff; + } + + /** + * Formats the time difference between two dates + * + * @param from the start date + * @param to the end date + * @return the formatted duration string + */ + public abstract String format(Calendar from, Calendar to); + + /** + * Formats a duration, in seconds + * + * @param seconds the duration + * @return the formatted duration string + */ + public String format(long seconds) { + Calendar from = new GregorianCalendar(); + from.setTimeInMillis(0); + + Calendar to = new GregorianCalendar(); + to.setTimeInMillis(seconds * 1000L); + + return format(from, to); + } + + /** + * Formats the duration between the current time and the given unix timestamp + * + * @param unixTimestamp the timestamp, in seconds + * @return the formatted duration string + */ + public String formatDateDiff(long unixTimestamp) { + long now = System.currentTimeMillis() / 1000L; + return format(unixTimestamp - now); + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/utils/PatternCache.java b/common/src/main/java/me/lucko/luckperms/common/utils/PatternCache.java index 899543db..436d5939 100644 --- a/common/src/main/java/me/lucko/luckperms/common/utils/PatternCache.java +++ b/common/src/main/java/me/lucko/luckperms/common/utils/PatternCache.java @@ -27,47 +27,56 @@ package me.lucko.luckperms.common.utils; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; -import com.google.common.collect.Maps; -import java.util.Map; +import java.util.Objects; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; public final class PatternCache { - private static final NullablePattern NULL_PATTERN = new NullablePattern(null); - - private static final LoadingCache CACHE = Caffeine.newBuilder().build(s -> { - try { - return new NullablePattern(Pattern.compile(s)); - } catch (PatternSyntaxException e) { - return NULL_PATTERN; - } - }); - - private static final LoadingCache, String> DELIMITER_CACHE = Caffeine.newBuilder() - .build(e -> { - // note the reversed order - return "(? CACHE = Caffeine.newBuilder() + .build(s -> { + try { + return new CachedPattern(Pattern.compile(s)); + } catch (PatternSyntaxException e) { + return new CachedPattern(e); + } }); public static Pattern compile(String regex) { - return CACHE.get(regex).pattern; + CachedPattern pattern = CACHE.get(regex); + Objects.requireNonNull(pattern, "pattern"); + if (pattern.ex != null) { + throw pattern.ex; + } else { + return pattern.instance; + } } - public static String buildDelimitedMatcher(String delim, String esc) { - return DELIMITER_CACHE.get(Maps.immutableEntry(delim, esc)); + /** + * Compiles delimiter pattern with the given escape sequence. + * + * @param delimiter the delimiter (the thing separating components) + * @param escape the string used to escape the delimiter where the pattern shouldn't match + * @return a pattern + */ + public static Pattern compileDelimiterPattern(String delimiter, String escape) { + String pattern = "(?