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;
+ }
+
}