misc cleanup

This commit is contained in:
Luck 2018-04-02 16:02:55 +01:00
parent 3453f05aca
commit c13b01da01
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
39 changed files with 526 additions and 543 deletions

View File

@ -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 {

View File

@ -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() {}
}

View File

@ -155,7 +155,7 @@ public class MigrationBPermissions extends SubCommand<Object> {
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;
}

View File

@ -163,7 +163,7 @@ public class MigrationGroupManager extends SubCommand<Object> {
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;
}

View File

@ -100,7 +100,7 @@ public class MigrationPermissionsBukkit extends SubCommand<Object> {
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;
}

View File

@ -162,7 +162,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
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;
}

View File

@ -116,7 +116,7 @@ public class MigrationZPermissions extends SubCommand<Object> {
// store user data for later
Set<Membership> 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;
}

View File

@ -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");

View File

@ -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<List<UUID>> 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<CompletableFuture<Void>> futures = new HashSet<>();
AtomicInteger userCount = new AtomicInteger(0);
// iterate through each user sublist.
for (List<UUID> 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<String> 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<String> 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<Void> 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 <T> List<T> nInstances(int count, Supplier<T> supplier) {
List<T> ret = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
ret.add(supplier.get());
}
return ret;
}
}

View File

@ -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<CompletableFuture<Void>> futures = new HashSet<>();
AtomicInteger processedCount = new AtomicInteger(0);

View File

@ -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<TemporaryModifier> parseTemporaryModifier(int index, List<String> args) {
if (index < 0 || index >= args.size()) {
return Optional.empty();

View File

@ -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,
'¥'

View File

@ -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),
'¥'

View File

@ -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)

View File

@ -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();

View File

@ -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();

View File

@ -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)

View File

@ -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<Group> {
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;

View File

@ -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<Group> {
return "";
}
return " &8(&7expires in " + DateUtil.formatDateDiff(node.getExpiryUnixTime()) + "&8)";
return " &8(&7expires in " + DurationFormatter.LONG.formatDateDiff(node.getExpiryUnixTime()) + "&8)";
}
private static Consumer<BuildableComponent.Builder<? ,?>> makeFancy(String holderName, HolderType holderType, String label, HeldPermission<?> perm) {

View File

@ -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<Log> {
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<Integer, ExtendedLogEntry> 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(),

View File

@ -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<Log> {
Message.LOG_RECENT_HEADER.send(sender, page, maxPage);
}
long now = DateUtil.unixSecondsNow();
long now = System.currentTimeMillis() / 1000L;
for (Map.Entry<Integer, ExtendedLogEntry> 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(),

View File

@ -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<Log> {
SortedMap<Integer, ExtendedLogEntry> 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<Integer, ExtendedLogEntry> 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(),

View File

@ -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<Log> {
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<Integer, ExtendedLogEntry> 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(),

View File

@ -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<Log> {
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<Integer, ExtendedLogEntry> 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(),

View File

@ -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<Node>, Set<Node>> diff(Set<Node> before, Set<Node> after) {

View File

@ -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()

View File

@ -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<BuildableComponent.Builder<?, ?>> makeFancy(String holderName, HolderType holderType, String label, HeldPermission<?> perm) {

View File

@ -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<User> {
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()));
}
}

View File

@ -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<Class<? extends LuckPermsEvent>, Set<LuckPermsEventHandler<?>>> handlerMap = new ConcurrentHashMap<>();
/**
* The registered handlers in this event bus
*/
private final Multimap<Class<? extends LuckPermsEvent>, 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<Class<? extends LuckPermsEvent>, List<LuckPermsEventHandler<?>>> handlerCache = Caffeine.newBuilder()
.build(eventClass -> {
ImmutableList.Builder<LuckPermsEventHandler<?>> 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<LuckPermsEventHandler<?>> handlers = this.handlerMap.computeIfAbsent(eventClass, c -> ConcurrentHashMap.newKeySet());
LuckPermsEventHandler<T> eventHandler = new LuckPermsEventHandler<>(this, eventClass, handler);
handlers.add(eventHandler);
this.handlerMap.put(eventClass, eventHandler);
this.handlerCache.invalidateAll();
return eventHandler;
}
@Nonnull
@Override
@SuppressWarnings("unchecked")
public <T extends LuckPermsEvent> Set<EventHandler<T>> getHandlers(@Nonnull Class<T> eventClass) {
Set<LuckPermsEventHandler<?>> handlers = this.handlerMap.get(eventClass);
if (handlers == null) {
return ImmutableSet.of();
} else {
ImmutableSet.Builder<EventHandler<T>> ret = ImmutableSet.builder();
for (LuckPermsEventHandler<?> handler : handlers) {
ret.add((EventHandler<T>) handler);
}
return ret.build();
Collection<LuckPermsEventHandler<?>> handlers = this.handlerMap.asMap().get(eventClass);
ImmutableSet.Builder<EventHandler<T>> ret = ImmutableSet.builder();
for (LuckPermsEventHandler<?> handler : handlers) {
//noinspection unchecked
ret.add((EventHandler<T>) handler);
}
return ret.build();
}
public void unregisterHandler(LuckPermsEventHandler<?> handler) {
Set<LuckPermsEventHandler<?>> 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<Class<? extends LuckPermsEvent>, Set<LuckPermsEventHandler<?>>> ent : this.handlerMap.entrySet()) {
if (!ent.getKey().isAssignableFrom(event.getClass())) {
continue;
}
List<LuckPermsEventHandler<?>> handlers = this.handlerCache.get(event.getClass());
if (handlers == null) {
return;
}
ent.getValue().forEach(h -> h.handle(event));
for (LuckPermsEventHandler<?> handler : handlers) {
handler.handle(event);
}
}

View File

@ -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

View File

@ -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));

View File

@ -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);

View File

@ -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<String> TRACK_NAME_TEST_ALLOW_SPACE = s -> !s.isEmpty() && s.length() <= MAX_TRACK_NAME_LENGTH;
public static final Predicate<Long> TIME_TEST = unixTime -> !DateUtil.shouldExpire(unixTime);
public static final Predicate<String> SERVER_NAME_TEST = s -> !s.isEmpty() && s.length() <= MAX_SERVER_LENGTH && !s.contains(" ");
public static final Predicate<String> WORLD_NAME_TEST = s -> !s.isEmpty() && s.length() <= MAX_WORLD_LENGTH;

View File

@ -1,93 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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 <E> the element type
*/
public class Cycle<E> {
/**
* The list that backs this instance
*/
private final List<E> 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<E> 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<E> getBacking() {
return this.objects;
}
}

View File

@ -0,0 +1,133 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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() {}
}

View File

@ -1,262 +0,0 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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() {}
}

View File

@ -0,0 +1,176 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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);
}
}

