diff --git a/Bukkit/pom.xml b/Bukkit/pom.xml index 1eace8462..68e49c901 100644 --- a/Bukkit/pom.xml +++ b/Bukkit/pom.xml @@ -66,6 +66,12 @@ + + com.github.pavog + SquirrelID + 0.6.1 + compile + com.sk89q.worldedit worldedit-core diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/listener/PaperListener.java b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/PaperListener.java index 2ff395c52..925798a8a 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/listener/PaperListener.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/PaperListener.java @@ -318,7 +318,6 @@ public class PaperListener implements Listener { } @EventHandler public void onAsyncTabCompletion(final AsyncTabCompleteEvent event) { - PlotSquared.debug("ASYNC COMPLETION"); String buffer = event.getBuffer(); if (!(event.getSender() instanceof Player)) { return; @@ -332,7 +331,7 @@ public class PaperListener implements Listener { final String[] unprocessedArgs = buffer.split(Pattern.quote(" ")); if (unprocessedArgs.length == 1) { return; // We don't do anything in this case - } else if (!Arrays.asList("plots", "p", "plotsquared", "plot2", "p2", "ps", "2", "plotme", "plotz", "ap") + } else if (!Arrays.asList("plot", "plots", "p", "plotsquared", "plot2", "p2", "ps", "2", "plotme", "plotz", "ap") .contains(unprocessedArgs[0].toLowerCase(Locale.ENGLISH))) { return; } @@ -350,7 +349,6 @@ public class PaperListener implements Listener { } event.setCompletions(result); event.setHandled(true); - PlotSquared.debug("ASYNC COMPLETION HANDLED"); } catch (final Exception ignored) {} } diff --git a/Core/src/main/java/com/plotsquared/core/command/Add.java b/Core/src/main/java/com/plotsquared/core/command/Add.java index bbd0b5559..3a3aa7ab7 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Add.java +++ b/Core/src/main/java/com/plotsquared/core/command/Add.java @@ -32,9 +32,12 @@ import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; import com.plotsquared.core.util.MainUtil; import com.plotsquared.core.util.Permissions; +import com.plotsquared.core.util.TabCompletions; import com.plotsquared.core.util.task.RunnableVal2; import com.plotsquared.core.util.task.RunnableVal3; +import java.util.Collection; +import java.util.Collections; import java.util.Iterator; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -127,4 +130,8 @@ public class Add extends Command { return future; } + @Override public Collection tab(final PlotPlayer player, final String[] args, final boolean space) { + return TabCompletions.completePlayers(String.join(",", args).trim(), Collections.emptyList()); + } + } diff --git a/Core/src/main/java/com/plotsquared/core/command/Debug.java b/Core/src/main/java/com/plotsquared/core/command/Debug.java index 7d0ced991..e104da337 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Debug.java +++ b/Core/src/main/java/com/plotsquared/core/command/Debug.java @@ -34,8 +34,10 @@ import com.plotsquared.core.util.StringMan; import com.plotsquared.core.util.entity.EntityCategories; import com.plotsquared.core.util.entity.EntityCategory; import com.plotsquared.core.util.task.TaskManager; +import com.plotsquared.core.uuid.UUIDMapping; import com.sk89q.worldedit.world.entity.EntityType; +import java.util.Collection; import java.util.Comparator; import java.util.Map; @@ -65,6 +67,11 @@ public class Debug extends SubCommand { .currentThread().getName())); return true; } + if (args.length > 0 && "uuids".equalsIgnoreCase(args[0])) { + final Collection mappings = PlotSquared.get().getImpromptuUUIDPipeline().getAllImmediately(); + MainUtil.sendMessage(player, String.format("There are %d cached UUIDs", mappings.size())); + return true; + } if (args.length > 0 && "entitytypes".equalsIgnoreCase(args[0])) { EntityCategories.init(); player.sendMessage(Captions.PREFIX.getTranslated() + "§cEntity Categories: "); diff --git a/Core/src/main/java/com/plotsquared/core/command/Owner.java b/Core/src/main/java/com/plotsquared/core/command/Owner.java index cb436fad2..f19790dc5 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Owner.java +++ b/Core/src/main/java/com/plotsquared/core/command/Owner.java @@ -150,7 +150,7 @@ public class Owner extends SetCommand { if (throwable instanceof TimeoutException) { MainUtil.sendMessage(player, Captions.FETCHING_PLAYERS_TIMEOUT); } else if (throwable != null) { - MainUtil.sendMessage(player, Captions.INVALID_PLAYER); + MainUtil.sendMessage(player, Captions.INVALID_PLAYER, value); } else { uuidConsumer.accept(uuid); } diff --git a/Core/src/main/java/com/plotsquared/core/command/Trust.java b/Core/src/main/java/com/plotsquared/core/command/Trust.java index 8bcaced4e..4f9c15e38 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Trust.java +++ b/Core/src/main/java/com/plotsquared/core/command/Trust.java @@ -32,9 +32,12 @@ import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; import com.plotsquared.core.util.MainUtil; import com.plotsquared.core.util.Permissions; +import com.plotsquared.core.util.TabCompletions; import com.plotsquared.core.util.task.RunnableVal2; import com.plotsquared.core.util.task.RunnableVal3; +import java.util.Collection; +import java.util.Collections; import java.util.Iterator; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -126,4 +129,8 @@ public class Trust extends Command { return CompletableFuture.completedFuture(true); } + @Override public Collection tab(final PlotPlayer player, final String[] args, final boolean space) { + return TabCompletions.completePlayers(String.join(",", args).trim(), Collections.emptyList()); + } + } diff --git a/Core/src/main/java/com/plotsquared/core/util/MainUtil.java b/Core/src/main/java/com/plotsquared/core/util/MainUtil.java index e214e382a..8546298b6 100644 --- a/Core/src/main/java/com/plotsquared/core/util/MainUtil.java +++ b/Core/src/main/java/com/plotsquared/core/util/MainUtil.java @@ -82,6 +82,7 @@ import java.util.Optional; import java.util.Scanner; import java.util.Set; import java.util.UUID; +import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.IntConsumer; @@ -902,24 +903,61 @@ public class MainUtil { return (directory.delete()); } - /** - * Get a list of names given a list of uuids.
- * - Uses the format {@link Captions#PLOT_USER_LIST} for the returned string - * - * @param uuids - * @return - */ - public static String getPlayerList(Collection uuids) { - ArrayList l = new ArrayList<>(uuids); - if (l.size() < 1) { + /* + @NotNull public static String getName(UUID owner) { + if (owner == null) { return Captions.NONE.getTranslated(); } - List users = - l.stream().map(MainUtil::getName).sorted().collect(Collectors.toList()); + if (owner.equals(DBFunc.EVERYONE)) { + return Captions.EVERYONE.getTranslated(); + } + if (owner.equals(DBFunc.SERVER)) { + return Captions.SERVER.getTranslated(); + } + String name = PlotSquared.get().getImpromptuUUIDPipeline().getSingle(owner, Settings.UUID.BLOCKING_TIMEOUT); + if (name == null) { + return Captions.UNKNOWN.getTranslated(); + } + return name; + } + */ + + /** + * Get a list of names given a list of UUIDs. + * - Uses the format {@link Captions#PLOT_USER_LIST} for the returned string + */ + public static String getPlayerList(final Collection uuids) { + if (uuids.size() < 1) { + return Captions.NONE.getTranslated(); + } + + final List players = new LinkedList<>(); + final List users = new LinkedList<>(); + for (final UUID uuid : uuids) { + if (uuid == null) { + users.add(Captions.NONE.getTranslated()); + } else if (DBFunc.EVERYONE.equals(uuid)) { + users.add(Captions.EVERYONE.getTranslated()); + } else if (DBFunc.SERVER.equals(uuid)) { + users.add(Captions.SERVER.getTranslated()); + } else { + players.add(uuid); + } + } + + try { + for (final UUIDMapping mapping : PlotSquared.get().getImpromptuUUIDPipeline().getNames(players).get(Settings.UUID.BLOCKING_TIMEOUT, + TimeUnit.MILLISECONDS)) { + users.add(mapping.getUsername()); + } + } catch (final Exception e) { + e.printStackTrace(); + } + String c = Captions.PLOT_USER_LIST.getTranslated(); StringBuilder list = new StringBuilder(); for (int x = 0; x < users.size(); x++) { - if (x + 1 == l.size()) { + if (x + 1 == uuids.size()) { list.append(c.replace("%user%", users.get(x)).replace(",", "")); } else { list.append(c.replace("%user%", users.get(x))); diff --git a/Core/src/main/java/com/plotsquared/core/uuid/CacheUUIDService.java b/Core/src/main/java/com/plotsquared/core/uuid/CacheUUIDService.java index 32bbbb6bc..484146dae 100644 --- a/Core/src/main/java/com/plotsquared/core/uuid/CacheUUIDService.java +++ b/Core/src/main/java/com/plotsquared/core/uuid/CacheUUIDService.java @@ -78,4 +78,7 @@ public class CacheUUIDService implements UUIDService, Consumer return this.usernameCache.asMap().values(); } + @Override public boolean canBeSynchronous() { + return true; + } } diff --git a/Core/src/main/java/com/plotsquared/core/uuid/UUIDPipeline.java b/Core/src/main/java/com/plotsquared/core/uuid/UUIDPipeline.java index 12d49abaa..3518fa5f8 100644 --- a/Core/src/main/java/com/plotsquared/core/uuid/UUIDPipeline.java +++ b/Core/src/main/java/com/plotsquared/core/uuid/UUIDPipeline.java @@ -27,6 +27,7 @@ package com.plotsquared.core.uuid; import com.google.common.collect.Lists; import com.plotsquared.core.PlotSquared; +import com.plotsquared.core.configuration.Captions; import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.util.ThreadUtils; import com.plotsquared.core.util.task.TaskManager; @@ -156,10 +157,14 @@ public class UUIDPipeline { @Nullable public UUID getSingle(@NotNull final String username, final long timeout) { ThreadUtils.catchSync("Blocking UUID retrieval from the main thread"); try { - this.getUUIDs(Collections.singletonList(username)).get(timeout, TimeUnit.MILLISECONDS); + final List mappings = this.getUUIDs(Collections.singletonList(username)).get(timeout, TimeUnit.MILLISECONDS); + if (mappings.size() == 1) { + return mappings.get(0).getUuid(); + } } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } catch (TimeoutException ignored) { + PlotSquared.log(Captions.PREFIX + " (UUID) Request for " + username + " timed out"); // This is completely valid, we just don't care anymore } return null; @@ -175,10 +180,14 @@ public class UUIDPipeline { @Nullable public String getSingle(@NotNull final UUID uuid, final long timeout) { ThreadUtils.catchSync("Blocking username retrieval from the main thread"); try { - this.getNames(Collections.singletonList(uuid)).get(timeout, TimeUnit.MILLISECONDS); + final List mappings = this.getNames(Collections.singletonList(uuid)).get(timeout, TimeUnit.MILLISECONDS); + if (mappings.size() == 1) { + return mappings.get(0).getUsername(); + } } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } catch (TimeoutException ignored) { + PlotSquared.log(Captions.PREFIX + " (UUID) Request for " + uuid + " timed out"); // This is completely valid, we just don't care anymore } return null; @@ -274,25 +283,45 @@ public class UUIDPipeline { if (requests.isEmpty()) { return CompletableFuture.completedFuture(Collections.emptyList()); } - final List serviceList = this.getServiceListInstance(); - return CompletableFuture.supplyAsync(() -> { - final List mappings = new ArrayList<>(requests.size()); - final List remainingRequests = new ArrayList<>(requests); - for (final UUIDService service : serviceList) { - if (remainingRequests.isEmpty()) { - break; - } + final List serviceList = this.getServiceListInstance(); + final List mappings = new ArrayList<>(requests.size()); + final List remainingRequests = new ArrayList<>(requests); + + for (final UUIDService service : serviceList) { + // We can chain multiple synchronous + // ones in a row + if (service.canBeSynchronous()) { final List completedRequests = service.getNames(remainingRequests); for (final UUIDMapping mapping : completedRequests) { remainingRequests.remove(mapping.getUuid()); } mappings.addAll(completedRequests); + } else { + break; + } + if (remainingRequests.isEmpty()) { + return CompletableFuture.completedFuture(mappings); + } + } + + return CompletableFuture.supplyAsync(() -> { + for (final UUIDService service : serviceList) { + final List completedRequests = service.getNames(remainingRequests); + for (final UUIDMapping mapping : completedRequests) { + remainingRequests.remove(mapping.getUuid()); + } + mappings.addAll(completedRequests); + if (remainingRequests.isEmpty()) { + break; + } } if (mappings.size() == requests.size()) { this.consume(mappings); return mappings; + } else if (Settings.DEBUG) { + PlotSquared.log("Failed to find all usernames"); } throw new ServiceError("End of pipeline"); @@ -310,25 +339,45 @@ public class UUIDPipeline { if (requests.isEmpty()) { return CompletableFuture.completedFuture(Collections.emptyList()); } - final List serviceList = this.getServiceListInstance(); - return CompletableFuture.supplyAsync(() -> { - final List mappings = new ArrayList<>(requests.size()); - final List remainingRequests = new ArrayList<>(requests); - for (final UUIDService service : serviceList) { - if (remainingRequests.isEmpty()) { - break; - } + final List serviceList = this.getServiceListInstance(); + final List mappings = new ArrayList<>(requests.size()); + final List remainingRequests = new ArrayList<>(requests); + + for (final UUIDService service : serviceList) { + // We can chain multiple synchronous + // ones in a row + if (service.canBeSynchronous()) { final List completedRequests = service.getUUIDs(remainingRequests); for (final UUIDMapping mapping : completedRequests) { remainingRequests.remove(mapping.getUsername()); } mappings.addAll(completedRequests); + } else { + break; + } + if (remainingRequests.isEmpty()) { + return CompletableFuture.completedFuture(mappings); + } + } + + return CompletableFuture.supplyAsync(() -> { + for (final UUIDService service : serviceList) { + final List completedRequests = service.getUUIDs(remainingRequests); + for (final UUIDMapping mapping : completedRequests) { + remainingRequests.remove(mapping.getUsername()); + } + mappings.addAll(completedRequests); + if (remainingRequests.isEmpty()) { + break; + } } if (mappings.size() == requests.size()) { this.consume(mappings); return mappings; + } else if (Settings.DEBUG) { + PlotSquared.log("Failed to find all UUIDs"); } throw new ServiceError("End of pipeline"); diff --git a/Core/src/main/java/com/plotsquared/core/uuid/UUIDService.java b/Core/src/main/java/com/plotsquared/core/uuid/UUIDService.java index 25588305e..3b6419bd6 100644 --- a/Core/src/main/java/com/plotsquared/core/uuid/UUIDService.java +++ b/Core/src/main/java/com/plotsquared/core/uuid/UUIDService.java @@ -65,4 +65,14 @@ public interface UUIDService { return Collections.emptyList(); } + /** + * Check whether or not this service can be safely used synchronously + * without blocking the server for an extended amount of time. + * + * @return True if the service can be used synchronously + */ + default boolean canBeSynchronous() { + return false; + } + }