playerMap = new HashMap<>();
+ private final Object playerLock = new Object();
+
+ /**
+ * Remove a player from the player map
+ *
+ * @param plotPlayer Player to remove
+ */
+ public void removePlayer(@NotNull final PlotPlayer plotPlayer) {
+ synchronized (playerLock) {
+ this.playerMap.remove(plotPlayer.getUUID());
+ }
+ }
+
+ /**
+ * Get the player from its UUID if it is stored in the player map.
+ *
+ * @param uuid Player UUID
+ * @return Player, or null
+ */
+ @Nullable public P getPlayerIfExists(@Nullable final UUID uuid) {
+ if (uuid == null) {
+ return null;
+ }
+ return this.playerMap.get(uuid);
+ }
+
+ @Nullable public P getPlayerIfExists(@Nullable final String name) {
+ for (final P plotPlayer : this.playerMap.values()) {
+ if (plotPlayer.getName().equalsIgnoreCase(name)) {
+ return plotPlayer;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get a plot player from a UUID. This method requires
+ * that the caller actually knows that the player exists.
+ *
+ * The method will throw an exception if there is no such
+ * player online.
+ *
+ * @param uuid Player UUID
+ * @return Player object
+ */
+ @NotNull public P getPlayer(@NotNull final UUID uuid) {
+ synchronized (playerLock) {
+ P player = this.playerMap.get(uuid);
+ if (player == null) {
+ player = createPlayer(uuid);
+ this.playerMap.put(uuid, player);
+ }
+ return player;
+ }
+ }
+
+ @NotNull protected abstract P createPlayer(@NotNull final UUID uuid);
+
+ /**
+ * Get an an offline player object from the player's UUID
+ *
+ * @param uuid Player UUID
+ * @return Offline player object
+ */
+ @Nullable public abstract O getOfflinePlayer(@Nullable final UUID uuid);
+
+ /**
+ * Get an offline player object from the player's username
+ *
+ * @param username Player name
+ * @return Offline player object
+ */
+ @Nullable public abstract O getOfflinePlayer(@NotNull final String username);
+
+ /**
+ * Get all online players
+ *
+ * @return Unmodifiable collection of players
+ */
+ public Collection
getPlayers() {
+ return Collections.unmodifiableCollection(this.playerMap.values());
+ }
+
+
+ public static final class NoSuchPlayerException extends IllegalArgumentException {
+
+ public NoSuchPlayerException(@NotNull final UUID uuid) {
+ super(String.format("There is no online player with UUID '%s'", uuid.toString()));
+ }
+
+ }
+
+}
diff --git a/Core/src/main/java/com/plotsquared/core/util/SchematicHandler.java b/Core/src/main/java/com/plotsquared/core/util/SchematicHandler.java
index 850cef3e0..547741e6f 100644
--- a/Core/src/main/java/com/plotsquared/core/util/SchematicHandler.java
+++ b/Core/src/main/java/com/plotsquared/core/util/SchematicHandler.java
@@ -35,7 +35,6 @@ import com.plotsquared.core.plot.schematic.Schematic;
import com.plotsquared.core.queue.LocalBlockQueue;
import com.plotsquared.core.util.task.RunnableVal;
import com.plotsquared.core.util.task.TaskManager;
-import com.plotsquared.core.util.uuid.UUIDHandler;
import com.sk89q.jnbt.ByteArrayTag;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.IntArrayTag;
@@ -118,27 +117,33 @@ public abstract class SchematicHandler {
Iterator i = plots.iterator();
final Plot plot = i.next();
i.remove();
- String owner = UUIDHandler.getName(plot.guessOwner());
- if (owner == null) {
+
+ final String owner;
+ if (plot.hasOwner()) {
+ owner = plot.getOwnerAbs().toString();
+ } else {
owner = "unknown";
}
+
final String name;
if (namingScheme == null) {
name =
plot.getId().x + ";" + plot.getId().y + ',' + plot.getArea() + ',' + owner;
} else {
- name = namingScheme.replaceAll("%owner%", owner)
+ name = namingScheme
.replaceAll("%id%", plot.getId().toString())
.replaceAll("%idx%", plot.getId().x + "")
.replaceAll("%idy%", plot.getId().y + "")
.replaceAll("%world%", plot.getArea().toString());
}
+
final String directory;
if (outputDir == null) {
directory = Settings.Paths.SCHEMATICS;
} else {
directory = outputDir.getAbsolutePath();
}
+
final Runnable THIS = this;
SchematicHandler.manager.getCompoundTag(plot, new RunnableVal() {
@Override public void run(final CompoundTag value) {
diff --git a/Core/src/main/java/com/plotsquared/core/util/TabCompletions.java b/Core/src/main/java/com/plotsquared/core/util/TabCompletions.java
new file mode 100644
index 000000000..0369ad730
--- /dev/null
+++ b/Core/src/main/java/com/plotsquared/core/util/TabCompletions.java
@@ -0,0 +1,112 @@
+/*
+ * _____ _ _ _____ _
+ * | __ \| | | | / ____| | |
+ * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| |
+ * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` |
+ * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| |
+ * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_|
+ * | |
+ * |_|
+ * PlotSquared plot management system for Minecraft
+ * Copyright (C) 2020 IntellectualSites
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.plotsquared.core.util;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.plotsquared.core.PlotSquared;
+import com.plotsquared.core.command.Command;
+import com.plotsquared.core.command.CommandCategory;
+import com.plotsquared.core.command.RequiredType;
+import com.plotsquared.core.configuration.Settings;
+import com.plotsquared.core.player.PlotPlayer;
+import com.plotsquared.core.uuid.UUIDMapping;
+import lombok.experimental.UtilityClass;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+/**
+ * Tab completion utilities
+ */
+@UtilityClass
+public class TabCompletions {
+
+ private final Cache> cachedCompletionValues =
+ CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build();
+
+ /**
+ * Get a list of tab completions corresponding to player names. This uses the UUID pipeline
+ * cache, so it will complete will all names known to PlotSquared
+ *
+ * @param input Command input
+ * @param existing Players that should not be included in completions
+ * @return List of completions
+ */
+ @NotNull public List completePlayers(@NotNull final String input,
+ @NotNull final List existing) {
+ List players;
+ if (Settings.Enabled_Components.EXTENDED_USERNAME_COMPLETION) {
+ players = cachedCompletionValues.getIfPresent("players");
+ if (players == null) {
+ final Collection mappings =
+ PlotSquared.get().getImpromptuUUIDPipeline().getAllImmediately();
+ players = new ArrayList<>(mappings.size());
+ for (final UUIDMapping mapping : mappings) {
+ players.add(mapping.getUsername());
+ }
+ cachedCompletionValues.put("players", players);
+ }
+ } else {
+ final Collection extends PlotPlayer> onlinePlayers = PlotSquared.imp().getPlayerManager().getPlayers();
+ players = new ArrayList<>(onlinePlayers.size());
+ for (final PlotPlayer player : onlinePlayers) {
+ players.add(player.getName());
+ }
+ }
+ final String processedInput = input.toLowerCase(Locale.ENGLISH);
+ return players.stream()
+ .filter(player -> player.toLowerCase(Locale.ENGLISH).startsWith(processedInput))
+ .filter(player -> !existing.contains(player)).map(
+ player -> new Command(null, false, player, "", RequiredType.NONE,
+ CommandCategory.INFO) {
+ })
+ /* If there are more than 200 suggestions, just send the first 200 */
+ .limit(200)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Get a list of completions corresponding to WorldEdit(/FAWE) patterns. This uses
+ * WorldEdit's pattern completer internally.
+ *
+ * @param input Command input
+ * @return List of completions
+ */
+ @NotNull public List completePatterns(@NotNull final String input) {
+ return PatternUtil.getSuggestions(input.trim()).stream()
+ .map(value -> value.toLowerCase(Locale.ENGLISH).replace("minecraft:", ""))
+ .filter(value -> value.startsWith(input.toLowerCase(Locale.ENGLISH)))
+ .map(value -> new Command(null, false, value, "", RequiredType.NONE, null) {
+ }).collect(Collectors.toList());
+ }
+
+}
diff --git a/Core/src/test/java/com/plotsquared/core/plot/util/UUIDHandlerImplementationTest.java b/Core/src/main/java/com/plotsquared/core/util/ThreadUtils.java
similarity index 60%
rename from Core/src/test/java/com/plotsquared/core/plot/util/UUIDHandlerImplementationTest.java
rename to Core/src/main/java/com/plotsquared/core/util/ThreadUtils.java
index 291461478..c7c011e3d 100644
--- a/Core/src/test/java/com/plotsquared/core/plot/util/UUIDHandlerImplementationTest.java
+++ b/Core/src/main/java/com/plotsquared/core/util/ThreadUtils.java
@@ -23,28 +23,35 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package com.plotsquared.core.plot.util;
+package com.plotsquared.core.util;
-import com.plotsquared.core.database.AbstractDBTest;
-import com.plotsquared.core.database.DBFunc;
-import com.plotsquared.core.util.task.RunnableVal;
-import com.plotsquared.core.util.uuid.UUIDHandlerImplementation;
-import com.plotsquared.core.util.uuid.UUIDWrapper;
-import org.junit.Before;
+import com.plotsquared.core.PlotSquared;
+import lombok.experimental.UtilityClass;
-import java.util.UUID;
+@UtilityClass public class ThreadUtils {
-public class UUIDHandlerImplementationTest extends UUIDHandlerImplementation {
-
- public UUIDHandlerImplementationTest(UUIDWrapper wrapper) {
- super(wrapper);
+ /**
+ * Throws {@link IllegalStateException} if the method
+ * is called from the server main thread
+ *
+ * @param message Message describing the issue
+ */
+ public void catchSync(final String message) {
+ if (PlotSquared.get().isMainThread(Thread.currentThread())) {
+ throw new IllegalStateException(message);
+ }
}
- @Before public void setUp() throws Exception {
- DBFunc.dbManager = new AbstractDBTest();
+ /**
+ * Throws {@link IllegalStateException} if the method
+ * is not called from the server main thread
+ *
+ * @param message Message describing the issue
+ */
+ public void catchAsync(final String message) {
+ if (!PlotSquared.get().isMainThread(Thread.currentThread())) {
+ throw new IllegalStateException(message);
+ }
}
- @Override public void fetchUUID(String name, RunnableVal ifFetch) {
-
- }
}
diff --git a/Core/src/main/java/com/plotsquared/core/util/query/PlotQuery.java b/Core/src/main/java/com/plotsquared/core/util/query/PlotQuery.java
index 6c90e969f..40d883062 100644
--- a/Core/src/main/java/com/plotsquared/core/util/query/PlotQuery.java
+++ b/Core/src/main/java/com/plotsquared/core/util/query/PlotQuery.java
@@ -288,12 +288,13 @@ public final class PlotQuery {
} else {
final Collection plots = this.plotProvider.getPlots();
result = new ArrayList<>(plots.size());
- for (final Plot plot : plots) {
+ outer: for (final Plot plot : plots) {
for (final PlotFilter filter : this.filters) {
- if (filter.accepts(plot)) {
- result.add(plot);
+ if (!filter.accepts(plot)) {
+ continue outer;
}
}
+ result.add(plot);
}
}
if (this.sortingStrategy == SortingStrategy.NO_SORTING) {
diff --git a/Core/src/main/java/com/plotsquared/core/util/uuid/UUIDHandler.java b/Core/src/main/java/com/plotsquared/core/util/uuid/UUIDHandler.java
deleted file mode 100644
index 6dd8b1c47..000000000
--- a/Core/src/main/java/com/plotsquared/core/util/uuid/UUIDHandler.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * _____ _ _ _____ _
- * | __ \| | | | / ____| | |
- * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| |
- * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` |
- * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| |
- * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_|
- * | |
- * |_|
- * PlotSquared plot management system for Minecraft
- * Copyright (C) 2020 IntellectualSites
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package com.plotsquared.core.util.uuid;
-
-import com.google.common.collect.BiMap;
-import com.plotsquared.core.PlotSquared;
-import com.plotsquared.core.configuration.Captions;
-import com.plotsquared.core.database.DBFunc;
-import com.plotsquared.core.player.OfflinePlotPlayer;
-import com.plotsquared.core.player.PlotPlayer;
-import com.plotsquared.core.util.StringWrapper;
-import com.plotsquared.core.util.task.RunnableVal;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.UUID;
-
-public class UUIDHandler {
-
- public static UUIDHandlerImplementation implementation;
-
- public static void add(StringWrapper name, UUID uuid) {
- implementation.add(name, uuid);
- }
-
- /**
- * Get the map containing all names/uuids.
- *
- * @return map with names + uuids
- * @see BiMap
- */
- public static BiMap getUuidMap() {
- return implementation.getUUIDMap();
- }
-
- /**
- * Check if a uuid is cached
- *
- * @param uuid to check
- * @return true of the uuid is cached
- * @see BiMap#containsValue(Object)
- */
- public static boolean uuidExists(UUID uuid) {
- return implementation.uuidExists(uuid);
- }
-
- /**
- * Check if a name is cached
- *
- * @param name to check
- * @return true of the name is cached
- * @see BiMap#containsKey(Object)
- */
- public static boolean nameExists(StringWrapper name) {
- return implementation.nameExists(name);
- }
-
- public static HashSet getAllUUIDS() {
- final HashSet uuids = new HashSet<>();
- PlotSquared.get().forEachPlotRaw(plot -> {
- if (plot.hasOwner()) {
- uuids.add(plot.getOwnerAbs());
- uuids.addAll(plot.getTrusted());
- uuids.addAll(plot.getMembers());
- uuids.addAll(plot.getDenied());
- }
- });
- return uuids;
- }
-
- public static UUIDWrapper getUUIDWrapper() {
- return implementation.getUUIDWrapper();
- }
-
- public static void setUUIDWrapper(UUIDWrapper wrapper) {
- implementation.setUUIDWrapper(wrapper);
- }
-
- public static void startCaching(Runnable whenDone) {
- implementation.startCaching(whenDone);
- }
-
- public static void cache(BiMap toAdd) {
- implementation.add(toAdd);
- }
-
- @NotNull public static UUID getUUID(PlotPlayer player) {
- return implementation.getUUID(player);
- }
-
- public static UUID getUUID(OfflinePlotPlayer player) {
- if (implementation == null) {
- return null;
- }
- return implementation.getUUID(player);
- }
-
- @Nullable public static String getName(UUID uuid) {
- if (implementation == null) {
- return null;
- }
- if (uuid != null && uuid.equals(DBFunc.SERVER)) {
- return Captions.SERVER.getTranslated();
- }
- return implementation.getName(uuid);
- }
-
- @Nullable public static PlotPlayer getPlayer(@Nullable final UUID uuid) {
- if (implementation == null || uuid == null) {
- return null;
- }
- return check(implementation.getPlayer(uuid));
- }
-
- public static PlotPlayer getPlayer(String name) {
- if (implementation == null) {
- return null;
- }
- return check(implementation.getPlayer(name));
- }
-
- private static PlotPlayer check(@Nullable PlotPlayer player) {
- if (player != null && !player.isOnline()) {
- UUIDHandler.getPlayers().remove(player.getName());
- PlotSquared.get().IMP.unregister(player);
- player = null;
- }
- return player;
- }
-
- public static UUID getUUIDFromString(String nameOrUUIDString) {
- if (implementation == null) {
- return null;
- }
- if (nameOrUUIDString.length() > 16) {
- try {
- return UUID.fromString(nameOrUUIDString);
- } catch (IllegalArgumentException e) {
- return null;
- }
- }
- return UUIDHandler.getUUID(nameOrUUIDString, null);
- }
-
- public static UUID getUUID(String name, RunnableVal ifFetch) {
- if (implementation == null) {
- return null;
- }
- return implementation.getUUID(name, ifFetch);
- }
-
- public static UUID getCachedUUID(String name, RunnableVal ifFetch) {
- if (implementation == null) {
- return null;
- }
- return implementation.getUUIDMap().get(new StringWrapper(name));
- }
-
- public static Map getPlayers() {
- if (implementation == null) {
- return new HashMap<>();
- }
- return implementation.getPlayers();
- }
-
- public static void handleShutdown() {
- if (implementation == null) {
- return;
- }
- implementation.handleShutdown();
- }
-}
diff --git a/Core/src/main/java/com/plotsquared/core/util/uuid/UUIDHandlerImplementation.java b/Core/src/main/java/com/plotsquared/core/util/uuid/UUIDHandlerImplementation.java
deleted file mode 100644
index 687af58e0..000000000
--- a/Core/src/main/java/com/plotsquared/core/util/uuid/UUIDHandlerImplementation.java
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * _____ _ _ _____ _
- * | __ \| | | | / ____| | |
- * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| |
- * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` |
- * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| |
- * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_|
- * | |
- * |_|
- * PlotSquared plot management system for Minecraft
- * Copyright (C) 2020 IntellectualSites
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package com.plotsquared.core.util.uuid;
-
-import com.google.common.base.Charsets;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
-import com.plotsquared.core.PlotSquared;
-import com.plotsquared.core.configuration.Captions;
-import com.plotsquared.core.configuration.Settings;
-import com.plotsquared.core.database.DBFunc;
-import com.plotsquared.core.player.OfflinePlotPlayer;
-import com.plotsquared.core.player.PlotPlayer;
-import com.plotsquared.core.plot.Plot;
-import com.plotsquared.core.util.StringMan;
-import com.plotsquared.core.util.StringWrapper;
-import com.plotsquared.core.util.task.RunnableVal;
-import com.plotsquared.core.util.task.TaskManager;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-
-public abstract class UUIDHandlerImplementation {
-
- public final HashSet unknown = new HashSet<>();
- private final ConcurrentHashMap players;
- protected UUIDWrapper uuidWrapper;
- private boolean cached = false;
- private BiMap uuidMap = HashBiMap.create(new HashMap<>());
-
- public UUIDHandlerImplementation(UUIDWrapper wrapper) {
- this.uuidWrapper = wrapper;
- this.players = new ConcurrentHashMap<>();
- }
-
- /**
- * If the UUID is not found, some commands can request to fetch the UUID when possible.
- *
- * @param name
- * @param ifFetch
- */
- public abstract void fetchUUID(String name, RunnableVal ifFetch);
-
- /**
- * Start UUID caching (this should be an async task)
- * Recommended to override this is you want to cache offline players
- */
- public boolean startCaching(Runnable whenDone) {
- if (this.cached) {
- return false;
- }
- return this.cached = true;
- }
-
- public UUIDWrapper getUUIDWrapper() {
- return this.uuidWrapper;
- }
-
- public void setUUIDWrapper(UUIDWrapper wrapper) {
- this.uuidWrapper = wrapper;
- }
-
- public void rename(UUID uuid, StringWrapper name) {
- this.uuidMap.inverse().remove(uuid);
- this.uuidMap.put(name, uuid);
- }
-
- public void add(BiMap toAdd) {
- if (this.uuidMap.isEmpty()) {
- this.uuidMap = toAdd;
- }
- for (Map.Entry entry : toAdd.entrySet()) {
- UUID uuid = entry.getValue();
- StringWrapper name = entry.getKey();
- if (uuid == null || name == null) {
- continue;
- }
- StringWrapper oldName = this.uuidMap.inverse().get(uuid);
- if (oldName != null) {
- if (this.uuidMap.containsKey(name)) {
- continue;
- }
- if (getPlayer(uuid) == null) {
- rename(uuid, name);
- }
- continue;
- }
- this.uuidMap.put(name, uuid);
- }
- PlotSquared
- .debug(Captions.PREFIX + "&6Cached a total of: " + this.uuidMap.size() + " UUIDs");
- }
-
- public boolean add(final StringWrapper name, final UUID uuid) {
- if (uuid == null) {
- PlotSquared.debug("UUID cannot be null!");
- return false;
- }
- if (name == null) {
- try {
- this.unknown.add(uuid);
- } catch (Exception e) {
- PlotSquared.log("&c(minor) Invalid UUID mapping: " + uuid);
- e.printStackTrace();
- }
- return false;
- }
-
- /*
- * lazy UUID conversion:
- * - Useful if the person misconfigured the database, or settings before
- * PlotMe conversion
- */
- if (!Settings.UUID.OFFLINE && !this.unknown.isEmpty()) {
- TaskManager.runTaskAsync(() -> {
- UUID offline = UUID.nameUUIDFromBytes(
- ("OfflinePlayer:" + name.value).getBytes(Charsets.UTF_8));
- if (!UUIDHandlerImplementation.this.unknown.contains(offline) && !name.value
- .equals(name.value.toLowerCase())) {
- offline = UUID.nameUUIDFromBytes(
- ("OfflinePlayer:" + name.value.toLowerCase()).getBytes(Charsets.UTF_8));
- if (!UUIDHandlerImplementation.this.unknown.contains(offline)) {
- offline = null;
- }
- }
- if (offline != null && !offline.equals(uuid)) {
- UUIDHandlerImplementation.this.unknown.remove(offline);
- Set plots = PlotSquared.get().getPlotsAbs(offline);
- if (!plots.isEmpty()) {
- for (final Plot plot : plots) {
- plot.setOwnerAbs(uuid);
- }
- DBFunc.replaceUUID(offline, uuid);
- PlotSquared.debug("&cDetected invalid UUID stored for: " + name.value);
- PlotSquared.debug(
- "&7 - Did you recently switch to online-mode storage without running `uuidconvert`?");
- PlotSquared.debug("&6" + PlotSquared.imp().getPluginName()
- + " will update incorrect entries when the user logs in, or you can reconstruct your database.");
- }
- }
- });
- } else if (Settings.UUID.FORCE_LOWERCASE && !this.unknown.isEmpty() && !name.value
- .equals(name.value.toLowerCase())) {
- TaskManager.runTaskAsync(() -> {
- UUID offlineUpper = UUID.nameUUIDFromBytes(
- ("OfflinePlayer:" + name.value).getBytes(Charsets.UTF_8));
- if (UUIDHandlerImplementation.this.unknown.contains(offlineUpper) && !offlineUpper
- .equals(uuid)) {
- UUIDHandlerImplementation.this.unknown.remove(offlineUpper);
- Set plots = PlotSquared.get().getPlotsAbs(offlineUpper);
- if (!plots.isEmpty()) {
- for (final Plot plot : plots) {
- plot.setOwnerAbs(uuid);
- }
- replace(offlineUpper, uuid, name.value);
- }
- }
- });
- }
- try {
- UUID existing = this.uuidMap.put(name, uuid);
- if (existing != null) {
- if (!existing.equals(uuid)) {
- Set plots = PlotSquared.get().getPlots(existing);
- if (!plots.isEmpty()) {
- for (final Plot plot : plots) {
- plot.setOwnerAbs(uuid);
- }
- replace(existing, uuid, name.value);
- }
- return true;
- } else {
- StringWrapper oName = this.uuidMap.inverse().get(existing);
- if (!oName.equals(name)) {
- this.uuidMap.remove(name);
- this.uuidMap.put(name, uuid);
- }
- }
- return false;
- }
- } catch (Exception ignored) {
- BiMap inverse = this.uuidMap.inverse();
- StringWrapper oldName = inverse.get(uuid);
- if (oldName != null) {
- if (this.uuidMap.containsKey(name)) {
- return false;
- }
- PlotPlayer player = getPlayer(uuid);
- if (player == null || player.getName().equalsIgnoreCase(name.value)) {
- rename(uuid, name);
- return false;
- }
- StringWrapper newName = new StringWrapper(player.getName());
- UUID newUUID = player.getUUID();
- if (newUUID.equals(uuid) && !newName.equals(oldName)) {
- inverse.remove(uuid);
- this.uuidMap.put(newName, newUUID);
- }
- return false;
- }
- this.uuidMap.put(name, uuid);
- }
- return true;
- }
-
- private void replace(UUID from, UUID to, String name) {
- DBFunc.replaceUUID(from, to);
- PlotSquared.debug("&cDetected invalid UUID stored for: " + name);
- PlotSquared.debug(
- "&7 - Did you recently switch to online-mode storage without running `uuidconvert`?");
- PlotSquared.debug("&6" + PlotSquared.imp().getPluginName()
- + " will update incorrect entries when the user logs in, or you can reconstruct your database.");
- }
-
- public boolean uuidExists(UUID uuid) {
- return this.uuidMap.containsValue(uuid);
- }
-
- public BiMap getUUIDMap() {
- return this.uuidMap;
- }
-
- public boolean nameExists(StringWrapper wrapper) {
- return this.uuidMap.containsKey(wrapper);
- }
-
- public void handleShutdown() {
- this.players.clear();
- this.uuidMap.clear();
- }
-
- @Nullable public String getName(UUID uuid) {
- if (uuid == null) {
- return null;
- }
- StringWrapper name = this.uuidMap.inverse().get(uuid);
- if (name != null) {
- return name.value;
- }
- return null;
- }
-
- @Nullable public UUID getUUID(String name, RunnableVal ifFetch) {
- if (name.isEmpty()) {
- return null;
- }
- // check online
- PlotPlayer player = getPlayer(name);
- if (player != null) {
- return player.getUUID();
- }
- // check cache
- StringWrapper wrap = new StringWrapper(name);
- UUID uuid = this.uuidMap.get(wrap);
- if (uuid != null) {
- return uuid;
- }
- // Read from disk OR convert directly to offline UUID
- if (Settings.UUID.OFFLINE && !StringMan.contains(name, ';')) {
- uuid = this.uuidWrapper.getUUID(name);
- add(new StringWrapper(name), uuid);
- return uuid;
- }
- if ((ifFetch != null)) {
- fetchUUID(name, ifFetch);
- return null;
- }
- return null;
- }
-
- @NotNull public UUID getUUID(PlotPlayer player) {
- return this.uuidWrapper.getUUID(player);
- }
-
- public UUID getUUID(OfflinePlotPlayer player) {
- return this.uuidWrapper.getUUID(player);
- }
-
- @Nullable public PlotPlayer getPlayer(UUID uuid) {
- String name = getName(uuid);
- if (name != null) {
- return getPlayer(name);
- }
- return null;
- }
-
- public PlotPlayer getPlayer(String name) {
- return this.players.get(name);
- }
-
- public Map getPlayers() {
- return this.players;
- }
-
-}
diff --git a/Core/src/main/java/com/plotsquared/core/uuid/CacheUUIDService.java b/Core/src/main/java/com/plotsquared/core/uuid/CacheUUIDService.java
new file mode 100644
index 000000000..484146dae
--- /dev/null
+++ b/Core/src/main/java/com/plotsquared/core/uuid/CacheUUIDService.java
@@ -0,0 +1,84 @@
+/*
+ * _____ _ _ _____ _
+ * | __ \| | | | / ____| | |
+ * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| |
+ * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` |
+ * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| |
+ * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_|
+ * | |
+ * |_|
+ * PlotSquared plot management system for Minecraft
+ * Copyright (C) 2020 IntellectualSites
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.plotsquared.core.uuid;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.UUID;
+import java.util.function.Consumer;
+
+/**
+ * UUID service backed by a Guava Cache
+ */
+public class CacheUUIDService implements UUIDService, Consumer> {
+
+ private final Cache usernameCache;
+ private final Cache uuidCache;
+
+ /**
+ * Construct a new Cache UUID service with a maximum number of entries.
+ * Because it stores the mappings in two ways, the actual number
+ * of entries is two times the specified size
+ *
+ * @param size Maximum number of entries
+ */
+ public CacheUUIDService(final int size) {
+ this.usernameCache = CacheBuilder.newBuilder().maximumSize(size).build();
+ this.uuidCache = CacheBuilder.newBuilder().maximumSize(size).build();
+ }
+
+ @Override @NotNull public List getNames(@NotNull final List uuids) {
+ final List mappings = new ArrayList<>(uuids.size());
+ mappings.addAll(this.uuidCache.getAllPresent(uuids).values());
+ return mappings;
+ }
+
+ @Override @NotNull public List getUUIDs(@NotNull final List usernames) {
+ final List mappings = new ArrayList<>(usernames.size());
+ mappings.addAll(this.usernameCache.getAllPresent(usernames).values());
+ return mappings;
+ }
+
+ @Override public void accept(final List uuidMappings) {
+ for (final UUIDMapping mapping : uuidMappings) {
+ this.uuidCache.put(mapping.getUuid(), mapping);
+ this.usernameCache.put(mapping.getUsername(), mapping);
+ }
+ }
+
+ @Override @NotNull public Collection getImmediately() {
+ return this.usernameCache.asMap().values();
+ }
+
+ @Override public boolean canBeSynchronous() {
+ return true;
+ }
+}
diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/uuid/DatFileFilter.java b/Core/src/main/java/com/plotsquared/core/uuid/ServiceError.java
similarity index 73%
rename from Bukkit/src/main/java/com/plotsquared/bukkit/util/uuid/DatFileFilter.java
rename to Core/src/main/java/com/plotsquared/core/uuid/ServiceError.java
index 65657fbff..0e248a2c5 100644
--- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/uuid/DatFileFilter.java
+++ b/Core/src/main/java/com/plotsquared/core/uuid/ServiceError.java
@@ -23,14 +23,25 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package com.plotsquared.bukkit.util.uuid;
+package com.plotsquared.core.uuid;
-import java.io.File;
-import java.io.FilenameFilter;
+import org.jetbrains.annotations.NotNull;
-public class DatFileFilter implements FilenameFilter {
+/**
+ * Thrown by {@link ServiceError} when something goes wrong
+ */
+public class ServiceError extends RuntimeException {
- @Override public boolean accept(File dir, String name) {
- return name.endsWith(".dat");
+ public ServiceError(@NotNull final String message) {
+ super(message);
}
+
+ public ServiceError(@NotNull final String message, @NotNull final Throwable cause) {
+ super(message, cause);
+ }
+
+ @Override public Throwable fillInStackTrace() {
+ return this;
+ }
+
}
diff --git a/Core/src/main/java/com/plotsquared/core/util/uuid/UUIDWrapper.java b/Core/src/main/java/com/plotsquared/core/uuid/UUIDMapping.java
similarity index 71%
rename from Core/src/main/java/com/plotsquared/core/util/uuid/UUIDWrapper.java
rename to Core/src/main/java/com/plotsquared/core/uuid/UUIDMapping.java
index 14c5a03ce..35a4c6c2c 100644
--- a/Core/src/main/java/com/plotsquared/core/util/uuid/UUIDWrapper.java
+++ b/Core/src/main/java/com/plotsquared/core/uuid/UUIDMapping.java
@@ -23,25 +23,32 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package com.plotsquared.core.util.uuid;
+package com.plotsquared.core.uuid;
-import com.plotsquared.core.player.OfflinePlotPlayer;
-import com.plotsquared.core.player.PlotPlayer;
+import lombok.EqualsAndHashCode;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
-public abstract class UUIDWrapper {
+/**
+ * A pair consisting of a UUID and a username
+ */
+@EqualsAndHashCode public class UUIDMapping {
- @NotNull public abstract UUID getUUID(PlotPlayer player);
+ private final UUID uuid;
+ private final String username;
- public abstract UUID getUUID(OfflinePlotPlayer player);
+ public UUIDMapping(@NotNull final UUID uuid, final String username) {
+ this.uuid = uuid;
+ this.username = username;
+ }
- public abstract UUID getUUID(String name);
+ @NotNull public String getUsername() {
+ return this.username;
+ }
- public abstract OfflinePlotPlayer getOfflinePlayer(UUID uuid);
+ @NotNull public UUID getUuid() {
+ return this.uuid;
+ }
- public abstract OfflinePlotPlayer getOfflinePlayer(String name);
-
- public abstract OfflinePlotPlayer[] getOfflinePlayers();
}
diff --git a/Core/src/main/java/com/plotsquared/core/uuid/UUIDPipeline.java b/Core/src/main/java/com/plotsquared/core/uuid/UUIDPipeline.java
new file mode 100644
index 000000000..8c8baa06d
--- /dev/null
+++ b/Core/src/main/java/com/plotsquared/core/uuid/UUIDPipeline.java
@@ -0,0 +1,408 @@
+/*
+ * _____ _ _ _____ _
+ * | __ \| | | | / ____| | |
+ * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| |
+ * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` |
+ * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| |
+ * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_|
+ * | |
+ * |_|
+ * PlotSquared plot management system for Minecraft
+ * Copyright (C) 2020 IntellectualSites
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+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;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+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.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * An UUID pipeline is essentially an ordered list of
+ * {@link UUIDService uuid services} that each get the
+ * opportunity of providing usernames or UUIDs.
+ *
+ * Each request is then passed through a secondary list of
+ * consumers, that can then be used to cache them, etc
+ */
+public class UUIDPipeline {
+
+ private final Executor executor;
+ private final List serviceList;
+ private final List>> consumerList;
+ private final ScheduledExecutorService timeoutExecutor;
+
+ /**
+ * Construct a new UUID pipeline
+ *
+ * @param executor Executor that is used to run asynchronous tasks inside
+ * of the pipeline
+ */
+ public UUIDPipeline(@NotNull final Executor executor) {
+ this.executor = executor;
+ this.serviceList = Lists.newLinkedList();
+ this.consumerList = Lists.newLinkedList();
+ this.timeoutExecutor = Executors.newSingleThreadScheduledExecutor();
+ }
+
+ /**
+ * Register a UUID service
+ *
+ * @param uuidService UUID service to register
+ */
+ public void registerService(@NotNull final UUIDService uuidService) {
+ this.serviceList.add(uuidService);
+ }
+
+ /**
+ * Register a mapping consumer
+ *
+ * @param mappingConsumer Consumer to register
+ */
+ public void registerConsumer(@NotNull final Consumer> mappingConsumer) {
+ this.consumerList.add(mappingConsumer);
+ }
+
+ /**
+ * Get a copy of the service list
+ *
+ * @return Copy of service list
+ */
+ public List getServiceListInstance() {
+ return Collections.unmodifiableList(this.serviceList);
+ }
+
+ /**
+ * Let all consumers act on the given mapping.
+ *
+ * @param mappings Mappings
+ */
+ public void consume(@NotNull final List mappings) {
+ final Runnable runnable = () -> {
+ for (final Consumer> consumer : this.consumerList) {
+ consumer.accept(mappings);
+ }
+ };
+ if (PlotSquared.get().isMainThread(Thread.currentThread())) {
+ TaskManager.runTaskAsync(runnable);
+ } else {
+ runnable.run();
+ }
+ }
+
+ /**
+ * Consume a single mapping
+ *
+ * @param mapping Mapping to consume
+ */
+ public void consume(@NotNull final UUIDMapping mapping) {
+ this.consume(Collections.singletonList(mapping));
+ }
+
+ /**
+ * This will store the given username-UUID pair directly, and overwrite
+ * any existing caches. This can be used to update usernames automatically
+ * whenever a player joins the server, to make sure an up-to-date UUID
+ * mapping is stored
+ *
+ * @param username Player username
+ * @param uuid Player uuid
+ */
+ public void storeImmediately(@NotNull final String username, @NotNull final UUID uuid) {
+ this.consume(new UUIDMapping(uuid, username));
+ }
+
+ /**
+ * Get a single UUID from a username. This is blocking.
+ *
+ * @param username Username
+ * @param timeout Timeout in milliseconds
+ * @return The mapped uuid. Will return null if the request timed out.
+ */
+ @Nullable public UUID getSingle(@NotNull final String username, final long timeout) {
+ ThreadUtils.catchSync("Blocking UUID retrieval from the main thread");
+ try {
+ 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;
+ }
+
+ /**
+ * Get a single username from a UUID. This is blocking.
+ *
+ * @param uuid UUID
+ * @param timeout Timeout in milliseconds
+ * @return The mapped username. Will return null if the request timeout.
+ */
+ @Nullable public String getSingle(@NotNull final UUID uuid, final long timeout) {
+ ThreadUtils.catchSync("Blocking username retrieval from the main thread");
+ try {
+ 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;
+ }
+
+ /**
+ * Get a single UUID from a username. This is non-blocking.
+ *
+ * @param username Username
+ * @param uuid UUID consumer
+ */
+ public void getSingle(@NotNull final String username,
+ @NotNull final BiConsumer