/* * Copyright 2014 ZerothAngel <zerothangel@tyrannyofheaven.org> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.tyrannyofheaven.bukkit.util.uuid; import static org.tyrannyofheaven.bukkit.util.ToHMessageUtils.colorize; import static org.tyrannyofheaven.bukkit.util.ToHMessageUtils.sendMessage; import static org.tyrannyofheaven.bukkit.util.ToHStringUtils.hasText; import static org.tyrannyofheaven.bukkit.util.command.reader.CommandReader.abortBatchProcessing; import static org.tyrannyofheaven.bukkit.util.command.reader.CommandReader.isBatchProcessing; import static org.tyrannyofheaven.bukkit.util.uuid.UuidUtils.parseUuidDisplayName; import java.util.UUID; import java.util.concurrent.Executor; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; public class CommandUuidResolver { private final Plugin plugin; private final UuidResolver uuidResolver; private final Executor executor; private final boolean abortInline; public CommandUuidResolver(Plugin plugin, UuidResolver uuidResolver, Executor executor, boolean abortInline) { this.plugin = plugin; this.uuidResolver = uuidResolver; this.executor = executor; this.abortInline = abortInline; } public void resolveUsername(CommandSender sender, String name, boolean skip, boolean forceInline, CommandUuidResolverHandler handler) { if (sender == null) throw new IllegalArgumentException("sender cannot be null"); if (handler == null) throw new IllegalArgumentException("handler cannot be null"); if (skip || name == null) { // Simple case: no need to resolve because skip is true or name is null, run inline handler.process(sender, name, null, skip); } else { // See if it's UUID or UUID/DisplayName UuidDisplayName udn = parseUuidDisplayName(name); if (udn != null) { String displayName; OfflinePlayer player = Bukkit.getOfflinePlayer(udn.getUuid()); if (player != null && player.getName() != null) { // Use last known name displayName = player.getName(); } else { // Default display name (either what was passed in or the UUID in string form) displayName = hasText(udn.getDisplayName()) ? udn.getDisplayName() : udn.getUuid().toString(); } handler.process(sender, displayName, udn.getUuid(), skip); } else { // Is the named player online? Player player = Bukkit.getPlayerExact(name); if (player != null) { // Simply run inline, no explicit lookup necessary handler.process(sender, player.getName(), player.getUniqueId(), skip); } else if (forceInline) { // Lookup & run inline udn = uuidResolver.resolve(name); if (udn == null) { fail(sender, name); } else { handler.process(sender, udn.getDisplayName(), udn.getUuid(), skip); } } else { // Check if cached by resolver udn = uuidResolver.resolve(name, true); if (udn != null) { // If so, run inline handler.process(sender, udn.getDisplayName(), udn.getUuid(), skip); } else { // As an absolute last resort, resolve and run async sendMessage(sender, colorize("{GRAY}(Resolving UUID...)")); Runnable task = new UsernameResolverHandlerRunnable(this, plugin, uuidResolver, sender, name, skip, handler); // NB Bukkit#getOfflinePlayer(String) provides almost the same service // However, it's not known whether it is fully thread-safe. executor.execute(task); } } } } } private static class UsernameResolverHandlerRunnable implements Runnable { private final CommandUuidResolver commandUuidResolver; private final Plugin plugin; private final UuidResolver uuidResolver; private final CommandSender sender; private final UUID senderUuid; private final String name; private final boolean skip; private final CommandUuidResolverHandler handler; private UsernameResolverHandlerRunnable(CommandUuidResolver commandUuidResolver, Plugin plugin, UuidResolver uuidResolver, CommandSender sender, String name, boolean skip, CommandUuidResolverHandler handler) { this.commandUuidResolver = commandUuidResolver; this.plugin = plugin; this.uuidResolver = uuidResolver; this.sender = sender instanceof Player ? null : sender; this.senderUuid = sender instanceof Player ? ((Player)sender).getUniqueId() : null; this.name = name; this.skip = skip; this.handler = handler; } private CommandSender getSender() { return sender; } @Override public void run() { // Perform lookup final UuidDisplayName udn = uuidResolver.resolve(name); // Run the rest in the main thread Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() { @Override public void run() { // Re-lookup sender CommandSender sender = getSender() != null ? getSender() : Bukkit.getPlayer(senderUuid); // Only execute if sender is still around if (sender != null) { if (udn == null) { commandUuidResolver.fail(sender, name); } else { handler.process(sender, udn.getDisplayName(), udn.getUuid(), skip); } } } }); } } public void resolveUsername(CommandSender sender, String name, boolean skip, CommandUuidResolverHandler handler) { resolveUsername(sender, name, skip, isBatchProcessing(), handler); } public void resolveUsername(CommandSender sender, String name, CommandUuidResolverHandler handler) { resolveUsername(sender, name, false, isBatchProcessing(), handler); } private void fail(CommandSender sender, String name) { sendMessage(sender, colorize("{RED}Failed to lookup UUID for {AQUA}%s"), name); if (abortInline) abortBatchProcessing(); } }