ProjectAres/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/commands/UserFinder.java

239 lines
9.6 KiB
Java

package tc.oc.commons.bukkit.commands;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import com.sk89q.minecraft.util.commands.CommandContext;
import com.sk89q.minecraft.util.commands.CommandException;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import tc.oc.api.bukkit.users.BukkitUserStore;
import tc.oc.api.bukkit.users.OnlinePlayers;
import tc.oc.api.docs.User;
import tc.oc.api.exceptions.NotFound;
import tc.oc.api.minecraft.MinecraftService;
import tc.oc.api.users.UserSearchRequest;
import tc.oc.api.users.UserSearchResponse;
import tc.oc.api.users.UserService;
import tc.oc.commons.bukkit.nick.IdentityProvider;
import tc.oc.commons.bukkit.users.PlayerSearchResponse;
import tc.oc.commons.core.commands.TranslatableCommandException;
import tc.oc.commons.core.util.Orderable;
import tc.oc.minecraft.scheduler.MainThreadExecutor;
@Singleton
public class UserFinder {
public enum Default implements Orderable<Default> { NULL, THROW, SENDER }
public enum Scope implements Orderable<Scope> { LOCAL, ONLINE, ALL }
private final MinecraftService minecraftService;
private final UserService userService;
private final BukkitUserStore userStore;
private final IdentityProvider identityProvider;
private final MainThreadExecutor mainThreadExecutor;
private final OnlinePlayers onlinePlayers;
@Inject UserFinder(MinecraftService minecraftService, UserService userService, BukkitUserStore userStore, IdentityProvider identityProvider, MainThreadExecutor mainThreadExecutor, OnlinePlayers onlinePlayers) {
this.minecraftService = minecraftService;
this.userService = userService;
this.userStore = userStore;
this.identityProvider = identityProvider;
this.mainThreadExecutor = mainThreadExecutor;
this.onlinePlayers = onlinePlayers;
}
public Player senderToPlayer(CommandSender sender) throws CommandException {
if(sender instanceof Player) {
return (Player) sender;
} else {
throw new TranslatableCommandException("command.onlyPlayers");
}
}
public @Nullable User getLocalUser(CommandSender sender) {
if(sender instanceof Player) {
return userStore.getUser((Player) sender);
}
return null;
}
public @Nullable Player getLocalPlayer(CommandSender sender, String username) {
return sender.getServer().getPlayerExact(username, sender);
}
public UserSearchResponse localUserResponse(CommandSender viewer, Player player) {
return new UserSearchResponse(
userStore.getUser(player),
true,
identityProvider.currentIdentity(player).isDisguised(viewer),
userStore.getSession(player),
minecraftService.getLocalServer()
);
}
public PlayerSearchResponse localPlayerResponse(CommandSender viewer, Player player) {
return new PlayerSearchResponse(
localUserResponse(viewer, player),
player
);
}
public ListenableFuture<UserSearchResponse> findUser(final CommandSender sender, @Nullable String name, Scope scope, Default def) {
try {
if(name != null) {
final Player player = getLocalPlayer(sender, name);
if(player != null) {
return Futures.immediateFuture(localUserResponse(sender, player));
}
if(scope.noGreaterThan(Scope.LOCAL)) {
throw new TranslatableCommandException("command.playerNotFound");
}
final SettableFuture<UserSearchResponse> result = SettableFuture.create();
Futures.addCallback(
userService.search(new UserSearchRequest(name, getLocalUser(sender))),
new FutureCallback<UserSearchResponse>() {
@Override
public void onSuccess(@Nullable UserSearchResponse response) {
if(!response.online && scope.noGreaterThan(Scope.ONLINE)) {
result.setException(new TranslatableCommandException("command.playerNotFound"));
} else {
result.set(response);
}
}
@Override
public void onFailure(Throwable e) {
if(e instanceof NotFound) {
result.setException(new TranslatableCommandException("command.playerNotFound"));
} else {
result.setException(e);
}
}
}
);
return result;
} else {
switch(def) {
case NULL:
return Futures.immediateFuture(null);
case SENDER:
return Futures.immediateFuture(localUserResponse(sender, senderToPlayer(sender)));
default:
throw new TranslatableCommandException("command.specifyPlayer");
}
}
} catch(CommandException e) {
return Futures.immediateFailedFuture(e);
}
}
public ListenableFuture<PlayerSearchResponse> findPlayer(CommandSender sender, @Nullable String name, Scope scope, Default def) {
try {
if (name == null || name.isEmpty()) {
switch(def) {
case NULL:
return Futures.immediateFuture(null);
case SENDER:
return Futures.immediateFuture(localPlayerResponse(sender, senderToPlayer(sender)));
default:
throw new TranslatableCommandException("command.specifyPlayer");
}
}
final Player player = getLocalPlayer(sender, name);
if(player != null) {
return Futures.immediateFuture(localPlayerResponse(sender, player));
}
if(scope.noGreaterThan(Scope.LOCAL)) {
throw new TranslatableCommandException("command.playerNotFound");
}
final SettableFuture<PlayerSearchResponse> playerResult = SettableFuture.create();
mainThreadExecutor.callback(
findUser(sender, name, scope, def),
new FutureCallback<UserSearchResponse>() {
@Override
public void onSuccess(@Nullable UserSearchResponse userResult) {
playerResult.set(new PlayerSearchResponse(userResult, onlinePlayers.find(userResult.user)));
}
@Override
public void onFailure(Throwable t) {
playerResult.setException(t);
}
}
);
return playerResult;
} catch(CommandException e) {
return Futures.immediateFailedFuture(e);
}
}
// findUser overloads
public ListenableFuture<UserSearchResponse> findUser(final CommandSender sender, CommandContext args, int index, Scope scope, Default def) {
return findUser(sender, args.getString(index, null), scope, def);
}
public ListenableFuture<UserSearchResponse> findUser(final CommandSender sender, CommandContext args, int index, Default def) {
return findUser(sender, args, index, Scope.ALL, def);
}
public ListenableFuture<UserSearchResponse> findUser(final CommandSender sender, CommandContext args, int index, Scope scope) {
return findUser(sender, args.getString(index, null), scope, Default.THROW);
}
public ListenableFuture<UserSearchResponse> findUser(final CommandSender sender, CommandContext args, int index) {
return findUser(sender, args, index, Scope.ALL, Default.THROW);
}
// findPlayer overloads
public ListenableFuture<PlayerSearchResponse> findPlayer(CommandSender sender, CommandContext args, int index, Scope scope, Default def) {
return findPlayer(sender, args.getString(index, null), scope, def);
}
public ListenableFuture<PlayerSearchResponse> findPlayer(CommandSender sender, CommandContext args, int index, Scope scope) {
return findPlayer(sender, args, index, scope, Default.THROW);
}
public ListenableFuture<PlayerSearchResponse> findPlayer(CommandSender sender, CommandContext args, int index, Default def) {
return findPlayer(sender, args, index, Scope.ALL, def);
}
public ListenableFuture<PlayerSearchResponse> findPlayer(CommandSender sender, CommandContext args, int index) {
return findPlayer(sender, args, index, Scope.ALL, Default.THROW);
}
// Special cases
public ListenableFuture<PlayerSearchResponse> findLocalPlayer(CommandSender sender, CommandContext args, int index, Default def) {
return findPlayer(sender, args, index, Scope.LOCAL, def);
}
public ListenableFuture<PlayerSearchResponse> findLocalPlayer(CommandSender sender, CommandContext args, int index) {
return findLocalPlayer(sender, args, index, Default.THROW);
}
public ListenableFuture<PlayerSearchResponse> findLocalPlayerOrSender(CommandSender sender, CommandContext args, int index) {
return findLocalPlayer(sender, args, index, Default.SENDER);
}
}