diff --git a/Bukkit/build.gradle b/Bukkit/build.gradle index 5a1c91e20..caafd3ef0 100644 --- a/Bukkit/build.gradle +++ b/Bukkit/build.gradle @@ -11,6 +11,7 @@ repositories { name = "papermc" url = "https://papermc.io/repo/repository/maven-public/" } + maven { url = "https://ci.ender.zone/plugin/repository/everything/" } maven { url = "https://mvn.intellectualsites.com/content/repositories/snapshots" } mavenLocal() } @@ -30,7 +31,10 @@ dependencies { exclude(module: "bukkit") } implementation("me.clip:placeholderapi:2.10.4") + implementation("net.luckperms:api:5.0") + implementation("net.ess3:EssentialsX:2.16.1") compile("se.hyperver.hyperverse:Core:0.6.0-SNAPSHOT"){ transitive = false } + compile 'com.github.pavog:SquirrelID:0.6.1' } sourceCompatibility = 1.8 @@ -90,10 +94,12 @@ shadowJar { include(dependency("com.github.davidmoten:rtree:0.8.7")) include(dependency("io.reactivex:rxjava:1.3.8")) include(dependency("com.github.davidmoten:guava-mini:0.1.1")) + include(dependency("com.github.pavog:SquirrelID:0.6.1")) } relocate('net.kyori.text', 'com.plotsquared.formatting.text') relocate("io.papermc.lib", "com.plotsquared.bukkit.paperlib") relocate("org.bstats", "com.plotsquared.metrics") + relocate('com.sk89q.squirrelid', 'com.plotsquared.squirrelid') archiveFileName = "${project.name}-${parent.version}.jar" destinationDirectory = file "../target" } diff --git a/Bukkit/pom.xml b/Bukkit/pom.xml index 1eace8462..57d06b615 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 @@ -134,6 +140,18 @@ 2.10.4 runtime + + net.luckperms + api + 5.0 + runtime + + + net.ess3 + EssentialsX + 2.16.1 + runtime + junit junit diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitCommand.java b/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitCommand.java index 2d3737ee0..20ab45529 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitCommand.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitCommand.java @@ -25,7 +25,6 @@ */ package com.plotsquared.bukkit; -import com.plotsquared.bukkit.command.DebugUUID; import com.plotsquared.bukkit.util.BukkitUtil; import com.plotsquared.core.command.MainCommand; import com.plotsquared.core.player.ConsolePlayer; @@ -46,10 +45,6 @@ import java.util.List; public class BukkitCommand implements CommandExecutor, TabCompleter { - public BukkitCommand() { - new DebugUUID(); - } - @Override public boolean onCommand(CommandSender commandSender, Command command, String commandLabel, String[] args) { diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitMain.java b/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitMain.java index 779aec999..84e474319 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitMain.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitMain.java @@ -38,6 +38,7 @@ import com.plotsquared.bukkit.managers.HyperverseWorldManager; import com.plotsquared.bukkit.managers.MultiverseWorldManager; import com.plotsquared.bukkit.placeholder.PlaceholderFormatter; import com.plotsquared.bukkit.placeholder.Placeholders; +import com.plotsquared.bukkit.player.BukkitPlayerManager; import com.plotsquared.bukkit.queue.BukkitLocalQueue; import com.plotsquared.bukkit.schematic.BukkitSchematicHandler; import com.plotsquared.bukkit.util.BukkitChatManager; @@ -50,11 +51,12 @@ import com.plotsquared.bukkit.util.BukkitTaskManager; import com.plotsquared.bukkit.util.BukkitUtil; import com.plotsquared.bukkit.util.SetGenCB; import com.plotsquared.bukkit.util.UpdateUtility; -import com.plotsquared.bukkit.util.uuid.DefaultUUIDWrapper; -import com.plotsquared.bukkit.util.uuid.FileUUIDHandler; -import com.plotsquared.bukkit.util.uuid.LowerOfflineUUIDWrapper; -import com.plotsquared.bukkit.util.uuid.OfflineUUIDWrapper; -import com.plotsquared.bukkit.util.uuid.SQLUUIDHandler; +import com.plotsquared.bukkit.uuid.EssentialsUUIDService; +import com.plotsquared.bukkit.uuid.LuckPermsUUIDService; +import com.plotsquared.bukkit.uuid.OfflinePlayerUUIDService; +import com.plotsquared.bukkit.uuid.PaperUUIDService; +import com.plotsquared.bukkit.uuid.SQLiteUUIDService; +import com.plotsquared.bukkit.uuid.SquirrelIdUUIDService; import com.plotsquared.core.IPlotMain; import com.plotsquared.core.PlotSquared; import com.plotsquared.core.backup.BackupManager; @@ -65,6 +67,7 @@ import com.plotsquared.core.configuration.ChatFormatter; import com.plotsquared.core.configuration.ConfigurationNode; import com.plotsquared.core.configuration.ConfigurationSection; import com.plotsquared.core.configuration.Settings; +import com.plotsquared.core.database.DBFunc; import com.plotsquared.core.generator.GeneratorWrapper; import com.plotsquared.core.generator.HybridGen; import com.plotsquared.core.generator.HybridUtils; @@ -90,6 +93,7 @@ import com.plotsquared.core.util.EconHandler; import com.plotsquared.core.util.InventoryUtil; import com.plotsquared.core.util.MainUtil; import com.plotsquared.core.util.PlatformWorldManager; +import com.plotsquared.core.util.PlayerManager; import com.plotsquared.core.util.PremiumVerification; import com.plotsquared.core.util.ReflectionUtils; import com.plotsquared.core.util.RegionManager; @@ -98,9 +102,9 @@ import com.plotsquared.core.util.SetupUtils; import com.plotsquared.core.util.StringMan; import com.plotsquared.core.util.WorldUtil; import com.plotsquared.core.util.task.TaskManager; -import com.plotsquared.core.util.uuid.UUIDHandler; -import com.plotsquared.core.util.uuid.UUIDHandlerImplementation; -import com.plotsquared.core.util.uuid.UUIDWrapper; +import com.plotsquared.core.uuid.CacheUUIDService; +import com.plotsquared.core.uuid.UUIDPipeline; +import com.plotsquared.core.uuid.offline.OfflineModeUUIDService; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.extension.platform.Actor; @@ -134,10 +138,17 @@ import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Queue; +import java.util.Set; import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; import static com.plotsquared.core.util.PremiumVerification.getDownloadID; import static com.plotsquared.core.util.PremiumVerification.getResourceID; @@ -164,6 +175,7 @@ public final class BukkitMain extends JavaPlugin implements Listener, IPlotMain private boolean metricsStarted; @Getter private BackupManager backupManager; @Getter private PlatformWorldManager worldManager; + @Getter private final PlayerManager playerManager = new BukkitPlayerManager(); @Override public int[] getServerVersion() { if (this.version == null) { @@ -221,6 +233,102 @@ public final class BukkitMain extends JavaPlugin implements Listener, IPlotMain PlotSquared.log(Captions.PREFIX + "&6Couldn't verify purchase :("); } + final UUIDPipeline impromptuPipeline = PlotSquared.get().getImpromptuUUIDPipeline(); + final UUIDPipeline backgroundPipeline = PlotSquared.get().getBackgroundUUIDPipeline(); + + // Services are accessed in order + final CacheUUIDService cacheUUIDService = new CacheUUIDService(Settings.UUID.UUID_CACHE_SIZE); + impromptuPipeline.registerService(cacheUUIDService); + backgroundPipeline.registerService(cacheUUIDService); + impromptuPipeline.registerConsumer(cacheUUIDService); + backgroundPipeline.registerConsumer(cacheUUIDService); + + // Now, if the server is in offline mode we can only use profiles and direct UUID + // access, and so we skip the player profile stuff as well as SquirrelID (Mojang lookups) + if (Settings.UUID.OFFLINE) { + final OfflineModeUUIDService offlineModeUUIDService = new OfflineModeUUIDService(); + impromptuPipeline.registerService(offlineModeUUIDService); + backgroundPipeline.registerService(offlineModeUUIDService); + PlotSquared.log(Captions.PREFIX + "(UUID) Using the offline mode UUID service"); + } + + final OfflinePlayerUUIDService offlinePlayerUUIDService = new OfflinePlayerUUIDService(); + impromptuPipeline.registerService(offlinePlayerUUIDService); + backgroundPipeline.registerService(offlinePlayerUUIDService); + + final SQLiteUUIDService sqLiteUUIDService = new SQLiteUUIDService("user_cache.db"); + + final SQLiteUUIDService legacyUUIDService; + if (Settings.UUID.LEGACY_DATABASE_SUPPORT && MainUtil.getFile(PlotSquared.get().IMP.getDirectory(), "usercache.db").exists()) { + legacyUUIDService = new SQLiteUUIDService("usercache.db"); + } else { + legacyUUIDService = null; + } + + final LuckPermsUUIDService luckPermsUUIDService; + if (Bukkit.getPluginManager().getPlugin("LuckPerms") != null) { + luckPermsUUIDService = new LuckPermsUUIDService(); + PlotSquared.log(Captions.PREFIX + "(UUID) Using LuckPerms as a complementary UUID service"); + } else { + luckPermsUUIDService = null; + } + + final EssentialsUUIDService essentialsUUIDService; + if (Bukkit.getPluginManager().getPlugin("Essentials") != null) { + essentialsUUIDService = new EssentialsUUIDService(); + PlotSquared.log(Captions.PREFIX + "(UUID) Using Essentials as a complementary UUID service"); + } else { + essentialsUUIDService = null; + } + + if (!Settings.UUID.OFFLINE) { + // If running Paper we'll also try to use their profiles + if (PaperLib.isPaper()) { + final PaperUUIDService paperUUIDService = new PaperUUIDService(); + impromptuPipeline.registerService(paperUUIDService); + backgroundPipeline.registerService(paperUUIDService); + PlotSquared.log(Captions.PREFIX + "(UUID) Using Paper as a complementary UUID service"); + } + + impromptuPipeline.registerService(sqLiteUUIDService); + backgroundPipeline.registerService(sqLiteUUIDService); + impromptuPipeline.registerConsumer(sqLiteUUIDService); + backgroundPipeline.registerConsumer(sqLiteUUIDService); + + if (legacyUUIDService != null) { + impromptuPipeline.registerService(legacyUUIDService); + backgroundPipeline.registerService(legacyUUIDService); + } + + // Plugin providers + if (luckPermsUUIDService != null) { + impromptuPipeline.registerService(luckPermsUUIDService); + backgroundPipeline.registerService(luckPermsUUIDService); + } + if (essentialsUUIDService != null) { + impromptuPipeline.registerService(essentialsUUIDService); + backgroundPipeline.registerService(essentialsUUIDService); + } + + final SquirrelIdUUIDService impromptuMojangService = new SquirrelIdUUIDService(Settings.UUID.IMPROMPTU_LIMIT); + impromptuPipeline.registerService(impromptuMojangService); + final SquirrelIdUUIDService backgroundMojangService = new SquirrelIdUUIDService(Settings.UUID.BACKGROUND_LIMIT); + backgroundPipeline.registerService(backgroundMojangService); + } else { + impromptuPipeline.registerService(sqLiteUUIDService); + backgroundPipeline.registerService(sqLiteUUIDService); + impromptuPipeline.registerConsumer(sqLiteUUIDService); + backgroundPipeline.registerConsumer(sqLiteUUIDService); + + if (legacyUUIDService != null) { + impromptuPipeline.registerService(legacyUUIDService); + backgroundPipeline.registerService(legacyUUIDService); + } + } + + impromptuPipeline.storeImmediately("*", DBFunc.EVERYONE); + this.startUuidCatching(sqLiteUUIDService, cacheUUIDService); + if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { new Placeholders().register(); if (Settings.Enabled_Components.EXTERNAL_PLACEHOLDERS) { @@ -342,6 +450,72 @@ public final class BukkitMain extends JavaPlugin implements Listener, IPlotMain } } + private void startUuidCatching(@NotNull final SQLiteUUIDService sqLiteUUIDService, + @NotNull final CacheUUIDService cacheUUIDService) { + // Load all uuids into a big chunky boi queue + final Queue uuidQueue = new LinkedBlockingQueue<>(); + PlotSquared.get().forEachPlotRaw(plot -> { + final Set uuids = new HashSet<>(); + uuids.add(plot.getOwnerAbs()); + uuids.addAll(plot.getMembers()); + uuids.addAll(plot.getTrusted()); + uuids.addAll(plot.getDenied()); + for (final UUID uuid : uuids) { + if (!uuidQueue.contains(uuid)) { + uuidQueue.add(uuid); + } + } + }); + PlotSquared.log(Captions.PREFIX.getTranslated() + "(UUID) " + uuidQueue.size() + " UUIDs will be cached."); + + Executors.newSingleThreadScheduledExecutor().schedule(() -> { + // Begin by reading all the SQLite cache at once + cacheUUIDService.accept(sqLiteUUIDService.getAll()); + // Now fetch names for all known UUIDs + final int totalSize = uuidQueue.size(); + int read = 0; + PlotSquared.log(Captions.PREFIX.getTranslated() + "(UUID) PlotSquared will fetch UUIDs in groups of " + + Settings.UUID.BACKGROUND_LIMIT); + final List uuidList = new ArrayList<>(Settings.UUID.BACKGROUND_LIMIT); + + // Used to indicate that the second retrieval has been attempted + boolean secondRun = false; + + while (!uuidQueue.isEmpty() || !uuidList.isEmpty()) { + if (!uuidList.isEmpty() && secondRun) { + PlotSquared.log("Giving up on last batch. Fetching new batch instead."); + uuidList.clear(); + } + if (uuidList.isEmpty()) { + // Retrieve the secondRun variable to indicate that we're retrieving a + // fresh batch + secondRun = false; + // Populate the request list + for (int i = 0; i < Settings.UUID.BACKGROUND_LIMIT && !uuidQueue.isEmpty(); i++) { + uuidList.add(uuidQueue.poll()); + read++; + } + } else { + // If the list isn't empty then this is a second run for + // an old batch, so we re-use the patch + secondRun = true; + } + try { + PlotSquared.get().getBackgroundUUIDPipeline().getNames(uuidList).get(); + // Clear the list if we successfully index all the names + uuidList.clear(); + // Print progress + final double percentage = ((double) read / (double) totalSize) * 100.0D; + PlotSquared.log(Captions.PREFIX.getTranslated() + String.format("(UUID) PlotSquared has cached %.1f%% of UUIDs", percentage)); + } catch (final InterruptedException | ExecutionException e) { + PlotSquared.log("Failed to retrieve that batch. Will try again."); + e.printStackTrace(); + } + } + PlotSquared.log(Captions.PREFIX.getTranslated() + "(UUID) PlotSquared has cached all UUIDs"); + }, 10, TimeUnit.SECONDS); + } + @Override public void onDisable() { PlotSquared.get().disable(); Bukkit.getScheduler().cancelTasks(this); @@ -798,39 +972,6 @@ public final class BukkitMain extends JavaPlugin implements Listener, IPlotMain return new BukkitInventoryUtil(); } - @Override public UUIDHandlerImplementation initUUIDHandler() { - final UUIDWrapper wrapper; - if (Settings.UUID.OFFLINE) { - if (Settings.UUID.FORCE_LOWERCASE) { - wrapper = new LowerOfflineUUIDWrapper(); - } else { - wrapper = new OfflineUUIDWrapper(); - } - Settings.UUID.OFFLINE = true; - } else { - wrapper = new DefaultUUIDWrapper(); - Settings.UUID.OFFLINE = false; - } - if (!Bukkit.getVersion().contains("git-Spigot")) { - if (wrapper instanceof DefaultUUIDWrapper - || wrapper.getClass() == OfflineUUIDWrapper.class && !Bukkit.getOnlineMode()) { - Settings.UUID.NATIVE_UUID_PROVIDER = true; - } - } - if (Settings.UUID.OFFLINE) { - PlotSquared.log(Captions.PREFIX + "&6" + getPluginName() - + " is using Offline Mode UUIDs either because of user preference, or because you are using an old version of " - + "Bukkit"); - } else { - PlotSquared.log(Captions.PREFIX + "&6" + getPluginName() + " is using online UUIDs"); - } - if (Settings.UUID.USE_SQLUUIDHANDLER) { - return new SQLUUIDHandler(wrapper); - } else { - return new FileUUIDHandler(wrapper); - } - } - @Override public void setGenerator(@NonNull final String worldName) { World world = BukkitUtil.getWorld(worldName); if (world == null) { @@ -881,10 +1022,10 @@ public final class BukkitMain extends JavaPlugin implements Listener, IPlotMain return BukkitUtil.getPlayer((OfflinePlayer) player); } if (player instanceof String) { - return UUIDHandler.getPlayer((String) player); + return PlotSquared.imp().getPlayerManager().getPlayerIfExists((String) player); } if (player instanceof UUID) { - return UUIDHandler.getPlayer((UUID) player); + return PlotSquared.imp().getPlayerManager().getPlayerIfExists((UUID) player); } return null; } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/command/DebugUUID.java b/Bukkit/src/main/java/com/plotsquared/bukkit/command/DebugUUID.java deleted file mode 100644 index 79b411791..000000000 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/command/DebugUUID.java +++ /dev/null @@ -1,323 +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.bukkit.command; - -import com.google.common.collect.Sets; -import com.plotsquared.bukkit.util.uuid.DatFileFilter; -import com.plotsquared.bukkit.util.uuid.DefaultUUIDWrapper; -import com.plotsquared.bukkit.util.uuid.LowerOfflineUUIDWrapper; -import com.plotsquared.bukkit.util.uuid.OfflineUUIDWrapper; -import com.plotsquared.core.PlotSquared; -import com.plotsquared.core.command.Argument; -import com.plotsquared.core.command.CommandCategory; -import com.plotsquared.core.command.CommandDeclaration; -import com.plotsquared.core.command.RequiredType; -import com.plotsquared.core.command.SubCommand; -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.plot.Plot; -import com.plotsquared.core.util.MainUtil; -import com.plotsquared.core.util.StringMan; -import com.plotsquared.core.util.StringWrapper; -import com.plotsquared.core.util.WorldUtil; -import com.plotsquared.core.util.task.TaskManager; -import com.plotsquared.core.util.uuid.UUIDHandler; -import com.plotsquared.core.util.uuid.UUIDWrapper; - -import java.io.File; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map.Entry; -import java.util.UUID; - -@CommandDeclaration(command = "uuidconvert", - permission = "plots.admin", - description = "Debug UUID conversion", - usage = "/plot uuidconvert ", - requiredType = RequiredType.CONSOLE, - category = CommandCategory.DEBUG) -public class DebugUUID extends SubCommand { - - public DebugUUID() { - super(Argument.String); - } - - @Override public boolean onCommand(final PlotPlayer player, String[] args) { - final UUIDWrapper currentUUIDWrapper = UUIDHandler.getUUIDWrapper(); - final UUIDWrapper newWrapper; - - switch (args[0].toLowerCase()) { - case "lower": - newWrapper = new LowerOfflineUUIDWrapper(); - break; - case "offline": - newWrapper = new OfflineUUIDWrapper(); - break; - case "online": - newWrapper = new DefaultUUIDWrapper(); - break; - default: - try { - Class clazz = Class.forName(args[0]); - newWrapper = (UUIDWrapper) clazz.newInstance(); - } catch (ClassNotFoundException | IllegalAccessException | InstantiationException ignored) { - MainUtil.sendMessage(player, Captions.COMMAND_SYNTAX, - "/plot uuidconvert "); - return false; - } - } - - if (args.length != 2 || !"-o".equals(args[1])) { - MainUtil.sendMessage(player, Captions.COMMAND_SYNTAX, - "/plot uuidconvert " + args[0] + " -o"); - MainUtil.sendMessage(player, "&cBe aware of the following!"); - MainUtil.sendMessage(player, - "&8 - &cUse the database command or another method to backup your plots beforehand"); - MainUtil.sendMessage(player, - "&8 - &cIf the process is interrupted, all plots could be deleted"); - MainUtil.sendMessage(player, "&8 - &cIf an error occurs, all plots could be deleted"); - MainUtil.sendMessage(player, "&8 - &cPlot settings WILL be lost upon conversion"); - MainUtil - .sendMessage(player, "&cTO REITERATE: BACK UP YOUR DATABASE BEFORE USING THIS!!!"); - MainUtil.sendMessage(player, - "&7Retype the command with the override parameter when ready :)"); - return false; - } - - if (currentUUIDWrapper.getClass().getCanonicalName() - .equals(newWrapper.getClass().getCanonicalName())) { - MainUtil.sendMessage(player, "&cUUID mode already in use!"); - return false; - } - MainUtil.sendMessage(player, "&6Beginning UUID mode conversion"); - MainUtil.sendMessage(player, "&7 - Disconnecting players"); - for (Entry entry : UUIDHandler.getPlayers().entrySet()) { - entry.getValue() - .kick("UUID conversion has been initiated. You may reconnect when finished."); - } - - MainUtil.sendMessage(player, "&7 - Initializing map"); - - final HashMap uCMap = new HashMap<>(); - final HashMap uCReverse = new HashMap<>(); - - MainUtil.sendMessage(player, "&7 - Collecting playerdata"); - - HashSet worlds = Sets.newHashSet(WorldUtil.IMP.getMainWorld(), "world"); - HashSet uuids = new HashSet<>(); - HashSet names = new HashSet<>(); - for (String worldName : worlds) { - File playerDataFolder = new File(worldName + File.separator + "playerdata"); - String[] dat = playerDataFolder.list(new DatFileFilter()); - if (dat != null) { - for (String current : dat) { - String s = current.replaceAll(".dat$", ""); - try { - UUID uuid = UUID.fromString(s); - uuids.add(uuid); - } catch (Exception ignored) { - MainUtil.sendMessage(player, - Captions.PREFIX + "Invalid playerdata: " + current); - } - } - } - File playersFolder = new File(worldName + File.separator + "players"); - dat = playersFolder.list(new DatFileFilter()); - if (dat != null) { - for (String current : dat) { - names.add(current.replaceAll(".dat$", "")); - } - } - } - - MainUtil.sendMessage(player, "&7 - Populating map"); - UUID uuid2; - UUIDWrapper wrapper = new DefaultUUIDWrapper(); - for (UUID uuid : uuids) { - try { - OfflinePlotPlayer op = wrapper.getOfflinePlayer(uuid); - uuid = currentUUIDWrapper.getUUID(op); - uuid2 = newWrapper.getUUID(op); - if (!uuid.equals(uuid2) && !uCMap.containsKey(uuid) && !uCReverse - .containsKey(uuid2)) { - uCMap.put(uuid, uuid2); - uCReverse.put(uuid2, uuid); - } - } catch (Throwable ignored) { - MainUtil.sendMessage(player, - Captions.PREFIX + "&6Invalid playerdata: " + uuid.toString() + ".dat"); - } - } - for (String name : names) { - UUID uuid = currentUUIDWrapper.getUUID(name); - uuid2 = newWrapper.getUUID(name); - if (!uuid.equals(uuid2)) { - uCMap.put(uuid, uuid2); - uCReverse.put(uuid2, uuid); - } - } - if (uCMap.isEmpty()) { - MainUtil.sendMessage(player, "&c - Error! Attempting to repopulate"); - for (OfflinePlotPlayer op : currentUUIDWrapper.getOfflinePlayers()) { - if (op.getLastPlayed() != 0) { - // String name = op.getPluginName(); - // StringWrapper wrap = new StringWrapper(name); - UUID uuid = currentUUIDWrapper.getUUID(op); - uuid2 = newWrapper.getUUID(op); - if (!uuid.equals(uuid2)) { - uCMap.put(uuid, uuid2); - uCReverse.put(uuid2, uuid); - } - } - } - if (uCMap.isEmpty()) { - MainUtil.sendMessage(player, "&cError. Failed to collect UUIDs!"); - return false; - } else { - MainUtil.sendMessage(player, "&a - Successfully repopulated"); - } - } - - MainUtil.sendMessage(player, "&7 - Replacing cache"); - TaskManager.runTaskAsync(() -> { - for (Entry entry : uCMap.entrySet()) { - String name = UUIDHandler.getName(entry.getKey()); - if (name != null) { - UUIDHandler.add(new StringWrapper(name), entry.getValue()); - } - } - - MainUtil.sendMessage(player, "&7 - Scanning for applicable files (uuids.txt)"); - - File file = new File(PlotSquared.get().IMP.getDirectory(), "uuids.txt"); - if (file.exists()) { - try { - List lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8); - for (String line : lines) { - try { - line = line.trim(); - if (line.isEmpty()) { - continue; - } - line = line.replaceAll("[\\|][0-9]+[\\|][0-9]+[\\|]", ""); - String[] split = line.split("\\|"); - String name = split[0]; - if (name.isEmpty() || name.length() > 16 || !StringMan - .isAlphanumericUnd(name)) { - continue; - } - UUID old = currentUUIDWrapper.getUUID(name); - if (old == null) { - continue; - } - UUID now = newWrapper.getUUID(name); - UUIDHandler.add(new StringWrapper(name), now); - uCMap.put(old, now); - uCReverse.put(now, old); - } catch (Exception e2) { - e2.printStackTrace(); - } - } - } catch (IOException e) { - e.printStackTrace(); - } - } - - MainUtil.sendMessage(player, "&7 - Replacing wrapper"); - UUIDHandler.setUUIDWrapper(newWrapper); - - MainUtil.sendMessage(player, "&7 - Updating plot objects"); - - for (Plot plot : PlotSquared.get().getPlots()) { - UUID value = uCMap.get(plot.getOwnerAbs()); - if (value != null) { - plot.setOwnerAbs(value); - } - plot.getTrusted().clear(); - plot.getMembers().clear(); - plot.getDenied().clear(); - } - - MainUtil.sendMessage(player, "&7 - Deleting database"); - boolean result = DBFunc.deleteTables(); - - MainUtil.sendMessage(player, "&7 - Creating tables"); - - try { - DBFunc.createTables(); - if (!result) { - MainUtil.sendMessage(player, "&cConversion failed! Attempting recovery"); - for (Plot plot : PlotSquared.get().getPlots()) { - UUID value = uCReverse.get(plot.getOwnerAbs()); - if (value != null) { - plot.setOwnerAbs(value); - } - } - DBFunc.createPlotsAndData(new ArrayList<>(PlotSquared.get().getPlots()), - () -> MainUtil.sendMessage(player, "&6Recovery was successful!")); - return; - } - } catch (Exception e) { - e.printStackTrace(); - return; - } - - if (newWrapper instanceof OfflineUUIDWrapper) { - PlotSquared.get().worlds.set("UUID.force-lowercase", false); - PlotSquared.get().worlds.set("UUID.offline", true); - } else if (newWrapper instanceof DefaultUUIDWrapper) { - PlotSquared.get().worlds.set("UUID.force-lowercase", false); - PlotSquared.get().worlds.set("UUID.offline", false); - } - try { - PlotSquared.get().worlds.save(PlotSquared.get().worldsFile); - } catch (IOException ignored) { - MainUtil.sendMessage(player, - "Could not save configuration. It will need to be manual set!"); - } - - MainUtil.sendMessage(player, "&7 - Populating tables"); - - TaskManager.runTaskAsync(() -> { - ArrayList plots = new ArrayList<>(PlotSquared.get().getPlots()); - DBFunc.createPlotsAndData(plots, - () -> MainUtil.sendMessage(player, "&aConversion complete!")); - }); - - MainUtil.sendMessage(player, "&aIt is now safe for players to join"); - MainUtil.sendMessage(player, - "&cConversion is still in progress, you will be notified when it is complete"); - }); - return true; - } -} 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 6c7e4c9eb..ec5384ab5 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/listener/PaperListener.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/PaperListener.java @@ -31,8 +31,11 @@ import com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent; import com.destroystokyo.paper.event.entity.PreSpawnerSpawnEvent; import com.destroystokyo.paper.event.entity.SlimePathfindEvent; import com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent; +import com.destroystokyo.paper.event.server.AsyncTabCompleteEvent; import com.plotsquared.bukkit.util.BukkitUtil; import com.plotsquared.core.PlotSquared; +import com.plotsquared.core.command.Command; +import com.plotsquared.core.command.MainCommand; import com.plotsquared.core.configuration.Captions; import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.location.Location; @@ -56,6 +59,13 @@ import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.projectiles.ProjectileSource; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Locale; +import java.util.regex.Pattern; + /** * Events specific to Paper. Some toit nups here */ @@ -306,4 +316,43 @@ public class PaperListener implements Listener { event.setCancelled(true); } } + + @EventHandler public void onAsyncTabCompletion(final AsyncTabCompleteEvent event) { + if (!Settings.Paper_Components.ASYNC_TAB_COMPLETION) { + return; + } + String buffer = event.getBuffer(); + if (!(event.getSender() instanceof Player)) { + return; + } + if ((!event.isCommand() && !buffer.startsWith("/")) || buffer.indexOf(' ') == -1) { + return; + } + if (buffer.startsWith("/")) { + buffer = buffer.substring(1); + } + final String[] unprocessedArgs = buffer.split(Pattern.quote(" ")); + if (unprocessedArgs.length == 1) { + return; // We don't do anything in this case + } else if (!Arrays.asList("plot", "plots", "p", "plotsquared", "plot2", "p2", "ps", "2", "plotme", "plotz", "ap") + .contains(unprocessedArgs[0].toLowerCase(Locale.ENGLISH))) { + return; + } + final String[] args = new String[unprocessedArgs.length - 1]; + System.arraycopy(unprocessedArgs, 1, args, 0, args.length); + try { + final PlotPlayer player = BukkitUtil.getPlayer((Player) event.getSender()); + final Collection objects = MainCommand.getInstance().tab(player, args, buffer.endsWith(" ")); + if (objects == null) { + return; + } + final List result = new ArrayList<>(); + for (final com.plotsquared.core.command.Command o : objects) { + result.add(o.toString()); + } + event.setCompletions(result); + event.setHandled(true); + } catch (final Exception ignored) {} + } + } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/listener/PlayerEvents.java b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/PlayerEvents.java index 069bae68b..70d3c6d6b 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/listener/PlayerEvents.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/PlayerEvents.java @@ -26,6 +26,7 @@ package com.plotsquared.bukkit.listener; import com.destroystokyo.paper.MaterialTags; +import com.google.common.base.Charsets; import com.plotsquared.bukkit.player.BukkitPlayer; import com.plotsquared.bukkit.util.BukkitUtil; import com.plotsquared.bukkit.util.UpdateUtility; @@ -103,10 +104,8 @@ import com.plotsquared.core.util.MathMan; import com.plotsquared.core.util.Permissions; import com.plotsquared.core.util.PremiumVerification; import com.plotsquared.core.util.RegExUtil; -import com.plotsquared.core.util.StringWrapper; import com.plotsquared.core.util.entity.EntityCategories; import com.plotsquared.core.util.task.TaskManager; -import com.plotsquared.core.util.uuid.UUIDHandler; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.world.block.BlockType; import io.papermc.lib.PaperLib; @@ -178,6 +177,7 @@ import org.bukkit.event.inventory.ClickType; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.player.AsyncPlayerPreLoginEvent; import org.bukkit.event.player.PlayerBucketEmptyEvent; import org.bukkit.event.player.PlayerBucketFillEvent; import org.bukkit.event.player.PlayerChangedWorldEvent; @@ -215,7 +215,6 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import java.util.UUID; @@ -253,8 +252,8 @@ public class PlayerEvents extends PlotListener implements Listener { int x = bloc.getBlockX(); int z = bloc.getBlockZ(); int distance = Bukkit.getViewDistance() * 16; - for (Entry entry : UUIDHandler.getPlayers().entrySet()) { - PlotPlayer player = entry.getValue(); + + for (final PlotPlayer player : PlotSquared.imp().getPlayerManager().getPlayers()) { Location location = player.getLocation(); if (location.getWorld().equals(world)) { if (16 * Math.abs(location.getX() - x) / 16 > distance @@ -355,18 +354,18 @@ public class PlayerEvents extends PlotListener implements Listener { if (plot.isMerged()) { disable = true; for (UUID owner : plot.getOwners()) { - if (UUIDHandler.getPlayer(owner) != null) { + if (PlotSquared.imp().getPlayerManager().getPlayerIfExists(owner) != null) { disable = false; break; } } } else { - disable = UUIDHandler.getPlayer(plot.guessOwner()) == null; + disable = PlotSquared.imp().getPlayerManager().getPlayerIfExists(plot.getOwnerAbs()) == null; } } if (disable) { for (UUID trusted : plot.getTrusted()) { - if (UUIDHandler.getPlayer(trusted) != null) { + if (PlotSquared.imp().getPlayerManager().getPlayerIfExists(trusted) != null) { disable = false; break; } @@ -378,8 +377,8 @@ public class PlayerEvents extends PlotListener implements Listener { } } if (Settings.Redstone.DISABLE_UNOCCUPIED) { - for (Entry entry : UUIDHandler.getPlayers().entrySet()) { - if (plot.equals(entry.getValue().getCurrentPlot())) { + for (final PlotPlayer player : PlotSquared.imp().getPlayerManager().getPlayers()) { + if (plot.equals(player.getCurrentPlot())) { return; } } @@ -629,17 +628,28 @@ public class PlayerEvents extends PlotListener implements Listener { } } + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onPreLoin(final AsyncPlayerPreLoginEvent event) { + final UUID uuid; + if (Settings.UUID.OFFLINE) { + if (Settings.UUID.FORCE_LOWERCASE) { + uuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + + event.getName().toLowerCase()).getBytes(Charsets.UTF_8)); + } else { + uuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + + event.getName()).getBytes(Charsets.UTF_8)); + } + } else { + uuid = event.getUniqueId(); + } + PlotSquared.get().getImpromptuUUIDPipeline().storeImmediately(event.getName(), uuid); + } + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) public void onConnect(PlayerJoinEvent event) { final Player player = event.getPlayer(); - UUIDHandler.getPlayers().remove(player.getName()); BukkitUtil.removePlayer(player.getName()); final PlotPlayer pp = BukkitUtil.getPlayer(player); - // Now - String name = pp.getName(); - StringWrapper sw = new StringWrapper(name); - UUID uuid = pp.getUUID(); - UUIDHandler.add(sw, uuid); Location location = pp.getLocation(); PlotArea area = location.getPlotArea(); @@ -945,8 +955,7 @@ public class PlayerEvents extends PlotListener implements Listener { Set recipients = event.getRecipients(); recipients.clear(); Set spies = new HashSet<>(); - for (Entry entry : UUIDHandler.getPlayers().entrySet()) { - PlotPlayer pp = entry.getValue(); + for (final PlotPlayer pp : PlotSquared.imp().getPlayerManager().getPlayers()) { if (pp.getAttribute("chatspy")) { spies.add(((BukkitPlayer) pp).player); } else { @@ -2870,7 +2879,7 @@ public class PlayerEvents extends PlotListener implements Listener { } return true; } else if (dplot != null && (!dplot.equals(vplot) || Objects - .equals(dplot.guessOwner(), vplot.guessOwner()))) { + .equals(dplot.getOwnerAbs(), vplot.getOwnerAbs()))) { return vplot != null && vplot.getFlag(PveFlag.class); } //disable the firework damage. too much of a headache to support at the moment. diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/managers/BukkitWorldManager.java b/Bukkit/src/main/java/com/plotsquared/bukkit/managers/BukkitWorldManager.java index 482d02896..4646868a1 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/managers/BukkitWorldManager.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/managers/BukkitWorldManager.java @@ -36,6 +36,9 @@ import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; /** * Default Bukkit world manager. It will handle world creation by @@ -76,4 +79,13 @@ public class BukkitWorldManager implements PlatformWorldManager { return "bukkit"; } + @Override public Collection getWorlds() { + final List worlds = Bukkit.getWorlds(); + final List worldNames = new ArrayList<>(); + for (final World world : worlds) { + worldNames.add(world.getName()); + } + return worldNames; + } + } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/placeholder/Placeholders.java b/Bukkit/src/main/java/com/plotsquared/bukkit/placeholder/Placeholders.java index bf2a7a8b9..0ae44fbcb 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/placeholder/Placeholders.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/placeholder/Placeholders.java @@ -25,9 +25,10 @@ */ package com.plotsquared.bukkit.placeholder; +import com.plotsquared.core.PlotSquared; +import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; -import com.plotsquared.core.util.uuid.UUIDHandler; import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.expansion.PlaceholderExpansion; import org.bukkit.Bukkit; @@ -62,7 +63,7 @@ public class Placeholders extends PlaceholderExpansion { } @Override public String onPlaceholderRequest(Player p, String identifier) { - final PlotPlayer pl = PlotPlayer.get(p.getName()); + final PlotPlayer pl = PlotSquared.imp().getPlayerManager().getPlayerIfExists(p.getUniqueId()); if (pl == null) { return ""; @@ -122,7 +123,9 @@ public class Placeholders extends PlaceholderExpansion { if (uid == null) { return ""; } - String name = UUIDHandler.getName(uid); + + String name = PlotSquared.get().getImpromptuUUIDPipeline() .getSingle(uid, + Settings.UUID.BLOCKING_TIMEOUT); if (name != null) { return name; diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/player/BukkitPlayer.java b/Bukkit/src/main/java/com/plotsquared/bukkit/player/BukkitPlayer.java index 854cc6ec0..23d856860 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/player/BukkitPlayer.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/player/BukkitPlayer.java @@ -25,9 +25,11 @@ */ package com.plotsquared.bukkit.player; +import com.google.common.base.Charsets; import com.plotsquared.bukkit.util.BukkitUtil; import com.plotsquared.core.PlotSquared; import com.plotsquared.core.configuration.Captions; +import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.events.TeleportCause; import com.plotsquared.core.location.Location; import com.plotsquared.core.player.PlotPlayer; @@ -35,7 +37,6 @@ import com.plotsquared.core.plot.PlotWeather; import com.plotsquared.core.util.EconHandler; import com.plotsquared.core.util.MathMan; import com.plotsquared.core.util.StringMan; -import com.plotsquared.core.util.uuid.UUIDHandler; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.world.item.ItemType; @@ -97,10 +98,16 @@ public class BukkitPlayer extends PlotPlayer { } @NotNull @Override public UUID getUUID() { - if (this.uuid == null) { - this.uuid = UUIDHandler.getUUID(this); + if (Settings.UUID.OFFLINE) { + if (Settings.UUID.FORCE_LOWERCASE) { + return UUID.nameUUIDFromBytes(("OfflinePlayer:" + + getName().toLowerCase()).getBytes(Charsets.UTF_8)); + } else { + return UUID.nameUUIDFromBytes(("OfflinePlayer:" + + getName()).getBytes(Charsets.UTF_8)); + } } - return this.uuid; + return player.getUniqueId(); } @Override public long getLastPlayed() { diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/uuid/LowerOfflineUUIDWrapper.java b/Bukkit/src/main/java/com/plotsquared/bukkit/player/BukkitPlayerManager.java similarity index 56% rename from Bukkit/src/main/java/com/plotsquared/bukkit/util/uuid/LowerOfflineUUIDWrapper.java rename to Bukkit/src/main/java/com/plotsquared/bukkit/player/BukkitPlayerManager.java index ad2be7ce3..cdb770e2f 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/uuid/LowerOfflineUUIDWrapper.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/player/BukkitPlayerManager.java @@ -23,38 +23,38 @@ * 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.bukkit.player; -import com.google.common.base.Charsets; -import com.plotsquared.core.player.OfflinePlotPlayer; -import com.plotsquared.core.player.PlotPlayer; -import org.bukkit.OfflinePlayer; +import com.plotsquared.core.util.PlayerManager; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import java.util.Objects; import java.util.UUID; -public class LowerOfflineUUIDWrapper extends OfflineUUIDWrapper { +/** + * Player manager providing {@link BukkitPlayer Bukkit players} + */ +public class BukkitPlayerManager extends PlayerManager { - @NotNull @Override public UUID getUUID(PlotPlayer player) { - return UUID.nameUUIDFromBytes( - ("OfflinePlayer:" + player.getName().toLowerCase()).getBytes(Charsets.UTF_8)); + @Override @NotNull public BukkitPlayer createPlayer(@NotNull final UUID uuid) { + final Player player = Bukkit.getPlayer(uuid); + if (player == null || !player.isOnline()) { + throw new NoSuchPlayerException(uuid); + } + return new BukkitPlayer(player); } - @Override public UUID getUUID(OfflinePlotPlayer player) { - return UUID.nameUUIDFromBytes( - ("OfflinePlayer:" + player.getName().toLowerCase()).getBytes(Charsets.UTF_8)); + @Nullable @Override public BukkitOfflinePlayer getOfflinePlayer(@Nullable final UUID uuid) { + if (uuid == null) { + return null; + } + return new BukkitOfflinePlayer(Bukkit.getOfflinePlayer(uuid)); } - @Override public UUID getUUID(OfflinePlayer player) { - return UUID.nameUUIDFromBytes( - ("OfflinePlayer:" + Objects.requireNonNull(player.getName()).toLowerCase()) - .getBytes(Charsets.UTF_8)); - } - - @Override public UUID getUUID(String name) { - return UUID - .nameUUIDFromBytes(("OfflinePlayer:" + name.toLowerCase()).getBytes(Charsets.UTF_8)); + @NotNull @Override public BukkitOfflinePlayer getOfflinePlayer(@NotNull final String username) { + return new BukkitOfflinePlayer(Bukkit.getOfflinePlayer(username)); } } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitUtil.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitUtil.java index 23fd8937b..8ba3caa9b 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitUtil.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitUtil.java @@ -39,7 +39,6 @@ import com.plotsquared.core.util.StringComparison; import com.plotsquared.core.util.WorldUtil; import com.plotsquared.core.util.task.RunnableVal; import com.plotsquared.core.util.task.TaskManager; -import com.plotsquared.core.util.uuid.UUIDHandler; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitWorld; import com.sk89q.worldedit.math.BlockVector2; @@ -268,15 +267,7 @@ public class BukkitUtil extends WorldUtil { if (player == lastPlayer) { return lastPlotPlayer; } - final String name = player.getName(); - final PlotPlayer plotPlayer = UUIDHandler.getPlayer(name); - if (plotPlayer != null) { - return plotPlayer; - } - lastPlotPlayer = new BukkitPlayer(player); - UUIDHandler.getPlayers().put(name, lastPlotPlayer); - lastPlayer = player; - return lastPlotPlayer; + return PlotSquared.imp().getPlayerManager().getPlayer(player.getUniqueId()); } public static Location getLocation(@NonNull final org.bukkit.Location location) { diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/SendChunk.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/SendChunk.java deleted file mode 100644 index 7a920fc8b..000000000 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/SendChunk.java +++ /dev/null @@ -1,167 +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.bukkit.util; - -import com.plotsquared.bukkit.player.BukkitPlayer; -import com.plotsquared.core.PlotSquared; -import com.plotsquared.core.location.Location; -import com.plotsquared.core.player.PlotPlayer; -import com.plotsquared.core.plot.Plot; -import com.plotsquared.core.util.ReflectionUtils.RefClass; -import com.plotsquared.core.util.ReflectionUtils.RefConstructor; -import com.plotsquared.core.util.ReflectionUtils.RefField; -import com.plotsquared.core.util.ReflectionUtils.RefMethod; -import com.plotsquared.core.util.task.TaskManager; -import com.plotsquared.core.util.uuid.UUIDHandler; -import com.sk89q.worldedit.math.BlockVector2; -import io.papermc.lib.PaperLib; -import org.bukkit.Bukkit; -import org.bukkit.Chunk; -import org.bukkit.World; -import org.bukkit.entity.Player; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map.Entry; - -import static com.plotsquared.core.util.ReflectionUtils.getRefClass; - -/** - * An utility that can be used to send chunks, rather than using bukkit code - * to do so (uses heavy NMS). - */ -public class SendChunk { - - private final RefMethod methodGetHandlePlayer; - private final RefMethod methodGetHandleChunk; - private final RefConstructor mapChunk; - private final RefField connection; - private final RefMethod send; - private final RefMethod methodInitLighting; - - /** - * Constructor. - */ - public SendChunk() throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException { - RefClass classCraftPlayer = getRefClass("{cb}.entity.CraftPlayer"); - this.methodGetHandlePlayer = classCraftPlayer.getMethod("getHandle"); - RefClass classCraftChunk = getRefClass("{cb}.CraftChunk"); - this.methodGetHandleChunk = classCraftChunk.getMethod("getHandle"); - RefClass classChunk = getRefClass("{nms}.Chunk"); - this.methodInitLighting = classChunk.getMethod("initLighting"); - RefClass classMapChunk = getRefClass("{nms}.PacketPlayOutMapChunk"); - this.mapChunk = classMapChunk.getConstructor(classChunk.getRealClass(), int.class); - RefClass classEntityPlayer = getRefClass("{nms}.EntityPlayer"); - this.connection = classEntityPlayer.getField("playerConnection"); - RefClass classPacket = getRefClass("{nms}.Packet"); - RefClass classConnection = getRefClass("{nms}.PlayerConnection"); - this.send = classConnection.getMethod("sendPacket", classPacket.getRealClass()); - } - - public void sendChunk(Collection input) { - HashSet chunks = new HashSet<>(input); - HashMap> map = new HashMap<>(); - int view = Bukkit.getServer().getViewDistance(); - for (Chunk chunk : chunks) { - String world = chunk.getWorld().getName(); - ArrayList list = map.computeIfAbsent(world, k -> new ArrayList<>()); - list.add(chunk); - Object c = this.methodGetHandleChunk.of(chunk).call(); - this.methodInitLighting.of(c).call(); - } - for (Entry entry : UUIDHandler.getPlayers().entrySet()) { - PlotPlayer pp = entry.getValue(); - Plot plot = pp.getCurrentPlot(); - Location location = null; - String world; - if (plot != null) { - world = plot.getWorldName(); - } else { - location = pp.getLocation(); - world = location.getWorld(); - } - ArrayList list = map.get(world); - if (list == null) { - continue; - } - if (location == null) { - location = pp.getLocation(); - } - int chunkX = location.getX() >> 4; - int chunkZ = location.getZ() >> 4; - Player player = ((BukkitPlayer) pp).player; - Object entity = this.methodGetHandlePlayer.of(player).call(); - - for (Chunk chunk : list) { - int dx = Math.abs(chunkX - chunk.getX()); - int dz = Math.abs(chunkZ - chunk.getZ()); - if ((dx > view) || (dz > view)) { - continue; - } - Object c = this.methodGetHandleChunk.of(chunk).call(); - chunks.remove(chunk); - Object con = this.connection.of(entity).get(); - Object packet = null; - try { - packet = this.mapChunk.create(c, 65535); - } catch (Exception ignored) { - } - if (packet == null) { - PlotSquared.debug("Error with PacketPlayOutMapChunk reflection."); - } - this.send.of(con).call(packet); - } - } - for (final Chunk chunk : chunks) { - TaskManager.runTask(() -> { - try { - chunk.unload(true); - } catch (Throwable ignored) { - String worldName = chunk.getWorld().getName(); - PlotSquared.debug( - "$4Could not save chunk: " + worldName + ';' + chunk.getX() + ";" + chunk - .getZ()); - PlotSquared.debug("$3 - $4File may be open in another process (e.g. MCEdit)"); - PlotSquared.debug("$3 - $4" + worldName + "/level.dat or " + worldName - + "/level_old.dat may be corrupt (try repairing or removing these)"); - } - }); - } - } - - public void sendChunk(String worldName, Collection chunkLocations) { - World myWorld = Bukkit.getWorld(worldName); - ArrayList chunks = new ArrayList<>(); - for (BlockVector2 loc : chunkLocations) { - if (myWorld.isChunkLoaded(loc.getX(), loc.getZ())) { - PaperLib.getChunkAtAsync(myWorld, loc.getX(), loc.getZ()).thenAccept(chunks::add); - } - } - sendChunk(chunks); - } -} diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/uuid/FileUUIDHandler.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/uuid/FileUUIDHandler.java deleted file mode 100644 index c5620ffa1..000000000 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/uuid/FileUUIDHandler.java +++ /dev/null @@ -1,289 +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.bukkit.util.uuid; - -import com.google.common.collect.HashBiMap; -import com.google.common.collect.Sets; -import com.plotsquared.core.PlotSquared; -import com.plotsquared.core.configuration.Captions; -import com.plotsquared.core.configuration.Settings; -import com.plotsquared.core.player.OfflinePlotPlayer; -import com.plotsquared.core.plot.expiration.ExpireManager; -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 com.plotsquared.core.util.uuid.UUIDHandler; -import com.plotsquared.core.util.uuid.UUIDHandlerImplementation; -import com.plotsquared.core.util.uuid.UUIDWrapper; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.NBTInputStream; -import com.sk89q.jnbt.Tag; -import org.bukkit.Bukkit; -import org.bukkit.World; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.zip.GZIPInputStream; - -public class FileUUIDHandler extends UUIDHandlerImplementation { - - public FileUUIDHandler(UUIDWrapper wrapper) { - super(wrapper); - } - - @Override public boolean startCaching(Runnable whenDone) { - return super.startCaching(whenDone) && cache(whenDone); - } - - private Tag readTag(File file) throws IOException { - // Don't chain the creation of the GZIP stream and the NBT stream, because their - // constructors may throw an IOException. - try (BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(file)); - GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream); - NBTInputStream nbtInputStream = new NBTInputStream(gzipInputStream)) { - return nbtInputStream.readNamedTag().getTag(); - } - } - - public boolean cache(final Runnable whenDone) { - final File container = Bukkit.getWorldContainer(); - List worlds = Bukkit.getWorlds(); - final String world; - if (worlds.isEmpty()) { - world = "world"; - } else { - world = worlds.get(0).getName(); - } - TaskManager.runTaskAsync(() -> { - PlotSquared.debug(Captions.PREFIX + "Starting player data caching for: " + world); - File uuidFile = new File(PlotSquared.get().IMP.getDirectory(), "uuids.txt"); - if (uuidFile.exists()) { - try { - List lines = - Files.readAllLines(uuidFile.toPath(), StandardCharsets.UTF_8); - for (String line : lines) { - try { - line = line.trim(); - if (line.isEmpty()) { - continue; - } - line = line.replaceAll("[\\|][0-9]+[\\|][0-9]+[\\|]", ""); - String[] split = line.split("\\|"); - String name = split[0]; - if (name.isEmpty() || (name.length() > 16) || !StringMan - .isAlphanumericUnd(name)) { - continue; - } - UUID uuid = FileUUIDHandler.this.uuidWrapper.getUUID(name); - if (uuid == null) { - continue; - } - UUIDHandler.add(new StringWrapper(name), uuid); - } catch (Exception e2) { - e2.printStackTrace(); - } - } - } catch (IOException e) { - e.printStackTrace(); - } - } - HashBiMap toAdd = HashBiMap.create(new HashMap<>()); - if (Settings.UUID.NATIVE_UUID_PROVIDER) { - HashSet all = UUIDHandler.getAllUUIDS(); - PlotSquared.debug("Fast mode UUID caching enabled!"); - File playerDataFolder = new File(container, world + File.separator + "playerdata"); - String[] dat = playerDataFolder.list(new DatFileFilter()); - boolean check = all.isEmpty(); - if (dat != null) { - for (String current : dat) { - String s = current.replaceAll(".dat$", ""); - try { - UUID uuid = UUID.fromString(s); - if (check || all.remove(uuid)) { - File file = new File(playerDataFolder, current); - CompoundTag compound = (CompoundTag) readTag(file); - if (!compound.containsKey("bukkit")) { - PlotSquared.debug("ERROR: Player data (" + uuid.toString() - + ".dat) does not contain the the key \"bukkit\""); - } else { - Map compoundMap = compound.getValue(); - CompoundTag bukkit = (CompoundTag) compoundMap.get("bukkit"); - Map bukkitMap = bukkit.getValue(); - String name = - (String) bukkitMap.get("lastKnownName").getValue(); - long last = (long) bukkitMap.get("lastPlayed").getValue(); - long first = (long) bukkitMap.get("firstPlayed").getValue(); - if (ExpireManager.IMP != null) { - ExpireManager.IMP.storeDate(uuid, last); - ExpireManager.IMP.storeAccountAge(uuid, last - first); - } - toAdd.put(new StringWrapper(name), uuid); - } - } - } catch (Exception e) { - e.printStackTrace(); - PlotSquared.debug(Captions.PREFIX + "Invalid playerdata: " + current); - } - } - } - add(toAdd); - if (all.isEmpty()) { - if (whenDone != null) { - whenDone.run(); - } - return; - } else { - PlotSquared.debug( - "Failed to cache: " + all.size() + " uuids - slowly processing all files"); - } - } - HashSet worlds1 = Sets.newHashSet(world, "world"); - HashSet uuids = new HashSet<>(); - HashSet names = new HashSet<>(); - File playerDataFolder = null; - for (String worldName : worlds1) { - // Getting UUIDs - playerDataFolder = new File(container, worldName + File.separator + "playerdata"); - String[] dat = playerDataFolder.list(new DatFileFilter()); - if ((dat != null) && (dat.length != 0)) { - for (String current : dat) { - String s = current.replaceAll(".dat$", ""); - try { - UUID uuid = UUID.fromString(s); - uuids.add(uuid); - } catch (Exception ignored) { - PlotSquared.debug(Captions.PREFIX + "Invalid PlayerData: " + current); - } - } - break; - } - // Getting names - File playersFolder = new File(worldName + File.separator + "players"); - dat = playersFolder.list(new DatFileFilter()); - if ((dat != null) && (dat.length != 0)) { - for (String current : dat) { - names.add(current.replaceAll(".dat$", "")); - } - break; - } - } - for (UUID uuid : uuids) { - try { - File file = - new File(playerDataFolder + File.separator + uuid.toString() + ".dat"); - if (!file.exists()) { - continue; - } - CompoundTag compound = (CompoundTag) readTag(file); - if (!compound.containsKey("bukkit")) { - PlotSquared.debug("ERROR: Player data (" + uuid.toString() - + ".dat) does not contain the the key \"bukkit\""); - } else { - Map compoundMap = compound.getValue(); - CompoundTag bukkit = (CompoundTag) compoundMap.get("bukkit"); - Map bukkitMap = bukkit.getValue(); - String name = (String) bukkitMap.get("lastKnownName").getValue(); - StringWrapper wrap = new StringWrapper(name); - if (!toAdd.containsKey(wrap)) { - long last = (long) bukkitMap.get("lastPlayed").getValue(); - long first = (long) bukkitMap.get("firstPlayed").getValue(); - if (Settings.UUID.OFFLINE) { - if (Settings.UUID.FORCE_LOWERCASE && !name.toLowerCase() - .equals(name)) { - uuid = FileUUIDHandler.this.uuidWrapper.getUUID(name); - } else { - long most = (long) compoundMap.get("UUIDMost").getValue(); - long least = (long) compoundMap.get("UUIDLeast").getValue(); - uuid = new UUID(most, least); - } - } - if (ExpireManager.IMP != null) { - ExpireManager.IMP.storeDate(uuid, last); - ExpireManager.IMP.storeAccountAge(uuid, last - first); - } - toAdd.put(wrap, uuid); - } - } - } catch (Exception ignored) { - PlotSquared.debug( - Captions.PREFIX + "&6Invalid PlayerData: " + uuid.toString() + ".dat"); - } - } - for (String name : names) { - UUID uuid = FileUUIDHandler.this.uuidWrapper.getUUID(name); - StringWrapper nameWrap = new StringWrapper(name); - toAdd.put(nameWrap, uuid); - } - - if (getUUIDMap().isEmpty()) { - for (OfflinePlotPlayer offlinePlotPlayer : FileUUIDHandler.this.uuidWrapper - .getOfflinePlayers()) { - long last = offlinePlotPlayer.getLastPlayed(); - if (last != 0) { - String name = offlinePlotPlayer.getName(); - StringWrapper wrap = new StringWrapper(name); - if (!toAdd.containsKey(wrap)) { - UUID uuid = FileUUIDHandler.this.uuidWrapper.getUUID(offlinePlotPlayer); - if (toAdd.containsValue(uuid)) { - StringWrapper duplicate = toAdd.inverse().get(uuid); - PlotSquared.debug( - "The UUID: " + uuid.toString() + " is already mapped to " - + duplicate - + "\n It cannot be added to the Map with a key of " + wrap); - } - toAdd.putIfAbsent(wrap, uuid); - if (ExpireManager.IMP != null) { - ExpireManager.IMP.storeDate(uuid, last); - } - } - } - } - } - add(toAdd); - if (whenDone != null) { - whenDone.run(); - } - }); - return true; - } - - @Override public void fetchUUID(final String name, final RunnableVal ifFetch) { - TaskManager.runTaskAsync(() -> { - ifFetch.value = FileUUIDHandler.this.uuidWrapper.getUUID(name); - TaskManager.runTask(ifFetch); - }); - } -} diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/uuid/OfflineUUIDWrapper.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/uuid/OfflineUUIDWrapper.java deleted file mode 100644 index 66ca52814..000000000 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/uuid/OfflineUUIDWrapper.java +++ /dev/null @@ -1,131 +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.bukkit.util.uuid; - -import com.google.common.base.Charsets; -import com.google.common.collect.BiMap; -import com.plotsquared.bukkit.player.BukkitOfflinePlayer; -import com.plotsquared.core.PlotSquared; -import com.plotsquared.core.player.OfflinePlotPlayer; -import com.plotsquared.core.player.PlotPlayer; -import com.plotsquared.core.util.StringWrapper; -import com.plotsquared.core.util.uuid.UUIDHandler; -import com.plotsquared.core.util.uuid.UUIDWrapper; -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; -import org.bukkit.Server; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Collection; -import java.util.UUID; - -public class OfflineUUIDWrapper extends UUIDWrapper { - - private final Object[] arg = new Object[0]; - private Method getOnline = null; - - public OfflineUUIDWrapper() { - try { - this.getOnline = Server.class.getMethod("getOnlinePlayers"); - } catch (NoSuchMethodException | SecurityException e) { - e.printStackTrace(); - } - } - - @NotNull @Override public UUID getUUID(PlotPlayer player) { - return UUID - .nameUUIDFromBytes(("OfflinePlayer:" + player.getName()).getBytes(Charsets.UTF_8)); - } - - @Override public UUID getUUID(OfflinePlotPlayer player) { - return UUID - .nameUUIDFromBytes(("OfflinePlayer:" + player.getName()).getBytes(Charsets.UTF_8)); - } - - public UUID getUUID(OfflinePlayer player) { - return UUID - .nameUUIDFromBytes(("OfflinePlayer:" + player.getName()).getBytes(Charsets.UTF_8)); - } - - @Override public OfflinePlotPlayer getOfflinePlayer(UUID uuid) { - BiMap map = UUIDHandler.getUuidMap().inverse(); - String name = null; - if (map.containsKey(uuid)) { - name = map.get(uuid).value; - } - if (name != null) { - OfflinePlayer op = Bukkit.getOfflinePlayer(name); - if (op.hasPlayedBefore()) { - return new BukkitOfflinePlayer(op); - } - } - for (OfflinePlayer player : Bukkit.getOfflinePlayers()) { - if (getUUID(player).equals(uuid)) { - return new BukkitOfflinePlayer(player); - } - } - return null; - } - - public Player[] getOnlinePlayers() { - if (this.getOnline == null) { - Collection onlinePlayers = Bukkit.getOnlinePlayers(); - return onlinePlayers.toArray(new Player[0]); - } - try { - Object players = this.getOnline.invoke(Bukkit.getServer(), this.arg); - if (players instanceof Player[]) { - return (Player[]) players; - } else { - @SuppressWarnings("unchecked") Collection p = - (Collection) players; - return p.toArray(new Player[0]); - } - } catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException ignored) { - PlotSquared.debug("Failed to resolve online players"); - this.getOnline = null; - Collection onlinePlayers = Bukkit.getOnlinePlayers(); - return onlinePlayers.toArray(new Player[0]); - } - } - - @Override public UUID getUUID(String name) { - return UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8)); - } - - @Override public OfflinePlotPlayer[] getOfflinePlayers() { - OfflinePlayer[] ops = Bukkit.getOfflinePlayers(); - return Arrays.stream(ops).map(BukkitOfflinePlayer::new).toArray(BukkitOfflinePlayer[]::new); - } - - @Override public OfflinePlotPlayer getOfflinePlayer(String name) { - return new BukkitOfflinePlayer(Bukkit.getOfflinePlayer(name)); - } -} diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/uuid/SQLUUIDHandler.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/uuid/SQLUUIDHandler.java deleted file mode 100644 index a08956369..000000000 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/uuid/SQLUUIDHandler.java +++ /dev/null @@ -1,270 +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.bukkit.util.uuid; - -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.SQLite; -import com.plotsquared.core.util.MainUtil; -import com.plotsquared.core.util.StringWrapper; -import com.plotsquared.core.util.task.RunnableVal; -import com.plotsquared.core.util.task.TaskManager; -import com.plotsquared.core.util.uuid.UUIDHandler; -import com.plotsquared.core.util.uuid.UUIDHandlerImplementation; -import com.plotsquared.core.util.uuid.UUIDWrapper; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; -import org.json.simple.parser.ParseException; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayDeque; -import java.util.Collections; -import java.util.HashMap; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -public class SQLUUIDHandler extends UUIDHandlerImplementation { - - final int MAX_REQUESTS = 500; - private final String PROFILE_URL = - "https://sessionserver.mojang.com/session/minecraft/profile/"; - private final JSONParser jsonParser = new JSONParser(); - private final SQLite sqlite; - - public SQLUUIDHandler(UUIDWrapper wrapper) { - super(wrapper); - this.sqlite = - new SQLite(MainUtil.getFile(PlotSquared.get().IMP.getDirectory(), "usercache.db")); - try { - this.sqlite.openConnection(); - } catch (ClassNotFoundException | SQLException e) { - e.printStackTrace(); - } - - try (PreparedStatement stmt = getConnection().prepareStatement( - "CREATE TABLE IF NOT EXISTS `usercache` (uuid VARCHAR(32) NOT NULL, username VARCHAR(32) NOT NULL, PRIMARY KEY (uuid, username))")) { - stmt.execute(); - } catch (SQLException e) { - e.printStackTrace(); - } - startCaching(null); - } - - private Connection getConnection() { - synchronized (this.sqlite) { - return this.sqlite.getConnection(); - } - } - - @Override public boolean startCaching(final Runnable whenDone) { - if (!super.startCaching(whenDone)) { - return false; - } - TaskManager.runTaskAsync(() -> { - try { - HashBiMap toAdd = HashBiMap.create(new HashMap<>()); - try (PreparedStatement statement = getConnection() - .prepareStatement("SELECT `uuid`, `username` FROM `usercache`"); - ResultSet resultSet = statement.executeQuery()) { - while (resultSet.next()) { - StringWrapper username = new StringWrapper(resultSet.getString("username")); - UUID uuid = UUID.fromString(resultSet.getString("uuid")); - toAdd.put(new StringWrapper(username.value), uuid); - } - } - add(toAdd); - // This should be called as long as there are some unknown plots - final ArrayDeque toFetch = new ArrayDeque<>(); - for (UUID u : UUIDHandler.getAllUUIDS()) { - if (!uuidExists(u)) { - toFetch.add(u); - } - } - if (toFetch.isEmpty()) { - if (whenDone != null) { - whenDone.run(); - } - return; - } - FileUUIDHandler fileHandler = new FileUUIDHandler(SQLUUIDHandler.this.uuidWrapper); - fileHandler.startCaching(() -> { - // If the file based UUID handler didn't cache it, then we can't cache offline mode - // Also, trying to cache based on files again, is useless as that's what the file based uuid cacher does - if (Settings.UUID.OFFLINE) { - if (whenDone != null) { - whenDone.run(); - } - return; - } - - TaskManager.runTaskAsync(() -> { - while (!toFetch.isEmpty()) { - try { - for (int i = 0; i < Math.min(MAX_REQUESTS, toFetch.size()); i++) { - UUID uuid = toFetch.pop(); - HttpURLConnection connection = (HttpURLConnection) new URL( - SQLUUIDHandler.this.PROFILE_URL + uuid.toString() - .replace("-", "")).openConnection(); - try (InputStream con = connection.getInputStream()) { - InputStreamReader reader = new InputStreamReader(con); - JSONObject response = - (JSONObject) SQLUUIDHandler.this.jsonParser - .parse(reader); - String name = (String) response.get("name"); - if (name != null) { - add(new StringWrapper(name), uuid); - } - } - connection.disconnect(); - } - } catch (IOException | ParseException e) { - PlotSquared.debug( - "Invalid response from Mojang: Some UUIDs will be cached later. (`unknown` until then or player joins)"); - } - try { - //Mojang allows requests every 10 minutes according to https://wiki.vg/Mojang_API - //15 Minutes is chosen here since system timers are not always precise - //and it should provide enough time where Mojang won't block requests. - TimeUnit.MINUTES.sleep(15); - } catch (InterruptedException e) { - e.printStackTrace(); - break; - } - } - if (whenDone != null) { - whenDone.run(); - } - }); - }); - } catch (SQLException e) { - throw new SQLUUIDHandlerException("Couldn't select :s", e); - } - }); - return true; - } - - @Override public void fetchUUID(final String name, final RunnableVal ifFetch) { - PlotSquared.debug(Captions.PREFIX + "UUID for '" + name - + "' was null. We'll cache this from the Mojang servers!"); - if (ifFetch == null) { - return; - } - TaskManager.runTaskAsync(() -> { - try { - URL url = new URL(SQLUUIDHandler.this.PROFILE_URL); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod("POST"); - connection.setRequestProperty("Content-Type", "application/json"); - connection.setUseCaches(false); - connection.setDoInput(true); - connection.setDoOutput(true); - String body = JSONArray.toJSONString(Collections.singletonList(name)); - OutputStream stream = connection.getOutputStream(); - stream.write(body.getBytes()); - stream.flush(); - stream.close(); - JSONArray array = (JSONArray) SQLUUIDHandler.this.jsonParser - .parse(new InputStreamReader(connection.getInputStream())); - JSONObject jsonProfile = (JSONObject) array.get(0); - String id = (String) jsonProfile.get("id"); - String name1 = (String) jsonProfile.get("name"); - ifFetch.value = UUID.fromString( - id.substring(0, 8) + '-' + id.substring(8, 12) + '-' + id.substring(12, 16) - + '-' + id.substring(16, 20) + '-' + id.substring(20, 32)); - } catch (IOException | ParseException e) { - e.printStackTrace(); - } - TaskManager.runTask(ifFetch); - }); - } - - @Override public void handleShutdown() { - super.handleShutdown(); - try { - getConnection().close(); - } catch (SQLException e) { - throw new SQLUUIDHandlerException("Couldn't close database connection", e); - } - } - - /** - * This is useful for name changes - */ - @Override public void rename(final UUID uuid, final StringWrapper name) { - super.rename(uuid, name); - TaskManager.runTaskAsync(() -> { - try (PreparedStatement statement = getConnection() - .prepareStatement("UPDATE usercache SET `username`=? WHERE `uuid`=?")) { - statement.setString(1, name.value); - statement.setString(2, uuid.toString()); - statement.execute(); - PlotSquared.debug( - Captions.PREFIX + "Name change for '" + uuid + "' to '" + name.value + '\''); - } catch (SQLException e) { - e.printStackTrace(); - } - }); - } - - @Override public boolean add(final StringWrapper name, final UUID uuid) { - // Ignoring duplicates - if (super.add(name, uuid)) { - TaskManager.runTaskAsync(() -> { - try (PreparedStatement statement = getConnection() - .prepareStatement("REPLACE INTO usercache (`uuid`, `username`) VALUES(?, ?)")) { - statement.setString(1, uuid.toString()); - statement.setString(2, name.toString()); - statement.execute(); - PlotSquared - .debug(Captions.PREFIX + "&cAdded '&6" + uuid + "&c' - '&6" + name + "&c'"); - } catch (SQLException e) { - e.printStackTrace(); - } - }); - return true; - } - return false; - } - - private static class SQLUUIDHandlerException extends RuntimeException { - - SQLUUIDHandlerException(String s, Throwable c) { - super("SQLUUIDHandler caused an exception: " + s, c); - } - } -} diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/uuid/EssentialsUUIDService.java b/Bukkit/src/main/java/com/plotsquared/bukkit/uuid/EssentialsUUIDService.java new file mode 100644 index 000000000..eb473d67e --- /dev/null +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/uuid/EssentialsUUIDService.java @@ -0,0 +1,70 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * 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.bukkit.uuid; + +import com.earth2me.essentials.Essentials; +import com.earth2me.essentials.User; +import com.plotsquared.core.uuid.UUIDMapping; +import com.plotsquared.core.uuid.UUIDService; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +/** + * UUID service using the EssentialsX API + */ +public class EssentialsUUIDService implements UUIDService { + + private final Essentials essentials; + + public EssentialsUUIDService() { + this.essentials = Essentials.getPlugin(Essentials.class); + } + + @Override @NotNull public List getNames(@NotNull final List uuids) { + return Collections.emptyList(); + } + + @Override @NotNull public List getUUIDs(@NotNull final List usernames) { + final List mappings = new ArrayList<>(usernames.size()); + for (final String username : usernames) { + try { + final User user = essentials.getUser(username); + if (user != null) { + final UUID uuid = user.getConfigUUID(); + if (uuid != null) { + mappings.add(new UUIDMapping(uuid, username)); + } + } + } catch (final Exception ignored){} + } + return mappings; + } + +} diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/uuid/LuckPermsUUIDService.java b/Bukkit/src/main/java/com/plotsquared/bukkit/uuid/LuckPermsUUIDService.java new file mode 100644 index 000000000..47bcc9bb3 --- /dev/null +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/uuid/LuckPermsUUIDService.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.bukkit.uuid; + +import com.plotsquared.core.uuid.UUIDMapping; +import com.plotsquared.core.uuid.UUIDService; +import net.luckperms.api.LuckPerms; +import net.luckperms.api.model.user.UserManager; +import org.bukkit.Bukkit; +import org.bukkit.plugin.RegisteredServiceProvider; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * UUID service that uses the LuckPerms API + */ +public class LuckPermsUUIDService implements UUIDService { + + private final LuckPerms luckPerms; + + public LuckPermsUUIDService() { + final RegisteredServiceProvider provider = Bukkit.getServicesManager().getRegistration(LuckPerms.class); + if (provider != null) { + this.luckPerms = provider.getProvider(); + } else { + throw new IllegalStateException("LuckPerms not available"); + } + } + + @Override @NotNull public List getNames(@NotNull final List uuids) { + final List mappings = new ArrayList<>(uuids.size()); + final UserManager userManager = this.luckPerms.getUserManager(); + for (final UUID uuid : uuids) { + try { + final String username = userManager.lookupUsername(uuid).get(); + if (username != null) { + mappings.add(new UUIDMapping(uuid, username)); + } + } catch (final Exception ignored) {} + } + return mappings; + } + + @Override @NotNull public List getUUIDs(@NotNull final List usernames) { + final List mappings = new ArrayList<>(usernames.size()); + final UserManager userManager = this.luckPerms.getUserManager(); + for (final String username : usernames) { + try { + final UUID uuid = userManager.lookupUniqueId(username).get(); + if (username != null) { + mappings.add(new UUIDMapping(uuid, username)); + } + } catch (final Exception ignored) {} + } + return mappings; + } + +} diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/uuid/OfflinePlayerUUIDService.java b/Bukkit/src/main/java/com/plotsquared/bukkit/uuid/OfflinePlayerUUIDService.java new file mode 100644 index 000000000..9c9f02344 --- /dev/null +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/uuid/OfflinePlayerUUIDService.java @@ -0,0 +1,74 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * 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.bukkit.uuid; + +import com.google.common.base.Charsets; +import com.plotsquared.core.configuration.Settings; +import com.plotsquared.core.uuid.UUIDMapping; +import com.plotsquared.core.uuid.UUIDService; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +/** + * UUID service that use {@link org.bukkit.OfflinePlayer offline players} + */ +public class OfflinePlayerUUIDService implements UUIDService { + + @Override @NotNull public List getNames(@NotNull final List uuids) { + if (Settings.UUID.FORCE_LOWERCASE) { + return Collections.emptyList(); // This is useless now + } + final List wrappers = new ArrayList<>(uuids.size()); + for (final UUID uuid : uuids) { + final OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuid); + if (offlinePlayer.hasPlayedBefore()) { + wrappers.add(new UUIDMapping(uuid, offlinePlayer.getName())); + } + } + return wrappers; + } + + @Override @NotNull public List getUUIDs(@NotNull final List usernames) { + final List wrappers = new ArrayList<>(usernames.size()); + for (final String username : usernames) { + if (Settings.UUID.FORCE_LOWERCASE) { + wrappers.add(new UUIDMapping(UUID.nameUUIDFromBytes(("OfflinePlayer:" + + username.toLowerCase()).getBytes(Charsets.UTF_8)), username)); + } else { + wrappers.add(new UUIDMapping(UUID.nameUUIDFromBytes(("OfflinePlayer:" + + username).getBytes(Charsets.UTF_8)), username)); + } + } + return wrappers; + } + +} diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/uuid/PaperUUIDService.java b/Bukkit/src/main/java/com/plotsquared/bukkit/uuid/PaperUUIDService.java new file mode 100644 index 000000000..4f370c8b3 --- /dev/null +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/uuid/PaperUUIDService.java @@ -0,0 +1,65 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * 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.bukkit.uuid; + +import com.destroystokyo.paper.profile.PlayerProfile; +import com.plotsquared.core.uuid.UUIDMapping; +import com.plotsquared.core.uuid.UUIDService; +import org.bukkit.Bukkit; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * UUID service that uses the Paper profile API + */ +public class PaperUUIDService implements UUIDService { + + @Override @NotNull public List getNames(@NotNull final List uuids) { + final List mappings = new ArrayList<>(uuids.size()); + for (final UUID uuid : uuids) { + final PlayerProfile playerProfile = Bukkit.createProfile(uuid); + if ((playerProfile.isComplete() || playerProfile.completeFromCache()) && playerProfile.getId() != null) { + mappings.add(new UUIDMapping(playerProfile.getId(), playerProfile.getName())); + } + } + return mappings; + } + + @Override @NotNull public List getUUIDs(@NotNull final List usernames) { + final List mappings = new ArrayList<>(usernames.size()); + for (final String username : usernames) { + final PlayerProfile playerProfile = Bukkit.createProfile(username); + if ((playerProfile.isComplete() || playerProfile.completeFromCache()) && playerProfile.getId() != null) { + mappings.add(new UUIDMapping(playerProfile.getId(), playerProfile.getName())); + } + } + return mappings; + } + +} diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/uuid/SQLiteUUIDService.java b/Bukkit/src/main/java/com/plotsquared/bukkit/uuid/SQLiteUUIDService.java new file mode 100644 index 000000000..16b3abff5 --- /dev/null +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/uuid/SQLiteUUIDService.java @@ -0,0 +1,145 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * 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.bukkit.uuid; + +import com.plotsquared.core.PlotSquared; +import com.plotsquared.core.database.SQLite; +import com.plotsquared.core.util.MainUtil; +import com.plotsquared.core.uuid.UUIDMapping; +import com.plotsquared.core.uuid.UUIDService; +import org.jetbrains.annotations.NotNull; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; + +/** + * UUID service that uses the (legacy) SQL UUID cache + */ +public class SQLiteUUIDService implements UUIDService, Consumer> { + + private final SQLite sqlite; + + public SQLiteUUIDService(final String fileName) { + this.sqlite = + new SQLite(MainUtil.getFile(PlotSquared.get().IMP.getDirectory(), fileName)); + try { + this.sqlite.openConnection(); + } catch (ClassNotFoundException | SQLException e) { + e.printStackTrace(); + } + + try (PreparedStatement stmt = getConnection().prepareStatement( + "CREATE TABLE IF NOT EXISTS `usercache` (uuid VARCHAR(32) NOT NULL, username VARCHAR(32) NOT NULL, PRIMARY KEY (uuid))")) { + stmt.execute(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private Connection getConnection() { + synchronized (this.sqlite) { + return this.sqlite.getConnection(); + } + } + + @Override @NotNull public List getNames(@NotNull final List uuids) { + final List mappings = new ArrayList<>(uuids.size()); + try (final PreparedStatement statement = getConnection() + .prepareStatement("SELECT `username` FROM `usercache` WHERE `uuid` = ?")) { + for (final UUID uuid : uuids) { + statement.setString(1, uuid.toString()); + try (final ResultSet resultSet = statement.executeQuery()) { + if (resultSet.next()) { + mappings.add(new UUIDMapping(uuid, resultSet.getString("username"))); + } + } + } + } catch (final Exception e) { + e.printStackTrace(); + } + return mappings; + } + + @Override @NotNull public List getUUIDs(@NotNull List usernames) { + final List mappings = new ArrayList<>(usernames.size()); + try (final PreparedStatement statement = getConnection() + .prepareStatement("SELECT `uuid` FROM `usercache` WHERE `username` = ?")) { + for (final String username : usernames) { + statement.setString(1, username); + try (final ResultSet resultSet = statement.executeQuery()) { + if (resultSet.next()) { + mappings.add(new UUIDMapping(UUID.fromString(resultSet.getString("uuid")), + username)); + } + } + } + } catch (final Exception e) { + e.printStackTrace(); + } + return mappings; + } + + @Override public void accept(final List uuidWrappers) { + try (final PreparedStatement statement = getConnection() + .prepareStatement("INSERT OR REPLACE INTO `usercache` (`uuid`, `username`) VALUES(?, ?)")) { + for (final UUIDMapping mapping : uuidWrappers) { + statement.setString(1, mapping.getUuid().toString()); + statement.setString(2, mapping.getUsername()); + statement.executeUpdate(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } + + /** + * Read the entire cache at once + * + * @return All read mappings + */ + @NotNull public List getAll() { + final List mappings = new LinkedList<>(); + try (final PreparedStatement statement = getConnection().prepareStatement("SELECT * FROM `usercache`")) { + try (final ResultSet resultSet = statement.executeQuery()) { + while (resultSet.next()) { + mappings.add(new UUIDMapping(UUID.fromString(resultSet.getString("uuid")), resultSet.getString("username"))); + } + } + } catch (final Exception e) { + e.printStackTrace(); + } + return mappings; + } + + +} diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/uuid/SquirrelIdUUIDService.java b/Bukkit/src/main/java/com/plotsquared/bukkit/uuid/SquirrelIdUUIDService.java new file mode 100644 index 000000000..4580519aa --- /dev/null +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/uuid/SquirrelIdUUIDService.java @@ -0,0 +1,90 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * 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.bukkit.uuid; + +import com.google.common.util.concurrent.RateLimiter; +import com.plotsquared.core.uuid.UUIDMapping; +import com.plotsquared.core.uuid.UUIDService; +import com.sk89q.squirrelid.Profile; +import com.sk89q.squirrelid.resolver.HttpRepositoryService; +import com.sk89q.squirrelid.resolver.ProfileService; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * UUID service using SquirrelID + */ +@SuppressWarnings("UnstableApiUsage") +public class SquirrelIdUUIDService implements UUIDService { + + private final ProfileService profileService; + private final RateLimiter rateLimiter; + + /** + * Create a new SquirrelID UUID service + * + * @param rateLimit Mojangs rate limit is 600 requests per 10 minutes. + * This parameter specifies how many of those requests + * we can use before our internal rate limit kicks in. + */ + public SquirrelIdUUIDService(final int rateLimit) { + this.profileService = HttpRepositoryService.forMinecraft(); + // RateLimiter uses request per seconds. The constructor + // parameter rateLimit is requests per 600 seconds + this.rateLimiter = RateLimiter.create(rateLimit / 600.0D); + } + + @Override @NotNull public List getNames(@NotNull final List uuids) { + final List results = new ArrayList<>(uuids.size()); + this.rateLimiter.acquire(uuids.size()); + try { + for (final Profile profile : this.profileService.findAllById(uuids)) { + results.add(new UUIDMapping(profile.getUniqueId(), profile.getName())); + } + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + return results; + } + + @Override @NotNull public List getUUIDs(@NotNull final List usernames) { + final List results = new ArrayList<>(usernames.size()); + this.rateLimiter.acquire(usernames.size()); + try { + for (final Profile profile : this.profileService.findAllByName(usernames)) { + results.add(new UUIDMapping(profile.getUniqueId(), profile.getName())); + } + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + return results; + } + +} diff --git a/Core/src/main/java/com/plotsquared/core/IPlotMain.java b/Core/src/main/java/com/plotsquared/core/IPlotMain.java index ed6694e01..9e04d85ec 100644 --- a/Core/src/main/java/com/plotsquared/core/IPlotMain.java +++ b/Core/src/main/java/com/plotsquared/core/IPlotMain.java @@ -36,13 +36,13 @@ import com.plotsquared.core.util.ChunkManager; import com.plotsquared.core.util.EconHandler; import com.plotsquared.core.util.InventoryUtil; import com.plotsquared.core.util.PlatformWorldManager; +import com.plotsquared.core.util.PlayerManager; import com.plotsquared.core.util.RegionManager; import com.plotsquared.core.util.SchematicHandler; import com.plotsquared.core.util.SetupUtils; import com.plotsquared.core.util.WorldUtil; import com.plotsquared.core.util.logger.ILogger; import com.plotsquared.core.util.task.TaskManager; -import com.plotsquared.core.util.uuid.UUIDHandlerImplementation; import com.sk89q.worldedit.extension.platform.Actor; import org.jetbrains.annotations.NotNull; @@ -220,12 +220,6 @@ public interface IPlotMain extends ILogger { */ void setGenerator(String world); - /** - * Gets the {@link UUIDHandlerImplementation} which will cache and - * provide UUIDs. - */ - UUIDHandlerImplementation initUUIDHandler(); - /** * Gets the {@link InventoryUtil} class (used for implementation specific * inventory guis). @@ -285,6 +279,13 @@ public interface IPlotMain extends ILogger { * * @return World manager */ - @NotNull PlatformWorldManager getWorldManager(); + @NotNull PlatformWorldManager getWorldManager(); + + /** + * Get the player manager implementation for the platform + * + * @return Player manager + */ + @NotNull PlayerManager getPlayerManager(); } diff --git a/Core/src/main/java/com/plotsquared/core/PlotSquared.java b/Core/src/main/java/com/plotsquared/core/PlotSquared.java index 42226c2a1..a33fd502e 100644 --- a/Core/src/main/java/com/plotsquared/core/PlotSquared.java +++ b/Core/src/main/java/com/plotsquared/core/PlotSquared.java @@ -80,12 +80,11 @@ import com.plotsquared.core.util.RegionManager; import com.plotsquared.core.util.SchematicHandler; import com.plotsquared.core.util.SetupUtils; import com.plotsquared.core.util.StringMan; -import com.plotsquared.core.util.StringWrapper; import com.plotsquared.core.util.WorldUtil; import com.plotsquared.core.util.logger.ILogger; import com.plotsquared.core.util.query.PlotQuery; import com.plotsquared.core.util.task.TaskManager; -import com.plotsquared.core.util.uuid.UUIDHandler; +import com.plotsquared.core.uuid.UUIDPipeline; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.regions.CuboidRegion; @@ -127,6 +126,7 @@ import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import java.util.UUID; +import java.util.concurrent.Executors; import java.util.function.Consumer; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -144,6 +144,11 @@ public class PlotSquared { public final IPlotMain IMP; // Current thread private final Thread thread; + // UUID pipelines + @Getter private final UUIDPipeline impromptuUUIDPipeline = + new UUIDPipeline(Executors.newCachedThreadPool()); + @Getter private final UUIDPipeline backgroundUUIDPipeline = + new UUIDPipeline(Executors.newSingleThreadExecutor()); // WorldEdit instance public WorldEdit worldedit; public File styleFile; @@ -256,15 +261,6 @@ public class PlotSquared { if (Settings.Enabled_Components.CHUNK_PROCESSOR) { this.IMP.registerChunkProcessor(); } - // create UUIDWrapper - UUIDHandler.implementation = this.IMP.initUUIDHandler(); - if (Settings.Enabled_Components.UUID_CACHE) { - startUuidCatching(); - } else { - // Start these separately - UUIDHandler.add(new StringWrapper("*"), DBFunc.EVERYONE); - startExpiryTasks(); - } // Create Event utility class eventDispatcher = new EventDispatcher(); // create Hybrid utility class @@ -390,11 +386,11 @@ public class PlotSquared { return PlotSquared.instance; } - public static IPlotMain imp() { - if (instance != null) { + @NotNull public static IPlotMain imp() { + if (instance != null && instance.IMP != null) { return instance.IMP; } - return null; + throw new IllegalStateException("Plot main implementation is missing"); } /** @@ -432,23 +428,6 @@ public class PlotSquared { } } - private void startUuidCatching() { - TaskManager.runTaskLater(() -> { - debug("Starting UUID caching"); - UUIDHandler.startCaching(() -> { - UUIDHandler.add(new StringWrapper("*"), DBFunc.EVERYONE); - forEachPlotRaw(plot -> { - if (plot.hasOwner() && plot.temp != -1) { - if (UUIDHandler.getName(plot.getOwnerAbs()) == null) { - UUIDHandler.implementation.unknown.add(plot.getOwnerAbs()); - } - } - }); - startExpiryTasks(); - }); - }, 20); - } - private void startExpiryTasks() { if (Settings.Enabled_Components.PLOT_EXPIRY) { ExpireManager.IMP = new ExpireManager(); @@ -959,7 +938,7 @@ public class PlotSquared { * @return Set of Plot */ public Set getPlots(String world, String player) { - final UUID uuid = UUIDHandler.getUUID(player, null); + final UUID uuid = this.impromptuUUIDPipeline.getSingle(player, Settings.UUID.BLOCKING_TIMEOUT); return getPlots(world, uuid); } @@ -971,7 +950,7 @@ public class PlotSquared { * @return Set of Plot */ public Set getPlots(PlotArea area, String player) { - UUID uuid = UUIDHandler.getUUID(player, null); + final UUID uuid = this.impromptuUUIDPipeline.getSingle(player, Settings.UUID.BLOCKING_TIMEOUT); return getPlots(area, uuid); } @@ -1584,7 +1563,6 @@ public class PlotSquared { // Close the connection DBFunc.close(); - UUIDHandler.handleShutdown(); } catch (NullPointerException throwable) { throwable.printStackTrace(); PlotSquared.log("&cCould not close database connection!"); diff --git a/Core/src/main/java/com/plotsquared/core/api/PlotAPI.java b/Core/src/main/java/com/plotsquared/core/api/PlotAPI.java index 70bd11673..3991643a3 100644 --- a/Core/src/main/java/com/plotsquared/core/api/PlotAPI.java +++ b/Core/src/main/java/com/plotsquared/core/api/PlotAPI.java @@ -38,8 +38,6 @@ import com.plotsquared.core.util.ChunkManager; import com.plotsquared.core.util.EventDispatcher; import com.plotsquared.core.util.MainUtil; import com.plotsquared.core.util.SchematicHandler; -import com.plotsquared.core.util.uuid.UUIDHandler; -import com.plotsquared.core.util.uuid.UUIDWrapper; import lombok.NoArgsConstructor; import java.util.Collections; @@ -60,9 +58,7 @@ import java.util.UUID; * * @version 5 */ -@SuppressWarnings({"unused", "WeakerAccess"}) -@NoArgsConstructor -public class PlotAPI { +@SuppressWarnings({"unused", "WeakerAccess"}) @NoArgsConstructor public class PlotAPI { /** * Gets all plots. @@ -140,17 +136,6 @@ public class PlotAPI { return GlobalBlockQueue.IMP; } - /** - * UUIDWrapper class has basic methods for getting UUIDS. It's recommended - * to use the UUIDHandler class instead. - * - * @return UUIDWrapper - * @see UUIDWrapper - */ - public UUIDWrapper getUUIDWrapper() { - return UUIDHandler.getUUIDWrapper(); - } - /** * SchematicHandler class contains methods related to pasting, reading * and writing schematics. diff --git a/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java b/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java index 8853f3dff..c71057ab5 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java +++ b/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java @@ -142,7 +142,7 @@ public class PlayerBackupProfile implements BackupProfile { } final List plots = Collections.singletonList(plot); final boolean result = SchematicHandler.manager.exportAll(plots, getBackupDirectory().toFile(), - "%world%-%id%-%owner%-" + System.currentTimeMillis(), () -> + "%world%-%id%-" + System.currentTimeMillis(), () -> future.complete(new Backup(this, System.currentTimeMillis(), null))); if (!result) { future.completeExceptionally(new RuntimeException("Failed to complete the backup")); 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 bc4905c68..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,13 +32,16 @@ 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.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeoutException; @CommandDeclaration(command = "add", description = "Allow a user to build in a plot while the plot owner is online.", @@ -62,51 +65,73 @@ public class Add extends Command { .hasPermission(player, Captions.PERMISSION_ADMIN_COMMAND_TRUST), Captions.NO_PLOT_PERMS); checkTrue(args.length == 1, Captions.COMMAND_SYNTAX, getUsage()); - final Set uuids = MainUtil.getUUIDsFromString(args[0]); - checkTrue(!uuids.isEmpty(), Captions.INVALID_PLAYER, args[0]); - Iterator iterator = uuids.iterator(); - int size = plot.getTrusted().size() + plot.getMembers().size(); - while (iterator.hasNext()) { - UUID uuid = iterator.next(); - if (uuid == DBFunc.EVERYONE && !( - Permissions.hasPermission(player, Captions.PERMISSION_TRUST_EVERYONE) || Permissions - .hasPermission(player, Captions.PERMISSION_ADMIN_COMMAND_TRUST))) { - MainUtil.sendMessage(player, Captions.INVALID_PLAYER, MainUtil.getName(uuid)); - iterator.remove(); - continue; - } - if (plot.isOwner(uuid)) { - MainUtil.sendMessage(player, Captions.ALREADY_ADDED, MainUtil.getName(uuid)); - iterator.remove(); - continue; - } - if (plot.getMembers().contains(uuid)) { - MainUtil.sendMessage(player, Captions.ALREADY_ADDED, MainUtil.getName(uuid)); - iterator.remove(); - continue; - } - size += plot.getTrusted().contains(uuid) ? 0 : 1; - } - checkTrue(!uuids.isEmpty(), null); - checkTrue(size <= plot.getArea().getMaxPlotMembers() || Permissions - .hasPermission(player, Captions.PERMISSION_ADMIN_COMMAND_TRUST), - Captions.PLOT_MAX_MEMBERS); - // Success - confirm.run(this, () -> { - for (UUID uuid : uuids) { - if (uuid != DBFunc.EVERYONE) { - if (!plot.removeTrusted(uuid)) { - if (plot.getDenied().contains(uuid)) { - plot.removeDenied(uuid); - } - } - } - plot.addMember(uuid); - PlotSquared.get().getEventDispatcher().callMember(player, plot, uuid, true); - MainUtil.sendMessage(player, Captions.MEMBER_ADDED); - } - }, null); - return CompletableFuture.completedFuture(true); + final CompletableFuture future = new CompletableFuture<>(); + MainUtil.getUUIDsFromString(args[0], (uuids, throwable) -> { + if (throwable != null) { + if (throwable instanceof TimeoutException) { + Captions.FETCHING_PLAYERS_TIMEOUT.send(player); + } else { + Captions.INVALID_PLAYER.send(player, args[0]); + } + future.completeExceptionally(throwable); + return; + } else { + try { + checkTrue(!uuids.isEmpty(), Captions.INVALID_PLAYER, args[0]); + Iterator iterator = uuids.iterator(); + int size = plot.getTrusted().size() + plot.getMembers().size(); + while (iterator.hasNext()) { + UUID uuid = iterator.next(); + if (uuid == DBFunc.EVERYONE && !( + Permissions.hasPermission(player, Captions.PERMISSION_TRUST_EVERYONE) || Permissions + .hasPermission(player, Captions.PERMISSION_ADMIN_COMMAND_TRUST))) { + MainUtil.sendMessage(player, Captions.INVALID_PLAYER, MainUtil.getName(uuid)); + iterator.remove(); + continue; + } + if (plot.isOwner(uuid)) { + MainUtil.sendMessage(player, Captions.ALREADY_ADDED, MainUtil.getName(uuid)); + iterator.remove(); + continue; + } + if (plot.getMembers().contains(uuid)) { + MainUtil.sendMessage(player, Captions.ALREADY_ADDED, MainUtil.getName(uuid)); + iterator.remove(); + continue; + } + size += plot.getTrusted().contains(uuid) ? 0 : 1; + } + checkTrue(!uuids.isEmpty(), null); + checkTrue(size <= plot.getArea().getMaxPlotMembers() || Permissions.hasPermission(player, Captions.PERMISSION_ADMIN_COMMAND_TRUST), + Captions.PLOT_MAX_MEMBERS); + // Success + confirm.run(this, () -> { + for (UUID uuid : uuids) { + if (uuid != DBFunc.EVERYONE) { + if (!plot.removeTrusted(uuid)) { + if (plot.getDenied().contains(uuid)) { + plot.removeDenied(uuid); + } + } + } + plot.addMember(uuid); + PlotSquared.get().getEventDispatcher().callMember(player, plot, uuid, true); + MainUtil.sendMessage(player, Captions.MEMBER_ADDED); + } + }, null); + } catch (final Throwable exception) { + future.completeExceptionally(exception); + return; + } + } + future.complete(true); + }); + 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/Alias.java b/Core/src/main/java/com/plotsquared/core/command/Alias.java index b1d8d8161..3f1273014 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Alias.java +++ b/Core/src/main/java/com/plotsquared/core/command/Alias.java @@ -33,8 +33,8 @@ import com.plotsquared.core.plot.Plot; import com.plotsquared.core.util.MainUtil; import com.plotsquared.core.util.MathMan; import com.plotsquared.core.util.Permissions; -import com.plotsquared.core.util.StringWrapper; -import com.plotsquared.core.util.uuid.UUIDHandler; + +import java.util.concurrent.TimeoutException; @CommandDeclaration(command = "setalias", permission = "plots.alias", @@ -79,7 +79,8 @@ public class Alias extends SubCommand { if (canExecuteCommand(player, Captions.PERMISSION_ALIAS_SET, false) || canExecuteCommand(player, Captions.PERMISSION_ALIAS_SET_OBSOLETE, false)) { - result = setAlias(player, plot, args[1]); + setAlias(player, plot, args[1]); + return true; } else { MainUtil.sendMessage(player, Captions.NO_PERMISSION); } @@ -99,38 +100,34 @@ public class Alias extends SubCommand { } - private boolean setAlias(PlotPlayer player, Plot plot, String alias) { + private void setAlias(PlotPlayer player, Plot plot, String alias) { if (alias.isEmpty()) { Captions.COMMAND_SYNTAX.send(player, getUsage()); - return false; - } - if (alias.length() >= 50) { + } else if (alias.length() >= 50) { MainUtil.sendMessage(player, Captions.ALIAS_TOO_LONG); - return false; - } - if (alias.contains(" ")) { + } else if (alias.contains(" ")) { Captions.NOT_VALID_VALUE.send(player); - return false; - } - if (MathMan.isInteger(alias)) { + } else if (MathMan.isInteger(alias)) { Captions.NOT_VALID_VALUE.send(player); - return false; - } - for (Plot p : PlotSquared.get().getPlots(plot.getArea())) { - if (p.getAlias().equalsIgnoreCase(alias)) { - MainUtil.sendMessage(player, Captions.ALIAS_IS_TAKEN); - return false; + } else { + for (Plot p : PlotSquared.get().getPlots(plot.getArea())) { + if (p.getAlias().equalsIgnoreCase(alias)) { + MainUtil.sendMessage(player, Captions.ALIAS_IS_TAKEN); + return; + } } + PlotSquared.get().getImpromptuUUIDPipeline().getSingle(alias, ((uuid, throwable) -> { + if (throwable instanceof TimeoutException) { + MainUtil.sendMessage(player, Captions.FETCHING_PLAYERS_TIMEOUT); + } else if (uuid != null) { + MainUtil.sendMessage(player, Captions.ALIAS_IS_TAKEN); + } else { + plot.setAlias(alias); + MainUtil.sendMessage(player, + Captions.ALIAS_SET_TO.getTranslated().replaceAll("%alias%", alias)); + } + })); } - if (UUIDHandler.nameExists(new StringWrapper(alias)) || PlotSquared.get() - .hasPlotArea(alias)) { - MainUtil.sendMessage(player, Captions.ALIAS_IS_TAKEN); - return false; - } - plot.setAlias(alias); - MainUtil.sendMessage(player, - Captions.ALIAS_SET_TO.getTranslated().replaceAll("%alias%", alias)); - return true; } private boolean removeAlias(PlotPlayer player, Plot plot) { diff --git a/Core/src/main/java/com/plotsquared/core/command/Buy.java b/Core/src/main/java/com/plotsquared/core/command/Buy.java index a660a39a1..b986a7983 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Buy.java +++ b/Core/src/main/java/com/plotsquared/core/command/Buy.java @@ -37,7 +37,6 @@ import com.plotsquared.core.util.EconHandler; import com.plotsquared.core.util.MainUtil; import com.plotsquared.core.util.task.RunnableVal2; import com.plotsquared.core.util.task.RunnableVal3; -import com.plotsquared.core.util.uuid.UUIDHandler; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -82,10 +81,10 @@ public class Buy extends Command { // Success confirm.run(this, () -> { Captions.REMOVED_BALANCE.send(player, price); - EconHandler.manager - .depositMoney(UUIDHandler.getUUIDWrapper().getOfflinePlayer(plot.getOwnerAbs()), - price); - PlotPlayer owner = UUIDHandler.getPlayer(plot.getOwnerAbs()); + + EconHandler.manager.depositMoney(PlotSquared.imp().getPlayerManager().getOfflinePlayer(plot.getOwnerAbs()), price); + + PlotPlayer owner = PlotSquared.imp().getPlayerManager().getPlayerIfExists(plot.getOwnerAbs()); if (owner != null) { Captions.PLOT_SOLD.send(owner, plot.getId(), player.getName(), price); } diff --git a/Core/src/main/java/com/plotsquared/core/command/Cluster.java b/Core/src/main/java/com/plotsquared/core/command/Cluster.java index ae220831e..c9ce3d5ef 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Cluster.java +++ b/Core/src/main/java/com/plotsquared/core/command/Cluster.java @@ -39,12 +39,12 @@ import com.plotsquared.core.plot.PlotCluster; import com.plotsquared.core.plot.PlotId; import com.plotsquared.core.util.MainUtil; import com.plotsquared.core.util.Permissions; -import com.plotsquared.core.util.uuid.UUIDHandler; import java.util.ArrayList; import java.util.HashSet; import java.util.Set; import java.util.UUID; +import java.util.concurrent.TimeoutException; @CommandDeclaration(command = "cluster", aliases = "clusters", @@ -371,22 +371,29 @@ public class Cluster extends SubCommand { return false; } } - // check uuid - UUID uuid = UUIDHandler.getUUID(args[1], null); - if (uuid == null) { - MainUtil.sendMessage(player, Captions.INVALID_PLAYER, args[2]); - return false; - } - if (!cluster.isAdded(uuid)) { - // add the user if not added - cluster.invited.add(uuid); - DBFunc.setInvited(cluster, uuid); - PlotPlayer player2 = UUIDHandler.getPlayer(uuid); - if (player2 != null) { - MainUtil.sendMessage(player2, Captions.CLUSTER_INVITED, cluster.getName()); - } - } - MainUtil.sendMessage(player, Captions.CLUSTER_ADDED_USER); + + PlotSquared.get().getImpromptuUUIDPipeline() + .getSingle(args[1], (uuid, throwable) -> { + if (throwable instanceof TimeoutException) { + MainUtil.sendMessage(player, Captions.FETCHING_PLAYERS_TIMEOUT); + } else if (throwable != null) { + MainUtil.sendMessage(player, Captions.INVALID_PLAYER, args[1]); + } else { + if (!cluster.isAdded(uuid)) { + // add the user if not added + cluster.invited.add(uuid); + DBFunc.setInvited(cluster, uuid); + + final PlotPlayer otherPlayer = + PlotSquared.imp().getPlayerManager().getPlayerIfExists(uuid); + if (otherPlayer != null) { + MainUtil.sendMessage(otherPlayer, Captions.CLUSTER_INVITED, + cluster.getName()); + } + } + MainUtil.sendMessage(player, Captions.CLUSTER_ADDED_USER); + } + }); return true; } case "k": @@ -420,35 +427,43 @@ public class Cluster extends SubCommand { } } // check uuid - UUID uuid = UUIDHandler.getUUID(args[1], null); - if (uuid == null) { - MainUtil.sendMessage(player, Captions.INVALID_PLAYER, args[1]); - return false; - } - // Can't kick if the player is yourself, the owner, or not added to the cluster - if (uuid.equals(player.getUUID()) || uuid.equals(cluster.owner) || !cluster - .isAdded(uuid)) { - MainUtil.sendMessage(player, Captions.CANNOT_KICK_PLAYER, cluster.getName()); - return false; - } - if (cluster.helpers.contains(uuid)) { - cluster.helpers.remove(uuid); - DBFunc.removeHelper(cluster, uuid); - } - cluster.invited.remove(uuid); - DBFunc.removeInvited(cluster, uuid); - PlotPlayer player2 = UUIDHandler.getPlayer(uuid); - if (player2 != null) { - MainUtil.sendMessage(player2, Captions.CLUSTER_REMOVED, cluster.getName()); - } - for (Plot plot : new ArrayList<>( - PlotSquared.get().getPlots(player2.getLocation().getWorld(), uuid))) { - PlotCluster current = plot.getCluster(); - if (current != null && current.equals(cluster)) { - plot.unclaim(); - } - } - MainUtil.sendMessage(player2, Captions.CLUSTER_KICKED_USER); + PlotSquared.get().getImpromptuUUIDPipeline() + .getSingle(args[1], (uuid, throwable) -> { + if (throwable instanceof TimeoutException) { + MainUtil.sendMessage(player, Captions.FETCHING_PLAYERS_TIMEOUT); + } else if (throwable != null) { + MainUtil.sendMessage(player, Captions.INVALID_PLAYER, args[1]); + } else { + // Can't kick if the player is yourself, the owner, or not added to the cluster + if (uuid.equals(player.getUUID()) || uuid.equals(cluster.owner) + || !cluster.isAdded(uuid)) { + MainUtil.sendMessage(player, Captions.CANNOT_KICK_PLAYER, + cluster.getName()); + } else { + if (cluster.helpers.contains(uuid)) { + cluster.helpers.remove(uuid); + DBFunc.removeHelper(cluster, uuid); + } + cluster.invited.remove(uuid); + DBFunc.removeInvited(cluster, uuid); + + final PlotPlayer player2 = + PlotSquared.imp().getPlayerManager().getPlayerIfExists(uuid); + if (player2 != null) { + MainUtil.sendMessage(player2, Captions.CLUSTER_REMOVED, + cluster.getName()); + } + for (Plot plot : new ArrayList<>(PlotSquared.get() + .getPlots(player2.getLocation().getWorld(), uuid))) { + PlotCluster current = plot.getCluster(); + if (current != null && current.equals(cluster)) { + plot.unclaim(); + } + } + MainUtil.sendMessage(player2, Captions.CLUSTER_KICKED_USER); + } + } + }); return true; } case "quit": @@ -529,24 +544,29 @@ public class Cluster extends SubCommand { MainUtil.sendMessage(player, Captions.NOT_IN_CLUSTER); return false; } - UUID uuid = UUIDHandler.getUUID(args[2], null); - if (uuid == null) { - MainUtil.sendMessage(player, Captions.INVALID_PLAYER, args[2]); - return false; - } - if (args[1].equalsIgnoreCase("add")) { - cluster.helpers.add(uuid); - DBFunc.setHelper(cluster, uuid); - return MainUtil.sendMessage(player, Captions.CLUSTER_ADDED_HELPER); - } - if (args[1].equalsIgnoreCase("remove")) { - cluster.helpers.remove(uuid); - DBFunc.removeHelper(cluster, uuid); - return MainUtil.sendMessage(player, Captions.CLUSTER_REMOVED_HELPER); - } - MainUtil.sendMessage(player, Captions.COMMAND_SYNTAX, - "/plot cluster helpers "); - return false; + + PlotSquared.get().getImpromptuUUIDPipeline() + .getSingle(args[2], (uuid, throwable) -> { + if (throwable instanceof TimeoutException) { + MainUtil.sendMessage(player, Captions.FETCHING_PLAYERS_TIMEOUT); + } else if (throwable != null) { + MainUtil.sendMessage(player, Captions.INVALID_PLAYER, args[2]); + } else { + if (args[1].equalsIgnoreCase("add")) { + cluster.helpers.add(uuid); + DBFunc.setHelper(cluster, uuid); + MainUtil.sendMessage(player, Captions.CLUSTER_ADDED_HELPER); + } else if (args[1].equalsIgnoreCase("remove")) { + cluster.helpers.remove(uuid); + DBFunc.removeHelper(cluster, uuid); + MainUtil.sendMessage(player, Captions.CLUSTER_REMOVED_HELPER); + } else { + MainUtil.sendMessage(player, Captions.COMMAND_SYNTAX, + "/plot cluster helpers "); + } + } + }); + return true; } case "spawn": case "home": @@ -615,21 +635,31 @@ public class Cluster extends SubCommand { } } String id = cluster.toString(); - String owner = UUIDHandler.getName(cluster.owner); - if (owner == null) { - owner = "unknown"; - } - String name = cluster.getName(); - String size = (cluster.getP2().x - cluster.getP1().x + 1) + "x" + ( - cluster.getP2().y - cluster.getP1().y + 1); - String rights = cluster.isAdded(player.getUUID()) + ""; - String message = Captions.CLUSTER_INFO.getTranslated(); - message = message.replaceAll("%id%", id); - message = message.replaceAll("%owner%", owner); - message = message.replaceAll("%name%", name); - message = message.replaceAll("%size%", size); - message = message.replaceAll("%rights%", rights); - MainUtil.sendMessage(player, message); + + PlotSquared.get().getImpromptuUUIDPipeline() + .getSingle(cluster.owner, (username, throwable) -> { + if (throwable instanceof TimeoutException) { + MainUtil.sendMessage(player, Captions.FETCHING_PLAYERS_TIMEOUT); + } else { + final String owner; + if (username == null) { + owner = "unknown"; + } else { + owner = username; + } + String name = cluster.getName(); + String size = (cluster.getP2().x - cluster.getP1().x + 1) + "x" + ( + cluster.getP2().y - cluster.getP1().y + 1); + String rights = cluster.isAdded(player.getUUID()) + ""; + String message = Captions.CLUSTER_INFO.getTranslated(); + message = message.replaceAll("%id%", id); + message = message.replaceAll("%owner%", owner); + message = message.replaceAll("%name%", name); + message = message.replaceAll("%size%", size); + message = message.replaceAll("%rights%", rights); + MainUtil.sendMessage(player, message); + } + }); return true; } case "sh": diff --git a/Core/src/main/java/com/plotsquared/core/command/Command.java b/Core/src/main/java/com/plotsquared/core/command/Command.java index 6535e3b2c..6e97eb30a 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Command.java +++ b/Core/src/main/java/com/plotsquared/core/command/Command.java @@ -37,6 +37,8 @@ import com.plotsquared.core.util.StringComparison; import com.plotsquared.core.util.StringMan; import com.plotsquared.core.util.task.RunnableVal2; import com.plotsquared.core.util.task.RunnableVal3; +import lombok.SneakyThrows; +import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.lang.reflect.InvocationTargetException; @@ -603,6 +605,10 @@ public abstract class Command { return object; } + @SneakyThrows protected static void sneakyThrow(@NotNull final Throwable throwable) { + throw throwable; + } + public enum CommandResult { FAILURE, SUCCESS } diff --git a/Core/src/main/java/com/plotsquared/core/command/Comment.java b/Core/src/main/java/com/plotsquared/core/command/Comment.java index 284587106..501ba4247 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Comment.java +++ b/Core/src/main/java/com/plotsquared/core/command/Comment.java @@ -25,6 +25,7 @@ */ package com.plotsquared.core.command; +import com.plotsquared.core.PlotSquared; import com.plotsquared.core.configuration.Captions; import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; @@ -33,11 +34,9 @@ import com.plotsquared.core.plot.comment.CommentManager; import com.plotsquared.core.plot.comment.PlotComment; import com.plotsquared.core.util.MainUtil; import com.plotsquared.core.util.StringMan; -import com.plotsquared.core.util.uuid.UUIDHandler; import java.util.Arrays; import java.util.Locale; -import java.util.Map.Entry; @CommandDeclaration(command = "comment", aliases = {"msg"}, @@ -96,12 +95,13 @@ public class Comment extends SubCommand { StringMan.join(CommentManager.inboxes.keySet(), "|")); return false; } - for (Entry entry : UUIDHandler.getPlayers().entrySet()) { - PlotPlayer pp = entry.getValue(); + + for (final PlotPlayer pp : PlotSquared.imp().getPlayerManager().getPlayers()) { if (pp.getAttribute("chatspy")) { MainUtil.sendMessage(pp, "/plot comment " + StringMan.join(args, " ")); } } + sendMessage(player, Captions.COMMENT_ADDED); return true; } 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/DebugClaimTest.java b/Core/src/main/java/com/plotsquared/core/command/DebugClaimTest.java deleted file mode 100644 index 5412ed6a2..000000000 --- a/Core/src/main/java/com/plotsquared/core/command/DebugClaimTest.java +++ /dev/null @@ -1,134 +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.command; - -import com.google.common.collect.BiMap; -import com.plotsquared.core.PlotSquared; -import com.plotsquared.core.configuration.Captions; -import com.plotsquared.core.location.Location; -import com.plotsquared.core.player.PlotPlayer; -import com.plotsquared.core.plot.Plot; -import com.plotsquared.core.plot.PlotArea; -import com.plotsquared.core.plot.PlotId; -import com.plotsquared.core.plot.PlotManager; -import com.plotsquared.core.util.ChunkManager; -import com.plotsquared.core.util.MainUtil; -import com.plotsquared.core.util.StringWrapper; -import com.plotsquared.core.util.WorldUtil; -import com.plotsquared.core.util.uuid.UUIDHandler; -import com.sk89q.worldedit.math.BlockVector2; - -import java.util.ArrayList; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; - -@CommandDeclaration(command = "debugclaimtest", - description = - "If you accidentally delete your database, this command will attempt to restore all plots based on the data from plot signs. " - + "Execution time may vary", - category = CommandCategory.DEBUG, - requiredType = RequiredType.CONSOLE, - permission = "plots.debugclaimtest") -public class DebugClaimTest extends SubCommand { - - @Override public boolean onCommand(final PlotPlayer player, String[] args) { - if (args.length < 3) { - return !MainUtil.sendMessage(null, - "If you accidentally delete your database, this command will attempt to restore all plots based on the data from the " - + "plot signs. \n\n&cMissing world arg /plot debugclaimtest {world} {PlotId min} {PlotId max}"); - } - PlotArea area = PlotSquared.get().getPlotAreaByString(args[0]); - if (area == null || !WorldUtil.IMP.isWorld(area.getWorldName())) { - Captions.NOT_VALID_PLOT_WORLD.send(player, args[0]); - return false; - } - PlotId min, max; - try { - args[1].split(";"); - args[2].split(";"); - min = PlotId.fromString(args[1]); - max = PlotId.fromString(args[2]); - } catch (Exception ignored) { - return !MainUtil.sendMessage(player, - "&cInvalid min/max values. &7The values are to Plot IDs in the format &cX;Y &7where X;Y are the plot coords\nThe conversion " - + "will only check the plots in the selected area."); - } - MainUtil.sendMessage(player, - "&3Sign Block&8->&3Plot&8: &7Beginning sign to plot conversion. This may take a while..."); - MainUtil.sendMessage(player, - "&3Sign Block&8->&3Plot&8: Found an excess of 250,000 chunks. Limiting search radius... (~3.8 min)"); - PlotManager manager = area.getPlotManager(); - CompletableFuture.runAsync(() -> { - ArrayList ids = MainUtil.getPlotSelectionIds(min, max); - MainUtil.sendMessage(player, - "&3Sign Block&8->&3Plot&8: " + ids.size() + " plot ids to check!"); - for (PlotId id : ids) { - Plot plot = area.getPlotAbs(id); - if (plot.hasOwner()) { - MainUtil.sendMessage(player, " - &cDB Already contains: " + plot.getId()); - continue; - } - Location location = manager.getSignLoc(plot); - BlockVector2 chunk = BlockVector2.at(location.getX() >> 4, location.getZ() >> 4); - ChunkManager.manager.loadChunk(area.getWorldName(), chunk, false).thenRun(() -> { - String[] lines = WorldUtil.IMP.getSignSynchronous(location); - if (lines != null) { - String line = lines[2]; - if (line != null && line.length() > 2) { - line = line.substring(2); - BiMap map = UUIDHandler.getUuidMap(); - UUID uuid = map.get(new StringWrapper(line)); - if (uuid == null) { - for (Map.Entry stringWrapperUUIDEntry : map - .entrySet()) { - if (stringWrapperUUIDEntry.getKey().value.toLowerCase() - .startsWith(line.toLowerCase())) { - uuid = stringWrapperUUIDEntry.getValue(); - break; - } - } - } - if (uuid == null) { - uuid = UUIDHandler.getUUID(line, null); - } - if (uuid != null) { - MainUtil.sendMessage(player, - " - &aFound plot: " + plot.getId() + " : " + line); - plot.setOwner(uuid); - MainUtil.sendMessage(player, " - &8Updated plot: " + plot.getId()); - } else { - MainUtil.sendMessage(player, - " - &cInvalid PlayerName: " + plot.getId() + " : " + line); - } - } - } - }).join(); - } - }).thenRun(() -> MainUtil.sendMessage(player, "&3Sign Block&8->&3Plot&8: Finished scan.")); - return true; - } -} diff --git a/Core/src/main/java/com/plotsquared/core/command/DebugExec.java b/Core/src/main/java/com/plotsquared/core/command/DebugExec.java index 8976c3348..9f179803f 100644 --- a/Core/src/main/java/com/plotsquared/core/command/DebugExec.java +++ b/Core/src/main/java/com/plotsquared/core/command/DebugExec.java @@ -35,7 +35,6 @@ import com.plotsquared.core.events.Result; import com.plotsquared.core.generator.HybridUtils; import com.plotsquared.core.location.Location; import com.plotsquared.core.player.ConsolePlayer; -import com.plotsquared.core.player.OfflinePlotPlayer; import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.PlotArea; @@ -59,7 +58,6 @@ import com.plotsquared.core.util.task.RunnableVal; import com.plotsquared.core.util.task.RunnableVal2; import com.plotsquared.core.util.task.RunnableVal3; import com.plotsquared.core.util.task.TaskManager; -import com.plotsquared.core.util.uuid.UUIDHandler; import com.sk89q.worldedit.world.block.BlockState; import javax.script.Bindings; @@ -71,13 +69,10 @@ import javax.script.SimpleScriptContext; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.sql.Timestamp; import java.util.Arrays; -import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.UUID; import java.util.concurrent.CompletableFuture; @CommandDeclaration(command = "debugexec", @@ -172,7 +167,6 @@ public class DebugExec extends SubCommand { this.scope.put("SetupUtils", SetupUtils.manager); this.scope.put("EventUtil", PlotSquared.get().getEventDispatcher()); this.scope.put("EconHandler", EconHandler.manager); - this.scope.put("UUIDHandler", UUIDHandler.implementation); this.scope.put("DBFunc", DBFunc.dbManager); this.scope.put("HybridUtils", HybridUtils.manager); this.scope.put("IMP", PlotSquared.get().IMP); @@ -301,27 +295,6 @@ public class DebugExec extends SubCommand { } else { return MainUtil.sendMessage(player, "Plot expiry task already started"); } - case "seen": - if (args.length != 2) { - return MainUtil.sendMessage(player, "Use /plot debugexec seen "); - } - UUID uuid = UUIDHandler.getUUID(args[1], null); - if (uuid == null) { - return MainUtil.sendMessage(player, "Player not found: " + args[1]); - } - OfflinePlotPlayer op = UUIDHandler.getUUIDWrapper().getOfflinePlayer(uuid); - if (op == null || op.getLastPlayed() == 0) { - return MainUtil - .sendMessage(player, "Player hasn't connected before: " + args[1]); - } - Timestamp stamp = new Timestamp(op.getLastPlayed()); - Date date = new Date(stamp.getTime()); - MainUtil.sendMessage(player, "PLAYER: " + args[1]); - MainUtil.sendMessage(player, "UUID: " + uuid); - MainUtil.sendMessage(player, "Object: " + date.toGMTString()); - MainUtil.sendMessage(player, "GMT: " + date.toGMTString()); - MainUtil.sendMessage(player, "Local: " + date.toLocaleString()); - return true; case "h": case "he": case "?": diff --git a/Core/src/main/java/com/plotsquared/core/command/DebugImportWorlds.java b/Core/src/main/java/com/plotsquared/core/command/DebugImportWorlds.java index 9cca145a5..1eedabb71 100644 --- a/Core/src/main/java/com/plotsquared/core/command/DebugImportWorlds.java +++ b/Core/src/main/java/com/plotsquared/core/command/DebugImportWorlds.java @@ -27,6 +27,7 @@ package com.plotsquared.core.command; import com.google.common.base.Charsets; import com.plotsquared.core.PlotSquared; +import com.plotsquared.core.configuration.Captions; import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.PlotId; import com.plotsquared.core.plot.world.PlotAreaManager; @@ -35,7 +36,6 @@ import com.plotsquared.core.plot.world.SinglePlotAreaManager; import com.plotsquared.core.util.WorldUtil; import com.plotsquared.core.util.task.RunnableVal2; import com.plotsquared.core.util.task.RunnableVal3; -import com.plotsquared.core.util.uuid.UUIDHandler; import java.io.File; import java.util.UUID; @@ -76,7 +76,8 @@ public class DebugImportWorlds extends Command { if (name.length() > 16) { uuid = UUID.fromString(name); } else { - uuid = UUIDHandler.getUUID(name, null); + Captions.FETCHING_PLAYER.send(player); + uuid = PlotSquared.get().getImpromptuUUIDPipeline().getSingle(name, 60000L); } if (uuid == null) { uuid = diff --git a/Core/src/main/java/com/plotsquared/core/command/DebugPaste.java b/Core/src/main/java/com/plotsquared/core/command/DebugPaste.java index ea223c60a..0c805d6bc 100644 --- a/Core/src/main/java/com/plotsquared/core/command/DebugPaste.java +++ b/Core/src/main/java/com/plotsquared/core/command/DebugPaste.java @@ -35,7 +35,6 @@ import com.plotsquared.core.util.MainUtil; import com.plotsquared.core.util.PremiumVerification; import com.plotsquared.core.util.net.IncendoPaster; import com.plotsquared.core.util.task.TaskManager; -import com.plotsquared.core.util.uuid.UUIDHandler; import lombok.NonNull; import java.io.BufferedReader; @@ -90,7 +89,7 @@ public class DebugPaste extends SubCommand { b.append("# Server Information\n"); b.append("Server Version: ").append(PlotSquared.get().IMP.getServerImplementation()) .append("\n"); - b.append("online_mode: ").append(UUIDHandler.getUUIDWrapper()).append(';') + b.append("online_mode: ").append(!Settings.UUID.OFFLINE).append(';') .append(!Settings.UUID.OFFLINE).append('\n'); b.append("Plugins:"); for (Map.Entry, Boolean> pluginInfo : PlotSquared diff --git a/Core/src/main/java/com/plotsquared/core/command/Deny.java b/Core/src/main/java/com/plotsquared/core/command/Deny.java index d591bfa1a..3cf742f0c 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Deny.java +++ b/Core/src/main/java/com/plotsquared/core/command/Deny.java @@ -34,11 +34,10 @@ import com.plotsquared.core.plot.Plot; import com.plotsquared.core.util.MainUtil; import com.plotsquared.core.util.Permissions; import com.plotsquared.core.util.WorldUtil; -import com.plotsquared.core.util.uuid.UUIDHandler; import com.sk89q.worldedit.world.gamemode.GameModes; -import java.util.Set; import java.util.UUID; +import java.util.concurrent.TimeoutException; @CommandDeclaration(command = "deny", aliases = {"d", "ban"}, @@ -68,48 +67,48 @@ public class Deny extends SubCommand { MainUtil.sendMessage(player, Captions.NO_PLOT_PERMS); return true; } - Set uuids = MainUtil.getUUIDsFromString(args[0]); - if (uuids.isEmpty()) { - MainUtil.sendMessage(player, Captions.INVALID_PLAYER, args[0]); - return false; - } - for (UUID uuid : uuids) { - if (uuid == DBFunc.EVERYONE && !( - Permissions.hasPermission(player, Captions.PERMISSION_DENY_EVERYONE) || Permissions - .hasPermission(player, Captions.PERMISSION_ADMIN_COMMAND_DENY))) { - MainUtil.sendMessage(player, Captions.INVALID_PLAYER, MainUtil.getName(uuid)); - continue; - } - if (plot.isOwner(uuid)) { - MainUtil.sendMessage(player, Captions.CANT_REMOVE_OWNER, MainUtil.getName(uuid)); - return false; - } - if (plot.getDenied().contains(uuid)) { - MainUtil.sendMessage(player, Captions.ALREADY_ADDED, MainUtil.getName(uuid)); - return false; - } - if (uuid != DBFunc.EVERYONE) { - plot.removeMember(uuid); - plot.removeTrusted(uuid); - } - plot.addDenied(uuid); - PlotSquared.get().getEventDispatcher().callDenied(player, plot, uuid, true); - if (!uuid.equals(DBFunc.EVERYONE)) { - handleKick(UUIDHandler.getPlayer(uuid), plot); + MainUtil.getUUIDsFromString(args[0], (uuids, throwable) -> { + if (throwable instanceof TimeoutException) { + MainUtil.sendMessage(player, Captions.FETCHING_PLAYERS_TIMEOUT); + } else if (throwable != null || uuids.isEmpty()) { + MainUtil.sendMessage(player, Captions.INVALID_PLAYER, args[0]); } else { - for (PlotPlayer plotPlayer : plot.getPlayersInPlot()) { - // Ignore plot-owners - if (plot.isAdded(plotPlayer.getUUID())) { - continue; + for (UUID uuid : uuids) { + if (uuid == DBFunc.EVERYONE && !( + Permissions.hasPermission(player, Captions.PERMISSION_DENY_EVERYONE) || Permissions + .hasPermission(player, Captions.PERMISSION_ADMIN_COMMAND_DENY))) { + MainUtil.sendMessage(player, Captions.INVALID_PLAYER, MainUtil.getName(uuid)); + } else if (plot.isOwner(uuid)) { + MainUtil.sendMessage(player, Captions.CANT_REMOVE_OWNER, MainUtil.getName(uuid)); + return; + } else if (plot.getDenied().contains(uuid)) { + MainUtil.sendMessage(player, Captions.ALREADY_ADDED, MainUtil.getName(uuid)); + return; + } else { + if (uuid != DBFunc.EVERYONE) { + plot.removeMember(uuid); + plot.removeTrusted(uuid); + } + plot.addDenied(uuid); + PlotSquared.get().getEventDispatcher().callDenied(player, plot, uuid, true); + if (!uuid.equals(DBFunc.EVERYONE)) { + handleKick(PlotSquared.imp().getPlayerManager().getPlayerIfExists(uuid), plot); + } else { + for (PlotPlayer plotPlayer : plot.getPlayersInPlot()) { + // Ignore plot-owners + if (plot.isAdded(plotPlayer.getUUID())) { + continue; + } + handleKick(plotPlayer, plot); + } + } } - handleKick(plotPlayer, plot); } + MainUtil.sendMessage(player, Captions.DENIED_ADDED); } - } - if (!uuids.isEmpty()) { - MainUtil.sendMessage(player, Captions.DENIED_ADDED); - } + }); + return true; } diff --git a/Core/src/main/java/com/plotsquared/core/command/Grant.java b/Core/src/main/java/com/plotsquared/core/command/Grant.java index acec1b084..39351c27f 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Grant.java +++ b/Core/src/main/java/com/plotsquared/core/command/Grant.java @@ -26,6 +26,7 @@ package com.plotsquared.core.command; import com.google.common.primitives.Ints; +import com.plotsquared.core.PlotSquared; import com.plotsquared.core.configuration.CaptionUtility; import com.plotsquared.core.configuration.Captions; import com.plotsquared.core.database.DBFunc; @@ -35,10 +36,10 @@ import com.plotsquared.core.util.Permissions; import com.plotsquared.core.util.task.RunnableVal; import com.plotsquared.core.util.task.RunnableVal2; import com.plotsquared.core.util.task.RunnableVal3; -import com.plotsquared.core.util.uuid.UUIDHandler; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeoutException; @CommandDeclaration(command = "grant", category = CommandCategory.CLAIMING, @@ -69,43 +70,44 @@ public class Grant extends Command { if (args.length > 2) { break; } - final UUID uuid; - if (args.length == 2) { - uuid = UUIDHandler.getUUIDFromString(args[1]); - } else { - uuid = player.getUUID(); - } - if (uuid == null) { - Captions.INVALID_PLAYER.send(player, args[1]); - return CompletableFuture.completedFuture(false); - } - MainUtil.getPersistentMeta(uuid, "grantedPlots", new RunnableVal() { - @Override public void run(byte[] array) { - if (arg0.equals("check")) { // check - int granted; - if (array == null) { - granted = 0; - } else { - granted = Ints.fromByteArray(array); + MainUtil.getUUIDsFromString(args[1], (uuids, throwable) -> { + if (throwable instanceof TimeoutException) { + MainUtil.sendMessage(player, Captions.FETCHING_PLAYERS_TIMEOUT); + } else if (throwable != null || uuids.size() != 1) { + MainUtil.sendMessage(player, Captions.INVALID_PLAYER); + } else { + final UUID uuid = uuids.toArray(new UUID[0])[0]; + MainUtil.getPersistentMeta(uuid, + "grantedPlots", new RunnableVal() { + @Override public void run(byte[] array) { + if (arg0.equals("check")) { // check + int granted; + if (array == null) { + granted = 0; + } else { + granted = Ints.fromByteArray(array); + } + Captions.GRANTED_PLOTS.send(player, granted); + } else { // add + int amount; + if (array == null) { + amount = 1; + } else { + amount = 1 + Ints.fromByteArray(array); + } + boolean replace = array != null; + String key = "grantedPlots"; + byte[] rawData = Ints.toByteArray(amount); + + PlotPlayer online = PlotSquared.imp().getPlayerManager().getPlayerIfExists(uuid); + if (online != null) { + online.setPersistentMeta(key, rawData); + } else { + DBFunc.addPersistentMeta(uuid, key, rawData, replace); + } + } } - Captions.GRANTED_PLOTS.send(player, granted); - } else { // add - int amount; - if (array == null) { - amount = 1; - } else { - amount = 1 + Ints.fromByteArray(array); - } - boolean replace = array != null; - String key = "grantedPlots"; - byte[] rawData = Ints.toByteArray(amount); - PlotPlayer online = UUIDHandler.getPlayer(uuid); - if (online != null) { - online.setPersistentMeta(key, rawData); - } else { - DBFunc.addPersistentMeta(uuid, key, rawData, replace); - } - } + }); } }); return CompletableFuture.completedFuture(true); diff --git a/Core/src/main/java/com/plotsquared/core/command/Kick.java b/Core/src/main/java/com/plotsquared/core/command/Kick.java index 1d113a874..69ace1482 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Kick.java +++ b/Core/src/main/java/com/plotsquared/core/command/Kick.java @@ -34,11 +34,11 @@ import com.plotsquared.core.plot.Plot; import com.plotsquared.core.util.MainUtil; import com.plotsquared.core.util.Permissions; import com.plotsquared.core.util.WorldUtil; -import com.plotsquared.core.util.uuid.UUIDHandler; import java.util.HashSet; import java.util.Set; import java.util.UUID; +import java.util.concurrent.TimeoutException; @CommandDeclaration(command = "kick", aliases = "k", @@ -64,57 +64,62 @@ public class Kick extends SubCommand { MainUtil.sendMessage(player, Captions.NO_PLOT_PERMS); return false; } - Set uuids = MainUtil.getUUIDsFromString(args[0]); - if (uuids.isEmpty()) { - MainUtil.sendMessage(player, Captions.INVALID_PLAYER, args[0]); - return false; - } - Set players = new HashSet<>(); - for (UUID uuid : uuids) { - if (uuid == DBFunc.EVERYONE) { - for (PlotPlayer pp : plot.getPlayersInPlot()) { - if (pp == player || Permissions - .hasPermission(pp, Captions.PERMISSION_ADMIN_ENTRY_DENIED)) { + + MainUtil.getUUIDsFromString(args[0], (uuids, throwable) -> { + if (throwable instanceof TimeoutException) { + MainUtil.sendMessage(player, Captions.FETCHING_PLAYERS_TIMEOUT); + } else if (throwable != null || uuids.isEmpty()) { + MainUtil.sendMessage(player, Captions.INVALID_PLAYER); + } else { + Set players = new HashSet<>(); + for (UUID uuid : uuids) { + if (uuid == DBFunc.EVERYONE) { + for (PlotPlayer pp : plot.getPlayersInPlot()) { + if (pp == player || Permissions + .hasPermission(pp, Captions.PERMISSION_ADMIN_ENTRY_DENIED)) { + continue; + } + players.add(pp); + } continue; } - players.add(pp); + PlotPlayer pp = PlotSquared.imp().getPlayerManager().getPlayerIfExists(uuid); + if (pp != null) { + players.add(pp); + } } - continue; - } - PlotPlayer pp = UUIDHandler.getPlayer(uuid); - if (pp != null) { - players.add(pp); - } - } - players.remove(player); // Don't ever kick the calling player - if (players.isEmpty()) { - MainUtil.sendMessage(player, Captions.INVALID_PLAYER, args[0]); - return false; - } - for (PlotPlayer player2 : players) { - if (!plot.equals(player2.getCurrentPlot())) { - MainUtil.sendMessage(player, Captions.INVALID_PLAYER, args[0]); - return false; - } - if (Permissions.hasPermission(player2, Captions.PERMISSION_ADMIN_ENTRY_DENIED)) { - Captions.CANNOT_KICK_PLAYER.send(player, player2.getName()); - return false; - } - Location spawn = WorldUtil.IMP.getSpawn(location.getWorld()); - Captions.YOU_GOT_KICKED.send(player2); - if (plot.equals(spawn.getPlot())) { - Location newSpawn = WorldUtil.IMP - .getSpawn(PlotSquared.get().getPlotAreaManager().getAllWorlds()[0]); - if (plot.equals(newSpawn.getPlot())) { - // Kick from server if you can't be teleported to spawn - player2.kick(Captions.YOU_GOT_KICKED.getTranslated()); - } else { - player2.plotkick(newSpawn); + players.remove(player); // Don't ever kick the calling player + if (players.isEmpty()) { + MainUtil.sendMessage(player, Captions.INVALID_PLAYER, args[0]); + return; + } + for (PlotPlayer player2 : players) { + if (!plot.equals(player2.getCurrentPlot())) { + MainUtil.sendMessage(player, Captions.INVALID_PLAYER, args[0]); + return; + } + if (Permissions.hasPermission(player2, Captions.PERMISSION_ADMIN_ENTRY_DENIED)) { + Captions.CANNOT_KICK_PLAYER.send(player, player2.getName()); + return; + } + Location spawn = WorldUtil.IMP.getSpawn(location.getWorld()); + Captions.YOU_GOT_KICKED.send(player2); + if (plot.equals(spawn.getPlot())) { + Location newSpawn = WorldUtil.IMP + .getSpawn(PlotSquared.get().getPlotAreaManager().getAllWorlds()[0]); + if (plot.equals(newSpawn.getPlot())) { + // Kick from server if you can't be teleported to spawn + player2.kick(Captions.YOU_GOT_KICKED.getTranslated()); + } else { + player2.plotkick(newSpawn); + } + } else { + player2.plotkick(spawn); + } } - } else { - player2.plotkick(spawn); } - } + }); + return true; } } diff --git a/Core/src/main/java/com/plotsquared/core/command/ListCmd.java b/Core/src/main/java/com/plotsquared/core/command/ListCmd.java index c83ee5c4a..0054df62e 100644 --- a/Core/src/main/java/com/plotsquared/core/command/ListCmd.java +++ b/Core/src/main/java/com/plotsquared/core/command/ListCmd.java @@ -28,6 +28,7 @@ package com.plotsquared.core.command; import com.plotsquared.core.PlotSquared; import com.plotsquared.core.configuration.CaptionUtility; import com.plotsquared.core.configuration.Captions; +import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.PlotArea; @@ -41,22 +42,31 @@ import com.plotsquared.core.util.MathMan; import com.plotsquared.core.util.Permissions; import com.plotsquared.core.util.StringComparison; import com.plotsquared.core.util.StringMan; +import com.plotsquared.core.util.TabCompletions; import com.plotsquared.core.util.query.PlotQuery; import com.plotsquared.core.util.query.SortingStrategy; import com.plotsquared.core.util.task.RunnableVal3; -import com.plotsquared.core.util.uuid.UUIDHandler; +import com.plotsquared.core.uuid.UUIDMapping; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; import java.util.List; import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; +import java.util.stream.Collectors; @CommandDeclaration(command = "list", aliases = {"l", "find", "search"}, description = "List plots", permission = "plots.list", category = CommandCategory.INFO, - usage = "/plot list > [#]") + usage = "/plot list > [#]") public class ListCmd extends SubCommand { private String[] getArgumentList(PlotPlayer player) { @@ -83,9 +93,6 @@ public class ListCmd extends SubCommand { if (Permissions.hasPermission(player, Captions.PERMISSION_LIST_UNOWNED)) { args.add("unowned"); } - if (Permissions.hasPermission(player, Captions.PERMISSION_LIST_UNKNOWN)) { - args.add("unknown"); - } if (Permissions.hasPermission(player, Captions.PERMISSION_LIST_PLAYER)) { args.add(""); } @@ -114,25 +121,52 @@ public class ListCmd extends SubCommand { noArgs(player); return false; } - int page = 0; + + final int page; if (args.length > 1) { + int tempPage = -1; try { - page = Integer.parseInt(args[args.length - 1]); - --page; - if (page < 0) { - page = 0; + tempPage = Integer.parseInt(args[args.length - 1]); + --tempPage; + if (tempPage < 0) { + tempPage = 0; } } catch (NumberFormatException ignored) { - page = -1; } + page = tempPage; + } else { + page = 0; } String world = player.getLocation().getWorld(); PlotArea area = player.getApplicablePlotArea(); String arg = args[0].toLowerCase(); - boolean sort = true; + final boolean[] sort = new boolean[] {true}; - PlotQuery query = null; + final Consumer plotConsumer = query -> { + if (query == null) { + sendMessage(player, Captions.DID_YOU_MEAN, + new StringComparison<>(args[0], new String[] {"mine", "shared", "world", "all"}) + .getBestMatch()); + return; + } + + if (area != null) { + query.relativeToArea(area); + } + + if (sort[0]) { + query.withSortingStrategy(SortingStrategy.SORT_BY_CREATION); + } + + final List plots = query.asList(); + + if (plots.isEmpty()) { + MainUtil.sendMessage(player, Captions.FOUND_NO_PLOTS); + return; + } + displayPlots(player, plots, 12, page, args); + }; switch (arg) { case "mine": @@ -141,8 +175,8 @@ public class ListCmd extends SubCommand { .sendMessage(player, Captions.NO_PERMISSION, Captions.PERMISSION_LIST_MINE); return false; } - query = PlotQuery.newQuery().ownedBy(player).whereBasePlot().withSortingStrategy(SortingStrategy.SORT_BY_TEMP); - sort = false; + sort[0] = false; + plotConsumer.accept(PlotQuery.newQuery().ownedBy(player).whereBasePlot().withSortingStrategy(SortingStrategy.SORT_BY_TEMP)); break; case "shared": if (!Permissions.hasPermission(player, Captions.PERMISSION_LIST_SHARED)) { @@ -150,8 +184,7 @@ public class ListCmd extends SubCommand { Captions.PERMISSION_LIST_SHARED); return false; } - // withMember checks for trusted + members + owner, we don't want the owner part - query = PlotQuery.newQuery().withMember(player.getUUID()).thatPasses(plot -> !plot.isOwnerAbs(player.getUUID())); + plotConsumer.accept(PlotQuery.newQuery().withMember(player.getUUID()).thatPasses(plot -> !plot.isOwnerAbs(player.getUUID()))); break; case "world": if (!Permissions.hasPermission(player, Captions.PERMISSION_LIST_WORLD)) { @@ -166,7 +199,7 @@ public class ListCmd extends SubCommand { world)); return false; } - query = PlotQuery.newQuery().inWorld(world); + plotConsumer.accept(PlotQuery.newQuery().inWorld(world)); break; case "expired": if (!Permissions.hasPermission(player, Captions.PERMISSION_LIST_EXPIRED)) { @@ -175,9 +208,9 @@ public class ListCmd extends SubCommand { return false; } if (ExpireManager.IMP == null) { - query = PlotQuery.newQuery().noPlots(); + plotConsumer.accept(PlotQuery.newQuery().noPlots()); } else { - query = PlotQuery.newQuery().expiredPlots(); + plotConsumer.accept(PlotQuery.newQuery().expiredPlots()); } break; case "area": @@ -194,9 +227,9 @@ public class ListCmd extends SubCommand { return false; } if (area == null) { - query = PlotQuery.newQuery().noPlots(); + plotConsumer.accept(PlotQuery.newQuery().noPlots()); } else { - query = PlotQuery.newQuery().inArea(area); + plotConsumer.accept(PlotQuery.newQuery().inArea(area)); } break; case "all": @@ -205,7 +238,7 @@ public class ListCmd extends SubCommand { .sendMessage(player, Captions.NO_PERMISSION, Captions.PERMISSION_LIST_ALL); return false; } - query = PlotQuery.newQuery().allPlots(); + plotConsumer.accept(PlotQuery.newQuery().allPlots()); break; case "done": if (!Permissions.hasPermission(player, Captions.PERMISSION_LIST_DONE)) { @@ -213,8 +246,8 @@ public class ListCmd extends SubCommand { .sendMessage(player, Captions.NO_PERMISSION, Captions.PERMISSION_LIST_DONE); return false; } - query = PlotQuery.newQuery().allPlots().thatPasses(DoneFlag::isDone).withSortingStrategy(SortingStrategy.SORT_BY_DONE); - sort = false; + sort[0] = false; + plotConsumer.accept(PlotQuery.newQuery().allPlots().thatPasses(DoneFlag::isDone).withSortingStrategy(SortingStrategy.SORT_BY_DONE)); break; case "top": if (!Permissions.hasPermission(player, Captions.PERMISSION_LIST_TOP)) { @@ -222,8 +255,8 @@ public class ListCmd extends SubCommand { .sendMessage(player, Captions.NO_PERMISSION, Captions.PERMISSION_LIST_TOP); return false; } - query = PlotQuery.newQuery().allPlots().withSortingStrategy(SortingStrategy.SORT_BY_RATING); - sort = false; + sort[0] = false; + plotConsumer.accept(PlotQuery.newQuery().allPlots().withSortingStrategy(SortingStrategy.SORT_BY_RATING)); break; case "forsale": if (!Permissions.hasPermission(player, Captions.PERMISSION_LIST_FOR_SALE)) { @@ -234,7 +267,7 @@ public class ListCmd extends SubCommand { if (EconHandler.manager == null) { break; } - query = PlotQuery.newQuery().allPlots().thatPasses(plot -> plot.getFlag(PriceFlag.class) > 0); + plotConsumer.accept(PlotQuery.newQuery().allPlots().thatPasses(plot -> plot.getFlag(PriceFlag.class) > 0)); break; case "unowned": if (!Permissions.hasPermission(player, Captions.PERMISSION_LIST_UNOWNED)) { @@ -242,20 +275,7 @@ public class ListCmd extends SubCommand { Captions.PERMISSION_LIST_UNOWNED); return false; } - query = PlotQuery.newQuery().allPlots().thatPasses(plot -> plot.getOwner() == null); - break; - case "unknown": - if (!Permissions.hasPermission(player, Captions.PERMISSION_LIST_UNKNOWN)) { - MainUtil.sendMessage(player, Captions.NO_PERMISSION, - Captions.PERMISSION_LIST_UNKNOWN); - return false; - } - query = PlotQuery.newQuery().allPlots().thatPasses(plot -> { - if (plot.getOwner() == null) { - return false; - } - return UUIDHandler.getName(plot.getOwner()) == null; - }); + plotConsumer.accept(PlotQuery.newQuery().allPlots().thatPasses(plot -> plot.getOwner() == null)); break; case "fuzzy": if (!Permissions.hasPermission(player, Captions.PERMISSION_LIST_FUZZY)) { @@ -273,8 +293,8 @@ public class ListCmd extends SubCommand { } else { term = StringMan.join(Arrays.copyOfRange(args, 1, args.length), " "); } - query = PlotQuery.newQuery().plotsBySearch(term); - sort = false; + sort[0] = false; + plotConsumer.accept(PlotQuery.newQuery().plotsBySearch(term)); break; default: if (PlotSquared.get().hasPlotArea(args[0])) { @@ -291,51 +311,37 @@ public class ListCmd extends SubCommand { args[0])); return false; } - query = PlotQuery.newQuery().inWorld(args[0]); - break; - } - UUID uuid = UUIDHandler.getUUID(args[0], null); - if (uuid == null) { - try { - uuid = UUID.fromString(args[0]); - } catch (Exception ignored) { - } - } - if (uuid != null) { - if (!Permissions.hasPermission(player, Captions.PERMISSION_LIST_PLAYER)) { - MainUtil.sendMessage(player, Captions.NO_PERMISSION, - Captions.PERMISSION_LIST_PLAYER); - return false; - } - sort = false; - query = PlotQuery.newQuery().ownedBy(uuid).withSortingStrategy(SortingStrategy.SORT_BY_TEMP); + plotConsumer.accept(PlotQuery.newQuery().inWorld(args[0])); break; } + + PlotSquared.get().getImpromptuUUIDPipeline() + .getSingle(args[0], (uuid, throwable) -> { + if (throwable instanceof TimeoutException) { + MainUtil.sendMessage(player, Captions.FETCHING_PLAYERS_TIMEOUT); + } else if (throwable != null) { + if (uuid == null) { + try { + uuid = UUID.fromString(args[0]); + } catch (Exception ignored) { + } + } + } + if (uuid == null) { + MainUtil.sendMessage(player, Captions.INVALID_PLAYER, args[0]); + } else { + if (!Permissions + .hasPermission(player, Captions.PERMISSION_LIST_PLAYER)) { + MainUtil.sendMessage(player, Captions.NO_PERMISSION, + Captions.PERMISSION_LIST_PLAYER); + } else { + sort[0] = false; + plotConsumer.accept(PlotQuery.newQuery().ownedBy(uuid).withSortingStrategy(SortingStrategy.SORT_BY_TEMP)); + } + } + }); } - if (query == null) { - sendMessage(player, Captions.DID_YOU_MEAN, - new StringComparison<>(args[0], new String[] {"mine", "shared", "world", "all"}) - .getBestMatch()); - return false; - } - - - if (area != null) { - query.relativeToArea(area); - } - - if (sort) { - query.withSortingStrategy(SortingStrategy.SORT_BY_CREATION); - } - - final List plots = query.asList(); - - if (plots.isEmpty()) { - MainUtil.sendMessage(player, Captions.FOUND_NO_PLOTS); - return false; - } - displayPlots(player, plots, 12, page, args); return true; } @@ -372,25 +378,72 @@ public class ListCmd extends SubCommand { .command("/plot info " + plot.getArea() + ";" + plot.getId()).color(color) .text(" - ").color("$2"); String prefix = ""; - for (UUID uuid : plot.getOwners()) { - String name = UUIDHandler.getName(uuid); - if (name == null) { - message = message.text(prefix).color("$4").text("unknown").color("$2") - .tooltip(uuid.toString()).suggest(uuid.toString()); - } else { - PlotPlayer pp = UUIDHandler.getPlayer(uuid); + + try { + final List names = PlotSquared.get().getImpromptuUUIDPipeline() + .getNames(plot.getOwners()).get(Settings.UUID.BLOCKING_TIMEOUT, TimeUnit.MILLISECONDS); + for (final UUIDMapping uuidMapping : names) { + PlotPlayer pp = PlotSquared.imp().getPlayerManager().getPlayerIfExists(uuidMapping.getUuid()); if (pp != null) { - message = message.text(prefix).color("$4").text(name).color("$1") + message = message.text(prefix).color("$4").text(uuidMapping.getUsername()).color("$1") .tooltip(new PlotMessage("Online").color("$4")); } else { - message = message.text(prefix).color("$4").text(name).color("$1") + message = message.text(prefix).color("$4").text(uuidMapping.getUsername()).color("$1") .tooltip(new PlotMessage("Offline").color("$3")); } + prefix = ", "; } - prefix = ", "; + } catch (InterruptedException | ExecutionException e) { + MainUtil.sendMessage(player, Captions.INVALID_PLAYER); + } catch (TimeoutException e) { + MainUtil.sendMessage(player, Captions.FETCHING_PLAYERS_TIMEOUT); } } }, "/plot list " + args[0], Captions.PLOT_LIST_HEADER_PAGED.getTranslated()); } + @Override public Collection tab(PlotPlayer player, String[] args, boolean space) { + final List completions = new LinkedList<>(); + if (EconHandler.manager != null && Permissions + .hasPermission(player, Captions.PERMISSION_LIST_FOR_SALE)) { + completions.add("forsale"); + } + if (Permissions.hasPermission(player, Captions.PERMISSION_LIST_MINE)) { + completions.add("mine"); + } + if (Permissions.hasPermission(player, Captions.PERMISSION_LIST_SHARED)) { + completions.add("shared"); + } + if (Permissions.hasPermission(player, Captions.PERMISSION_LIST_WORLD)) { + completions.addAll(PlotSquared.imp().getWorldManager().getWorlds()); + } + if (Permissions.hasPermission(player, Captions.PERMISSION_LIST_TOP)) { + completions.add("top"); + } + if (Permissions.hasPermission(player, Captions.PERMISSION_LIST_ALL)) { + completions.add("all"); + } + if (Permissions.hasPermission(player, Captions.PERMISSION_LIST_UNOWNED)) { + completions.add("unowned"); + } + if (Permissions.hasPermission(player, Captions.PERMISSION_LIST_DONE)) { + completions.add("done"); + } + if (Permissions.hasPermission(player, Captions.PERMISSION_LIST_EXPIRED)) { + completions.add("expired"); + } + + final List commands = new LinkedList<>(); + commands.addAll(completions.stream() + .filter(completion -> completion.toLowerCase().startsWith(args[0].toLowerCase())) + .map(completion -> new Command(null, true, completion, "", RequiredType.NONE, CommandCategory.TELEPORT) {}) + .collect(Collectors.toList())); + + if (Permissions.hasPermission(player, Captions.PERMISSION_LIST_PLAYER) && args[0].length() > 0) { + commands.addAll(TabCompletions.completePlayers(args[0], Collections.emptyList())); + } + + return commands; + } + } diff --git a/Core/src/main/java/com/plotsquared/core/command/MainCommand.java b/Core/src/main/java/com/plotsquared/core/command/MainCommand.java index 03be061ca..133e27ab2 100644 --- a/Core/src/main/java/com/plotsquared/core/command/MainCommand.java +++ b/Core/src/main/java/com/plotsquared/core/command/MainCommand.java @@ -99,7 +99,6 @@ public class MainCommand extends Command { new DebugPaste(); new Unlink(); new Kick(); - new DebugClaimTest(); new Inbox(); new Comment(); new DatabaseCommand(); diff --git a/Core/src/main/java/com/plotsquared/core/command/Merge.java b/Core/src/main/java/com/plotsquared/core/command/Merge.java index 480112c14..53f88e2e7 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Merge.java +++ b/Core/src/main/java/com/plotsquared/core/command/Merge.java @@ -40,7 +40,6 @@ import com.plotsquared.core.util.Expression; import com.plotsquared.core.util.MainUtil; import com.plotsquared.core.util.Permissions; import com.plotsquared.core.util.StringMan; -import com.plotsquared.core.util.uuid.UUIDHandler; import java.util.UUID; @@ -172,7 +171,7 @@ public class Merge extends SubCommand { MainUtil.sendMessage(player, Captions.NO_PLOT_PERMS); return false; } else { - uuid = plot.guessOwner(); + uuid = plot.getOwnerAbs(); } } if (!force && EconHandler.manager != null && plotArea.useEconomy() && price > 0d @@ -213,7 +212,7 @@ public class Merge extends SubCommand { java.util.Set uuids = adjacent.getOwners(); boolean isOnline = false; for (final UUID owner : uuids) { - final PlotPlayer accepter = UUIDHandler.getPlayer(owner); + final PlotPlayer accepter = PlotSquared.imp().getPlayerManager().getPlayerIfExists(owner); if (!force && accepter == null) { continue; } @@ -222,7 +221,7 @@ public class Merge extends SubCommand { Runnable run = () -> { MainUtil.sendMessage(accepter, Captions.MERGE_ACCEPTED); plot.autoMerge(dir, maxSize - size, owner, terrain); - PlotPlayer plotPlayer = UUIDHandler.getPlayer(player.getUUID()); + PlotPlayer plotPlayer = PlotSquared.imp().getPlayerManager().getPlayerIfExists(player.getUUID()); if (plotPlayer == null) { sendMessage(accepter, Captions.MERGE_NOT_VALID); return; 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 2c3d8e965..f19790dc5 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Owner.java +++ b/Core/src/main/java/com/plotsquared/core/command/Owner.java @@ -36,10 +36,11 @@ import com.plotsquared.core.plot.Plot; import com.plotsquared.core.util.MainUtil; import com.plotsquared.core.util.Permissions; import com.plotsquared.core.util.task.TaskManager; -import com.plotsquared.core.util.uuid.UUIDHandler; import java.util.Set; import java.util.UUID; +import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; @CommandDeclaration(command = "setowner", permission = "plots.set.owner", @@ -57,91 +58,103 @@ public class Owner extends SetCommand { return false; } Set plots = plot.getConnectedPlots(); - UUID uuid = null; + + final Consumer uuidConsumer = uuid -> { + if (uuid == null && !value.equalsIgnoreCase("none") && !value.equalsIgnoreCase("null") + && !value.equalsIgnoreCase("-")) { + Captions.INVALID_PLAYER.send(player, value); + return; + } + PlotChangeOwnerEvent event = PlotSquared.get().getEventDispatcher() + .callOwnerChange(player, plot, plot.hasOwner() ? plot.getOwnerAbs() : null, uuid, + plot.hasOwner()); + if (event.getEventResult() == Result.DENY) { + sendMessage(player, Captions.EVENT_DENIED, "Owner change"); + return; + } + uuid = event.getNewOwner(); + boolean force = event.getEventResult() == Result.FORCE; + if (uuid == null) { + if (!force && !Permissions + .hasPermission(player, Captions.PERMISSION_ADMIN_COMMAND_SET_OWNER.getTranslated(), + true)) { + return; + } + PlotUnlinkEvent unlinkEvent = PlotSquared.get().getEventDispatcher() + .callUnlink(plot.getArea(), plot, false, false, PlotUnlinkEvent.REASON.NEW_OWNER); + if (unlinkEvent.getEventResult() == Result.DENY) { + sendMessage(player, Captions.EVENT_DENIED, "Unlink on owner change"); + return; + } + plot.unlinkPlot(unlinkEvent.isCreateRoad(), unlinkEvent.isCreateRoad()); + Set connected = plot.getConnectedPlots(); + for (Plot current : connected) { + current.unclaim(); + current.removeSign(); + } + MainUtil.sendMessage(player, Captions.SET_OWNER); + return; + } + final PlotPlayer other = PlotSquared.imp().getPlayerManager().getPlayerIfExists(uuid); + if (plot.isOwner(uuid)) { + Captions.ALREADY_OWNER.send(player, MainUtil.getName(uuid)); + return; + } + if (!force && !Permissions + .hasPermission(player, Captions.PERMISSION_ADMIN_COMMAND_SET_OWNER)) { + if (other == null) { + Captions.INVALID_PLAYER_OFFLINE.send(player, value); + return; + } + int size = plots.size(); + int currentPlots = (Settings.Limit.GLOBAL ? + other.getPlotCount() : + other.getPlotCount(plot.getWorldName())) + size; + if (currentPlots > other.getAllowedPlots()) { + sendMessage(player, Captions.CANT_TRANSFER_MORE_PLOTS); + return; + } + } + final UUID finalUUID = uuid; + PlotSquared.get().getImpromptuUUIDPipeline().getSingle(uuid, (finalName, throwable) -> { + final boolean removeDenied = plot.isDenied(finalUUID); + Runnable run = () -> { + if (plot.setOwner(finalUUID, player)) { + if (removeDenied) + plot.removeDenied(finalUUID); + plot.setSign(finalName); + MainUtil.sendMessage(player, Captions.SET_OWNER); + if (other != null) { + MainUtil.sendMessage(other, Captions.NOW_OWNER, + plot.getArea() + ";" + plot.getId()); + } + } else { + MainUtil.sendMessage(player, Captions.SET_OWNER_CANCELLED); + } + }; + if (hasConfirmation(player)) { + CmdConfirm.addPending(player, "/plot set owner " + value, run); + } else { + TaskManager.runTask(run); + } + }); + }; + if (value.length() == 36) { try { - uuid = UUID.fromString(value); + uuidConsumer.accept(UUID.fromString(value)); } catch (Exception ignored) { } } else { - uuid = UUIDHandler.getUUID(value, null); - } - if (uuid == null && !value.equalsIgnoreCase("none") && !value.equalsIgnoreCase("null") - && !value.equalsIgnoreCase("-")) { - Captions.INVALID_PLAYER.send(player, value); - return false; - } - PlotChangeOwnerEvent event = PlotSquared.get().getEventDispatcher() - .callOwnerChange(player, plot, plot.hasOwner() ? plot.getOwnerAbs() : null, uuid, - plot.hasOwner()); - if (event.getEventResult() == Result.DENY) { - sendMessage(player, Captions.EVENT_DENIED, "Owner change"); - return false; - } - uuid = event.getNewOwner(); - boolean force = event.getEventResult() == Result.FORCE; - if (uuid == null) { - if (!force && !Permissions - .hasPermission(player, Captions.PERMISSION_ADMIN_COMMAND_SET_OWNER.getTranslated(), - true)) { - return false; - } - PlotUnlinkEvent unlinkEvent = PlotSquared.get().getEventDispatcher() - .callUnlink(plot.getArea(), plot, false, false, PlotUnlinkEvent.REASON.NEW_OWNER); - if (unlinkEvent.getEventResult() == Result.DENY) { - sendMessage(player, Captions.EVENT_DENIED, "Unlink on owner change"); - return true; - } - plot.unlinkPlot(unlinkEvent.isCreateRoad(), unlinkEvent.isCreateRoad()); - Set connected = plot.getConnectedPlots(); - for (Plot current : connected) { - current.unclaim(); - current.removeSign(); - } - MainUtil.sendMessage(player, Captions.SET_OWNER); - return true; - } - final PlotPlayer other = UUIDHandler.getPlayer(uuid); - if (plot.isOwner(uuid)) { - Captions.ALREADY_OWNER.send(player, MainUtil.getName(uuid)); - return false; - } - if (!force && !Permissions - .hasPermission(player, Captions.PERMISSION_ADMIN_COMMAND_SET_OWNER)) { - if (other == null) { - Captions.INVALID_PLAYER_OFFLINE.send(player, value); - return false; - } - int size = plots.size(); - int currentPlots = (Settings.Limit.GLOBAL ? - other.getPlotCount() : - other.getPlotCount(plot.getWorldName())) + size; - if (currentPlots > other.getAllowedPlots()) { - sendMessage(player, Captions.CANT_TRANSFER_MORE_PLOTS); - return false; - } - } - final String finalName = UUIDHandler.getName(uuid); - final UUID finalUUID = uuid; - final boolean removeDenied = plot.isDenied(finalUUID); - Runnable run = () -> { - if (plot.setOwner(finalUUID, player)) { - if (removeDenied) - plot.removeDenied(finalUUID); - plot.setSign(finalName); - MainUtil.sendMessage(player, Captions.SET_OWNER); - if (other != null) { - MainUtil.sendMessage(other, Captions.NOW_OWNER, - plot.getArea() + ";" + plot.getId()); - } - } else { - MainUtil.sendMessage(player, Captions.SET_OWNER_CANCELLED); - } - }; - if (hasConfirmation(player)) { - CmdConfirm.addPending(player, "/plot set owner " + value, run); - } else { - TaskManager.runTask(run); + PlotSquared.get().getImpromptuUUIDPipeline().getSingle(value, (uuid, throwable) -> { + if (throwable instanceof TimeoutException) { + MainUtil.sendMessage(player, Captions.FETCHING_PLAYERS_TIMEOUT); + } else if (throwable != null) { + MainUtil.sendMessage(player, Captions.INVALID_PLAYER, value); + } else { + uuidConsumer.accept(uuid); + } + }); } return true; } diff --git a/Core/src/main/java/com/plotsquared/core/command/Purge.java b/Core/src/main/java/com/plotsquared/core/command/Purge.java index 3691f44c9..50b323300 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Purge.java +++ b/Core/src/main/java/com/plotsquared/core/command/Purge.java @@ -36,7 +36,6 @@ import com.plotsquared.core.plot.PlotArea; import com.plotsquared.core.plot.PlotId; import com.plotsquared.core.util.StringMan; import com.plotsquared.core.util.task.TaskManager; -import com.plotsquared.core.util.uuid.UUIDHandler; import java.util.HashMap; import java.util.HashSet; @@ -65,7 +64,6 @@ public class Purge extends SubCommand { PlotId id = null; UUID owner = null; UUID added = null; - boolean unknown = false; boolean clear = false; for (String arg : args) { String[] split = arg.split(":"); @@ -97,7 +95,7 @@ public class Purge extends SubCommand { break; case "owner": case "o": - owner = UUIDHandler.getUUID(split[1], null); + owner = PlotSquared.get().getImpromptuUUIDPipeline().getSingle(split[1], Settings.UUID.BLOCKING_TIMEOUT); if (owner == null) { Captions.INVALID_PLAYER.send(player, split[1]); return false; @@ -105,17 +103,12 @@ public class Purge extends SubCommand { break; case "shared": case "s": - added = UUIDHandler.getUUID(split[1], null); + added = PlotSquared.get().getImpromptuUUIDPipeline().getSingle(split[1], Settings.UUID.BLOCKING_TIMEOUT); if (added == null) { Captions.INVALID_PLAYER.send(player, split[1]); return false; } break; - case "unknown": - case "?": - case "u": - unknown = Boolean.parseBoolean(split[1]); - break; case "clear": case "c": case "delete": @@ -145,9 +138,6 @@ public class Purge extends SubCommand { if (added != null && !plot.isAdded(added)) { continue; } - if (unknown && UUIDHandler.getName(plot.getOwnerAbs()) != null) { - continue; - } toDelete.addAll(plot.getConnectedPlots()); } if (PlotSquared.get().plots_tmp != null) { @@ -168,9 +158,6 @@ public class Purge extends SubCommand { if (added != null && !plot.isAdded(added)) { continue; } - if (unknown && UUIDHandler.getName(plot.getOwnerAbs()) != null) { - continue; - } toDelete.add(plot); } } diff --git a/Core/src/main/java/com/plotsquared/core/command/Remove.java b/Core/src/main/java/com/plotsquared/core/command/Remove.java index 2ed34aca9..1a079f709 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Remove.java +++ b/Core/src/main/java/com/plotsquared/core/command/Remove.java @@ -33,12 +33,9 @@ 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.uuid.UUIDHandler; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; import java.util.UUID; +import java.util.concurrent.TimeoutException; @CommandDeclaration(command = "remove", aliases = {"r", "untrust", "ut", "undeny", "unban", "ud"}, @@ -68,74 +65,59 @@ public class Remove extends SubCommand { MainUtil.sendMessage(player, Captions.NO_PLOT_PERMS); return true; } - int count = 0; - switch (args[0]) { - case "unknown": { - HashSet all = new HashSet<>(); - all.addAll(plot.getMembers()); - all.addAll(plot.getTrusted()); - all.addAll(plot.getDenied()); - ArrayList toRemove = new ArrayList<>(); - for (UUID uuid : all) { - if (UUIDHandler.getName(uuid) == null) { - toRemove.add(uuid); - count++; - } - } - for (UUID uuid : toRemove) { - plot.removeDenied(uuid); - plot.removeTrusted(uuid); - plot.removeMember(uuid); - } - break; - } - default: - Set uuids = MainUtil.getUUIDsFromString(args[0]); - if (!uuids.isEmpty()) { - for (UUID uuid : uuids) { - if (plot.getTrusted().contains(uuid)) { - if (plot.removeTrusted(uuid)) { - PlotSquared.get().getEventDispatcher() - .callTrusted(player, plot, uuid, false); - count++; - } - } else if (plot.getMembers().contains(uuid)) { - if (plot.removeMember(uuid)) { - PlotSquared.get().getEventDispatcher() - .callMember(player, plot, uuid, false); - count++; - } - } else if (plot.getDenied().contains(uuid)) { - if (plot.removeDenied(uuid)) { - PlotSquared.get().getEventDispatcher() - .callDenied(player, plot, uuid, false); - count++; - } - } else if (uuid == DBFunc.EVERYONE) { - if (plot.removeTrusted(uuid)) { - PlotSquared.get().getEventDispatcher() - .callTrusted(player, plot, uuid, false); - count++; - } else if (plot.removeMember(uuid)) { - PlotSquared.get().getEventDispatcher() - .callMember(player, plot, uuid, false); - count++; - } else if (plot.removeDenied(uuid)) { - PlotSquared.get().getEventDispatcher() - .callDenied(player, plot, uuid, false); - count++; - } + + MainUtil.getUUIDsFromString(args[0], (uuids, throwable) -> { + int count = 0; + if (throwable instanceof TimeoutException) { + MainUtil.sendMessage(player, Captions.FETCHING_PLAYERS_TIMEOUT); + return; + } else if (throwable != null) { + MainUtil.sendMessage(player, Captions.INVALID_PLAYER, args[0]); + return; + } else if (!uuids.isEmpty()) { + for (UUID uuid : uuids) { + if (plot.getTrusted().contains(uuid)) { + if (plot.removeTrusted(uuid)) { + PlotSquared.get().getEventDispatcher() + .callTrusted(player, plot, uuid, false); + count++; + } + } else if (plot.getMembers().contains(uuid)) { + if (plot.removeMember(uuid)) { + PlotSquared.get().getEventDispatcher() + .callMember(player, plot, uuid, false); + count++; + } + } else if (plot.getDenied().contains(uuid)) { + if (plot.removeDenied(uuid)) { + PlotSquared.get().getEventDispatcher() + .callDenied(player, plot, uuid, false); + count++; + } + } else if (uuid == DBFunc.EVERYONE) { + if (plot.removeTrusted(uuid)) { + PlotSquared.get().getEventDispatcher() + .callTrusted(player, plot, uuid, false); + count++; + } else if (plot.removeMember(uuid)) { + PlotSquared.get().getEventDispatcher() + .callMember(player, plot, uuid, false); + count++; + } else if (plot.removeDenied(uuid)) { + PlotSquared.get().getEventDispatcher() + .callDenied(player, plot, uuid, false); + count++; } } } - break; - } - if (count == 0) { - MainUtil.sendMessage(player, Captions.INVALID_PLAYER, args[0]); - return false; - } else { - MainUtil.sendMessage(player, Captions.REMOVED_PLAYERS, count + ""); - } + } + if (count == 0) { + MainUtil.sendMessage(player, Captions.INVALID_PLAYER, args[0]); + } else { + MainUtil.sendMessage(player, Captions.REMOVED_PLAYERS, count + ""); + } + }); return true; } + } diff --git a/Core/src/main/java/com/plotsquared/core/command/Set.java b/Core/src/main/java/com/plotsquared/core/command/Set.java index 4e6fcafc9..bf5285691 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Set.java +++ b/Core/src/main/java/com/plotsquared/core/command/Set.java @@ -37,6 +37,7 @@ import com.plotsquared.core.util.MainUtil; import com.plotsquared.core.util.PatternUtil; import com.plotsquared.core.util.Permissions; import com.plotsquared.core.util.StringMan; +import com.plotsquared.core.util.TabCompletions; import com.plotsquared.core.util.WorldUtil; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.world.block.BlockCategory; @@ -76,7 +77,6 @@ public class Set extends SubCommand { @Override public boolean set(PlotPlayer player, final Plot plot, String value) { PlotManager manager = player.getLocation().getPlotManager(); String[] components = manager.getPlotComponents(plot.getId()); - boolean allowUnsafe = DebugAllowUnsafe.unsafeAllowed.contains(player.getUUID()); String[] args = value.split(" "); String material = @@ -159,11 +159,7 @@ public class Set extends SubCommand { @Override public Collection tab(final PlotPlayer player, final String[] args, final boolean space) { - return PatternUtil.getSuggestions(player, StringMan.join(args, ",").trim()).stream() - .map(value -> value.toLowerCase(Locale.ENGLISH).replace("minecraft:", "")) - .filter(value -> value.startsWith(args[0].toLowerCase(Locale.ENGLISH))) - .map(value -> new Command(null, false, value, "", RequiredType.NONE, null) { - }).collect(Collectors.toList()); + return TabCompletions.completePatterns(StringMan.join(args, ",")); } }; } 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 8a93e6cd8..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,13 +32,16 @@ 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.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeoutException; @CommandDeclaration(command = "trust", aliases = {"t"}, @@ -65,51 +68,69 @@ public class Trust extends Command { .hasPermission(player, Captions.PERMISSION_ADMIN_COMMAND_TRUST), Captions.NO_PLOT_PERMS); checkTrue(args.length == 1, Captions.COMMAND_SYNTAX, getUsage()); - final Set uuids = MainUtil.getUUIDsFromString(args[0]); - checkTrue(!uuids.isEmpty(), Captions.INVALID_PLAYER, args[0]); - Iterator iterator = uuids.iterator(); - int size = currentPlot.getTrusted().size() + currentPlot.getMembers().size(); - while (iterator.hasNext()) { - UUID uuid = iterator.next(); - if (uuid == DBFunc.EVERYONE && !( - Permissions.hasPermission(player, Captions.PERMISSION_TRUST_EVERYONE) || Permissions - .hasPermission(player, Captions.PERMISSION_ADMIN_COMMAND_TRUST))) { - MainUtil.sendMessage(player, Captions.INVALID_PLAYER, MainUtil.getName(uuid)); - iterator.remove(); - continue; - } - if (currentPlot.isOwner(uuid)) { - MainUtil.sendMessage(player, Captions.ALREADY_ADDED, MainUtil.getName(uuid)); - iterator.remove(); - continue; - } - if (currentPlot.getTrusted().contains(uuid)) { - MainUtil.sendMessage(player, Captions.ALREADY_ADDED, MainUtil.getName(uuid)); - iterator.remove(); - continue; - } - size += currentPlot.getMembers().contains(uuid) ? 0 : 1; - } - checkTrue(!uuids.isEmpty(), null); - checkTrue(size <= currentPlot.getArea().getMaxPlotMembers() || Permissions - .hasPermission(player, Captions.PERMISSION_ADMIN_COMMAND_TRUST), - Captions.PLOT_MAX_MEMBERS); - // Success - confirm.run(this, () -> { - for (UUID uuid : uuids) { - if (uuid != DBFunc.EVERYONE) { - if (!currentPlot.removeMember(uuid)) { - if (currentPlot.getDenied().contains(uuid)) { - currentPlot.removeDenied(uuid); - } - } - } - currentPlot.addTrusted(uuid); - PlotSquared.get().getEventDispatcher().callTrusted(player, currentPlot, uuid, true); - MainUtil.sendMessage(player, Captions.TRUSTED_ADDED); - } - }, null); + final CompletableFuture future = new CompletableFuture<>(); + MainUtil.getUUIDsFromString(args[0], (uuids, throwable) -> { + if (throwable != null) { + if (throwable instanceof TimeoutException) { + Captions.FETCHING_PLAYERS_TIMEOUT.send(player); + } else { + Captions.INVALID_PLAYER.send(player, args[0]); + } + future.completeExceptionally(throwable); + return; + } else { + checkTrue(!uuids.isEmpty(), Captions.INVALID_PLAYER, args[0]); + Iterator iterator = uuids.iterator(); + int size = currentPlot.getTrusted().size() + currentPlot.getMembers().size(); + while (iterator.hasNext()) { + UUID uuid = iterator.next(); + if (uuid == DBFunc.EVERYONE && !( + Permissions.hasPermission(player, Captions.PERMISSION_TRUST_EVERYONE) || Permissions + .hasPermission(player, Captions.PERMISSION_ADMIN_COMMAND_TRUST))) { + MainUtil.sendMessage(player, Captions.INVALID_PLAYER, MainUtil.getName(uuid)); + iterator.remove(); + continue; + } + if (currentPlot.isOwner(uuid)) { + MainUtil.sendMessage(player, Captions.ALREADY_ADDED, MainUtil.getName(uuid)); + iterator.remove(); + continue; + } + if (currentPlot.getTrusted().contains(uuid)) { + MainUtil.sendMessage(player, Captions.ALREADY_ADDED, MainUtil.getName(uuid)); + iterator.remove(); + continue; + } + size += currentPlot.getMembers().contains(uuid) ? 0 : 1; + } + checkTrue(!uuids.isEmpty(), null); + checkTrue(size <= currentPlot.getArea().getMaxPlotMembers() || Permissions + .hasPermission(player, Captions.PERMISSION_ADMIN_COMMAND_TRUST), + Captions.PLOT_MAX_MEMBERS); + // Success + confirm.run(this, () -> { + for (UUID uuid : uuids) { + if (uuid != DBFunc.EVERYONE) { + if (!currentPlot.removeMember(uuid)) { + if (currentPlot.getDenied().contains(uuid)) { + currentPlot.removeDenied(uuid); + } + } + } + currentPlot.addTrusted(uuid); + PlotSquared.get().getEventDispatcher().callTrusted(player, currentPlot, uuid, true); + MainUtil.sendMessage(player, Captions.TRUSTED_ADDED); + } + }, null); + } + future.complete(true); + }); 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/command/Visit.java b/Core/src/main/java/com/plotsquared/core/command/Visit.java index 442c40aeb..651a1c0b0 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Visit.java +++ b/Core/src/main/java/com/plotsquared/core/command/Visit.java @@ -27,7 +27,6 @@ package com.plotsquared.core.command; import com.plotsquared.core.PlotSquared; import com.plotsquared.core.configuration.Captions; -import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.events.TeleportCause; import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; @@ -36,16 +35,20 @@ import com.plotsquared.core.plot.flag.implementations.UntrustedVisitFlag; import com.plotsquared.core.util.MainUtil; import com.plotsquared.core.util.MathMan; import com.plotsquared.core.util.Permissions; +import com.plotsquared.core.util.TabCompletions; import com.plotsquared.core.util.query.PlotQuery; import com.plotsquared.core.util.query.SortingStrategy; import com.plotsquared.core.util.task.RunnableVal2; import com.plotsquared.core.util.task.RunnableVal3; -import com.plotsquared.core.util.uuid.UUIDHandler; +import org.jetbrains.annotations.NotNull; import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; import java.util.List; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeoutException; @CommandDeclaration(command = "visit", permission = "plots.visit", @@ -60,98 +63,36 @@ public class Visit extends Command { super(MainCommand.getInstance(), true); } - @Override public Collection tab(PlotPlayer player, String[] args, boolean space) { - return tabOf(player, args, space, getUsage()); + private void visit(@NotNull final PlotPlayer player, @NotNull final PlotQuery query, final PlotArea sortByArea, + final RunnableVal3 confirm, final RunnableVal2 whenDone) { + this.visit(player, query, sortByArea, confirm, whenDone, 1); } - @Override - public CompletableFuture execute(final PlotPlayer player, String[] args, - RunnableVal3 confirm, - final RunnableVal2 whenDone) throws CommandException { - if (args.length == 1 && args[0].contains(":")) { - args = args[0].split(":"); - } - int page = Integer.MIN_VALUE; - PlotArea sortByArea = player.getApplicablePlotArea(); - boolean shouldSortByArea = Settings.Teleport.PER_WORLD_VISIT; - - final PlotQuery query = PlotQuery.newQuery(); - - switch (args.length) { - case 3: - if (!MathMan.isInteger(args[1])) { - Captions.NOT_VALID_NUMBER.send(player, "(1, ∞)"); - Captions.COMMAND_SYNTAX.send(player, getUsage()); - return CompletableFuture.completedFuture(false); - } - page = Integer.parseInt(args[2]); - case 2: - if (page != Integer.MIN_VALUE || !MathMan.isInteger(args[1])) { - sortByArea = PlotSquared.get().getPlotAreaByString(args[1]); - if (sortByArea == null) { - Captions.NOT_VALID_NUMBER.send(player, "(1, ∞)"); - Captions.COMMAND_SYNTAX.send(player, getUsage()); - return CompletableFuture.completedFuture(false); - } - UUID user = UUIDHandler.getUUIDFromString(args[0]); - if (user == null) { - Captions.COMMAND_SYNTAX.send(player, getUsage()); - return CompletableFuture.completedFuture(false); - } - query.ownedBy(user).whereBasePlot(); - shouldSortByArea = true; - break; - } - page = Integer.parseInt(args[1]); - case 1: - UUID user = args[0].length() >= 2 ? UUIDHandler.getUUIDFromString(args[0]) : null; - if (user != null && !PlotSquared.get().hasPlot(user)) { - user = null; - } - if (page == Integer.MIN_VALUE && user == null && MathMan.isInteger(args[0])) { - page = Integer.parseInt(args[0]); - query.ownedBy(player).whereBasePlot(); - break; - } - if (user != null) { - query.ownedBy(player).whereBasePlot(); - } else { - Plot plot = MainUtil.getPlotFromString(player, args[0], true); - if (plot != null) { - query.withPlot(plot); - } - } - break; - case 0: - page = 1; - query.ownedBy(player); - break; - default: - - } - if (page == Integer.MIN_VALUE) { - page = 1; - } - + private void visit(@NotNull final PlotPlayer player, @NotNull final PlotQuery query, final PlotArea sortByArea, + final RunnableVal3 confirm, final RunnableVal2 whenDone, int page) { // We get the query once, // then we get it another time further on final List unsorted = query.asList(); if (unsorted.isEmpty()) { Captions.FOUND_NO_PLOTS.send(player); - return CompletableFuture.completedFuture(false); + return; } if (unsorted.size() > 1) { query.whereBasePlot(); } - if (page < 1 || page > unsorted.size()) { - Captions.NOT_VALID_NUMBER.send(player, "(1, " + unsorted.size() + ")"); - return CompletableFuture.completedFuture(false); + if (page == Integer.MIN_VALUE) { + page = 1; } - if (shouldSortByArea) { + if (page < 1 || page > unsorted.size()) { + MainUtil.sendMessage(player, String.format("(1, %d)", unsorted.size())); + return; + } + + if (sortByArea != null) { query.relativeToArea(sortByArea).withSortingStrategy(SortingStrategy.SORT_BY_CREATION); } else { query.withSortingStrategy(SortingStrategy.SORT_BY_TEMP); @@ -163,28 +104,28 @@ public class Visit extends Command { if (!plot.hasOwner()) { if (!Permissions.hasPermission(player, Captions.PERMISSION_VISIT_UNOWNED)) { Captions.NO_PERMISSION.send(player, Captions.PERMISSION_VISIT_UNOWNED); - return CompletableFuture.completedFuture(false); + return; } } else if (plot.isOwner(player.getUUID())) { if (!Permissions.hasPermission(player, Captions.PERMISSION_VISIT_OWNED) && !Permissions .hasPermission(player, Captions.PERMISSION_HOME)) { Captions.NO_PERMISSION.send(player, Captions.PERMISSION_VISIT_OWNED); - return CompletableFuture.completedFuture(false); + return; } } else if (plot.isAdded(player.getUUID())) { if (!Permissions.hasPermission(player, Captions.PERMISSION_SHARED)) { Captions.NO_PERMISSION.send(player, Captions.PERMISSION_SHARED); - return CompletableFuture.completedFuture(false); + return; } } else { if (!Permissions.hasPermission(player, Captions.PERMISSION_VISIT_OTHER)) { Captions.NO_PERMISSION.send(player, Captions.PERMISSION_VISIT_OTHER); - return CompletableFuture.completedFuture(false); + return; } if (!plot.getFlag(UntrustedVisitFlag.class) && !Permissions .hasPermission(player, Captions.PERMISSION_ADMIN_VISIT_UNTRUSTED)) { Captions.NO_PERMISSION.send(player, Captions.PERMISSION_ADMIN_VISIT_UNTRUSTED); - return CompletableFuture.completedFuture(false); + return; } } @@ -195,8 +136,173 @@ public class Visit extends Command { whenDone.run(Visit.this, CommandResult.FAILURE); } }), () -> whenDone.run(Visit.this, CommandResult.FAILURE)); + } + + @Override + public CompletableFuture execute(final PlotPlayer player, + String[] args, + final RunnableVal3 confirm, + final RunnableVal2 whenDone) throws CommandException { + if (args.length == 1 && args[0].contains(":")) { + args = args[0].split(":"); + } + + PlotArea sortByArea; + + int page = Integer.MIN_VALUE; + + switch (args.length) { + // /p v [...] [...] + case 3: + if (!MathMan.isInteger(args[1])) { + Captions.NOT_VALID_NUMBER.send(player, "(1, ∞)"); + Captions.COMMAND_SYNTAX.send(player, getUsage()); + return CompletableFuture.completedFuture(false); + } + page = Integer.parseInt(args[2]); + // /p v [page] + // /p v [page] + case 2: + if (page != Integer.MIN_VALUE || !MathMan.isInteger(args[1])) { + sortByArea = PlotSquared.get().getPlotAreaByString(args[1]); + if (sortByArea == null) { + Captions.NOT_VALID_NUMBER.send(player, "(1, ∞)"); + Captions.COMMAND_SYNTAX.send(player, getUsage()); + return CompletableFuture.completedFuture(false); + } + + final PlotArea finalSortByArea = sortByArea; + int finalPage1 = page; + MainUtil.getUUIDsFromString(args[0], (uuids, throwable) -> { + if (throwable instanceof TimeoutException) { + Captions.FETCHING_PLAYERS_TIMEOUT.send(player); + } else if (throwable != null || uuids.size() != 1) { + Captions.COMMAND_SYNTAX.send(player, getUsage()); + } else { + final UUID uuid = uuids.toArray(new UUID[0])[0]; + this.visit(player, PlotQuery.newQuery().ownedBy(uuid).whereBasePlot(), finalSortByArea, confirm, whenDone, finalPage1); + } + }); + break; + } + page = Integer.parseInt(args[1]); + // /p v [page] + // /p v [page] + // /p v [page] + // /p v [page] + case 1: + final String[] finalArgs = args; + int finalPage = page; + if (args[0].length() >= 2 && !args[0].contains(";") && !args[0].contains(",")) { + PlotSquared.get().getImpromptuUUIDPipeline().getSingle(args[0], (uuid, throwable) -> { + if (throwable instanceof TimeoutException) { + // The request timed out + MainUtil.sendMessage(player, Captions.FETCHING_PLAYERS_TIMEOUT); + } else if (uuid != null && !PlotSquared.get().hasPlot(uuid)) { + // It was a valid UUID but the player has no plots + MainUtil.sendMessage(player, Captions.PLAYER_NO_PLOTS); + } else if (uuid == null) { + if (finalPage == Integer.MIN_VALUE && MathMan.isInteger(finalArgs[0])) { + // The argument was a number, so we assume it's the page number + int parsedPage; + try { + parsedPage = Integer.parseInt(finalArgs[0]); + } catch (final Throwable t) { + MainUtil.sendMessage(player, Captions.NOT_A_NUMBER, finalArgs[0]); + return; + } + this.visit(player, PlotQuery.newQuery().ownedBy(player).whereBasePlot(), null, + confirm, whenDone, parsedPage); + } else { + // Try to parse a plot + final Plot plot = MainUtil.getPlotFromString(player, finalArgs[0], true); + if (plot == null) { + MainUtil.sendMessage(player, Captions.NOT_VALID_PLOT_ID); + return; + } + this.visit(player, PlotQuery.newQuery().withPlot(plot), null, confirm, whenDone, 1); + } + } else { + this.visit(player, PlotQuery.newQuery().ownedBy(uuid).whereBasePlot(), null, confirm, whenDone, finalPage); + } + }); + } else { + if (finalPage == Integer.MIN_VALUE && MathMan.isInteger(finalArgs[0])) { + // The argument was a number, so we assume it's the page number + int parsedPage; + try { + parsedPage = Integer.parseInt(finalArgs[0]); + this.visit(player, PlotQuery.newQuery().ownedBy(player).whereBasePlot(), null, confirm, + whenDone, parsedPage); + } catch (final Throwable throwable) { + MainUtil.sendMessage(player, Captions.NOT_A_NUMBER, finalArgs[0]); + } + } else { + // Try to parse a plot + final Plot plot = MainUtil.getPlotFromString(player, finalArgs[0], true); + if (plot == null) { + MainUtil.sendMessage(player, Captions.NOT_VALID_PLOT_ID); + } else { + this.visit(player, PlotQuery.newQuery().withPlot(plot), null, confirm, whenDone, 1); + } + } + } + break; + case 0: + // /p v + this.visit(player, PlotQuery.newQuery().ownedBy(player), null, confirm, whenDone); + break; + default: + } return CompletableFuture.completedFuture(true); } + public Collection tab(PlotPlayer player, String[] args, boolean space) { + final List completions = new LinkedList<>(); + switch (args.length - 1) { + case 0: + this.completeNumbers(completions, args[0], 0); + completions.addAll(TabCompletions.completePlayers(args[0], Collections.emptyList())); + break; + case 1: + if (MathMan.isInteger(args[0])) { + break; + } + this.completeNumbers(completions, args[1], 0); + this.completeAreas(completions, args[1]); + break; + case 2: + if (MathMan.isInteger(args[1])) { + break; + } + this.completeNumbers(completions, args[2], 0); + break; + } + + return completions; + } + + private void completeNumbers(final List commands, final String arg, final int start) { + for (int i = 0; i < 100; i++) { + final String command = Integer.toString(start + 1); + if (!command.toLowerCase().startsWith(arg.toLowerCase())) { + continue; + } + commands.add(new Command(this, false, command, "", + RequiredType.NONE, CommandCategory.TELEPORT) {}); + } + } + + private void completeAreas(final List commands, final String arg) { + for (final PlotArea area : PlotSquared.get().getPlotAreas()) { + final String areaName = area.getWorldName() + ";" + area.getId(); + if (!areaName.toLowerCase().startsWith(arg.toLowerCase())) { + continue; + } + commands.add(new Command(this, false, area.getWorldName() + ";" + area.getId(), "", + RequiredType.NONE, CommandCategory.TELEPORT) {}); + } + } + } diff --git a/Core/src/main/java/com/plotsquared/core/configuration/Captions.java b/Core/src/main/java/com/plotsquared/core/configuration/Captions.java index 1e411d38c..26515db06 100644 --- a/Core/src/main/java/com/plotsquared/core/configuration/Captions.java +++ b/Core/src/main/java/com/plotsquared/core/configuration/Captions.java @@ -445,11 +445,14 @@ public enum Captions implements Caption { NOT_VALID_WORLD("$2That is not a valid world (case sensitive)", "Errors"), NOT_VALID_PLOT_WORLD("$2That is not a valid plot area (case sensitive)", "Errors"), NO_PLOTS("$2You don't have any plots", "Errors"), + PLAYER_NO_PLOTS("$2That player does not own any plots", "Errors"), WAIT_FOR_TIMER("$2A set block timer is bound to either the current plot or you. Please wait for it to finish", "Errors"), TILE_ENTITY_CAP_REACHED("$2The total number of tile entities in this chunk may not exceed $1%s", "Errors"), // DEBUG_REPORT_CREATED("$1Uploaded a full debug to: $1%url%", "Paste"), PURGE_SUCCESS("$4Successfully purged %s plots", "Purge"), + FETCHING_PLAYER("$1PlotSquared is attempting to find the specified player from your argument(s). This may take a while.", "Players"), + FETCHING_PLAYERS_TIMEOUT("$2The specified users did not exist in the cache and will be fetched in the background. Please wait a couple of minutes.", "Players"), // TRIM_IN_PROGRESS("A world trim task is already in progress!", "Trim"), // diff --git a/Core/src/main/java/com/plotsquared/core/configuration/Settings.java b/Core/src/main/java/com/plotsquared/core/configuration/Settings.java index 76d384fff..78b7c6e37 100644 --- a/Core/src/main/java/com/plotsquared/core/configuration/Settings.java +++ b/Core/src/main/java/com/plotsquared/core/configuration/Settings.java @@ -238,7 +238,20 @@ public class Settings extends Config { @Comment("Force using lowercase UUIDs") public static boolean FORCE_LOWERCASE = false; @Comment("Use a database to store UUID/name info") public static boolean USE_SQLUUIDHANDLER = false; - @Ignore public static boolean NATIVE_UUID_PROVIDER = false; + @Comment("How many UUIDs that may be stored in the cache") + public static int UUID_CACHE_SIZE = 100000; + @Comment("Rate limit (per 10 minutes) for background UUID fetching from the Mojang API") + public static int BACKGROUND_LIMIT = 200; + @Comment("Rate limit (per 10 minutes) for random UUID fetching from the Mojang API") + public static int IMPROMPTU_LIMIT = 300; + @Comment("Timeout (in milliseconds) for non-blocking UUID requests (mostly commands)") + public static long NON_BLOCKING_TIMEOUT = 3000L; + @Comment("Timeout (in milliseconds) for blocking UUID requests (events)") + public static long BLOCKING_TIMEOUT = 10L; + @Comment("Whether or not PlotSquared should read from the legacy database") + public static boolean LEGACY_DATABASE_SUPPORT = true; + @Comment("Whether or not PlotSquared should return Unknown if it fails to fulfill a request") + public static boolean UNKNOWN_AS_DEFAULT = true; } @@ -487,6 +500,8 @@ public class Settings extends Config { public static boolean CREATURE_SPAWN = true; @Comment("Check the tile entity limit on block placement") public static boolean TILE_ENTITY_CHECK = true; + @Comment("Use Paper's async tab completion") + public static boolean ASYNC_TAB_COMPLETION; } @Comment("Settings relating to PlotSquared's GlobalBlockQueue") @@ -504,8 +519,6 @@ public class Settings extends Config { @Comment("Events are needed to track a lot of things") public static boolean EVENTS = true; @Comment("Commands are used to interact with the plugin") public static boolean COMMANDS = true; - @Comment("The UUID cacher is used to resolve player names") public static boolean - UUID_CACHE = true; @Comment("Whether we should notify you about updates or not.") public static boolean UPDATE_NOTIFICATIONS = true; @Comment("Stores user metadata in a database") public static boolean PERSISTENT_META = true; @@ -534,10 +547,10 @@ public class Settings extends Config { public static boolean EXTERNAL_PLACEHOLDERS = true; @Comment("Make road regeneration persistent across restarts") public static boolean PERSISTENT_ROAD_REGEN = false; - @Comment("Try to guess plot owners from sign data. This may decrease server performance") - public static boolean GUESS_PLOT_OWNER = false; @Comment("Plot component preset GUI") public static boolean COMPONENT_PRESETS = true; + @Comment("Use UUID cache to complete usernames") + public static boolean EXTENDED_USERNAME_COMPLETION = true; } } diff --git a/Core/src/main/java/com/plotsquared/core/listener/PlotListener.java b/Core/src/main/java/com/plotsquared/core/listener/PlotListener.java index 08133387e..5a4e43142 100644 --- a/Core/src/main/java/com/plotsquared/core/listener/PlotListener.java +++ b/Core/src/main/java/com/plotsquared/core/listener/PlotListener.java @@ -61,7 +61,6 @@ import com.plotsquared.core.util.StringMan; import com.plotsquared.core.util.WorldUtil; import com.plotsquared.core.util.task.RunnableVal; import com.plotsquared.core.util.task.TaskManager; -import com.plotsquared.core.util.uuid.UUIDHandler; import com.sk89q.worldedit.world.gamemode.GameMode; import com.sk89q.worldedit.world.gamemode.GameModes; import com.sk89q.worldedit.world.item.ItemType; @@ -161,7 +160,7 @@ public class PlotListener { if (plot.getFlag(NotifyEnterFlag.class)) { if (!Permissions.hasPermission(player, "plots.flag.notify-enter.bypass")) { for (UUID uuid : plot.getOwners()) { - PlotPlayer owner = UUIDHandler.getPlayer(uuid); + final PlotPlayer owner = PlotSquared.imp().getPlayerManager().getPlayerIfExists(uuid); if (owner != null && !owner.getUUID().equals(player.getUUID())) { MainUtil.sendMessage(owner, Captions.NOTIFY_ENTER.getTranslated() .replace("%player", player.getName()) @@ -337,7 +336,7 @@ public class PlotListener { if (plot.getFlag(NotifyLeaveFlag.class)) { if (!Permissions.hasPermission(player, "plots.flag.notify-enter.bypass")) { for (UUID uuid : plot.getOwners()) { - PlotPlayer owner = UUIDHandler.getPlayer(uuid); + final PlotPlayer owner = PlotSquared.imp().getPlayerManager().getPlayerIfExists(uuid); if ((owner != null) && !owner.getUUID().equals(player.getUUID())) { MainUtil.sendMessage(owner, Captions.NOTIFY_LEAVE.getTranslated() .replace("%player", player.getName()) diff --git a/Core/src/main/java/com/plotsquared/core/player/PlotPlayer.java b/Core/src/main/java/com/plotsquared/core/player/PlotPlayer.java index 911c56adb..586809880 100644 --- a/Core/src/main/java/com/plotsquared/core/player/PlotPlayer.java +++ b/Core/src/main/java/com/plotsquared/core/player/PlotPlayer.java @@ -49,7 +49,6 @@ import com.plotsquared.core.util.EconHandler; import com.plotsquared.core.util.Permissions; import com.plotsquared.core.util.task.RunnableVal; import com.plotsquared.core.util.task.TaskManager; -import com.plotsquared.core.util.uuid.UUIDHandler; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.world.gamemode.GameMode; import com.sk89q.worldedit.world.item.ItemType; @@ -108,17 +107,6 @@ public abstract class PlotPlayer implements CommandCaller, OfflinePlotPlayer { return PlotSquared.get().IMP.wrapPlayer(player); } - /** - * Get the cached PlotPlayer from a username
- * - This will return null if the player has not finished logging in or is not online - * - * @param name - * @return - */ - public static PlotPlayer get(String name) { - return UUIDHandler.getPlayer(name); - } - public abstract Actor toActor(); /** @@ -363,8 +351,6 @@ public abstract class PlotPlayer implements CommandCaller, OfflinePlotPlayer { /** * Get this player's full location (including yaw/pitch) - * - * @return */ public abstract Location getLocationFull(); @@ -563,11 +549,10 @@ public abstract class PlotPlayer implements CommandCaller, OfflinePlotPlayer { plot.getId(), getName())); } } - String name = getName(); if (ExpireManager.IMP != null) { ExpireManager.IMP.storeDate(getUUID(), System.currentTimeMillis()); } - UUIDHandler.getPlayers().remove(name); + PlotSquared.imp().getPlayerManager().removePlayer(this); PlotSquared.get().IMP.unregister(this); } diff --git a/Core/src/main/java/com/plotsquared/core/plot/Plot.java b/Core/src/main/java/com/plotsquared/core/plot/Plot.java index 9461219ef..df9ee12c4 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/Plot.java +++ b/Core/src/main/java/com/plotsquared/core/plot/Plot.java @@ -25,7 +25,6 @@ */ package com.plotsquared.core.plot; -import com.google.common.collect.BiMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import com.plotsquared.core.PlotSquared; @@ -56,17 +55,14 @@ import com.plotsquared.core.plot.flag.implementations.KeepFlag; import com.plotsquared.core.plot.schematic.Schematic; import com.plotsquared.core.queue.GlobalBlockQueue; import com.plotsquared.core.queue.LocalBlockQueue; -import com.plotsquared.core.util.ChunkManager; import com.plotsquared.core.util.MainUtil; import com.plotsquared.core.util.MathMan; import com.plotsquared.core.util.Permissions; import com.plotsquared.core.util.RegionManager; import com.plotsquared.core.util.SchematicHandler; -import com.plotsquared.core.util.StringWrapper; import com.plotsquared.core.util.WorldUtil; 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.CompoundTag; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector2; @@ -83,7 +79,6 @@ import java.awt.geom.PathIterator; import java.awt.geom.Rectangle2D; import java.io.File; import java.net.URL; -import java.nio.charset.StandardCharsets; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; @@ -405,11 +400,10 @@ public class Plot { * @return list of PlotPlayer(s) or an empty list */ public List getPlayersInPlot() { - ArrayList players = new ArrayList<>(); - for (Entry entry : UUIDHandler.getPlayers().entrySet()) { - PlotPlayer plotPlayer = entry.getValue(); - if (this.equals(plotPlayer.getCurrentPlot())) { - players.add(plotPlayer); + final List players = new ArrayList<>(); + for (final PlotPlayer player : PlotSquared.imp().getPlayerManager().getPlayers()) { + if (this.equals(player.getCurrentPlot())) { + players.add(player); } } return players; @@ -502,7 +496,7 @@ public class Plot { } if (isMerged()) { Set plots = getConnectedPlots(); - Plot[] array = plots.toArray(new Plot[plots.size()]); + Plot[] array = plots.toArray(new Plot[0]); ImmutableSet.Builder owners = ImmutableSet.builder(); UUID last = this.getOwner(); owners.add(this.getOwner()); @@ -1052,9 +1046,11 @@ public class Plot { } if (createSign) { GlobalBlockQueue.IMP.addEmptyTask(() -> { - for (Plot current : plots) { - current.setSign(MainUtil.getName(current.getOwnerAbs())); - } + TaskManager.runTaskAsync(() -> { + for (Plot current : plots) { + current.setSign(MainUtil.getName(current.getOwnerAbs())); + } + }); }); } if (createRoad) { @@ -1717,12 +1713,8 @@ public class Plot { this.setSign("unknown"); return; } - String name = UUIDHandler.getName(this.getOwnerAbs()); - if (name == null) { - this.setSign("unknown"); - } else { - this.setSign(name); - } + PlotSquared.get().getImpromptuUUIDPipeline().getSingle(this.getOwnerAbs(), (username, sign) -> + this.setSign(username)); } /** @@ -2372,7 +2364,6 @@ public class Plot { * Check if a plot can be claimed by the provided player. * * @param player the claiming player - * @return */ public boolean canClaim(@NotNull PlotPlayer player) { PlotCluster cluster = this.getCluster(); @@ -2382,81 +2373,13 @@ public class Plot { return false; } } - final UUID owner = this.guessOwner(); + final UUID owner = this.getOwnerAbs(); if (owner != null) { return false; } return !isMerged(); } - /** - * Guess the owner of a plot either by the value in memory, or the sign data
- * Note: Recovering from sign information is useful if e.g. PlotMe conversion wasn't successful - * - * @return UUID - */ - public UUID guessOwner() { - if (this.hasOwner()) { - return this.getOwnerAbs(); - } - if (!this.area.allowSigns() || !Settings.Enabled_Components.GUESS_PLOT_OWNER) { - return null; - } - try { - final Location location = this.getManager().getSignLoc(this); - String[] lines = TaskManager.IMP.sync(new RunnableVal() { - @Override public void run(String[] value) { - ChunkManager.manager - .loadChunk(location.getWorld(), location.getBlockVector2(), false); - this.value = WorldUtil.IMP.getSignSynchronous(location); - } - }); - if (lines == null) { - return null; - } - loop: - for (int i = 4; i > 0; i--) { - String caption = Captions.valueOf("OWNER_SIGN_LINE_" + i).getTranslated(); - int index = caption.indexOf("%plr%"); - if (index < 0) { - continue; - } - String line = lines[i - 1]; - if (line.length() <= index) { - return null; - } - String name = line.substring(index); - if (name.isEmpty()) { - return null; - } - UUID owner = UUIDHandler.getUUID(name, null); - if (owner != null) { - this.setOwnerAbs(owner); - break; - } - if (lines[i - 1].length() == 15) { - BiMap map = UUIDHandler.getUuidMap(); - for (Entry entry : map.entrySet()) { - String key = entry.getKey().value; - if (key.length() > name.length() && key.startsWith(name)) { - this.setOwnerAbs(entry.getValue()); - break loop; - } - } - } - this.setOwnerAbs(UUID.nameUUIDFromBytes( - ("OfflinePlayer:" + name).getBytes(StandardCharsets.UTF_8))); - break; - } - if (this.hasOwner()) { - this.create(); - } - return this.getOwnerAbs(); - } catch (IllegalArgumentException ignored) { - return null; - } - } - /** * Remove the south road section of a plot
* - Used when a plot is merged
@@ -3099,10 +3022,12 @@ public class Plot { return false; } if (!isMerged()) { - return UUIDHandler.getPlayer(this.getOwnerAbs()) != null; + return PlotSquared.imp().getPlayerManager() + .getPlayerIfExists(Objects.requireNonNull(this.getOwnerAbs())) != null; } for (final Plot current : getConnectedPlots()) { - if (current.hasOwner() && UUIDHandler.getPlayer(current.getOwnerAbs()) != null) { + if (current.hasOwner() && PlotSquared.imp().getPlayerManager() + .getPlayerIfExists(Objects.requireNonNull(current.getOwnerAbs())) != null) { return true; } } @@ -3114,9 +3039,9 @@ public class Plot { * - E.g. floor, wall, border etc.
* - The available components depend on the generator being used
* - * @param component - * @param blocks - * @return + * @param component Component to set + * @param blocks Pattern to use the generation + * @return True if the component was set successfully */ public boolean setComponent(String component, Pattern blocks) { PlotComponentSetEvent event = diff --git a/Core/src/main/java/com/plotsquared/core/plot/comment/CommentManager.java b/Core/src/main/java/com/plotsquared/core/plot/comment/CommentManager.java index 6c41b905c..1e945a8bb 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/comment/CommentManager.java +++ b/Core/src/main/java/com/plotsquared/core/plot/comment/CommentManager.java @@ -38,8 +38,7 @@ import java.util.HashMap; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; -@Beta -public class CommentManager { +@Beta public class CommentManager { public static final HashMap inboxes = new HashMap<>(); diff --git a/Core/src/main/java/com/plotsquared/core/plot/expiration/ExpireManager.java b/Core/src/main/java/com/plotsquared/core/plot/expiration/ExpireManager.java index 6b5f2bbae..16d4ee41f 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/expiration/ExpireManager.java +++ b/Core/src/main/java/com/plotsquared/core/plot/expiration/ExpireManager.java @@ -27,7 +27,6 @@ package com.plotsquared.core.plot.expiration; 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.events.PlotFlagAddEvent; import com.plotsquared.core.events.PlotUnlinkEvent; @@ -48,7 +47,6 @@ import com.plotsquared.core.util.StringMan; import com.plotsquared.core.util.task.RunnableVal; import com.plotsquared.core.util.task.RunnableVal3; import com.plotsquared.core.util.task.TaskManager; -import com.plotsquared.core.util.uuid.UUIDHandler; import java.util.ArrayDeque; import java.util.ArrayList; @@ -411,13 +409,13 @@ public class ExpireManager { } } for (UUID helper : plot.getTrusted()) { - PlotPlayer player = UUIDHandler.getPlayer(helper); + PlotPlayer player = PlotSquared.imp().getPlayerManager().getPlayerIfExists(helper); if (player != null) { MainUtil.sendMessage(player, Captions.PLOT_REMOVED_USER, plot.toString()); } } for (UUID helper : plot.getMembers()) { - PlotPlayer player = UUIDHandler.getPlayer(helper); + PlotPlayer player = PlotSquared.imp().getPlayerManager().getPlayerIfExists(helper); if (player != null) { MainUtil.sendMessage(player, Captions.PLOT_REMOVED_USER, plot.toString()); } @@ -433,56 +431,34 @@ public class ExpireManager { .getString(plots)); PlotSquared.debug("$4 - Area: " + plot.getArea()); if (plot.hasOwner()) { - PlotSquared.debug("$4 - Owner: " + UUIDHandler.getName(plot.getOwner())); + PlotSquared.debug("$4 - Owner: " + plot.getOwner()); } else { PlotSquared.debug("$4 - Owner: Unowned"); } } public long getAge(UUID uuid) { - if (UUIDHandler.getPlayer(uuid) != null) { + if (PlotSquared.imp().getPlayerManager().getPlayerIfExists(uuid) != null) { return 0; } - String name = UUIDHandler.getName(uuid); - if (name != null) { - Long last = this.dates_cache.get(uuid); - if (last == null) { - OfflinePlotPlayer opp; - if (Settings.UUID.NATIVE_UUID_PROVIDER) { - opp = UUIDHandler.getUUIDWrapper().getOfflinePlayer(uuid); - } else { - opp = UUIDHandler.getUUIDWrapper().getOfflinePlayer(name); - } - if (opp != null && (last = opp.getLastPlayed()) != 0) { - this.dates_cache.put(uuid, last); - } else { - return 0; - } - } - if (last == 0) { + Long last = this.dates_cache.get(uuid); + if (last == null) { + OfflinePlotPlayer opp = PlotSquared.imp().getPlayerManager().getOfflinePlayer(uuid); + if (opp != null && (last = opp.getLastPlayed()) != 0) { + this.dates_cache.put(uuid, last); + } else { return 0; } - return System.currentTimeMillis() - last; } - return 0; - } - - public long getAccountAge(Plot plot) { - if (!plot.hasOwner() || Objects.equals(DBFunc.EVERYONE, plot.getOwner()) - || UUIDHandler.getPlayer(plot.getOwner()) != null || plot.getRunning() > 0) { - return Long.MAX_VALUE; + if (last == 0) { + return 0; } - long max = 0; - for (UUID owner : plot.getOwners()) { - long age = getAccountAge(owner); - max = Math.max(age, max); - } - return max; + return System.currentTimeMillis() - last; } public long getAge(Plot plot) { if (!plot.hasOwner() || Objects.equals(DBFunc.EVERYONE, plot.getOwner()) - || UUIDHandler.getPlayer(plot.getOwner()) != null || plot.getRunning() > 0) { + || PlotSquared.imp().getPlayerManager().getPlayerIfExists(plot.getOwner()) != null || plot.getRunning() > 0) { return 0; } diff --git a/Core/src/main/java/com/plotsquared/core/plot/flag/FlagContainer.java b/Core/src/main/java/com/plotsquared/core/plot/flag/FlagContainer.java index 7b60fea97..e0bb40fb8 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/flag/FlagContainer.java +++ b/Core/src/main/java/com/plotsquared/core/plot/flag/FlagContainer.java @@ -42,8 +42,7 @@ import java.util.Map; /** * Container type for {@link PlotFlag plot flags}. */ -@EqualsAndHashCode(of = "flagMap") -public class FlagContainer { +@EqualsAndHashCode(of = "flagMap") public class FlagContainer { private final Map unknownFlags = new HashMap<>(); private final Map, PlotFlag> flagMap = new HashMap<>(); @@ -341,8 +340,7 @@ public class FlagContainer { /** * Handler for update events in {@link FlagContainer flag containers}. */ - @FunctionalInterface - public interface PlotFlagUpdateHandler { + @FunctionalInterface public interface PlotFlagUpdateHandler { /** * Act on the flag update event diff --git a/Core/src/main/java/com/plotsquared/core/plot/flag/PlotFlag.java b/Core/src/main/java/com/plotsquared/core/plot/flag/PlotFlag.java index 513398a39..af3306b25 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/flag/PlotFlag.java +++ b/Core/src/main/java/com/plotsquared/core/plot/flag/PlotFlag.java @@ -41,8 +41,7 @@ import java.util.Collections; * * @param Value contained in the flag. */ -@EqualsAndHashCode(of = "value") -public abstract class PlotFlag> { +@EqualsAndHashCode(of = "value") public abstract class PlotFlag> { private final T value; private final Caption flagCategory; diff --git a/Core/src/main/java/com/plotsquared/core/plot/flag/types/BlockTypeWrapper.java b/Core/src/main/java/com/plotsquared/core/plot/flag/types/BlockTypeWrapper.java index 638653f0e..9af840d4c 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/flag/types/BlockTypeWrapper.java +++ b/Core/src/main/java/com/plotsquared/core/plot/flag/types/BlockTypeWrapper.java @@ -44,6 +44,8 @@ import java.util.Map; */ public class BlockTypeWrapper { + private static final Map blockTypes = new HashMap<>(); + private static final Map blockCategories = new HashMap<>(); @Nullable @Getter private final BlockType blockType; @Nullable private final String blockCategoryId; @Nullable private BlockCategory blockCategory; @@ -66,6 +68,19 @@ public class BlockTypeWrapper { this.blockCategoryId = Preconditions.checkNotNull(blockCategoryId); } + public static BlockTypeWrapper get(final BlockType blockType) { + return blockTypes.computeIfAbsent(blockType, BlockTypeWrapper::new); + } + + public static BlockTypeWrapper get(final BlockCategory blockCategory) { + return blockCategories + .computeIfAbsent(blockCategory.getId(), id -> new BlockTypeWrapper(blockCategory)); + } + + public static BlockTypeWrapper get(final String blockCategoryId) { + return blockCategories.computeIfAbsent(blockCategoryId, BlockTypeWrapper::new); + } + @Override public String toString() { if (this.blockType != null) { final String key = this.blockType.toString(); @@ -135,21 +150,6 @@ public class BlockTypeWrapper { return Objects.hashCode(this.blockType, this.blockCategoryId); } - private static final Map blockTypes = new HashMap<>(); - private static final Map blockCategories = new HashMap<>(); - - public static BlockTypeWrapper get(final BlockType blockType) { - return blockTypes.computeIfAbsent(blockType, BlockTypeWrapper::new); - } - - public static BlockTypeWrapper get(final BlockCategory blockCategory) { - return blockCategories - .computeIfAbsent(blockCategory.getId(), id -> new BlockTypeWrapper(blockCategory)); - } - - public static BlockTypeWrapper get(final String blockCategoryId) { - return blockCategories.computeIfAbsent(blockCategoryId, BlockTypeWrapper::new); - } /** * Prevents exceptions when loading/saving block categories diff --git a/Core/src/main/java/com/plotsquared/core/queue/LocalBlockQueue.java b/Core/src/main/java/com/plotsquared/core/queue/LocalBlockQueue.java index f979b4e9f..76fa5e28a 100644 --- a/Core/src/main/java/com/plotsquared/core/queue/LocalBlockQueue.java +++ b/Core/src/main/java/com/plotsquared/core/queue/LocalBlockQueue.java @@ -25,13 +25,13 @@ */ package com.plotsquared.core.queue; +import com.plotsquared.core.PlotSquared; import com.plotsquared.core.location.Location; import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.util.PatternUtil; import com.plotsquared.core.util.SchematicHandler; import com.plotsquared.core.util.StringMan; import com.plotsquared.core.util.WorldUtil; -import com.plotsquared.core.util.uuid.UUIDHandler; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector2; @@ -43,8 +43,6 @@ import lombok.Setter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Map; - public abstract class LocalBlockQueue { @Getter @Setter private boolean forceSync = false; @@ -124,8 +122,8 @@ public abstract class LocalBlockQueue { regenChunk(x, z); fixChunkLighting(x, z); BlockVector2 loc = BlockVector2.at(x, z); - for (Map.Entry entry : UUIDHandler.getPlayers().entrySet()) { - PlotPlayer pp = entry.getValue(); + + for (final PlotPlayer pp : PlotSquared.imp().getPlayerManager().getPlayers()) { Location pLoc = pp.getLocation(); if (!StringMan.isEqual(getWorld(), pLoc.getWorld()) || !pLoc.getBlockVector2() .equals(loc)) { 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 93cb7b3e4..8546298b6 100644 --- a/Core/src/main/java/com/plotsquared/core/util/MainUtil.java +++ b/Core/src/main/java/com/plotsquared/core/util/MainUtil.java @@ -49,7 +49,7 @@ import com.plotsquared.core.plot.flag.types.DoubleFlag; import com.plotsquared.core.util.net.AbstractDelegateOutputStream; import com.plotsquared.core.util.task.RunnableVal; import com.plotsquared.core.util.task.TaskManager; -import com.plotsquared.core.util.uuid.UUIDHandler; +import com.plotsquared.core.uuid.UUIDMapping; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; @@ -73,6 +73,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -81,6 +82,8 @@ 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; import java.util.function.IntFunction; @@ -99,12 +102,6 @@ public class MainUtil { FLAG_DECIMAL_FORMAT.setMaximumFractionDigits(340); } - /** - * If the NMS code for sending chunk updates is functional
- * - E.g. If using an older version of Bukkit, or before the plugin is updated to 1.5
- * - Slower fallback code will be used if not.
- */ - public static boolean canSendChunk = false; /** * Cache of mapping x,y,z coordinates to the chunk array
* - Used for efficient world generation
@@ -152,15 +149,6 @@ public class MainUtil { } } - public static void sendAdmin(final String s) { - for (final PlotPlayer player : UUIDHandler.getPlayers().values()) { - if (player.hasPermission(Captions.PERMISSION_ADMIN.getTranslated())) { - player.sendMessage(Captions.color(s)); - } - } - PlotSquared.debug(s); - } - public static void upload(UUID uuid, String file, String extension, final RunnableVal writeTask, final RunnableVal whenDone) { if (writeTask == null) { @@ -396,7 +384,7 @@ public class MainUtil { if (owner.equals(DBFunc.SERVER)) { return Captions.SERVER.getTranslated(); } - String name = UUIDHandler.getName(owner); + String name = PlotSquared.get().getImpromptuUUIDPipeline().getSingle(owner, Settings.UUID.BLOCKING_TIMEOUT); if (name == null) { return Captions.UNKNOWN.getTranslated(); } @@ -467,7 +455,7 @@ public class MainUtil { for (String term : split) { try { - UUID uuid = UUIDHandler.getUUID(term, null); + UUID uuid = PlotSquared.get().getImpromptuUUIDPipeline().getSingle(term, Settings.UUID.BLOCKING_TIMEOUT); if (uuid == null) { uuid = UUID.fromString(term); } @@ -738,33 +726,45 @@ public class MainUtil { return ratings; } - public static Set getUUIDsFromString(String list) { + public static void getUUIDsFromString(final String list, final BiConsumer, Throwable> consumer) { String[] split = list.split(","); - HashSet result = new HashSet<>(); - for (String name : split) { + + final Set result = new HashSet<>(); + final List request = new LinkedList<>(); + + for (final String name : split) { if (name.isEmpty()) { - // Invalid - return Collections.emptySet(); - } - if ("*".equals(name)) { + consumer.accept(Collections.emptySet(), null); + return; + } else if ("*".equals(name)) { result.add(DBFunc.EVERYONE); - continue; - } - if (name.length() > 16) { + } else if (name.length() > 16) { try { result.add(UUID.fromString(name)); - continue; } catch (IllegalArgumentException ignored) { - return Collections.emptySet(); + consumer.accept(Collections.emptySet(), null); + return; } + } else { + request.add(name); } - UUID uuid = UUIDHandler.getUUID(name, null); - if (uuid == null) { - return Collections.emptySet(); - } - result.add(uuid); } - return result; + + if (request.isEmpty()) { + consumer.accept(result, null); + } else { + PlotSquared.get().getImpromptuUUIDPipeline().getUUIDs(request, Settings.UUID.NON_BLOCKING_TIMEOUT) + .whenComplete((uuids, throwable) -> { + if (throwable != null) { + consumer.accept(null, throwable); + } else { + for (final UUIDMapping uuid : uuids) { + result.add(uuid.getUuid()); + } + consumer.accept(result, null); + } + }); + } } /** @@ -903,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))); @@ -931,7 +968,7 @@ public class MainUtil { public static void getPersistentMeta(UUID uuid, final String key, final RunnableVal result) { - PlotPlayer player = UUIDHandler.getPlayer(uuid); + PlotPlayer player = PlotSquared.imp().getPlayerManager().getPlayerIfExists(uuid); if (player != null) { result.run(player.getPersistentMeta(key)); } else { diff --git a/Core/src/main/java/com/plotsquared/core/util/PatternUtil.java b/Core/src/main/java/com/plotsquared/core/util/PatternUtil.java index ee390cc04..c8d18c136 100644 --- a/Core/src/main/java/com/plotsquared/core/util/PatternUtil.java +++ b/Core/src/main/java/com/plotsquared/core/util/PatternUtil.java @@ -62,7 +62,7 @@ public class PatternUtil { return parse(plotPlayer, input, true); } - public static List getSuggestions(PlotPlayer plotPlayer, String input) { + public static List getSuggestions(String input) { try { return WorldEdit.getInstance().getPatternFactory().getSuggestions(input); } catch (final Exception ignored) { diff --git a/Core/src/main/java/com/plotsquared/core/util/PlatformWorldManager.java b/Core/src/main/java/com/plotsquared/core/util/PlatformWorldManager.java index f57e42a21..c304ff04c 100644 --- a/Core/src/main/java/com/plotsquared/core/util/PlatformWorldManager.java +++ b/Core/src/main/java/com/plotsquared/core/util/PlatformWorldManager.java @@ -28,6 +28,8 @@ package com.plotsquared.core.util; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; + /** * This class should be implemented by each platform to allow PlotSquared to interact * with the world management solution used on the server. @@ -62,4 +64,11 @@ public interface PlatformWorldManager { */ String getName(); + /** + * Get the names of all worlds on the server + * + * @return Worlds + */ + Collection getWorlds(); + } diff --git a/Core/src/main/java/com/plotsquared/core/util/PlayerManager.java b/Core/src/main/java/com/plotsquared/core/util/PlayerManager.java new file mode 100644 index 000000000..473826b2d --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/util/PlayerManager.java @@ -0,0 +1,137 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * 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.plotsquared.core.player.OfflinePlotPlayer; +import com.plotsquared.core.player.PlotPlayer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * Manages player instances + */ +public abstract class PlayerManager

{ + + private final Map 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 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 uuid) { + this.getUUIDs(Collections.singletonList(username)).applyToEither(timeoutAfter(Settings.UUID.NON_BLOCKING_TIMEOUT), Function.identity()) + .whenComplete((uuids, throwable) -> { + if (throwable != null) { + uuid.accept(null, throwable); + } else { + if (!uuids.isEmpty()) { + uuid.accept(uuids.get(0).getUuid(), null); + } else { + uuid.accept(null, null); + } + } + }); + } + + /** + * Get a single username from a UUID. This is non-blocking. + * + * @param uuid UUID + * @param username Username consumer + */ + public void getSingle(@NotNull final UUID uuid, + @NotNull final BiConsumer username) { + this.getNames(Collections.singletonList(uuid)).applyToEither(timeoutAfter(Settings.UUID.NON_BLOCKING_TIMEOUT), Function.identity()) + .whenComplete((uuids, throwable) -> { + if (throwable != null) { + username.accept(null, throwable); + } else { + if (!uuids.isEmpty()) { + username.accept(uuids.get(0).getUsername(), null); + } else { + username.accept(null, null); + } + } + }); + } + + /** + * Asynchronously attempt to fetch the mapping from a list of UUIDs. + *

+ * This will timeout after the specified time and throws a {@link TimeoutException} + * if this happens + * + * @param requests UUIDs + * @param timeout Timeout in milliseconds + * @return Mappings + */ + public CompletableFuture> getNames(@NotNull final Collection requests, + final long timeout) { + return this.getNames(requests).applyToEither(timeoutAfter(timeout), Function.identity()); + } + + /** + * Asynchronously attempt to fetch the mapping from a list of names. + *

+ * This will timeout after the specified time and throws a {@link TimeoutException} + * if this happens + * + * @param requests Names + * @param timeout Timeout in milliseconds + * @return Mappings + */ + public CompletableFuture> getUUIDs(@NotNull final Collection requests, + final long timeout) { + return this.getUUIDs(requests).applyToEither(timeoutAfter(timeout), Function.identity()); + } + + private CompletableFuture> timeoutAfter(final long timeout) { + final CompletableFuture> result = new CompletableFuture<>(); + this.timeoutExecutor.schedule(() -> result.completeExceptionally(new TimeoutException()), timeout, TimeUnit.MILLISECONDS); + return result; + } + + /** + * Asynchronously attempt to fetch the mapping from a list of UUIDs + * + * @param requests UUIDs + * @return Mappings + */ + public CompletableFuture> getNames(@NotNull final Collection requests) { + if (requests.isEmpty()) { + return CompletableFuture.completedFuture(Collections.emptyList()); + } + + 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"); + } + + if (Settings.UUID.UNKNOWN_AS_DEFAULT) { + for (final UUID uuid : remainingRequests) { + mappings.add(new UUIDMapping(uuid, Captions.UNKNOWN.getTranslated())); + } + return mappings; + } else { + throw new ServiceError("End of pipeline"); + } + }, this.executor); + } + + /** + * Asynchronously attempt to fetch the mapping from a list of names + * + * @param requests Names + * @return Mappings + */ + public CompletableFuture> getUUIDs( + @NotNull final Collection requests) { + if (requests.isEmpty()) { + return CompletableFuture.completedFuture(Collections.emptyList()); + } + + 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"); + }, this.executor); + } + + /** + * Get as many UUID mappings as possible under the condition + * that the operation cannot be blocking (for an extended amount of time) + * + * @return All mappings that could be provided immediately + */ + @NotNull public final Collection getAllImmediately() { + final Set mappings = new LinkedHashSet<>(); + for (final UUIDService service : this.getServiceListInstance()) { + mappings.addAll(service.getImmediately()); + } + return mappings; + } + +} diff --git a/Core/src/main/java/com/plotsquared/core/uuid/UUIDService.java b/Core/src/main/java/com/plotsquared/core/uuid/UUIDService.java new file mode 100644 index 000000000..3b6419bd6 --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/uuid/UUIDService.java @@ -0,0 +1,78 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * 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 org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +/** + * Service used to provide usernames from player UUIDs + */ +public interface UUIDService { + + /** + * Attempt to complete the given requests. Returns the mappings + * that could be created by this server + * + * @param uuids Requests + * @return Completed requests + */ + @NotNull List getNames(@NotNull final List uuids); + + /** + * Attempt to complete the given requests. Returns the mappings + * that could be created by this server + * + * @param usernames Requests + * @return Completed requests + */ + @NotNull List getUUIDs(@NotNull final List usernames); + + /** + * Get as many UUID mappings as possible under the condition + * that the operation cannot be blocking (for an extended amount of time) + * + * @return All mappings that could be provided immediately + */ + default @NotNull Collection getImmediately() { + 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; + } + +} diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/uuid/DefaultUUIDWrapper.java b/Core/src/main/java/com/plotsquared/core/uuid/offline/OfflineModeUUIDService.java similarity index 52% rename from Bukkit/src/main/java/com/plotsquared/bukkit/util/uuid/DefaultUUIDWrapper.java rename to Core/src/main/java/com/plotsquared/core/uuid/offline/OfflineModeUUIDService.java index a2898ee91..49f038db8 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/uuid/DefaultUUIDWrapper.java +++ b/Core/src/main/java/com/plotsquared/core/uuid/offline/OfflineModeUUIDService.java @@ -23,44 +23,42 @@ * 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.offline; -import com.plotsquared.bukkit.player.BukkitOfflinePlayer; -import com.plotsquared.bukkit.player.BukkitPlayer; -import com.plotsquared.core.player.OfflinePlotPlayer; -import com.plotsquared.core.player.PlotPlayer; -import com.plotsquared.core.util.uuid.UUIDWrapper; -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; +import com.google.common.base.Charsets; +import com.plotsquared.core.configuration.Settings; +import com.plotsquared.core.uuid.UUIDMapping; +import com.plotsquared.core.uuid.UUIDService; import org.jetbrains.annotations.NotNull; -import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; import java.util.UUID; -public class DefaultUUIDWrapper extends UUIDWrapper { +/** + * Name provider service that creates UUIDs from usernames + */ +public class OfflineModeUUIDService implements UUIDService { - @NotNull @Override public UUID getUUID(PlotPlayer player) { - return ((BukkitPlayer) player).player.getUniqueId(); + @NotNull protected final UUID getFromUsername(@NotNull String username) { + if (Settings.UUID.FORCE_LOWERCASE) { + username = username.toLowerCase(Locale.ENGLISH); + } + return UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(Charsets.UTF_8)); } - @Override public UUID getUUID(OfflinePlotPlayer player) { - return player.getUUID(); + @Override @NotNull public List getNames(@NotNull final List uuids) { + return Collections.emptyList(); } - @Override public OfflinePlotPlayer getOfflinePlayer(UUID uuid) { - return new BukkitOfflinePlayer(Bukkit.getOfflinePlayer(uuid)); + @Override @NotNull public List getUUIDs(@NotNull List usernames) { + final List mappings = new ArrayList<>(usernames.size()); + for (final String username : usernames) { + mappings.add(new UUIDMapping(getFromUsername(username), username)); + } + return mappings; } - @Override public UUID getUUID(String name) { - return Bukkit.getOfflinePlayer(name).getUniqueId(); - } - - @Override public OfflinePlotPlayer[] getOfflinePlayers() { - OfflinePlayer[] ops = Bukkit.getOfflinePlayers(); - return Arrays.stream(ops).map(BukkitOfflinePlayer::new).toArray(BukkitOfflinePlayer[]::new); - } - - @Override public OfflinePlotPlayer getOfflinePlayer(String name) { - return new BukkitOfflinePlayer(Bukkit.getOfflinePlayer(name)); - } }