Add api to resolve components (#7648)

This commit is contained in:
Jake Potrebic 2022-11-22 20:53:50 -08:00
parent 3c0aa837c1
commit 48d31a56e9
6 changed files with 153 additions and 4 deletions

View file

@ -567,7 +567,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer;
+import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
+import org.bukkit.Bukkit;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Entity;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+
+/**
+ * Paper API-specific methods for working with {@link Component}s and related.
@ -578,6 +583,66 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ }
+
+ /**
+ * Resolves a component with a specific command sender and subject.
+ * <p>
+ * Note that in Vanilla, elevated permissions are usually required to use
+ * '@' selectors in various component types, but this method should not
+ * check such permissions from the sender.
+ * <p>
+ * A {@link CommandSender} argument is required to resolve:
+ * <ul>
+ * <li>{@link net.kyori.adventure.text.NBTComponent}</li>
+ * <li>{@link net.kyori.adventure.text.ScoreComponent}</li>
+ * <li>{@link net.kyori.adventure.text.SelectorComponent}</li>
+ * </ul>
+ * A {@link Entity} argument is optional to help resolve:
+ * <ul>
+ * <li>{@link net.kyori.adventure.text.ScoreComponent}</li>
+ * </ul>
+ * {@link net.kyori.adventure.text.TranslatableComponent}s don't require any extra arguments.
+ *
+ * @param input the component to resolve
+ * @param context the command sender to resolve with
+ * @param scoreboardSubject the scoreboard subject to use (for use with {@link net.kyori.adventure.text.ScoreComponent}s)
+ * @return the resolved component
+ * @throws IOException if a syntax error tripped during resolving
+ */
+ public static @NotNull Component resolveWithContext(@NotNull Component input, @Nullable CommandSender context, @Nullable Entity scoreboardSubject) throws IOException {
+ return resolveWithContext(input, context, scoreboardSubject, true);
+ }
+
+ /**
+ * Resolves a component with a specific command sender and subject.
+ * <p>
+ * Note that in Vanilla, elevated permissions are required to use
+ * '@' selectors in various component types. If the boolean {@code bypassPermissions}
+ * argument is {@code false}, the {@link CommandSender} argument will be used to query
+ * those permissions.
+ * <p>
+ * A {@link CommandSender} argument is required to resolve:
+ * <ul>
+ * <li>{@link net.kyori.adventure.text.NBTComponent}</li>
+ * <li>{@link net.kyori.adventure.text.ScoreComponent}</li>
+ * <li>{@link net.kyori.adventure.text.SelectorComponent}</li>
+ * </ul>
+ * A {@link Entity} argument is optional to help resolve:
+ * <ul>
+ * <li>{@link net.kyori.adventure.text.ScoreComponent}</li>
+ * </ul>
+ * {@link net.kyori.adventure.text.TranslatableComponent}s don't require any extra arguments.
+ *
+ * @param input the component to resolve
+ * @param context the command sender to resolve with
+ * @param scoreboardSubject the scoreboard subject to use (for use with {@link net.kyori.adventure.text.ScoreComponent}s)
+ * @param bypassPermissions true to bypass permissions checks for resolving components
+ * @return the resolved component
+ * @throws IOException if a syntax error tripped during resolving
+ */
+ public static @NotNull Component resolveWithContext(@NotNull Component input, @Nullable CommandSender context, @Nullable Entity scoreboardSubject, boolean bypassPermissions) throws IOException {
+ return Bukkit.getUnsafe().resolveWithContext(input, context, scoreboardSubject, bypassPermissions);
+ }
+
+ /**
+ * Return a component flattener that can use game data to resolve extra information about components.
+ *
+ * @return a component flattener
@ -1317,6 +1382,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ @Deprecated(forRemoval = true) net.kyori.adventure.text.serializer.gson.GsonComponentSerializer gsonComponentSerializer();
+ @Deprecated(forRemoval = true) net.kyori.adventure.text.serializer.gson.GsonComponentSerializer colorDownsamplingGsonComponentSerializer();
+ @Deprecated(forRemoval = true) net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer legacyComponentSerializer();
+ net.kyori.adventure.text.Component resolveWithContext(net.kyori.adventure.text.Component component, org.bukkit.command.CommandSender context, org.bukkit.entity.Entity scoreboardSubject, boolean bypassPermissions) throws java.io.IOException;
+ // Paper end
Material toLegacy(Material material);

View file

@ -2838,7 +2838,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
--- a/src/main/java/org/bukkit/UnsafeValues.java
+++ b/src/main/java/org/bukkit/UnsafeValues.java
@@ -0,0 +0,0 @@ public interface UnsafeValues {
@Deprecated(forRemoval = true) net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer legacyComponentSerializer();
net.kyori.adventure.text.Component resolveWithContext(net.kyori.adventure.text.Component component, org.bukkit.command.CommandSender context, org.bukkit.entity.Entity scoreboardSubject, boolean bypassPermissions) throws java.io.IOException;
// Paper end
+ void reportTimings(); // Paper

View file

@ -726,17 +726,22 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+import net.kyori.adventure.translation.Translator;
+import net.kyori.adventure.util.Codec;
+import net.minecraft.ChatFormatting;
+import net.minecraft.commands.CommandSourceStack;
+import net.minecraft.locale.Language;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.ListTag;
+import net.minecraft.nbt.StringTag;
+import net.minecraft.nbt.TagParser;
+import net.minecraft.network.chat.ComponentUtils;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.sounds.SoundSource;
+import net.minecraft.world.BossEvent;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.WrittenBookItem;
+import org.bukkit.ChatColor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.craftbukkit.command.VanillaCommandWrapper;
+import org.bukkit.craftbukkit.entity.CraftEntity;
+import org.bukkit.entity.Entity;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
@ -894,6 +899,24 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ );
+ }
+
+ public static Component resolveWithContext(final @NotNull Component component, final @Nullable CommandSender context, final @Nullable Entity scoreboardSubject, final boolean bypassPermissions) throws IOException {
+ final CommandSourceStack css = context != null ? VanillaCommandWrapper.getListener(context) : null;
+ Boolean previous = null;
+ if (css != null && bypassPermissions) {
+ previous = css.bypassSelectorPermissions;
+ css.bypassSelectorPermissions = true;
+ }
+ try {
+ return asAdventure(ComponentUtils.updateForEntity(css, asVanilla(component), scoreboardSubject == null ? null : ((CraftEntity) scoreboardSubject).getHandle(), 0));
+ } catch (CommandSyntaxException e) {
+ throw new IOException(e);
+ } finally {
+ if (css != null && previous != null) {
+ css.bypassSelectorPermissions = previous;
+ }
+ }
+ }
+
+ // BossBar
+
+ public static BossEvent.BossBarColor asVanilla(final BossBar.Color color) {
@ -1423,6 +1446,18 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
@Nullable
public static ChatFormatting getById(int colorIndex) {
if (colorIndex < 0) {
diff --git a/src/main/java/net/minecraft/commands/CommandSourceStack.java b/src/main/java/net/minecraft/commands/CommandSourceStack.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/commands/CommandSourceStack.java
+++ b/src/main/java/net/minecraft/commands/CommandSourceStack.java
@@ -0,0 +0,0 @@ public class CommandSourceStack implements SharedSuggestionProvider {
private final CommandSigningContext signingContext;
private final TaskChainer chatMessageChainer;
public volatile CommandNode currentCommand; // CraftBukkit
+ public boolean bypassSelectorPermissions = false; // Paper
public CommandSourceStack(CommandSource output, Vec3 pos, Vec2 rot, ServerLevel world, int level, String name, Component displayName, MinecraftServer server, @Nullable Entity entity) {
this(output, pos, rot, world, level, name, displayName, server, entity, false, (commandcontext, flag, j) -> {
diff --git a/src/main/java/net/minecraft/commands/arguments/MessageArgument.java b/src/main/java/net/minecraft/commands/arguments/MessageArgument.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/commands/arguments/MessageArgument.java
@ -1445,6 +1480,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
MessageArgument.logResolutionFailure(source, completableFuture);
return completableFuture;
}
diff --git a/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java b/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java
+++ b/src/main/java/net/minecraft/commands/arguments/selector/EntitySelector.java
@@ -0,0 +0,0 @@ public class EntitySelector {
}
private void checkPermissions(CommandSourceStack source) throws CommandSyntaxException {
- if (this.usesSelector && !source.hasPermission(2, "minecraft.command.selector")) { // CraftBukkit
+ if (source.bypassSelectorPermissions || (this.usesSelector && !source.hasPermission(2, "minecraft.command.selector"))) { // CraftBukkit // Paper
throw EntityArgument.ERROR_SELECTORS_NOT_ALLOWED.create();
}
}
diff --git a/src/main/java/net/minecraft/network/FriendlyByteBuf.java b/src/main/java/net/minecraft/network/FriendlyByteBuf.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/network/FriendlyByteBuf.java
@ -1693,6 +1741,22 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
JsonObject jsonobject = new JsonObject();
if (!ichatbasecomponent.getStyle().isEmpty()) {
diff --git a/src/main/java/net/minecraft/network/chat/ComponentUtils.java b/src/main/java/net/minecraft/network/chat/ComponentUtils.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/network/chat/ComponentUtils.java
+++ b/src/main/java/net/minecraft/network/chat/ComponentUtils.java
@@ -0,0 +0,0 @@ public class ComponentUtils {
if (depth > 100) {
return text.copy();
} else {
+ // Paper start
+ if (text instanceof io.papermc.paper.adventure.AdventureComponent adventureComponent) {
+ text = adventureComponent.deepConverted();
+ }
+ // Paper end
MutableComponent mutableComponent = text.getContents().resolve(source, sender, depth + 1);
for(Component component : text.getSiblings()) {
diff --git a/src/main/java/net/minecraft/network/chat/OutgoingPlayerChatMessage.java b/src/main/java/net/minecraft/network/chat/OutgoingPlayerChatMessage.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/network/chat/OutgoingPlayerChatMessage.java
@ -4432,6 +4496,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ public net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer legacyComponentSerializer() {
+ return net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection();
+ }
+
+ @Override
+ public net.kyori.adventure.text.Component resolveWithContext(final net.kyori.adventure.text.Component component, final org.bukkit.command.CommandSender context, final org.bukkit.entity.Entity scoreboardSubject, final boolean bypassPermissions) throws IOException {
+ return io.papermc.paper.adventure.PaperAdventure.resolveWithContext(component, context, scoreboardSubject, bypassPermissions);
+ }
+ // Paper end
+
public static BlockState getBlock(MaterialData material) {

View file

@ -19,7 +19,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
- EntitySelectorParser argumentparserselector = new EntitySelectorParser(stringreader, icompletionprovider.hasPermission(2), true); // Paper
+ // Paper start
+ final boolean permission = object instanceof CommandSourceStack stack
+ ? stack.hasPermission(2, "minecraft.command.selector")
+ ? stack.bypassSelectorPermissions || stack.hasPermission(2, "minecraft.command.selector")
+ : icompletionprovider.hasPermission(2);
+ EntitySelectorParser argumentparserselector = new EntitySelectorParser(stringreader, permission, true); // Paper
+ // Paper end

View file

@ -128,6 +128,20 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
private static final int MIN_PROTOCOL_ID = -1;
private static final int MAX_PROTOCOL_ID = 2;
private static final ConnectionProtocol[] LOOKUP = new ConnectionProtocol[4];
diff --git a/src/main/java/net/minecraft/network/chat/ComponentUtils.java b/src/main/java/net/minecraft/network/chat/ComponentUtils.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/network/chat/ComponentUtils.java
+++ b/src/main/java/net/minecraft/network/chat/ComponentUtils.java
@@ -0,0 +0,0 @@ public class ComponentUtils {
ComponentContents string = text.getContents();
if (string instanceof TranslatableContents) {
TranslatableContents translatableContents = (TranslatableContents)string;
- String string = translatableContents.getKey();
- return Language.getInstance().has(string);
+ return Language.getInstance().has(translatableContents.getKey()); // Paper - decompile fix
}
}
diff --git a/src/main/java/net/minecraft/resources/RegistryLoader.java b/src/main/java/net/minecraft/resources/RegistryLoader.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/resources/RegistryLoader.java

View file

@ -35,9 +35,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
private final TaskChainer chatMessageChainer;
- public volatile CommandNode currentCommand; // CraftBukkit
+ public java.util.Map<Thread, CommandNode> currentCommand = new java.util.concurrent.ConcurrentHashMap<>(); // CraftBukkit // Paper
public boolean bypassSelectorPermissions = false; // Paper
public CommandSourceStack(CommandSource output, Vec3 pos, Vec2 rot, ServerLevel world, int level, String name, Component displayName, MinecraftServer server, @Nullable Entity entity) {
this(output, pos, rot, world, level, name, displayName, server, entity, false, (commandcontext, flag, j) -> {
@@ -0,0 +0,0 @@ public class CommandSourceStack implements SharedSuggestionProvider, com.destroy
@Override
public boolean hasPermission(int level) {