Even more javadoc

This commit is contained in:
Marc Baloup 2022-08-10 19:25:06 +02:00
parent 54bc8ab99a
commit b6fc3c2b61
Signed by: marcbal
GPG Key ID: BBC0FE3ABC30B893
6 changed files with 656 additions and 394 deletions

View File

@ -5,6 +5,9 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
import fr.pandacube.lib.players.standalone.AbstractOffPlayer; import fr.pandacube.lib.players.standalone.AbstractOffPlayer;
/**
* Represents any player on a Bungeecord proxy, either offline or online.
*/
public interface BungeeOffPlayer extends AbstractOffPlayer { public interface BungeeOffPlayer extends AbstractOffPlayer {
/* /*
@ -12,8 +15,8 @@ public interface BungeeOffPlayer extends AbstractOffPlayer {
*/ */
/** /**
* @return l'instance Bungee du joueur en ligne, ou null si il n'est pas en * Returns the {@link ProxiedPlayer} instance of this player, or null if not available (offline).
* ligne * @return the {@link ProxiedPlayer} instance of this player, or null if not available (offline).
*/ */
default ProxiedPlayer getBungeeProxiedPlayer() { default ProxiedPlayer getBungeeProxiedPlayer() {
return ProxyServer.getInstance().getPlayer(getUniqueId()); return ProxyServer.getInstance().getPlayer(getUniqueId());

View File

@ -23,6 +23,9 @@ import fr.pandacube.lib.players.standalone.AbstractOnlinePlayer;
import fr.pandacube.lib.reflect.Reflect; import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.util.MinecraftVersion; import fr.pandacube.lib.util.MinecraftVersion;
/**
* Represents any online player on a Bungeecord proxy.
*/
public interface BungeeOnlinePlayer extends BungeeOffPlayer, AbstractOnlinePlayer { public interface BungeeOnlinePlayer extends BungeeOffPlayer, AbstractOnlinePlayer {
/* /*
@ -41,24 +44,24 @@ public interface BungeeOnlinePlayer extends BungeeOffPlayer, AbstractOnlinePlaye
return getServer().getInfo().getName(); return getServer().getInfo().getName();
} }
/**
* Returns the server on which the player is.
* @return the server on which the player is.
*/
default Server getServer() { default Server getServer() {
return getBungeeProxiedPlayer().getServer(); return getBungeeProxiedPlayer().getServer();
} }
/**
* Gets the minecraft version of this players client.
* @return the minecraft version of this players client.
*/
default MinecraftVersion getMinecraftVersion() { default MinecraftVersion getMinecraftVersion() {
return MinecraftVersion.getVersion(getBungeeProxiedPlayer().getPendingConnection().getVersion()); return MinecraftVersion.getVersion(getBungeeProxiedPlayer().getPendingConnection().getVersion());
} }
/*
* Related class instances
*/
@Override
ProxiedPlayer getBungeeProxiedPlayer();
@ -95,11 +98,17 @@ public interface BungeeOnlinePlayer extends BungeeOffPlayer, AbstractOnlinePlaye
@Override @Override
default void sendMessage(Component message, Identified sender) { default void sendMessage(Component message, Identified sender) {
getBungeeProxiedPlayer().sendMessage(sender.identity().uuid(), Chat.toBungee(message)); getBungeeProxiedPlayer().sendMessage(sender == null ? null : sender.identity().uuid(), Chat.toBungee(message));
} }
/**
* Display the provided message in the players chat, if they allows to display CHAT messages.
* @param message the message to send.
* @param sender the player causing the send of this message. Client side filtering may occur. May be null if we
* dont want client filtering, but still consider the message as CHAT message.
*/
default void sendMessage(ComponentLike message, ProxiedPlayer sender) { default void sendMessage(ComponentLike message, ProxiedPlayer sender) {
getBungeeProxiedPlayer().sendMessage(sender.getUniqueId(), Chat.toBungee(message.asComponent())); getBungeeProxiedPlayer().sendMessage(sender == null ? null : sender.getUniqueId(), Chat.toBungee(message.asComponent()));
} }
@Override @Override
@ -134,9 +143,17 @@ public interface BungeeOnlinePlayer extends BungeeOffPlayer, AbstractOnlinePlaye
return new BungeeClientOptions(this); return new BungeeClientOptions(this);
} }
/**
* Provides various configuration values of the Minecraft client.
*/
class BungeeClientOptions implements AbstractOnlinePlayer.ClientOptions { class BungeeClientOptions implements AbstractOnlinePlayer.ClientOptions {
private final BungeeOnlinePlayer op; private final BungeeOnlinePlayer op;
/**
* Create a new instance of {@link BungeeClientOptions}.
* @param op the {@link BungeeOnlinePlayer} instance.
*/
public BungeeClientOptions(BungeeOnlinePlayer op) { public BungeeClientOptions(BungeeOnlinePlayer op) {
this.op = op; this.op = op;
} }
@ -155,6 +172,10 @@ public interface BungeeOnlinePlayer extends BungeeOffPlayer, AbstractOnlinePlaye
return op.getBungeeProxiedPlayer().hasChatColors(); return op.getBungeeProxiedPlayer().hasChatColors();
} }
/**
* Gets the chat visibility configuration.
* @return the chat visibility configuration.
*/
public ChatMode getChatMode() { public ChatMode getChatMode() {
return op.getBungeeProxiedPlayer().getChatMode(); return op.getBungeeProxiedPlayer().getChatMode();
} }
@ -185,6 +206,10 @@ public interface BungeeOnlinePlayer extends BungeeOffPlayer, AbstractOnlinePlaye
return op.getBungeeProxiedPlayer().getViewDistance(); return op.getBungeeProxiedPlayer().getViewDistance();
} }
/**
* Gets the players main hand.
* @return the players main hand.
*/
public MainHand getMainHand() { public MainHand getMainHand() {
return op.getBungeeProxiedPlayer().getMainHand(); return op.getBungeeProxiedPlayer().getMainHand();
} }
@ -211,6 +236,10 @@ public interface BungeeOnlinePlayer extends BungeeOffPlayer, AbstractOnlinePlaye
return settings != null && settings.isAllowServerListing(); return settings != null && settings.isAllowServerListing();
} }
/**
* Gets the players skin configuration.
* @return the players skin configuration.
*/
public SkinConfiguration getSkinParts() { public SkinConfiguration getSkinParts() {
return op.getBungeeProxiedPlayer().getSkinParts(); return op.getBungeeProxiedPlayer().getSkinParts();
} }

