diff --git a/Bukkit/build.gradle b/Bukkit/build.gradle index 9fa1f8cf1..9b6b862a6 100644 --- a/Bukkit/build.gradle +++ b/Bukkit/build.gradle @@ -31,6 +31,7 @@ dependencies { } implementation("me.clip:placeholderapi:2.10.4") compile("se.hyperver.hyperverse:Core:0.6.0-SNAPSHOT"){ transitive = false } + compile 'com.github.pavog:SquirrelID:0.6.1' } sourceCompatibility = 1.8 @@ -87,10 +88,12 @@ shadowJar { include(dependency("io.papermc:paperlib:1.0.2")) include(dependency("net.kyori:text-adapter-bukkit:3.0.3")) include(dependency("org.bstats:bstats-bukkit:1.7")) + 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 40202c32e..1eace8462 100644 --- a/Bukkit/pom.xml +++ b/Bukkit/pom.xml @@ -59,6 +59,12 @@ Core 0.6.0-SNAPSHOT compile + + + * + * + + com.sk89q.worldedit 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..8ba2ad76b --- /dev/null +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/uuid/OfflinePlayerUUIDService.java @@ -0,0 +1,57 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * 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.UUIDService; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; +import java.util.UUID; + +/** + * UUID service that use {@link org.bukkit.OfflinePlayer offline players} + */ +public class OfflinePlayerUUIDService implements UUIDService { + + @Override @NotNull public Optional get(@NotNull final UUID uuid) { + final OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuid); + if (offlinePlayer.hasPlayedBefore()) { + return Optional.ofNullable(offlinePlayer.getName()); + } + return Optional.empty(); + } + + @Override @NotNull public Optional get(@NotNull final String username) { + final OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(username); + if (offlinePlayer.hasPlayedBefore()) { + return Optional.of(offlinePlayer.getUniqueId()); + } + return Optional.empty(); + } + +} 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/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/uuid/UUIDPipeline.java b/Core/src/main/java/com/plotsquared/core/uuid/UUIDPipeline.java index 87ef3f529..64da919f4 100644 --- a/Core/src/main/java/com/plotsquared/core/uuid/UUIDPipeline.java +++ b/Core/src/main/java/com/plotsquared/core/uuid/UUIDPipeline.java @@ -32,7 +32,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; @@ -92,35 +91,58 @@ public class UUIDPipeline { } /** - * Asynchronously attempt to fetch the mapping from a given UUID or username + * Asynchronously attempt to fetch the mapping from a list of UUIDs * - * @param requests UUIDs or usernames - * @return Future that may complete with the mapping + * @param requests UUIDs + * @return Mappings */ - public CompletableFuture> get(@NotNull final Collection requests) { + public CompletableFuture> getNames(@NotNull final Collection requests) { final List serviceList = this.getServiceListInstance(); return CompletableFuture.supplyAsync(() -> { final List mappings = new ArrayList<>(requests.size()); - outer: for (final Object request : requests) { - if (!(request instanceof String) && !(request instanceof UUID)) { - throw new IllegalArgumentException("Request has to be either a username or UUID"); + final List remainingRequests = new ArrayList<>(requests); + + for (final UUIDService service : serviceList) { + final List completedRequests = service.getNames(remainingRequests); + for (final UUIDMapping mapping : completedRequests) { + remainingRequests.remove(mapping.getUuid()); } - for (final UUIDService service : serviceList) { - final Optional result = service.get(request); - if (result.isPresent()) { - final String username = request instanceof String ? (String) request - : (String) result.get(); - final UUID uuid = request instanceof UUID ? (UUID) request - : (UUID) result.get(); - final UUIDMapping mapping = new UUIDMapping(uuid, username); - this.consume(mapping); - mappings.add(mapping); - continue outer; - } - } - throw new ServiceError("End of pipeline"); + mappings.addAll(completedRequests); } - return mappings; + + if (mappings.size() == requests.size()) { + return mappings; + } + + 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) { + final List serviceList = this.getServiceListInstance(); + return CompletableFuture.supplyAsync(() -> { + final List mappings = new ArrayList<>(requests.size()); + final List remainingRequests = new ArrayList<>(requests); + + for (final UUIDService service : serviceList) { + final List completedRequests = service.getUUIDs(remainingRequests); + for (final UUIDMapping mapping : completedRequests) { + remainingRequests.remove(mapping.getUsername()); + } + mappings.addAll(completedRequests); + } + + if (mappings.size() == requests.size()) { + return mappings; + } + + throw new ServiceError("End of pipeline"); }, this.executor); } diff --git a/Core/src/main/java/com/plotsquared/core/uuid/UUIDService.java b/Core/src/main/java/com/plotsquared/core/uuid/UUIDService.java index db29480d7..468de09b2 100644 --- a/Core/src/main/java/com/plotsquared/core/uuid/UUIDService.java +++ b/Core/src/main/java/com/plotsquared/core/uuid/UUIDService.java @@ -27,7 +27,7 @@ package com.plotsquared.core.uuid; import org.jetbrains.annotations.NotNull; -import java.util.Optional; +import java.util.List; import java.util.UUID; /** @@ -35,40 +35,22 @@ import java.util.UUID; */ public interface UUIDService { - default Optional get(@NotNull final Object request) { - if (request instanceof UUID) { - return get((UUID) request); - } else if (request instanceof String) { - return get((String) request); - } else { - throw new IllegalArgumentException("Request has to be either a username or UUID"); - } - } + /** + * 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); /** - * Get a stored username from the service if it exists. - * This should not trigger any fetching of - * usernames from other services. - *

- * If the username isn't stored in this service, - * this completes with an empty optional. + * Attempt to complete the given requests. Returns the mappings + * that could be created by this server * - * @param uuid Player UUID - * @return Optional that may contain the username if it exists + * @param usernames Requests + * @return Completed requests */ - @NotNull Optional get(@NotNull final UUID uuid); - - /** - * Get a stored UUID from the service if it exists. - * This should not trigger any fetching of - * UUID from other services. - *

- * If the UUID isn't stored in this service, - * this completes with an empty optional. - * - * @param username Player username - * @return Optional that may contain the UUID if it exists - */ - @NotNull Optional get(@NotNull final String username); + @NotNull List getUUIDs(@NotNull final List usernames); } diff --git a/Core/src/main/java/com/plotsquared/core/uuid/offline/OfflineModeUUIDService.java b/Core/src/main/java/com/plotsquared/core/uuid/offline/OfflineModeUUIDService.java index 7e397119e..49f038db8 100644 --- a/Core/src/main/java/com/plotsquared/core/uuid/offline/OfflineModeUUIDService.java +++ b/Core/src/main/java/com/plotsquared/core/uuid/offline/OfflineModeUUIDService.java @@ -27,11 +27,14 @@ package com.plotsquared.core.uuid.offline; 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.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Locale; -import java.util.Optional; import java.util.UUID; /** @@ -46,12 +49,16 @@ public class OfflineModeUUIDService implements UUIDService { return UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(Charsets.UTF_8)); } - @Override @NotNull public Optional get(@NotNull final UUID uuid) { - return Optional.empty(); + @Override @NotNull public List getNames(@NotNull final List uuids) { + return Collections.emptyList(); } - @Override @NotNull public Optional get(@NotNull final String username) { - return Optional.of(this.getFromUsername(username)); + @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; } }