View File

@ -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<String, NullablePattern> CACHE = Caffeine.newBuilder().build(s -> {
try {
return new NullablePattern(Pattern.compile(s));
} catch (PatternSyntaxException e) {
return NULL_PATTERN;
}
});
private static final LoadingCache<Map.Entry<String, String>, String> DELIMITER_CACHE = Caffeine.newBuilder()
.build(e -> {
// note the reversed order
return "(?<!" + Pattern.quote(e.getValue()) + ")" + Pattern.quote(e.getKey());
private static final LoadingCache<String, CachedPattern> 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 = "(?<!" + Pattern.quote(escape) + ")" + Pattern.quote(delimiter);
return compile(pattern);
}
public static Pattern compileDelimitedMatcher(String delim, String esc) {
return compile(buildDelimitedMatcher(delim, esc));
}
private static final class CachedPattern {
private final Pattern instance;
private final PatternSyntaxException ex;
private static final class NullablePattern {
private final Pattern pattern;
public CachedPattern(Pattern instance) {
this.instance = instance;
this.ex = null;
}
public NullablePattern(Pattern pattern) {
this.pattern = pattern;
public CachedPattern(PatternSyntaxException ex) {
this.instance = null;
this.ex = ex;
}
}

View File

@ -32,7 +32,7 @@ import me.lucko.luckperms.common.command.CommandManager;
import me.lucko.luckperms.common.command.utils.MessageUtils;
import me.lucko.luckperms.common.locale.message.Message;
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.StackTracePrinter;
import me.lucko.luckperms.common.utils.TextUtils;
import me.lucko.luckperms.common.utils.gson.JArray;
@ -181,7 +181,7 @@ public class VerboseListener {
String startDate = DATE_FORMAT.format(new Date(this.startTime));
String endDate = DATE_FORMAT.format(new Date(now));
long secondsTaken = (now - this.startTime) / 1000L;
String duration = DateUtil.formatTimeShort(secondsTaken);
String duration = DurationFormatter.CONCISE.format(secondsTaken);
String filter;
if (this.filter.isBlank()){