View File

@ -54,6 +54,13 @@
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-paper-permissions</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<!-- Paper --> <!-- Paper -->
<dependency> <dependency>
<groupId>io.papermc.paper</groupId> <groupId>io.papermc.paper</groupId>

View File

@ -2,12 +2,9 @@ package fr.pandacube.lib.paper.commands;
import com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource; import com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.LiteralMessage;
import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.context.ParsedCommandNode;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import com.mojang.brigadier.suggestion.SuggestionProvider; import com.mojang.brigadier.suggestion.SuggestionProvider;
import com.mojang.brigadier.tree.CommandNode; import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.LiteralCommandNode; import com.mojang.brigadier.tree.LiteralCommandNode;
@ -15,7 +12,7 @@ import com.mojang.brigadier.tree.RootCommandNode;
import fr.pandacube.lib.chat.Chat; import fr.pandacube.lib.chat.Chat;
import fr.pandacube.lib.commands.BrigadierCommand; import fr.pandacube.lib.commands.BrigadierCommand;
import fr.pandacube.lib.commands.SuggestionsSupplier; import fr.pandacube.lib.commands.SuggestionsSupplier;
import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftNamespacedKey; import fr.pandacube.lib.paper.permissions.PandalibPaperPermissions;
import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftServer; import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftServer;
import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftVector; import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftVector;
import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.VanillaCommandWrapper; import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.VanillaCommandWrapper;
@ -25,18 +22,14 @@ import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.ComponentArgume
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.Coordinates; import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.Coordinates;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.EntityArgument; import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.EntityArgument;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.EntitySelector; import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.EntitySelector;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.GameProfileArgument;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.ResourceLocationArgument;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.Vec3Argument; import fr.pandacube.lib.paper.reflect.wrapper.minecraft.commands.Vec3Argument;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.core.BlockPos; import fr.pandacube.lib.paper.reflect.wrapper.minecraft.core.BlockPos;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.resources.ResourceLocation;
import fr.pandacube.lib.paper.reflect.wrapper.minecraft.server.ServerPlayer; import fr.pandacube.lib.paper.reflect.wrapper.minecraft.server.ServerPlayer;
import fr.pandacube.lib.paper.reflect.wrapper.paper.PaperAdventure; import fr.pandacube.lib.paper.reflect.wrapper.paper.PaperAdventure;
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper; import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
import fr.pandacube.lib.util.Log; import fr.pandacube.lib.util.Log;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandMap; import org.bukkit.command.CommandMap;
@ -55,16 +48,17 @@ import org.bukkit.util.BlockVector;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
/**
* Abstract class to hold a command to be integrated into a Paper server vanilla command dispatcher.
*/
public abstract class PaperBrigadierCommand extends BrigadierCommand<BukkitBrigadierCommandSource> implements Listener { public abstract class PaperBrigadierCommand extends BrigadierCommand<BukkitBrigadierCommandSource> implements Listener {
protected static final Commands vanillaCommandDispatcher; private static final Commands vanillaCommandDispatcher;
private static final CommandDispatcher<BukkitBrigadierCommandSource> nmsDispatcher; private static final CommandDispatcher<BukkitBrigadierCommandSource> nmsDispatcher;
static { static {
@ -74,27 +68,90 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<BukkitBriga
nmsDispatcher = vanillaCommandDispatcher.dispatcher(); nmsDispatcher = vanillaCommandDispatcher.dispatcher();
} }
/**
* Removes a plugin command that overrides a vanilla command, so the vanilla command functionnalities are fully
* restored (so, not only the usage, but also the suggestions and the command structure sent to the client).
* @param name the name of the command to restore.
*/
public static void restoreVanillaCommand(String name) {
CommandMap bukkitCmdMap = Bukkit.getCommandMap();
Command bukkitCommand = bukkitCmdMap.getCommand(name);
if (bukkitCommand != null) {
if (VanillaCommandWrapper.REFLECT.get().isInstance(bukkitCommand)) {
Log.info("Command /" + name + " is already a vanilla command.");
return;
}
Log.info("Removing Bukkit command /" + name + " (" + getCommandIdentity(bukkitCommand) + ")");
bukkitCmdMap.getKnownCommands().remove(name.toLowerCase(java.util.Locale.ENGLISH));
bukkitCommand.unregister(bukkitCmdMap);
LiteralCommandNode<BukkitBrigadierCommandSource> node = (LiteralCommandNode<BukkitBrigadierCommandSource>) getRootNode().getChild(name);
Command newCommand = new VanillaCommandWrapper(vanillaCommandDispatcher, node).__getRuntimeInstance();
bukkitCmdMap.getKnownCommands().put(name.toLowerCase(), newCommand);
newCommand.register(bukkitCmdMap);
}
}
/**
* Returns the vanilla instance of the Brigadier dispatcher.
* @return the vanilla instance of the Brigadier dispatcher.
*/
public static CommandDispatcher<BukkitBrigadierCommandSource> getNMSDispatcher() { public static CommandDispatcher<BukkitBrigadierCommandSource> getNMSDispatcher() {
return nmsDispatcher; return nmsDispatcher;
} }
/**
* Returns the root command node of the Brigadier dispatcher.
* @return the root command node of the Brigadier dispatcher.
*/
protected static RootCommandNode<BukkitBrigadierCommandSource> getRootNode() { protected static RootCommandNode<BukkitBrigadierCommandSource> getRootNode() {
return nmsDispatcher.getRoot(); return nmsDispatcher.getRoot();
} }
protected Plugin plugin;
protected LiteralCommandNode<BukkitBrigadierCommandSource> commandNode;
private Set<String> allAliases;
public PaperBrigadierCommand(Plugin pl) {
private final Plugin plugin;
/**
* The command node of this command.
*/
protected final LiteralCommandNode<BukkitBrigadierCommandSource> commandNode;
private final RegistrationPolicy registrationPolicy;
private Set<String> registeredAliases;
/**
* Instanciate this command isntance.
*
* @param pl the plugin instance.
* @param regPolicy the registration policy for this command.
*/
public PaperBrigadierCommand(Plugin pl, RegistrationPolicy regPolicy) {
plugin = pl; plugin = pl;
registrationPolicy = regPolicy;
commandNode = buildCommand().build(); commandNode = buildCommand().build();
postBuildCommand(commandNode); postBuildCommand(commandNode);
register(); register();
Bukkit.getPluginManager().registerEvents(this, plugin); Bukkit.getPluginManager().registerEvents(this, plugin);
try {
PandalibPaperPermissions.addPermissionMapping("minecraft.command." + commandNode.getLiteral().toLowerCase(), getTargetPermission().toLowerCase());
} catch (NoClassDefFoundError ignored) { }
}
/**
* Instanciate this command isntance with a registration policy of {@link RegistrationPolicy#ONLY_BASE_COMMAND}.
* @param pl the plugin instance.
*/
public PaperBrigadierCommand(Plugin pl) {
this(pl, RegistrationPolicy.ONLY_BASE_COMMAND);
} }
@ -108,7 +165,7 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<BukkitBriga
String pluginName = plugin.getName().toLowerCase(); String pluginName = plugin.getName().toLowerCase();
allAliases = new HashSet<>(); registeredAliases = new HashSet<>();
registerNode(commandNode, false); registerNode(commandNode, false);
registerAlias(pluginName + ":" + commandNode.getLiteral(), true); registerAlias(pluginName + ":" + commandNode.getLiteral(), true);
@ -132,8 +189,12 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<BukkitBriga
private void registerNode(LiteralCommandNode<BukkitBrigadierCommandSource> node, boolean prefixed) { private void registerNode(LiteralCommandNode<BukkitBrigadierCommandSource> node, boolean prefixed) {
RootCommandNode<BukkitBrigadierCommandSource> root = getRootNode(); RootCommandNode<BukkitBrigadierCommandSource> root = getRootNode();
String name = node.getLiteral(); String name = node.getLiteral();
//boolean isAlias = node.getRedirect() == commandNode; boolean isAlias = node.getRedirect() == commandNode;
boolean forceRegistration = true;//prefixed || !isAlias; boolean forceRegistration = switch (registrationPolicy) {
case NONE -> false;
case ONLY_BASE_COMMAND -> prefixed || !isAlias;
case ALL -> true;
};
// nmsDispatcher integration and conflit resolution // nmsDispatcher integration and conflit resolution
boolean nmsRegister = false, nmsRegistered = false; boolean nmsRegister = false, nmsRegistered = false;
@ -141,8 +202,7 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<BukkitBriga
if (nmsConflited != null) { if (nmsConflited != null) {
if (isFromThisCommand(nmsConflited)) { if (isFromThisCommand(nmsConflited)) {
// this command is already registered in NMS // this command is already registered in NMS. Dont need to register again
// dont need to register again
nmsRegistered = true; nmsRegistered = true;
} }
else if (forceRegistration) { else if (forceRegistration) {
@ -170,17 +230,15 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<BukkitBriga
return; return;
} }
allAliases.add(name); registeredAliases.add(name);
// bukkit dispatcher conflict resolution // bukkit dispatcher conflict resolution
boolean bukkitRegister = false; boolean bukkitRegister = false;
CommandMap bukkitCmdMap = Bukkit.getCommandMap(); CommandMap bukkitCmdMap = Bukkit.getCommandMap();
Command bukkitConflicted = bukkitCmdMap.getCommand(name); Command bukkitConflicted = bukkitCmdMap.getCommand(name);
if (bukkitConflicted != null) { if (bukkitConflicted != null) {
if (isFromThisCommand(bukkitConflicted)) { if (!isFromThisCommand(bukkitConflicted)) {
// nothing to do, already good if (forceRegistration) {
}
else if (forceRegistration) {
bukkitRegister = true; bukkitRegister = true;
Log.info("Overwriting Bukkit command /" + name Log.info("Overwriting Bukkit command /" + name
+ " (" + getCommandIdentity(bukkitConflicted) + ")"); + " (" + getCommandIdentity(bukkitConflicted) + ")");
@ -191,6 +249,7 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<BukkitBriga
+ "). Wont replace it because registration is not forced."); + "). Wont replace it because registration is not forced.");
} }
} }
}
else { else {
bukkitRegister = true; bukkitRegister = true;
} }
@ -218,7 +277,7 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<BukkitBriga
return false; return false;
} }
protected static String getCommandIdentity(Command bukkitCmd) { private static String getCommandIdentity(Command bukkitCmd) {
if (bukkitCmd instanceof PluginCommand cmd) { if (bukkitCmd instanceof PluginCommand cmd) {
return "Bukkit command: /" + cmd.getName() + " from plugin " + cmd.getPlugin().getName(); return "Bukkit command: /" + cmd.getName() + " from plugin " + cmd.getPlugin().getName();
} }
@ -230,14 +289,20 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<BukkitBriga
} }
/**
* Player command sender event handler.
* @param event the event.
*/
@EventHandler @EventHandler
public void onPlayerCommandSend(PlayerCommandSendEvent event) { public void onPlayerCommandSend(PlayerCommandSendEvent event) {
event.getCommands().removeAll(allAliases.stream().map(s -> "minecraft:" + s).toList()); event.getCommands().removeAll(registeredAliases.stream().map(s -> "minecraft:" + s).toList());
} }
/**
* Server load event handler.
* @param event the event.
*/
@EventHandler @EventHandler
public void onServerLoad(ServerLoadEvent event) { public void onServerLoad(ServerLoadEvent event) {
register(); register();
@ -245,8 +310,19 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<BukkitBriga
/** /**
* The permission that should be tested instead of "minecraft.command.cmdName". * Returns the permission that should be tested instead of "minecraft.command.cmdName". The conversion from the
* minecraft prefixed permission node to the returned node is done by the {@code pandalib-paper-permissions} if it
* is present in the classpath during runtime.
* @return the permission that should be tested instead of "minecraft.command.cmdName".
*/ */
protected abstract String getTargetPermission(); protected abstract String getTargetPermission();
@ -274,51 +350,82 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<BukkitBriga
} }
/**
* Tells if the provided command sender is the console.
* @param sender the sender to test if its the console or not.
* @return true if the sender is the console, false otherwise.
*/
public boolean isConsole(CommandSender sender) { public boolean isConsole(CommandSender sender) {
return sender instanceof ConsoleCommandSender; return sender instanceof ConsoleCommandSender;
} }
/**
* Tells if the provided command sender is a player.
* @param sender the sender to test if its a player or not.
* @return true if the sender is a player, false otherwise.
*/
public boolean isPlayer(CommandSender sender) { public boolean isPlayer(CommandSender sender) {
return sender instanceof Player; return sender instanceof Player;
} }
/**
* Gets the Bukkit command sender from the provided context.
* @param context the command context from which to get the Bukkit command sender.
* @return the Bukkit command sender.
*/
public static CommandSender getCommandSender(CommandContext<BukkitBrigadierCommandSource> context) { public static CommandSender getCommandSender(CommandContext<BukkitBrigadierCommandSource> context) {
return getCommandSender(context.getSource()); return getCommandSender(context.getSource());
} }
/**
* Gets the Bukkit command sender from the provided wrapper.
* @param wrapper the wrapper from which to get the Bukkit command sender.
* @return the Bukkit command sender.
*/
public static CommandSender getCommandSender(BukkitBrigadierCommandSource wrapper) { public static CommandSender getCommandSender(BukkitBrigadierCommandSource wrapper) {
return wrapper.getBukkitSender(); return wrapper.getBukkitSender();
} }
/**
* Gets a new instance of a command sender wrapper for the provided command sender.
* @param sender the command sender.
* @return a new instance of a command sender wrapper for the provided command sender.
*/
public static BukkitBrigadierCommandSource getBrigadierCommandSource(CommandSender sender) { public static BukkitBrigadierCommandSource getBrigadierCommandSource(CommandSender sender) {
return VanillaCommandWrapper.getListener(sender); return VanillaCommandWrapper.getListener(sender);
} }
/**
* A suggestions supplier that suggests the names of the worlds currently loaded on this server.
*/
public static final SuggestionsSupplier<CommandSender> TAB_WORLDS = SuggestionsSupplier.fromStreamSupplier(() -> Bukkit.getWorlds().stream().map(World::getName));
/**
* Wraps the provided {@link SuggestionsSupplier} into a Brigadiers {@link SuggestionProvider}.
* @param suggestions the suggestions to wrap.
* @return a {@link SuggestionProvider} generating the suggestions from the provided {@link SuggestionsSupplier}.
*/
public static final SuggestionsSupplier<CommandSender> TAB_WORLDS = (s, ti, token, a) -> SuggestionsSupplier.collectFilteredStream(Bukkit.getWorlds().stream().map(World::getName), token);
protected SuggestionProvider<BukkitBrigadierCommandSource> wrapSuggestions(SuggestionsSupplier<CommandSender> suggestions) { protected SuggestionProvider<BukkitBrigadierCommandSource> wrapSuggestions(SuggestionsSupplier<CommandSender> suggestions) {
return wrapSuggestions(suggestions, PaperBrigadierCommand::getCommandSender); return wrapSuggestions(suggestions, PaperBrigadierCommand::getCommandSender);
} }
/**
* Wraps the provided brigadier command executor in another one that logs eventual throw exceptions and informs the
* player.
* The default behaviour of the vanilla instance of the Brigadier dispatcher is to ignore any unchecked exception
* thrown by a command executor.
* @param cmd the command executor to wrap.
* @return a wrapper command executor.
*/
protected static com.mojang.brigadier.Command<BukkitBrigadierCommandSource> wrapCommand(com.mojang.brigadier.Command<BukkitBrigadierCommandSource> cmd) { protected static com.mojang.brigadier.Command<BukkitBrigadierCommandSource> wrapCommand(com.mojang.brigadier.Command<BukkitBrigadierCommandSource> cmd) {
return context -> { return context -> {
try { try {
@ -327,7 +434,7 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<BukkitBriga
throw e; throw e;
} catch (Throwable t) { } catch (Throwable t) {
Log.severe(t); Log.severe(t);
getCommandSender(context).sendMessage(Chat.failureText("Error while using the command: " + t)); getCommandSender(context).sendMessage(Chat.failureText("Error while executing the command: " + t));
return 0; return 0;
} }
}; };
@ -345,6 +452,12 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<BukkitBriga
*/ */
/**
* Creates a new instance of the Brigadier argument type {@code minecraft:entity}.
* @param singleTarget if this argument takes only a single target.
* @param playersOnly if this argument takes players only.
* @return the {@code minecraft:entity} argument type with the specified parameters.
*/
public static ArgumentType<Object> argumentMinecraftEntity(boolean singleTarget, boolean playersOnly) { public static ArgumentType<Object> argumentMinecraftEntity(boolean singleTarget, boolean playersOnly) {
if (playersOnly) { if (playersOnly) {
return singleTarget ? EntityArgument.player() : EntityArgument.players(); return singleTarget ? EntityArgument.player() : EntityArgument.players();
@ -354,6 +467,12 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<BukkitBriga
} }
} }
/**
* Gets the value of the provided argument of type {@code minecraft:entity} (list of entities), from the provided context.
* @param context the command execution context.
* @param argument the argument name.
* @return the value of the argument, or null if not found.
*/
public List<Entity> tryGetMinecraftEntityArgument(CommandContext<BukkitBrigadierCommandSource> context, String argument) { public List<Entity> tryGetMinecraftEntityArgument(CommandContext<BukkitBrigadierCommandSource> context, String argument) {
EntitySelector es = ReflectWrapper.wrap(tryGetArgument(context, argument, EntitySelector.MAPPING.runtimeClass()), EntitySelector.class); EntitySelector es = ReflectWrapper.wrap(tryGetArgument(context, argument, EntitySelector.MAPPING.runtimeClass()), EntitySelector.class);
if (es == null) if (es == null)
@ -366,6 +485,12 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<BukkitBriga
return entityList; return entityList;
} }
/**
* Gets the value of the provided argument of type {@code minecraft:entity} (list of players), from the provided context.
* @param context the command execution context.
* @param argument the argument name.
* @return the value of the argument, or null if not found.
*/
public List<Player> tryGetMinecraftEntityArgumentPlayers(CommandContext<BukkitBrigadierCommandSource> context, String argument) { public List<Player> tryGetMinecraftEntityArgumentPlayers(CommandContext<BukkitBrigadierCommandSource> context, String argument) {
EntitySelector es = ReflectWrapper.wrap(tryGetArgument(context, argument, EntitySelector.MAPPING.runtimeClass()), EntitySelector.class); EntitySelector es = ReflectWrapper.wrap(tryGetArgument(context, argument, EntitySelector.MAPPING.runtimeClass()), EntitySelector.class);
if (es == null) if (es == null)
@ -378,6 +503,12 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<BukkitBriga
return playerList; return playerList;
} }
/**
* Gets the value of the provided argument of type {@code minecraft:entity} (one entity), from the provided context.
* @param context the command execution context.
* @param argument the argument name.
* @return the value of the argument, or null if not found.
*/
public Entity tryGetMinecraftEntityArgumentOneEntity(CommandContext<BukkitBrigadierCommandSource> context, String argument) { public Entity tryGetMinecraftEntityArgumentOneEntity(CommandContext<BukkitBrigadierCommandSource> context, String argument) {
EntitySelector es = ReflectWrapper.wrap(tryGetArgument(context, argument, EntitySelector.MAPPING.runtimeClass()), EntitySelector.class); EntitySelector es = ReflectWrapper.wrap(tryGetArgument(context, argument, EntitySelector.MAPPING.runtimeClass()), EntitySelector.class);
if (es == null) if (es == null)
@ -386,6 +517,12 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<BukkitBriga
return nmsEntity == null ? null : nmsEntity.getBukkitEntity(); return nmsEntity == null ? null : nmsEntity.getBukkitEntity();
} }
/**
* Gets the value of the provided argument of type {@code minecraft:entity} (one player), from the provided context.
* @param context the command execution context.
* @param argument the argument name.
* @return the value of the argument, or null if not found.
*/
public Player tryGetMinecraftEntityArgumentOnePlayer(CommandContext<BukkitBrigadierCommandSource> context, String argument) { public Player tryGetMinecraftEntityArgumentOnePlayer(CommandContext<BukkitBrigadierCommandSource> context, String argument) {
EntitySelector es = ReflectWrapper.wrap(tryGetArgument(context, argument, EntitySelector.MAPPING.runtimeClass()), EntitySelector.class); EntitySelector es = ReflectWrapper.wrap(tryGetArgument(context, argument, EntitySelector.MAPPING.runtimeClass()), EntitySelector.class);
if (es == null) if (es == null)
@ -397,64 +534,109 @@ public abstract class PaperBrigadierCommand extends BrigadierCommand<BukkitBriga
public static ArgumentType<Object> argumentMinecraftGameProfile() {
return GameProfileArgument.gameProfile();
/**
* Creates a new instance of the Brigadier argument type {@code minecraft:block_pos}.
* @return the {@code minecraft:block_pos} argument type.
*/
public static ArgumentType<Object> argumentMinecraftBlockPosition() {
return BlockPosArgument.blockPos();
}
/**
* Gets the value of the provided argument of type {@code minecraft:block_pos}, from the provided context.
* @param context the command execution context.
* @param argument the argument name.
* @param deflt a defualt value if the argument is not found.
* @return the value of the argument.
*/
public BlockVector tryGetMinecraftBlockPositionArgument(CommandContext<BukkitBrigadierCommandSource> context,
String argument, BlockVector deflt) {
return tryGetArgument(context, argument, Coordinates.MAPPING.runtimeClass(), nmsCoord -> {
BlockPos bp = ReflectWrapper.wrap(nmsCoord, Coordinates.class).getBlockPos(context.getSource());
return new BlockVector(bp.getX(), bp.getY(), bp.getZ());
}, deflt);
} }
public static ArgumentType<Object> argumentMinecraftResourceLocation() {
return ResourceLocationArgument.id(); /**
* Creates a new instance of the Brigadier argument type {@code minecraft:vec3}.
* @return the {@code minecraft:vec3} argument type.
*/
public static ArgumentType<Object> argumentMinecraftVec3() {
return Vec3Argument.vec3(true);
} }
public NamespacedKey tryGetMinecraftResourceLocationArgument(CommandContext<BukkitBrigadierCommandSource> context, String argument, NamespacedKey deflt) {
return tryGetArgument(context, argument, ResourceLocation.MAPPING.runtimeClass(), /**
nmsKey -> CraftNamespacedKey.fromMinecraft(ReflectWrapper.wrap(nmsKey, ResourceLocation.class)), * Gets the value of the provided argument of type {@code minecraft:vec3}, from the provided context.
* @param context the command execution context.
* @param argument the argument name.
* @param deflt a defualt value if the argument is not found.
* @return the value of the argument.
*/
public Vector tryGetMinecraftVec3Argument(CommandContext<BukkitBrigadierCommandSource> context, String argument,
Vector deflt) {
return tryGetArgument(context, argument, Coordinates.MAPPING.runtimeClass(),
nmsCoord -> CraftVector.toBukkit(
ReflectWrapper.wrap(nmsCoord, Coordinates.class).getPosition(context.getSource())
),
deflt); deflt);
} }
public static ArgumentType<Object> argumentMinecraftBlockPosition() { /**
return BlockPosArgument.blockPos(); * Creates a new instance of the Brigadier argument type {@code minecraft:component}.
} * @return the {@code minecraft:component} argument type.
public BlockVector tryGetMinecraftBlockPositionArgument(CommandContext<BukkitBrigadierCommandSource> context, String argument) { */
Coordinates coord = ReflectWrapper.wrap(tryGetArgument(context, argument, Coordinates.MAPPING.runtimeClass()), Coordinates.class);
if (coord == null)
return null;
BlockPos bp = coord.getBlockPos(context.getSource());
return new BlockVector(bp.getX(), bp.getY(), bp.getZ());
}
public static ArgumentType<Object> argumentMinecraftVec3() {
return Vec3Argument.vec3(true);
}
public Vector tryGetMinecraftVec3Argument(CommandContext<BukkitBrigadierCommandSource> context, String argument) {
Coordinates coord = ReflectWrapper.wrap(tryGetArgument(context, argument, Coordinates.MAPPING.runtimeClass()), Coordinates.class);
return coord == null ? null : CraftVector.toBukkit(coord.getPosition(context.getSource()));
}
public static ArgumentType<Object> argumentMinecraftChatComponent() { public static ArgumentType<Object> argumentMinecraftChatComponent() {
return ComponentArgument.textComponent(); return ComponentArgument.textComponent();
} }
public Component tryGetMinecraftChatComponentArgument(CommandContext<BukkitBrigadierCommandSource> context, String argument) {
var nmsComponent = ReflectWrapper.wrap( /**
tryGetArgument(context, argument, fr.pandacube.lib.paper.reflect.wrapper.minecraft.network.chat.Component.MAPPING.runtimeClass()), * Gets the value of the provided argument of type {@code minecraft:component}, from the provided context.
fr.pandacube.lib.paper.reflect.wrapper.minecraft.network.chat.Component.class * @param context the command execution context.
); * @param argument the argument name.
return nmsComponent == null ? null : PaperAdventure.asAdventure(nmsComponent); * @param deflt a defualt value if the argument is not found.
* @return the value of the argument.
*/
public Component tryGetMinecraftChatComponentArgument(CommandContext<BukkitBrigadierCommandSource> context,
String argument, Component deflt) {
return tryGetArgument(context, argument,
fr.pandacube.lib.paper.reflect.wrapper.minecraft.network.chat.Component.MAPPING.runtimeClass(),
nmsComp -> PaperAdventure.asAdventure(
ReflectWrapper.wrap(nmsComp,
fr.pandacube.lib.paper.reflect.wrapper.minecraft.network.chat.Component.class)
),
deflt);
} }
/**
* All possible choices on how to force the registration of a command, based on certain conditions.
*/
public enum RegistrationPolicy {
/**
* Do not force to register a command node or an alias if there is already a command with that name in the
* vanilla Brigadier dispatcher.
* Note that all plugin-name-prefixed aliases will be registered anyway.
*/
NONE,
/**
* Force only the base command (but not the aliases) to be registered, even if a command with that name already
* exists in the vanilla Brigadier dispatcher.
*/
ONLY_BASE_COMMAND,
/**
* Force the command and all of its aliases to be registered, even if a command with the same name or alias
* already exists in the vanilla Brigadier dispatcher.
*/
ALL
}
} }

View File

@ -7,6 +7,9 @@ import org.bukkit.scoreboard.Team;
import fr.pandacube.lib.players.standalone.AbstractOffPlayer; import fr.pandacube.lib.players.standalone.AbstractOffPlayer;
/**
* Represents any player on a paper server, either offline or online.
*/
public interface PaperOffPlayer extends AbstractOffPlayer { public interface PaperOffPlayer extends AbstractOffPlayer {
/* /*
@ -15,7 +18,7 @@ public interface PaperOffPlayer extends AbstractOffPlayer {
@Override @Override
default boolean isOnline() { default boolean isOnline() {
return (getBukkitPlayer() != null); return getBukkitPlayer() != null;
} }
@ -29,8 +32,8 @@ public interface PaperOffPlayer extends AbstractOffPlayer {
PaperOnlinePlayer getOnlineInstance(); PaperOnlinePlayer getOnlineInstance();
/** /**
* @return l'instance Bukkit du joueur en ligne, ou null si il n'est pas en * Returns the Bukkit online {@link Player} instance of this player, or null if not available (offline).
* ligne * @return the Bukkit online {@link Player} instance of this player, or null if not available (offline).
*/ */
default Player getBukkitPlayer() { default Player getBukkitPlayer() {
return Bukkit.getPlayer(getUniqueId()); return Bukkit.getPlayer(getUniqueId());

View File

@ -1,11 +1,9 @@
package fr.pandacub.lib.paper.players; package fr.pandacub.lib.paper.players;
import java.util.Locale;
import java.util.UUID;
import com.destroystokyo.paper.ClientOption; import com.destroystokyo.paper.ClientOption;
import com.destroystokyo.paper.ClientOption.ChatVisibility; import com.destroystokyo.paper.ClientOption.ChatVisibility;
import com.destroystokyo.paper.SkinParts; import com.destroystokyo.paper.SkinParts;
import fr.pandacube.lib.players.standalone.AbstractOnlinePlayer;
import net.kyori.adventure.audience.MessageType; import net.kyori.adventure.audience.MessageType;
import net.kyori.adventure.identity.Identified; import net.kyori.adventure.identity.Identified;
import net.kyori.adventure.identity.Identity; import net.kyori.adventure.identity.Identity;
@ -18,11 +16,14 @@ import org.bukkit.Location;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.Sound; import org.bukkit.Sound;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.MainHand; import org.bukkit.inventory.MainHand;
import fr.pandacube.lib.players.standalone.AbstractOnlinePlayer; import java.util.Locale;
import java.util.UUID;
/**
* Represents any online player on a paper server.
*/
public interface PaperOnlinePlayer extends PaperOffPlayer, AbstractOnlinePlayer { public interface PaperOnlinePlayer extends PaperOffPlayer, AbstractOnlinePlayer {
/* /*
@ -46,12 +47,6 @@ public interface PaperOnlinePlayer extends PaperOffPlayer, AbstractOnlinePlayer
* Related class instances * Related class instances
*/ */
/**
* @return l'instance Bukkit du joueur en ligne, ou null si il n'est pas en
* ligne
*/
Player getBukkitPlayer();
@Override @Override
default OfflinePlayer getBukkitOfflinePlayer() { default OfflinePlayer getBukkitOfflinePlayer() {
return getBukkitPlayer(); return getBukkitPlayer();
@ -100,10 +95,23 @@ public interface PaperOnlinePlayer extends PaperOffPlayer, AbstractOnlinePlayer
getBukkitPlayer().showTitle(Title.title(title, subtitle, Times.times(Ticks.duration(fadeIn), Ticks.duration(stay), Ticks.duration(fadeOut)))); getBukkitPlayer().showTitle(Title.title(title, subtitle, Times.times(Ticks.duration(fadeIn), Ticks.duration(stay), Ticks.duration(fadeOut))));
} }
/**
* Play a sound on this players client, sourced at this players location.
* @param sound the sound to play
* @param volume the volume of the sound.
* @param pitch the pich in which the sound is played.
*/
default void playSound(Sound sound, float volume, float pitch) { default void playSound(Sound sound, float volume, float pitch) {
playSound(sound, getBukkitPlayer().getLocation(), volume, pitch); playSound(sound, getBukkitPlayer().getLocation(), volume, pitch);
} }
/**
* Play a sound on this players client.
* @param sound the sound to play
* @param location the source location of the sound.
* @param volume the volume of the sound.
* @param pitch the pich in which the sound is played.
*/
default void playSound(Sound sound, Location location, float volume, float pitch) { default void playSound(Sound sound, Location location, float volume, float pitch) {
getBukkitPlayer().playSound(location, sound, volume, pitch); getBukkitPlayer().playSound(location, sound, volume, pitch);
} }
@ -119,11 +127,18 @@ public interface PaperOnlinePlayer extends PaperOffPlayer, AbstractOnlinePlayer
@Override @Override
PaperClientOptions getClientOptions(); PaperClientOptions getClientOptions();
/**
* Provides various configuration values of the Minecraft client.
*/
abstract class PaperClientOptions implements AbstractOnlinePlayer.ClientOptions { abstract class PaperClientOptions implements AbstractOnlinePlayer.ClientOptions {
private final PaperOnlinePlayer op; private final PaperOnlinePlayer op;
public PaperClientOptions(PaperOnlinePlayer op) { /**
* Create a new instance of {@link PaperClientOptions}.
* @param op the {@link PaperOnlinePlayer} instance.
*/
protected PaperClientOptions(PaperOnlinePlayer op) {
this.op = op; this.op = op;
} }
@ -132,6 +147,10 @@ public interface PaperOnlinePlayer extends PaperOffPlayer, AbstractOnlinePlayer
return op.getBukkitPlayer().getClientOption(ClientOption.CHAT_COLORS_ENABLED); return op.getBukkitPlayer().getClientOption(ClientOption.CHAT_COLORS_ENABLED);
} }
/**
* Gets the chat visibility configuration.
* @return the chat visibility configuration.
*/
public ChatVisibility getChatVisibility() { public ChatVisibility getChatVisibility() {
return op.getBukkitPlayer().getClientOption(ClientOption.CHAT_VISIBILITY); return op.getBukkitPlayer().getClientOption(ClientOption.CHAT_VISIBILITY);
} }
@ -162,6 +181,10 @@ public interface PaperOnlinePlayer extends PaperOffPlayer, AbstractOnlinePlayer
return op.getBukkitPlayer().getClientViewDistance(); return op.getBukkitPlayer().getClientViewDistance();
} }
/**
* Gets the players main hand.
* @return the players main hand.
*/
public MainHand getMainHand() { public MainHand getMainHand() {
return op.getBukkitPlayer().getMainHand(); return op.getBukkitPlayer().getMainHand();
} }
@ -177,13 +200,19 @@ public interface PaperOnlinePlayer extends PaperOffPlayer, AbstractOnlinePlayer
} }
@Override @Override
public abstract boolean isTextFilteringEnabled(); // needs reflection public boolean isTextFilteringEnabled() { // needs reflection to get the actual value
return false;
}
@Override @Override
public boolean allowsServerListing() { public boolean allowsServerListing() {
return op.getBukkitPlayer().isAllowingServerListings(); return op.getBukkitPlayer().isAllowingServerListings();
} }
/**
* Gets the players skin configuration.
* @return the players skin configuration.
*/
public SkinParts getSkinParts() { public SkinParts getSkinParts() {
return op.getBukkitPlayer().getClientOption(ClientOption.SKIN_PARTS); return op.getBukkitPlayer().getClientOption(ClientOption.SKIN_PARTS);
} }
@ -231,10 +260,19 @@ public interface PaperOnlinePlayer extends PaperOffPlayer, AbstractOnlinePlayer
* Custom damage * Custom damage
*/ */
/**
* Deals damages to this player.
* @param amount the amount of damage to deal.
*/
default void damage(double amount) { default void damage(double amount) {
getBukkitPlayer().damage(amount); // uses DamageSource.GENERIC getBukkitPlayer().damage(amount); // uses DamageSource.GENERIC
} }
/**
* Deals damages to this player, from the provided entity.
* @param amount the amount of damage to deal.
* @param source the entity from which the damage comes from.
*/
default void damage(double amount, LivingEntity source) { default void damage(double amount, LivingEntity source) {
getBukkitPlayer().damage(amount, source); // uses appropriate DamageSource according to provided player or entity getBukkitPlayer().damage(amount, source); // uses appropriate DamageSource according to provided player or entity
} }