Adventure 4.23.0 (#12690)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package io.papermc.paper.adventure;
|
||||
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.DataResult;
|
||||
@@ -14,6 +15,7 @@ import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.nbt.api.BinaryTagHolder;
|
||||
import net.kyori.adventure.text.BlockNBTComponent;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.EntityNBTComponent;
|
||||
@@ -37,6 +39,9 @@ import net.kyori.adventure.text.format.TextDecoration;
|
||||
import net.minecraft.commands.arguments.selector.SelectorPattern;
|
||||
import net.minecraft.core.UUIDUtil;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.nbt.TagParser;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.chat.ComponentSerialization;
|
||||
import net.minecraft.network.chat.contents.KeybindContents;
|
||||
@@ -44,6 +49,7 @@ import net.minecraft.network.chat.contents.ScoreContents;
|
||||
import net.minecraft.network.chat.contents.TranslatableContents;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.ExtraCodecs;
|
||||
import net.minecraft.util.StringRepresentable;
|
||||
import net.minecraft.world.item.Item;
|
||||
@@ -63,7 +69,14 @@ import static net.kyori.adventure.text.TranslationArgument.numeric;
|
||||
|
||||
@DefaultQualifier(NonNull.class)
|
||||
public final class AdventureCodecs {
|
||||
|
||||
public static final Codec<BinaryTagHolder> BINARY_TAG_HOLDER_CODEC = ExtraCodecs.NBT.flatComapMap(tag -> BinaryTagHolder.encode(tag, PaperAdventure.NBT_CODEC), api -> {
|
||||
try {
|
||||
final Tag tag = api.get(PaperAdventure.NBT_CODEC);
|
||||
return DataResult.success(tag);
|
||||
} catch (CommandSyntaxException e) {
|
||||
return DataResult.error(e::getMessage);
|
||||
}
|
||||
});
|
||||
public static final Codec<Component> COMPONENT_CODEC = recursive("adventure Component", AdventureCodecs::createCodec);
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, Component> STREAM_COMPONENT_CODEC = ByteBufCodecs.fromCodecWithRegistriesTrusted(COMPONENT_CODEC);
|
||||
|
||||
@@ -89,27 +102,37 @@ public final class AdventureCodecs {
|
||||
return Key.parseable(s) ? DataResult.success(Key.key(s)) : DataResult.error(() -> "Cannot convert " + s + " to adventure Key");
|
||||
}, Key::asString);
|
||||
|
||||
static final Function<ClickEvent, String> TEXT_PAYLOAD_EXTRACTOR = a -> ((ClickEvent.Payload.Text) a.payload()).value();
|
||||
|
||||
/*
|
||||
* Click
|
||||
*/
|
||||
static final MapCodec<ClickEvent> OPEN_URL_CODEC = mapCodec((instance) -> instance.group(
|
||||
ExtraCodecs.UNTRUSTED_URI.fieldOf("url").forGetter(a -> URI.create(!a.value().contains("://") ? "https://" + a.value() : a.value()))
|
||||
ExtraCodecs.UNTRUSTED_URI.fieldOf("url").forGetter(a -> {
|
||||
final String url = ((ClickEvent.Payload.Text) a.payload()).value();
|
||||
return URI.create(!url.contains("://") ? "https://" + url : url);
|
||||
}
|
||||
)
|
||||
).apply(instance, (url) -> ClickEvent.openUrl(url.toString())));
|
||||
static final MapCodec<ClickEvent> OPEN_FILE_CODEC = mapCodec((instance) -> instance.group(
|
||||
Codec.STRING.fieldOf("path").forGetter(ClickEvent::value)
|
||||
Codec.STRING.fieldOf("path").forGetter(TEXT_PAYLOAD_EXTRACTOR)
|
||||
).apply(instance, ClickEvent::openFile));
|
||||
static final MapCodec<ClickEvent> RUN_COMMAND_CODEC = mapCodec((instance) -> instance.group(
|
||||
ExtraCodecs.CHAT_STRING.fieldOf("command").forGetter(ClickEvent::value)
|
||||
ExtraCodecs.CHAT_STRING.fieldOf("command").forGetter(TEXT_PAYLOAD_EXTRACTOR)
|
||||
).apply(instance, ClickEvent::runCommand));
|
||||
static final MapCodec<ClickEvent> SUGGEST_COMMAND_CODEC = mapCodec((instance) -> instance.group(
|
||||
ExtraCodecs.CHAT_STRING.fieldOf("command").forGetter(ClickEvent::value)
|
||||
ExtraCodecs.CHAT_STRING.fieldOf("command").forGetter(TEXT_PAYLOAD_EXTRACTOR)
|
||||
).apply(instance, ClickEvent::suggestCommand));
|
||||
static final MapCodec<ClickEvent> CHANGE_PAGE_CODEC = mapCodec((instance) -> instance.group(
|
||||
ExtraCodecs.POSITIVE_INT.fieldOf("page").forGetter(a -> Integer.parseInt(a.value()))
|
||||
ExtraCodecs.POSITIVE_INT.fieldOf("page").forGetter(a -> ((ClickEvent.Payload.Int) a.payload()).integer())
|
||||
).apply(instance, ClickEvent::changePage));
|
||||
static final MapCodec<ClickEvent> COPY_TO_CLIPBOARD_CODEC = mapCodec((instance) -> instance.group(
|
||||
Codec.STRING.fieldOf("value").forGetter(ClickEvent::value)
|
||||
Codec.STRING.fieldOf("value").forGetter(TEXT_PAYLOAD_EXTRACTOR)
|
||||
).apply(instance, ClickEvent::copyToClipboard));
|
||||
static final MapCodec<ClickEvent> CUSTOM_CODEC = mapCodec((instance) -> instance.group(
|
||||
KEY_CODEC.fieldOf("id").forGetter(a -> ((ClickEvent.Payload.Custom) a.payload()).key()),
|
||||
BINARY_TAG_HOLDER_CODEC.fieldOf("payload").forGetter(a -> ((ClickEvent.Payload.Custom) a.payload()).nbt())
|
||||
).apply(instance, ClickEvent::custom));
|
||||
|
||||
static final ClickEventType OPEN_URL_CLICK_EVENT_TYPE = new ClickEventType(OPEN_URL_CODEC, "open_url");
|
||||
static final ClickEventType OPEN_FILE_CLICK_EVENT_TYPE = new ClickEventType(OPEN_FILE_CODEC, "open_file");
|
||||
@@ -117,7 +140,8 @@ public final class AdventureCodecs {
|
||||
static final ClickEventType SUGGEST_COMMAND_CLICK_EVENT_TYPE = new ClickEventType(SUGGEST_COMMAND_CODEC, "suggest_command");
|
||||
static final ClickEventType CHANGE_PAGE_CLICK_EVENT_TYPE = new ClickEventType(CHANGE_PAGE_CODEC, "change_page");
|
||||
static final ClickEventType COPY_TO_CLIPBOARD_CLICK_EVENT_TYPE = new ClickEventType(COPY_TO_CLIPBOARD_CODEC, "copy_to_clipboard");
|
||||
static final Codec<ClickEventType> CLICK_EVENT_TYPE_CODEC = StringRepresentable.fromValues(() -> new ClickEventType[]{OPEN_URL_CLICK_EVENT_TYPE, OPEN_FILE_CLICK_EVENT_TYPE, RUN_COMMAND_CLICK_EVENT_TYPE, SUGGEST_COMMAND_CLICK_EVENT_TYPE, CHANGE_PAGE_CLICK_EVENT_TYPE, COPY_TO_CLIPBOARD_CLICK_EVENT_TYPE});
|
||||
static final ClickEventType CUSTOM_CLICK_EVENT_TYPE = new ClickEventType(CUSTOM_CODEC, "custom");
|
||||
static final Codec<ClickEventType> CLICK_EVENT_TYPE_CODEC = StringRepresentable.fromValues(() -> new ClickEventType[]{OPEN_URL_CLICK_EVENT_TYPE, OPEN_FILE_CLICK_EVENT_TYPE, RUN_COMMAND_CLICK_EVENT_TYPE, SUGGEST_COMMAND_CLICK_EVENT_TYPE, CHANGE_PAGE_CLICK_EVENT_TYPE, COPY_TO_CLIPBOARD_CLICK_EVENT_TYPE, CUSTOM_CLICK_EVENT_TYPE});
|
||||
|
||||
record ClickEventType(MapCodec<ClickEvent> codec, String id) implements StringRepresentable {
|
||||
@Override
|
||||
@@ -126,23 +150,17 @@ public final class AdventureCodecs {
|
||||
}
|
||||
}
|
||||
|
||||
private static final Function<ClickEvent, ClickEventType> GET_CLICK_EVENT_TYPE = he -> {
|
||||
if (he.action() == ClickEvent.Action.OPEN_URL) {
|
||||
return OPEN_URL_CLICK_EVENT_TYPE;
|
||||
} else if (he.action() == ClickEvent.Action.OPEN_FILE) {
|
||||
return OPEN_FILE_CLICK_EVENT_TYPE;
|
||||
} else if (he.action() == ClickEvent.Action.RUN_COMMAND) {
|
||||
return RUN_COMMAND_CLICK_EVENT_TYPE;
|
||||
} else if (he.action() == ClickEvent.Action.SUGGEST_COMMAND) {
|
||||
return SUGGEST_COMMAND_CLICK_EVENT_TYPE;
|
||||
} else if (he.action() == ClickEvent.Action.CHANGE_PAGE) {
|
||||
return CHANGE_PAGE_CLICK_EVENT_TYPE;
|
||||
} else if (he.action() == ClickEvent.Action.COPY_TO_CLIPBOARD) {
|
||||
return COPY_TO_CLIPBOARD_CLICK_EVENT_TYPE;
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
};
|
||||
private static final Function<ClickEvent, ClickEventType> GET_CLICK_EVENT_TYPE =
|
||||
he -> switch (he.action()) {
|
||||
case OPEN_URL -> OPEN_URL_CLICK_EVENT_TYPE;
|
||||
case OPEN_FILE -> OPEN_FILE_CLICK_EVENT_TYPE;
|
||||
case RUN_COMMAND -> RUN_COMMAND_CLICK_EVENT_TYPE;
|
||||
case SUGGEST_COMMAND -> SUGGEST_COMMAND_CLICK_EVENT_TYPE;
|
||||
case CHANGE_PAGE -> CHANGE_PAGE_CLICK_EVENT_TYPE;
|
||||
case COPY_TO_CLIPBOARD -> COPY_TO_CLIPBOARD_CLICK_EVENT_TYPE;
|
||||
case SHOW_DIALOG -> throw new UnsupportedOperationException(); // todo: dialog codec with dialog "api"
|
||||
case CUSTOM -> CUSTOM_CLICK_EVENT_TYPE;
|
||||
};
|
||||
|
||||
static final Codec<ClickEvent> CLICK_EVENT_CODEC = CLICK_EVENT_TYPE_CODEC.dispatch("action", GET_CLICK_EVENT_TYPE, ClickEventType::codec);
|
||||
|
||||
|
||||
@@ -35,8 +35,6 @@ import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import net.kyori.adventure.translation.GlobalTranslator;
|
||||
import net.kyori.adventure.translation.TranslationRegistry;
|
||||
import net.kyori.adventure.translation.Translator;
|
||||
import net.kyori.adventure.util.Codec;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
@@ -78,16 +76,16 @@ import static java.util.Objects.requireNonNull;
|
||||
public final class PaperAdventure {
|
||||
private static final Pattern LOCALIZATION_PATTERN = Pattern.compile("%(?:(\\d+)\\$)?s");
|
||||
public static final ComponentFlattener FLATTENER = ComponentFlattener.basic().toBuilder()
|
||||
.nestingLimit(30) // todo: should this be configurable? a system property or config value?
|
||||
.complexMapper(TranslatableComponent.class, (translatable, consumer) -> {
|
||||
if (!Language.getInstance().has(translatable.key())) {
|
||||
for (final Translator source : GlobalTranslator.translator().sources()) {
|
||||
if (source instanceof TranslationRegistry registry && registry.contains(translatable.key())) {
|
||||
consumer.accept(GlobalTranslator.render(translatable, Locale.US));
|
||||
return;
|
||||
}
|
||||
final Language language = Language.getInstance();
|
||||
final @Nullable String fallback = translatable.fallback();
|
||||
if (!language.has(translatable.key()) && (fallback == null || !language.has(fallback))) {
|
||||
if (GlobalTranslator.translator().canTranslate(translatable.key(), Locale.US)) {
|
||||
consumer.accept(GlobalTranslator.render(translatable, Locale.US));
|
||||
return;
|
||||
}
|
||||
}
|
||||
final @Nullable String fallback = translatable.fallback();
|
||||
final @NotNull String translated = Language.getInstance().getOrDefault(translatable.key(), fallback != null ? fallback : translatable.key());
|
||||
|
||||
final Matcher matcher = LOCALIZATION_PATTERN.matcher(translated);
|
||||
@@ -379,6 +377,7 @@ public final class PaperAdventure {
|
||||
case PLAYER -> SoundSource.PLAYERS;
|
||||
case AMBIENT -> SoundSource.AMBIENT;
|
||||
case VOICE -> SoundSource.VOICE;
|
||||
case UI -> SoundSource.UI;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,24 +1,33 @@
|
||||
package io.papermc.paper.adventure.providers;
|
||||
|
||||
import io.papermc.paper.adventure.PaperAdventure;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.text.event.ClickCallback;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage") // permitted provider
|
||||
public class ClickCallbackProviderImpl implements ClickCallback.Provider {
|
||||
private static final Key CLICK_CALLBACK_KEY = Key.key("paper", "click_callback");
|
||||
private static final String ID_KEY = "id";
|
||||
|
||||
public static final ResourceLocation CLICK_CALLBACK_RESOURCE_LOCATION = PaperAdventure.asVanilla(CLICK_CALLBACK_KEY);
|
||||
public static final CallbackManager CALLBACK_MANAGER = new CallbackManager();
|
||||
|
||||
@Override
|
||||
public @NotNull ClickEvent create(final @NotNull ClickCallback<Audience> callback, final ClickCallback.@NotNull Options options) {
|
||||
return ClickEvent.runCommand("/paper:callback " + CALLBACK_MANAGER.addCallback(callback, options));
|
||||
final CompoundTag tag = new CompoundTag();
|
||||
tag.putString(ID_KEY, CALLBACK_MANAGER.addCallback(callback, options).toString());
|
||||
return ClickEvent.custom(CLICK_CALLBACK_KEY, PaperAdventure.asBinaryTagHolder(tag));
|
||||
}
|
||||
|
||||
public static final class CallbackManager {
|
||||
@@ -48,12 +57,21 @@ public class ClickCallbackProviderImpl implements ClickCallback.Provider {
|
||||
}
|
||||
}
|
||||
|
||||
public void runCallback(final @NotNull Audience audience, final UUID id) {
|
||||
final StoredCallback callback = this.callbacks.get(id);
|
||||
if (callback != null && callback.valid()) { //TODO Message if expired/invalid?
|
||||
callback.takeUse();
|
||||
callback.callback.accept(audience);
|
||||
}
|
||||
public void tryRunCallback(final @NotNull Audience audience, final Tag tag) {
|
||||
tag.asCompound().flatMap(t -> t.getString(ID_KEY)).ifPresent(s -> {
|
||||
final UUID id;
|
||||
try {
|
||||
id = UUID.fromString(s);
|
||||
} catch (final IllegalArgumentException ignored) {
|
||||
return;
|
||||
}
|
||||
|
||||
final StoredCallback callback = this.callbacks.get(id);
|
||||
if (callback != null && callback.valid()) {
|
||||
callback.takeUse();
|
||||
callback.callback.accept(audience);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
package io.papermc.paper.command;
|
||||
|
||||
import io.papermc.paper.adventure.providers.ClickCallbackProviderImpl;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
import java.util.UUID;
|
||||
|
||||
@DefaultQualifier(NonNull.class)
|
||||
public class CallbackCommand extends Command {
|
||||
|
||||
protected CallbackCommand(final String name) {
|
||||
super(name);
|
||||
this.description = "ClickEvent callback";
|
||||
this.usageMessage = "/callback <uuid>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(final CommandSender sender, final String commandLabel, final String[] args) {
|
||||
if (args.length != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final UUID id;
|
||||
try {
|
||||
id = UUID.fromString(args[0]);
|
||||
} catch (final IllegalArgumentException ignored) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ClickCallbackProviderImpl.CALLBACK_MANAGER.runCallback(sender, id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ public final class PaperCommands {
|
||||
|
||||
public static void registerCommands(final MinecraftServer server) {
|
||||
COMMANDS.put("paper", new PaperCommand("paper"));
|
||||
COMMANDS.put("callback", new CallbackCommand("callback"));
|
||||
COMMANDS.put("mspt", new MSPTCommand("mspt"));
|
||||
|
||||
COMMANDS.forEach((s, command) -> {
|
||||
|
||||
Reference in New Issue
Block a user