Dialog API (#12671)

This commit is contained in:
Jake Potrebic
2025-07-06 11:49:43 -07:00
committed by GitHub
parent f7d5a0a017
commit b4466ec981
116 changed files with 4404 additions and 465 deletions
@@ -23867,7 +23867,7 @@ index 46de98a6bbbae48c4837e1e588ba198a363d2dde..fd3553bdc1c3cdbf6aa3dc00e0a4987f
thread1 -> {
DedicatedServer dedicatedServer1 = new DedicatedServer(
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 75aba65cbe1a943f21c7464ff9465e64f63e8e5b..32475c0958fd7e0f1f9b494b0cc78a4a718d12b8 100644
index 03a616fc9b0325aa163fe4950ec4ce9ffdd3a9ea..b6aee251027cf124d6597137abefc7fd177358c9 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -173,7 +173,7 @@ import net.minecraft.world.phys.Vec2;
@@ -24059,7 +24059,7 @@ index 75aba65cbe1a943f21c7464ff9465e64f63e8e5b..32475c0958fd7e0f1f9b494b0cc78a4a
return true;
} else {
boolean ret = false; // Paper - force execution of all worlds, do not just bias the first
@@ -2478,6 +2566,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2479,6 +2567,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
}
@@ -48,10 +48,10 @@ index 0000000000000000000000000000000000000000..24a2090e068ad3c0d08705050944abdf
+ }
+}
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index cff9d761dfee8a90b19fb2f3e678f99a39fc000c..0a260fdf6b198a8ab52e60bf6db2fb5eab719c48 100644
index 41e24da7f0c120af96446603d234ddd417b0de60..7c5b6973769f6eda97f5a08a974b43a98f276ac7 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1718,6 +1718,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1719,6 +1719,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
serverLevel.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent
serverLevel.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent
serverLevel.updateLagCompensationTick(); // Paper - lag compensation
@@ -6,10 +6,10 @@ Subject: [PATCH] Optimise collision checking in player move packet handling
Move collision logic to just the hasNewCollision call instead of getCubes + hasNewCollision
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index ff2f83dc1cbf10baccd07e65c894dff1d2f9a3cf..63d01c3753b865532a222b5b7408c8857c064d55 100644
index 08aba415735733f5968fd032ab7ca249cdcf6cde..ee4397711625344622c81424afd11fd6d967efba 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -594,6 +594,7 @@ public class ServerGamePacketListenerImpl
@@ -606,6 +606,7 @@ public class ServerGamePacketListenerImpl
}
rootVehicle.move(MoverType.PLAYER, new Vec3(d3, d4, d5));
@@ -17,7 +17,7 @@ index ff2f83dc1cbf10baccd07e65c894dff1d2f9a3cf..63d01c3753b865532a222b5b7408c885
double verticalDelta = d4;
d3 = d - rootVehicle.getX();
d4 = d1 - rootVehicle.getY();
@@ -605,12 +606,21 @@ public class ServerGamePacketListenerImpl
@@ -617,12 +618,21 @@ public class ServerGamePacketListenerImpl
d7 = d3 * d3 + d4 * d4 + d5 * d5;
boolean flag1 = false;
if (d7 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot
@@ -42,7 +42,7 @@ index ff2f83dc1cbf10baccd07e65c894dff1d2f9a3cf..63d01c3753b865532a222b5b7408c885
rootVehicle.absSnapTo(x, y, z, f, f1);
this.send(ClientboundMoveVehiclePacket.fromEntity(rootVehicle));
rootVehicle.removeLatestMovementRecording();
@@ -689,9 +699,32 @@ public class ServerGamePacketListenerImpl
@@ -701,9 +711,32 @@ public class ServerGamePacketListenerImpl
}
private boolean noBlocksAround(Entity entity) {
@@ -78,7 +78,7 @@ index ff2f83dc1cbf10baccd07e65c894dff1d2f9a3cf..63d01c3753b865532a222b5b7408c885
}
@Override
@@ -1467,7 +1500,7 @@ public class ServerGamePacketListenerImpl
@@ -1479,7 +1512,7 @@ public class ServerGamePacketListenerImpl
}
}
@@ -87,7 +87,7 @@ index ff2f83dc1cbf10baccd07e65c894dff1d2f9a3cf..63d01c3753b865532a222b5b7408c885
d3 = d - this.lastGoodX; // Paper - diff on change, used for checking large move vectors above
d4 = d1 - this.lastGoodY; // Paper - diff on change, used for checking large move vectors above
d5 = d2 - this.lastGoodZ; // Paper - diff on change, used for checking large move vectors above
@@ -1506,6 +1539,7 @@ public class ServerGamePacketListenerImpl
@@ -1518,6 +1551,7 @@ public class ServerGamePacketListenerImpl
boolean flag1 = this.player.verticalCollisionBelow;
this.player.move(MoverType.PLAYER, new Vec3(d3, d4, d5));
this.player.onGround = packet.isOnGround(); // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move
@@ -95,7 +95,7 @@ index ff2f83dc1cbf10baccd07e65c894dff1d2f9a3cf..63d01c3753b865532a222b5b7408c885
// Paper start - prevent position desync
if (this.awaitingPositionFromClient != null) {
return; // ... thanks Mojang for letting move calls teleport across dimensions.
@@ -1538,7 +1572,17 @@ public class ServerGamePacketListenerImpl
@@ -1550,7 +1584,17 @@ public class ServerGamePacketListenerImpl
}
// Paper start - Add fail move event
@@ -114,7 +114,7 @@ index ff2f83dc1cbf10baccd07e65c894dff1d2f9a3cf..63d01c3753b865532a222b5b7408c885
if (!allowMovement) {
io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.CLIPPED_INTO_BLOCK,
toX, toY, toZ, toYaw, toPitch, false);
@@ -1674,7 +1718,7 @@ public class ServerGamePacketListenerImpl
@@ -1686,7 +1730,7 @@ public class ServerGamePacketListenerImpl
private boolean updateAwaitingTeleport() {
if (this.awaitingPositionFromClient != null) {
@@ -123,7 +123,7 @@ index ff2f83dc1cbf10baccd07e65c894dff1d2f9a3cf..63d01c3753b865532a222b5b7408c885
this.awaitingTeleportTime = this.tickCount;
this.teleport(
this.awaitingPositionFromClient.x,
@@ -1693,6 +1737,33 @@ public class ServerGamePacketListenerImpl
@@ -1705,6 +1749,33 @@ public class ServerGamePacketListenerImpl
}
}
@@ -100,7 +100,7 @@ index 962084054c0208470d0c3c99c5dca6327c9b8752..2abc21102bbd2da79dc0c50826cff7da
}
}
diff --git a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
index 140f0ac42626662354e9b5386d91f1b096a945a3..2e7c4c74818befd25e296b58ef9f20319544c2fb 100644
index 1b42d5e9f9fd07f99009de6f4483648f416db733..08c59d603fca038fc2dde36384eea1b6c971e659 100644
--- a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
@@ -38,12 +38,13 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
@@ -137,7 +137,7 @@ index 140f0ac42626662354e9b5386d91f1b096a945a3..2e7c4c74818befd25e296b58ef9f2031
// Paper end
}
@@ -93,13 +95,41 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
@@ -99,13 +101,41 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
@Override
public void handleKeepAlive(ServerboundKeepAlivePacket packet) {
@@ -185,7 +185,7 @@ index 140f0ac42626662354e9b5386d91f1b096a945a3..2e7c4c74818befd25e296b58ef9f2031
}
@Override
@@ -220,20 +250,23 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
@@ -232,20 +262,23 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
protected void keepConnectionAlive() {
Profiler.get().push("keepAlive");
long millis = Util.getMillis();
@@ -223,7 +223,7 @@ index 140f0ac42626662354e9b5386d91f1b096a945a3..2e7c4c74818befd25e296b58ef9f2031
}
}
@@ -413,6 +446,6 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
@@ -425,6 +458,6 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
}
protected CommonListenerCookie createCookie(ClientInformation clientInformation) {
@@ -20,7 +20,7 @@ index 5f2deeb5cc01d8bbeb7449bd4e59c466b3dfdf57..82824ae7ffbced513a8bcace684af949
@Override
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 0a260fdf6b198a8ab52e60bf6db2fb5eab719c48..52fa5112cd90ba766c94512a02401dd3aee82cc9 100644
index 382d2b6b53bd144f4d56dccdc603ed0da8fe07a7..7aac2a6889af3edaebfaf94deecbf00d00758b68 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1654,33 +1654,22 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -63,9 +63,9 @@ index 0a260fdf6b198a8ab52e60bf6db2fb5eab719c48..52fa5112cd90ba766c94512a02401dd3
- });
- // Paper end - Folia scheduler API
+ // Paper end - optimise Folia entity scheduler
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.CALLBACK_MANAGER.handleQueue(this.tickCount); // Paper
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.ADVENTURE_CLICK_MANAGER.handleQueue(this.tickCount); // Paper
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.DIALOG_CLICK_MANAGER.handleQueue(this.tickCount); // Paper
profilerFiller.push("commandFunctions");
this.getFunctions().tick();
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index da880f52920b1101f23ef94f3fd0dbdea218c373..3d2c0a4d3a1f9d3e5cc6cd0cdb988ae1205de821 100644
--- a/net/minecraft/world/entity/Entity.java
@@ -984,7 +984,7 @@
ObjectArrayList<GameProfile> list = new ObjectArrayList<>(min);
int randomInt = Mth.nextInt(this.random, 0, players.size() - min);
@@ -1040,17 +_,75 @@
@@ -1040,17 +_,76 @@
protected void tickChildren(BooleanSupplier hasTimeLeft) {
ProfilerFiller profilerFiller = Profiler.get();
this.getPlayerList().getPlayers().forEach(serverPlayer1 -> serverPlayer1.connection.suspendFlushing());
@@ -1012,7 +1012,8 @@
+ }
+ });
+ // Paper end - Folia scheduler API
+ io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.CALLBACK_MANAGER.handleQueue(this.tickCount); // Paper
+ io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.ADVENTURE_CLICK_MANAGER.handleQueue(this.tickCount); // Paper
+ io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.DIALOG_CLICK_MANAGER.handleQueue(this.tickCount); // Paper
profilerFiller.push("commandFunctions");
this.getFunctions().tick();
profilerFiller.popPush("levels");
@@ -0,0 +1,13 @@
--- a/net/minecraft/server/dialog/body/ItemBody.java
+++ b/net/minecraft/server/dialog/body/ItemBody.java
@@ -15,8 +_,8 @@
PlainMessage.CODEC.optionalFieldOf("description").forGetter(ItemBody::description),
Codec.BOOL.optionalFieldOf("show_decorations", true).forGetter(ItemBody::showDecorations),
Codec.BOOL.optionalFieldOf("show_tooltip", true).forGetter(ItemBody::showTooltip),
- ExtraCodecs.intRange(1, 256).optionalFieldOf("width", 16).forGetter(ItemBody::width),
- ExtraCodecs.intRange(1, 256).optionalFieldOf("height", 16).forGetter(ItemBody::height)
+ ExtraCodecs.intRange(1, 256).optionalFieldOf("width", 16).forGetter(ItemBody::width), // Paper - diff on change - update builder defaults/limits
+ ExtraCodecs.intRange(1, 256).optionalFieldOf("height", 16).forGetter(ItemBody::height) // Paper - diff on change - update builder defaults/limits
)
.apply(instance, ItemBody::new)
);
@@ -27,7 +27,7 @@
public ServerCommonPacketListenerImpl(MinecraftServer server, Connection connection, CommonListenerCookie cookie) {
this.server = server;
@@ -52,6 +_,11 @@
@@ -52,7 +_,18 @@
this.keepAliveTime = Util.getMillis();
this.latency = cookie.latency();
this.transferred = cookie.transferred();
@@ -37,8 +37,15 @@
+ this.pluginMessagerChannels = cookie.channels();
+ // Paper end
}
+
+ // Paper start - configuration phase API
+ public abstract io.papermc.paper.connection.PlayerCommonConnection getApiConnection();
+
+ public abstract net.kyori.adventure.audience.Audience getAudience();
+ // Paper end - configuration phase API
private void close() {
if (!this.closed) {
@@ -82,7 +_,7 @@
this.latency = (this.latency * 3 + i) / 4;
this.keepAlivePending = false;
@@ -48,7 +55,7 @@
}
}
@@ -90,8 +_,76 @@
@@ -90,14 +_,88 @@
public void handlePong(ServerboundPongPacket packet) {
}
@@ -124,6 +131,18 @@
+ // Paper end
}
@Override
public void handleCustomClickAction(ServerboundCustomClickActionPacket packet) {
PacketUtils.ensureRunningOnSameThread(packet, this, this.server);
this.server.handleCustomClickAction(packet.id(), packet.payload());
+ // Paper start - Implement click callbacks with custom click action
+ final io.papermc.paper.event.player.PaperPlayerCustomClickEvent event = new io.papermc.paper.event.player.PaperPlayerCustomClickEvent(io.papermc.paper.adventure.PaperAdventure.asAdventure(packet.id()), this.getApiConnection(), packet.payload().orElse(null));
+ event.callEvent();
+ io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.DIALOG_CLICK_MANAGER.tryRunCallback(this.getAudience(), packet.id(), packet.payload());
+ io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.ADVENTURE_CLICK_MANAGER.tryRunCallback(this.getAudience(), packet.id(), packet.payload());
+ // Paper end - Implement click callbacks with custom click action
}
@Override
@@ -105,21 +_,46 @@
PacketUtils.ensureRunningOnSameThread(packet, this, this.server);
@@ -1,6 +1,6 @@
--- a/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
@@ -47,11 +_,13 @@
@@ -47,12 +_,26 @@
public ClientInformation clientInformation;
@Nullable
private SynchronizeRegistriesTask synchronizeRegistriesTask;
@@ -10,10 +10,24 @@
super(server, connection, cookie);
this.gameProfile = cookie.gameProfile();
this.clientInformation = cookie.clientInformation();
- }
+ this.paperConnection = new io.papermc.paper.connection.PaperPlayerConfigurationConnection(this); // Paper
}
+ }
+
+ // Paper start - configuration phase API
+ @Override
+ public io.papermc.paper.connection.PlayerCommonConnection getApiConnection() {
+ return this.paperConnection;
+ }
+
+ @Override
+ public net.kyori.adventure.audience.Audience getAudience() {
+ return this.paperConnection.getAudience();
+ }
+ // Paper end - configuration phase API
@Override
protected GameProfile playerProfile() {
@@ -61,6 +_,11 @@
@Override
@@ -83,17 +83,31 @@
public ServerGamePacketListenerImpl(MinecraftServer server, Connection connection, ServerPlayer player, CommonListenerCookie cookie) {
super(server, connection, cookie);
@@ -268,7 +_,9 @@
@@ -268,8 +_,22 @@
player.connection = this;
player.getTextFilter().join();
this.signedMessageDecoder = SignedMessageChain.Decoder.unsigned(player.getUUID(), server::enforceSecureProfile);
- this.chatMessageChain = new FutureChain(server);
- }
+ this.chatMessageChain = new FutureChain(server.chatExecutor); // CraftBukkit - async chat
+ this.tickEndEvent = new io.papermc.paper.event.packet.ClientTickEndEvent(player.getBukkitEntity()); // Paper - add client tick end event
+ this.playerGameConnection = new io.papermc.paper.connection.PaperPlayerGameConnection(this); // Paper
}
+ }
+
+ // Paper start - configuration phase API
+ @Override
+ public io.papermc.paper.connection.PlayerCommonConnection getApiConnection() {
+ return this.playerGameConnection;
+ }
+
+ @Override
+ public net.kyori.adventure.audience.Audience getAudience() {
+ return this.getCraftPlayer();
+ }
+ // Paper end - configuration phase API
@Override
public void tick() {
@@ -288,8 +_,8 @@
this.knownMovePacketCount = this.receivedMovePacketCount;
if (this.clientIsFloating && !this.player.isSleeping() && !this.player.isPassenger() && !this.player.isDeadOrDying()) {
@@ -2610,7 +2624,7 @@
if (!this.receivedMovementThisTick) {
this.player.setKnownMovement(Vec3.ZERO);
}
@@ -2078,4 +_,92 @@
@@ -2078,4 +_,80 @@
interface EntityInteraction {
InteractionResult run(ServerPlayer player, Entity entity, InteractionHand hand);
}
@@ -2628,18 +2642,6 @@
+ }
+ // Paper end - Add fail move event
+
+ // Paper start - Implement click callbacks with custom click action
+ @Override
+ public void handleCustomClickAction(final net.minecraft.network.protocol.common.ServerboundCustomClickActionPacket packet) {
+ super.handleCustomClickAction(packet);
+ if (packet.id().equals(io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.CLICK_CALLBACK_RESOURCE_LOCATION)) {
+ packet.payload().ifPresent(tag ->
+ io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.CALLBACK_MANAGER.tryRunCallback(this.player.getBukkitEntity(), tag)
+ );
+ }
+ }
+ // Paper end - Implement click callbacks with custom click action
+
+ // Paper start - add utilities
+ public org.bukkit.craftbukkit.entity.CraftPlayer getCraftPlayer() {
+ return this.player == null ? null : this.player.getBukkitEntity();
@@ -5,6 +5,9 @@ import com.mojang.datafixers.util.Either;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.registry.data.dialog.PaperDialogCodecs;
import java.net.URI;
import java.util.Collections;
import java.util.List;
@@ -13,7 +16,7 @@ import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import java.util.function.Supplier;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.nbt.api.BinaryTagHolder;
import net.kyori.adventure.text.BlockNBTComponent;
@@ -36,12 +39,12 @@ import net.kyori.adventure.text.format.ShadowColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import net.kyori.adventure.util.Index;
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;
@@ -49,7 +52,6 @@ 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;
@@ -98,7 +100,7 @@ public final class AdventureCodecs {
}
});
static final Codec<Key> KEY_CODEC = Codec.STRING.comapFlatMap(s -> {
public static final Codec<Key> KEY_CODEC = Codec.STRING.comapFlatMap(s -> {
return Key.parseable(s) ? DataResult.success(Key.key(s)) : DataResult.error(() -> "Cannot convert " + s + " to adventure Key");
}, Key::asString);
@@ -129,6 +131,10 @@ public final class AdventureCodecs {
static final MapCodec<ClickEvent> COPY_TO_CLIPBOARD_CODEC = mapCodec((instance) -> instance.group(
Codec.STRING.fieldOf("value").forGetter(TEXT_PAYLOAD_EXTRACTOR)
).apply(instance, ClickEvent::copyToClipboard));
// needs to be lazy loaded due to depending on PaperDialogCodecs static init
static final MapCodec<ClickEvent> SHOW_DIALOG_CODEC = MapCodec.recursive("show_dialog", ignored -> mapCodec((instance) -> instance.group(
PaperDialogCodecs.DIALOG_CODEC.fieldOf("dialog").forGetter(a -> (Dialog) ((ClickEvent.Payload.Dialog) a.payload()).dialog())
).apply(instance, ClickEvent::showDialog)));
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())
@@ -140,17 +146,19 @@ 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 ClickEventType SHOW_DIALOG_CLICK_EVENT_TYPE = new ClickEventType(SHOW_DIALOG_CODEC, "show_dialog");
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});
public static final Supplier<ClickEventType[]> CLICK_EVENT_TYPES = () -> 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, SHOW_DIALOG_CLICK_EVENT_TYPE, CUSTOM_CLICK_EVENT_TYPE};
static final Codec<ClickEventType> CLICK_EVENT_TYPE_CODEC = StringRepresentable.fromValues(CLICK_EVENT_TYPES);
record ClickEventType(MapCodec<ClickEvent> codec, String id) implements StringRepresentable {
public record ClickEventType(MapCodec<ClickEvent> codec, String id) implements StringRepresentable {
@Override
public String getSerializedName() {
return this.id;
}
}
private static final Function<ClickEvent, ClickEventType> GET_CLICK_EVENT_TYPE =
public 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;
@@ -158,7 +166,7 @@ public final class AdventureCodecs {
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 SHOW_DIALOG -> SHOW_DIALOG_CLICK_EVENT_TYPE;
case CUSTOM -> CUSTOM_CLICK_EVENT_TYPE;
};
@@ -450,6 +458,23 @@ public final class AdventureCodecs {
return component;
}
public static final Codec<BinaryTagHolder> BINARY_TAG_HOLDER_COMPOUND_CODEC = CompoundTag.CODEC.flatComapMap(PaperAdventure::asBinaryTagHolder, api -> {
try {
final Tag tag = api.get(PaperAdventure.NBT_CODEC);
if (!(tag instanceof final CompoundTag compoundTag)) {
return DataResult.error(() -> "Expected a CompoundTag, but got " + tag.getClass().getSimpleName());
}
return DataResult.success(compoundTag);
} catch (CommandSyntaxException e) {
return DataResult.error(e::getMessage);
}
});
public static <T> Codec<T> indexCodec(final Index<String, T> index) {
return Codec.of(Codec.STRING.comap(index::keyOrThrow), Codec.STRING.map(index::valueOrThrow));
}
private AdventureCodecs() {
}
}
@@ -1,15 +1,22 @@
package io.papermc.paper.adventure.providers;
import com.google.common.base.Predicates;
import io.papermc.paper.adventure.PaperAdventure;
import io.papermc.paper.dialog.PaperDialogResponseView;
import io.papermc.paper.registry.data.dialog.action.DialogActionCallback;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Queue;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.Consumer;
import java.util.function.Predicate;
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.core.UUIDUtil;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceLocation;
@@ -17,30 +24,81 @@ import org.jetbrains.annotations.NotNull;
@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";
private static final Key ADVENTURE_CLICK_CALLBACK_KEY = Key.key("paper", "click_callback");
public static final Key DIALOG_CLICK_CALLBACK_KEY = Key.key("paper", "dialog_click_callback");
public 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();
public static final AdventureClick ADVENTURE_CLICK_MANAGER = new AdventureClick();
public static final DialogClickManager DIALOG_CLICK_MANAGER = new DialogClickManager();
@Override
public @NotNull ClickEvent create(final @NotNull ClickCallback<Audience> callback, final ClickCallback.@NotNull Options 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));
tag.store(ID_KEY, UUIDUtil.CODEC, ADVENTURE_CLICK_MANAGER.addCallback(callback, options));
return ClickEvent.custom(ADVENTURE_CLICK_CALLBACK_KEY, PaperAdventure.asBinaryTagHolder(tag));
}
public static final class CallbackManager {
public static final class AdventureClick extends CallbackManager<ClickCallback<Audience>, UUID> {
private final Map<UUID, StoredCallback> callbacks = new HashMap<>();
private final Queue<StoredCallback> queue = new ConcurrentLinkedQueue<>();
private CallbackManager() {
private AdventureClick() {
super(PaperAdventure.asVanilla(ADVENTURE_CLICK_CALLBACK_KEY)::equals);
}
public UUID addCallback(final @NotNull ClickCallback<Audience> callback, final ClickCallback.@NotNull Options options) {
final UUID id = UUID.randomUUID();
this.queue.add(new StoredCallback(callback, options, id));
return this.addCallback(UUID.randomUUID(), callback, options);
}
@Override
void doRunCallback(final @NotNull Audience audience, final Key key, final Tag tag) {
tag.asCompound().ifPresent(t -> {
final Optional<UUID> id = t.read(ID_KEY, UUIDUtil.CODEC);
if (id.isEmpty()) {
return;
}
this.tryConsumeCallback(id.get(), callback -> {
callback.accept(audience);
});
});
}
}
public static final class DialogClickManager extends CallbackManager<DialogActionCallback, UUID> {
public DialogClickManager() {
super(PaperAdventure.asVanilla(DIALOG_CLICK_CALLBACK_KEY)::equals);
}
@Override
void doRunCallback(final @NotNull Audience audience, final Key key, final Tag tag) {
tag.asCompound().ifPresent(t -> {
final Optional<UUID> id = t.read(ID_KEY, UUIDUtil.CODEC);
if (id.isEmpty()) {
return;
}
this.tryConsumeCallback(id.get(), callback -> {
callback.accept(PaperDialogResponseView.createUnvalidatedResponse(t), audience);
});
});
}
}
abstract static class CallbackManager<C, I> {
private final Predicate<ResourceLocation> locationPredicate;
protected final Map<I, StoredCallback<C, I>> callbacks = new HashMap<>();
private final Queue<StoredCallback<C, I>> queue = new ConcurrentLinkedQueue<>();
protected CallbackManager(final Predicate<ResourceLocation> locationPredicate) {
this.locationPredicate = locationPredicate;
}
protected CallbackManager() {
this.locationPredicate = Predicates.alwaysTrue();
}
public I addCallback(final I id, final @NotNull C callback, final ClickCallback.@NotNull Options options) {
this.queue.add(new StoredCallback<>(callback, options, id));
return id;
}
@@ -51,38 +109,36 @@ public class ClickCallbackProviderImpl implements ClickCallback.Provider {
}
// Add entries from queue
StoredCallback callback;
StoredCallback<C, I> callback;
while ((callback = this.queue.poll()) != null) {
this.callbacks.put(callback.id(), callback);
}
}
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 void tryConsumeCallback(final I key, final Consumer<? super C> callbackConsumer) {
final StoredCallback<C, I> callback = this.callbacks.get(key);
if (callback != null && callback.valid()) {
callback.takeUse();
callbackConsumer.accept(callback.callback);
}
}
final StoredCallback callback = this.callbacks.get(id);
if (callback != null && callback.valid()) {
callback.takeUse();
callback.callback.accept(audience);
}
});
abstract void doRunCallback(final @NotNull Audience audience, final Key key, final Tag tag);
public final void tryRunCallback(final @NotNull Audience audience, final ResourceLocation key, final Optional<? extends Tag> tag) {
if (!this.locationPredicate.test(key) || tag.isEmpty()) return;
this.doRunCallback(audience, PaperAdventure.asAdventure(key), tag.get());
}
}
private static final class StoredCallback {
public static final class StoredCallback<C, I> {
private final long startedAt = System.nanoTime();
private final ClickCallback<Audience> callback;
private final C callback;
private final long lifetime;
private final UUID id;
private final I id;
private int remainingUses;
private StoredCallback(final @NotNull ClickCallback<Audience> callback, final ClickCallback.@NotNull Options options, final UUID id) {
private StoredCallback(final @NotNull C callback, final ClickCallback.@NotNull Options options, final I id) {
long lifetimeValue;
this.callback = callback;
try {
@@ -95,7 +151,7 @@ public class ClickCallbackProviderImpl implements ClickCallback.Provider {
this.id = id;
}
public void takeUse() {
private void takeUse() {
if (this.remainingUses != ClickCallback.UNLIMITED_USES) {
this.remainingUses--;
}
@@ -110,11 +166,11 @@ public class ClickCallbackProviderImpl implements ClickCallback.Provider {
return System.nanoTime() - this.startedAt >= this.lifetime;
}
public boolean valid() {
return hasRemainingUses() && !expired();
private boolean valid() {
return this.hasRemainingUses() && !this.expired();
}
public UUID id() {
public I id() {
return this.id;
}
}
@@ -3,26 +3,31 @@ package io.papermc.paper.connection;
import com.destroystokyo.paper.profile.CraftPlayerProfile;
import com.destroystokyo.paper.profile.PlayerProfile;
import io.papermc.paper.adventure.PaperAdventure;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.dialog.PaperDialog;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.dialog.DialogLike;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.pointer.Pointers;
import net.kyori.adventure.resource.ResourcePackCallback;
import net.kyori.adventure.resource.ResourcePackInfo;
import net.kyori.adventure.resource.ResourcePackRequest;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.common.ClientboundResourcePackPopPacket;
import net.minecraft.network.protocol.common.ClientboundResourcePackPushPacket;
import net.minecraft.network.protocol.common.ClientboundShowDialogPacket;
import net.minecraft.network.protocol.configuration.ClientboundResetChatPacket;
import net.minecraft.server.level.ClientInformation;
import net.minecraft.server.network.ConfigurationTask;
import net.minecraft.server.network.ServerConfigurationPacketListenerImpl;
import org.jspecify.annotations.Nullable;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
public class PaperPlayerConfigurationConnection extends PaperCommonConnection<ServerConfigurationPacketListenerImpl> implements PlayerConfigurationConnection, Audience, PluginMessageBridgeImpl {
private @Nullable Pointers adventurePointers;
@@ -37,13 +42,13 @@ public class PaperPlayerConfigurationConnection extends PaperCommonConnection<Se
}
@Override
public void sendResourcePacks(ResourcePackRequest request) {
final List<ClientboundResourcePackPushPacket> packs = new java.util.ArrayList<>(request.packs().size());
public void sendResourcePacks(final ResourcePackRequest request) {
final List<ClientboundResourcePackPushPacket> packs = new ArrayList<>(request.packs().size());
if (request.replace()) {
this.clearResourcePacks();
}
final net.minecraft.network.chat.Component prompt = PaperAdventure.asVanilla(request.prompt());
for (final java.util.Iterator<ResourcePackInfo> iter = request.packs().iterator(); iter.hasNext(); ) {
final Component prompt = PaperAdventure.asVanilla(request.prompt());
for (final Iterator<ResourcePackInfo> iter = request.packs().iterator(); iter.hasNext(); ) {
final ResourcePackInfo pack = iter.next();
packs.add(new ClientboundResourcePackPushPacket(pack.id(), pack.uri().toASCIIString(), pack.hash(), request.required(), iter.hasNext() ? Optional.empty() : Optional.ofNullable(prompt)));
if (request.callback() != ResourcePackCallback.noOp()) {
@@ -54,7 +59,7 @@ public class PaperPlayerConfigurationConnection extends PaperCommonConnection<Se
}
@Override
public void removeResourcePacks(UUID id, UUID... others) {
public void removeResourcePacks(final UUID id, final UUID... others) {
net.kyori.adventure.util.MonkeyBars.nonEmptyArrayToList(pack -> new ClientboundResourcePackPopPacket(Optional.of(pack)), id, others).forEach(this.handle::send);
}
@@ -63,13 +68,18 @@ public class PaperPlayerConfigurationConnection extends PaperCommonConnection<Se
this.handle.send(new ClientboundResourcePackPopPacket(Optional.empty()));
}
@Override
public void showDialog(final DialogLike dialog) {
this.handle.send(new ClientboundShowDialogPacket(PaperDialog.bukkitToMinecraftHolder((Dialog) dialog)));
}
@Override
public Pointers pointers() {
if (this.adventurePointers == null) {
this.adventurePointers = Pointers.builder()
.withDynamic(Identity.NAME, () -> this.handle.getOwner().getName())
.withDynamic(Identity.UUID, () -> this.handle.getOwner().getId())
.build();
.withDynamic(Identity.NAME, () -> this.handle.getOwner().getName())
.withDynamic(Identity.UUID, () -> this.handle.getOwner().getId())
.build();
}
return this.adventurePointers;
@@ -92,7 +102,7 @@ public class PaperPlayerConfigurationConnection extends PaperCommonConnection<Se
@Override
public void completeReconfiguration() {
ConfigurationTask task = this.handle.currentTask;
final ConfigurationTask task = this.handle.currentTask;
if (task != null) {
// This means that the player is going through the normal configuration process, or is already returning to the game phase.
// Be safe and just ignore, as many plugins may call this.
@@ -0,0 +1,29 @@
package io.papermc.paper.dialog;
import io.papermc.paper.registry.HolderableBase;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
import org.bukkit.craftbukkit.CraftRegistry;
public final class PaperDialog extends HolderableBase<net.minecraft.server.dialog.Dialog> implements Dialog {
public static net.minecraft.server.dialog.Dialog bukkitToMinecraft(final Dialog bukkit) {
return CraftRegistry.bukkitToMinecraft(bukkit);
}
public static Holder<net.minecraft.server.dialog.Dialog> bukkitToMinecraftHolder(final Dialog bukkit) {
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
}
public static Dialog minecraftToBukkit(final net.minecraft.server.dialog.Dialog minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.DIALOG);
}
public static Dialog minecraftHolderToBukkit(final Holder<net.minecraft.server.dialog.Dialog> minecraft) {
return CraftRegistry.minecraftHolderToBukkit(minecraft, Registries.DIALOG);
}
public PaperDialog(final Holder<net.minecraft.server.dialog.Dialog> holder) {
super(holder);
}
}
@@ -0,0 +1,48 @@
package io.papermc.paper.dialog;
import io.papermc.paper.adventure.PaperAdventure;
import net.kyori.adventure.nbt.api.BinaryTagHolder;
import net.minecraft.nbt.CompoundTag;
import org.jspecify.annotations.Nullable;
public class PaperDialogResponseView implements DialogResponseView {
private final CompoundTag payload;
private PaperDialogResponseView(final CompoundTag payload) {
this.payload = payload;
}
public static DialogResponseView createUnvalidatedResponse(final CompoundTag tag) {
return new PaperDialogResponseView(tag);
}
@Override
public BinaryTagHolder payload() {
return BinaryTagHolder.encode(this.payload, PaperAdventure.NBT_CODEC);
}
@Override
public @Nullable String getText(final String key) {
if (!this.payload.contains(key)) {
return null;
}
return this.payload.getString(key).orElse(null);
}
@Override
public @Nullable Boolean getBoolean(final String key) {
if (!this.payload.contains(key)) {
return null;
}
return this.payload.getBoolean(key).orElse(null);
}
@Override
public @Nullable Float getFloat(final String key) {
if (!this.payload.contains(key)) {
return null;
}
return this.payload.getFloat(key).orElse(null);
}
}
@@ -0,0 +1,44 @@
package io.papermc.paper.event.player;
import io.papermc.paper.adventure.PaperAdventure;
import io.papermc.paper.connection.PlayerCommonConnection;
import io.papermc.paper.dialog.DialogResponseView;
import io.papermc.paper.dialog.PaperDialogResponseView;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.nbt.api.BinaryTagHolder;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import org.bukkit.entity.Player;
import org.jspecify.annotations.Nullable;
public class PaperPlayerCustomClickEvent extends PlayerCustomClickEvent {
private final @Nullable Tag payload;
private @Nullable BinaryTagHolder apiPayload;
private @Nullable DialogResponseView rawResponse;
public PaperPlayerCustomClickEvent(final Key key, final PlayerCommonConnection commonConnection, final @Nullable Tag payload) {
super(key, commonConnection);
this.payload = payload;
}
@Override
public @Nullable BinaryTagHolder getTag() {
if (this.apiPayload == null && this.payload != null) {
this.apiPayload = BinaryTagHolder.encode(this.payload, PaperAdventure.NBT_CODEC);
}
return this.apiPayload;
}
@Override
public @Nullable DialogResponseView getDialogResponseView() {
if (this.payload == null || !(this.payload instanceof final CompoundTag compoundPayload)) {
return null;
}
if (this.rawResponse == null) {
this.rawResponse = PaperDialogResponseView.createUnvalidatedResponse(compoundPayload);
}
return this.rawResponse;
}
}
@@ -0,0 +1,4 @@
@NullMarked
package io.papermc.paper.event.player;
import org.jspecify.annotations.NullMarked;
@@ -4,6 +4,8 @@ import com.google.common.base.Preconditions;
import io.papermc.paper.adventure.PaperAdventure;
import io.papermc.paper.datacomponent.DataComponentTypes;
import io.papermc.paper.datacomponent.PaperDataComponentType;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.dialog.PaperDialog;
import io.papermc.paper.registry.data.PaperBannerPatternRegistryEntry;
import io.papermc.paper.registry.data.PaperCatTypeRegistryEntry;
import io.papermc.paper.registry.data.PaperChickenVariantRegistryEntry;
@@ -18,6 +20,7 @@ import io.papermc.paper.registry.data.PaperPaintingVariantRegistryEntry;
import io.papermc.paper.registry.data.PaperPigVariantRegistryEntry;
import io.papermc.paper.registry.data.PaperSoundEventRegistryEntry;
import io.papermc.paper.registry.data.PaperWolfVariantRegistryEntry;
import io.papermc.paper.registry.data.dialog.PaperDialogRegistryEntry;
import io.papermc.paper.registry.entry.RegistryEntry;
import io.papermc.paper.registry.entry.RegistryEntryMeta;
import io.papermc.paper.registry.tag.TagKey;
@@ -132,6 +135,7 @@ public final class PaperRegistries {
start(Registries.CHICKEN_VARIANT, RegistryKey.CHICKEN_VARIANT).craft(Chicken.Variant.class, CraftChicken.CraftVariant::new).writable(PaperChickenVariantRegistryEntry.PaperBuilder::new),
start(Registries.COW_VARIANT, RegistryKey.COW_VARIANT).craft(Cow.Variant.class, CraftCow.CraftVariant::new).writable(PaperCowVariantRegistryEntry.PaperBuilder::new),
start(Registries.PIG_VARIANT, RegistryKey.PIG_VARIANT).craft(Pig.Variant.class, CraftPig.CraftVariant::new).writable(PaperPigVariantRegistryEntry.PaperBuilder::new),
start(Registries.DIALOG, RegistryKey.DIALOG).craft(Dialog.class, PaperDialog::new, true).writable(PaperDialogRegistryEntry.PaperBuilder::new),
// api-only
start(Registries.ENTITY_TYPE, RegistryKey.ENTITY_TYPE).apiOnly(PaperSimpleRegistry::entityType),
@@ -161,13 +165,13 @@ public final class PaperRegistries {
}
@SuppressWarnings("unchecked")
public static <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> RegistryEntryMeta.Buildable<M, T, B> getBuildableMeta(final ResourceKey<? extends Registry<M>> resourceKey) {
final RegistryEntry<M, T> entry = getEntry(resourceKey);
public static <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> RegistryEntryMeta.Buildable<M, T, B> getBuildableMeta(final RegistryKey<T> registryKey) {
final RegistryEntry<M, T> entry = getEntry(registryKey);
if (entry == null) {
throw new IllegalArgumentException("No registry entry for " + resourceKey);
throw new IllegalArgumentException("No registry entry for " + registryKey);
}
if (!(entry.meta() instanceof final RegistryEntryMeta.Buildable<M, T, ?> buildableMeta)) {
throw new IllegalArgumentException("Registry entry for " + resourceKey + " is not buildable");
throw new IllegalArgumentException("Registry entry for " + registryKey + " is not buildable");
}
return (RegistryEntryMeta.Buildable<M, T, B>) buildableMeta;
}
@@ -1,15 +1,22 @@
package io.papermc.paper.registry.data;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.registry.RegistryBuilderFactory;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.dialog.DialogRegistryEntry;
import io.papermc.paper.registry.data.util.Conversions;
import java.util.function.Consumer;
import net.minecraft.core.registries.Registries;
import org.bukkit.MusicInstrument;
public final class InlinedRegistryBuilderProviderImpl implements InlinedRegistryBuilderProvider {
@Override
public MusicInstrument createInstrument(final Consumer<RegistryBuilderFactory<MusicInstrument, ? extends InstrumentRegistryEntry.Builder>> value) {
return Conversions.global().createApiInstanceFromBuilder(Registries.INSTRUMENT, value);
return Conversions.global().createApiInstanceFromBuilder(RegistryKey.INSTRUMENT, value);
}
@Override
public Dialog createDialog(final Consumer<RegistryBuilderFactory<Dialog, ? extends DialogRegistryEntry.Builder>> value) {
return Conversions.global().createApiInstanceFromBuilder(RegistryKey.DIALOG, value);
}
}
@@ -3,13 +3,13 @@ package io.papermc.paper.registry.data;
import io.papermc.paper.registry.PaperRegistries;
import io.papermc.paper.registry.PaperRegistryBuilder;
import io.papermc.paper.registry.RegistryBuilderFactory;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.TypedKey;
import io.papermc.paper.registry.data.util.Conversions;
import io.papermc.paper.registry.holder.PaperRegistryHolders;
import io.papermc.paper.registry.holder.RegistryHolder;
import java.util.function.Consumer;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.chat.Component;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.item.Instrument;
@@ -77,7 +77,7 @@ public class PaperInstrumentRegistryEntry implements InstrumentRegistryEntry {
@Override
public Builder soundEvent(final Consumer<RegistryBuilderFactory<Sound, ? extends SoundEventRegistryEntry.Builder>> soundEvent) {
this.soundEvent = this.conversions.createHolderFromBuilder(Registries.SOUND_EVENT, asArgument(soundEvent, "soundEvent"));
this.soundEvent = this.conversions.createHolderFromBuilder(RegistryKey.SOUND_EVENT, asArgument(soundEvent, "soundEvent"));
return this;
}
@@ -3,6 +3,7 @@ package io.papermc.paper.registry.data;
import io.papermc.paper.registry.PaperRegistries;
import io.papermc.paper.registry.PaperRegistryBuilder;
import io.papermc.paper.registry.RegistryBuilderFactory;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.TypedKey;
import io.papermc.paper.registry.data.util.Conversions;
import io.papermc.paper.registry.holder.PaperRegistryHolders;
@@ -10,7 +11,6 @@ import io.papermc.paper.registry.holder.RegistryHolder;
import java.util.OptionalInt;
import java.util.function.Consumer;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.chat.Component;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.item.JukeboxSong;
@@ -80,7 +80,7 @@ public class PaperJukeboxSongRegistryEntry implements JukeboxSongRegistryEntry {
@Override
public JukeboxSongRegistryEntry.Builder soundEvent(final Consumer<RegistryBuilderFactory<Sound, ? extends SoundEventRegistryEntry.Builder>> soundEvent) {
this.soundEvent = this.conversions.createHolderFromBuilder(Registries.SOUND_EVENT, asArgument(soundEvent, "soundEvent"));
this.soundEvent = this.conversions.createHolderFromBuilder(RegistryKey.SOUND_EVENT, asArgument(soundEvent, "soundEvent"));
return this;
}
@@ -0,0 +1,46 @@
package io.papermc.paper.registry.data.dialog;
import com.google.common.base.Preconditions;
import io.papermc.paper.registry.data.dialog.action.DialogAction;
import net.kyori.adventure.text.Component;
import net.minecraft.server.dialog.CommonButtonData;
import org.jspecify.annotations.Nullable;
public record ActionButtonImpl(Component label, @Nullable Component tooltip, int width, @Nullable DialogAction action) implements ActionButton {
public static final class BuilderImpl implements ActionButton.Builder {
private final Component label;
private @Nullable Component tooltip;
private int width = CommonButtonData.DEFAULT_WIDTH;
private @Nullable DialogAction action;
public BuilderImpl(final Component label) {
this.label = label;
}
@Override
public Builder tooltip(final @Nullable Component tooltip) {
this.tooltip = tooltip;
return this;
}
@Override
public Builder width(final int width) {
Preconditions.checkArgument(width >= 1 && width <= 1024, "Width must be between 1 and 1024");
this.width = width;
return this;
}
@Override
public Builder action(final @Nullable DialogAction action) {
this.action = action;
return this;
}
@Override
public ActionButton build() {
return new ActionButtonImpl(this.label, this.tooltip, this.width, this.action);
}
}
}
@@ -0,0 +1,83 @@
package io.papermc.paper.registry.data.dialog;
import com.google.common.base.Preconditions;
import io.papermc.paper.registry.data.dialog.body.DialogBody;
import io.papermc.paper.registry.data.dialog.input.DialogInput;
import java.util.Collections;
import java.util.List;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.Nullable;
public record DialogBaseImpl(
Component title,
@Nullable Component externalTitle,
boolean canCloseWithEscape,
boolean pause,
DialogAfterAction afterAction,
@Unmodifiable List<DialogBody> body,
@Unmodifiable List<DialogInput> inputs
) implements DialogBase {
public DialogBaseImpl {
Preconditions.checkArgument(!pause || afterAction != DialogAfterAction.NONE);
body = List.copyOf(body);
inputs = List.copyOf(inputs);
}
public static final class BuilderImpl implements DialogBase.Builder {
private final Component title;
private @Nullable Component externalTitle;
private boolean canCloseWithEscape = true;
private boolean pause = true;
private DialogAfterAction afterAction = DialogAfterAction.CLOSE;
private List<DialogBody> body = Collections.emptyList();
private List<DialogInput> inputs = Collections.emptyList();
public BuilderImpl(final Component title) {
this.title = title;
}
@Override
public BuilderImpl externalTitle(final @Nullable Component externalTitle) {
this.externalTitle = externalTitle;
return this;
}
@Override
public BuilderImpl canCloseWithEscape(final boolean canCloseWithEscape) {
this.canCloseWithEscape = canCloseWithEscape;
return this;
}
@Override
public BuilderImpl pause(final boolean pause) {
this.pause = pause;
return this;
}
@Override
public BuilderImpl afterAction(final DialogAfterAction afterAction) {
this.afterAction = afterAction;
return this;
}
@Override
public BuilderImpl body(final List<? extends DialogBody> body) {
this.body = List.copyOf(body);
return this;
}
@Override
public BuilderImpl inputs(final List<? extends DialogInput> inputs) {
this.inputs = List.copyOf(inputs);
return this;
}
@Override
public DialogBase build() {
return new DialogBaseImpl(this.title, this.externalTitle, this.canCloseWithEscape, this.pause, this.afterAction, this.body, this.inputs);
}
}
}
@@ -0,0 +1,186 @@
package io.papermc.paper.registry.data.dialog;
import com.mojang.serialization.Codec;
import com.mojang.serialization.Lifecycle;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import io.papermc.paper.adventure.AdventureCodecs;
import io.papermc.paper.dialog.PaperDialog;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.dialog.action.DialogAction;
import io.papermc.paper.registry.data.dialog.body.DialogBody;
import io.papermc.paper.registry.data.dialog.body.ItemDialogBody;
import io.papermc.paper.registry.data.dialog.body.PlainMessageDialogBody;
import io.papermc.paper.registry.data.dialog.input.BooleanDialogInput;
import io.papermc.paper.registry.data.dialog.input.DialogInput;
import io.papermc.paper.registry.data.dialog.input.NumberRangeDialogInput;
import io.papermc.paper.registry.data.dialog.input.SingleOptionDialogInput;
import io.papermc.paper.registry.data.dialog.input.TextDialogInput;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import net.minecraft.Util;
import net.minecraft.core.MappedRegistry;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.dialog.CommonButtonData;
import net.minecraft.server.dialog.Dialog;
import net.minecraft.server.dialog.action.ParsedTemplate;
import net.minecraft.server.dialog.body.PlainMessage;
import net.minecraft.server.dialog.input.TextInput;
import net.minecraft.util.ExtraCodecs;
import net.minecraft.world.item.ItemStack;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import static io.papermc.paper.util.PaperCodecs.registryFileDecoderFor;
public final class PaperDialogCodecs {
private PaperDialogCodecs() {
}
// dialog actions
private static final MapCodec<DialogAction.CommandTemplateAction> COMMAND_TEMPLATE_ACTION_CODEC = RecordCodecBuilder.mapCodec(instance ->
instance.group(Codec.STRING.fieldOf("template").forGetter(DialogAction.CommandTemplateAction::template))
.apply(instance, DialogAction::commandTemplate)
);
private static final MapCodec<DialogAction.CustomClickAction> CUSTOM_ALL_ACTION_CODEC = RecordCodecBuilder.mapCodec(instance ->
instance.group(AdventureCodecs.KEY_CODEC.fieldOf("id").forGetter(DialogAction.CustomClickAction::id), AdventureCodecs.BINARY_TAG_HOLDER_COMPOUND_CODEC.optionalFieldOf("additions").forGetter(action -> Optional.ofNullable(action.additions())))
.apply(instance, (key, binaryTagHolder) -> DialogAction.customClick(key, binaryTagHolder.orElse(null)))
);
private static final Map<AdventureCodecs.ClickEventType, MapCodec<DialogAction.StaticAction>> STATIC_ACTION_CODECS = Arrays.stream(AdventureCodecs.CLICK_EVENT_TYPES.get()).collect(Collectors.toMap(Function.identity(), type -> type.codec().xmap(DialogAction::staticAction, DialogAction.StaticAction::value)));
private static final Registry<MapCodec<? extends DialogAction>> DIALOG_ACTION_TYPES = Util.make(() -> {
final MappedRegistry<MapCodec<? extends DialogAction>> registry = new MappedRegistry<>(ResourceKey.createRegistryKey(ResourceLocation.fromNamespaceAndPath(ResourceLocation.PAPER_NAMESPACE, "dialog_action_type")), Lifecycle.experimental());
STATIC_ACTION_CODECS.forEach((clickType, actionCodec) -> {
Registry.register(registry, clickType.getSerializedName(), actionCodec);
});
Registry.register(registry, "dynamic/run_command", COMMAND_TEMPLATE_ACTION_CODEC);
Registry.register(registry, "dynamic/custom", CUSTOM_ALL_ACTION_CODEC);
return registry.freeze();
});
private static final Function<DialogAction, MapCodec<? extends DialogAction>> GET_DIALOG_ACTION_TYPE = dialogAction -> switch (dialogAction) {
case DialogAction.CommandTemplateAction $ -> COMMAND_TEMPLATE_ACTION_CODEC;
case DialogAction.CustomClickAction $ -> CUSTOM_ALL_ACTION_CODEC;
case DialogAction.StaticAction action -> STATIC_ACTION_CODECS.get(AdventureCodecs.GET_CLICK_EVENT_TYPE.apply(action.value()));
};
private static final Codec<DialogAction> DIALOG_ACTION_CODEC = DIALOG_ACTION_TYPES.byNameCodec().dispatch(GET_DIALOG_ACTION_TYPE, Function.identity());
// buttons
public static final Codec<ActionButton> ACTION_BUTTON_CODEC = RecordCodecBuilder.create(instance -> instance.group(
AdventureCodecs.COMPONENT_CODEC.fieldOf("label").forGetter(ActionButton::label),
AdventureCodecs.COMPONENT_CODEC.optionalFieldOf("tooltip").forGetter(button -> Optional.ofNullable(button.tooltip())),
Dialog.WIDTH_CODEC.optionalFieldOf("width", CommonButtonData.DEFAULT_WIDTH).forGetter(ActionButton::width),
DIALOG_ACTION_CODEC.optionalFieldOf("action").forGetter(button -> Optional.ofNullable(button.action()))
).apply(instance, (label, tooltip, width, action) -> ActionButton.create(label, tooltip.orElse(null), width, action.orElse(null))));
// dialog bodies
private static final MapCodec<PlainMessageDialogBody> PLAIN_MESSAGE_BODY_CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
AdventureCodecs.COMPONENT_CODEC.fieldOf("contents").forGetter(PlainMessageDialogBody::contents),
Dialog.WIDTH_CODEC.optionalFieldOf("width", PlainMessage.DEFAULT_WIDTH).forGetter(PlainMessageDialogBody::width)
).apply(instance, DialogBody::plainMessage)
);
private static final Codec<PlainMessageDialogBody> SIMPLE_PLAIN_MESSAGE_BODY_CODEC = Codec.withAlternative(PLAIN_MESSAGE_BODY_CODEC.codec(), AdventureCodecs.COMPONENT_CODEC, component -> DialogBody.plainMessage(component, PlainMessage.DEFAULT_WIDTH));
private static final MapCodec<ItemDialogBody> ITEM_BODY_CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
ItemStack.STRICT_CODEC.xmap(CraftItemStack::asBukkitCopy, CraftItemStack::asNMSCopy).fieldOf("item").forGetter(ItemDialogBody::item),
SIMPLE_PLAIN_MESSAGE_BODY_CODEC.optionalFieldOf("description").forGetter(body -> Optional.ofNullable(body.description())),
Codec.BOOL.optionalFieldOf("show_decorations", true).forGetter(ItemDialogBody::showDecorations),
Codec.BOOL.optionalFieldOf("show_tooltip", true).forGetter(ItemDialogBody::showTooltip),
ExtraCodecs.intRange(1, 256).optionalFieldOf("width", 16).forGetter(ItemDialogBody::width),
ExtraCodecs.intRange(1, 256).optionalFieldOf("height", 16).forGetter(ItemDialogBody::height)
).apply(instance, (itemStack, plainMessageBody, showDecorations, showTooltip, width, height) -> DialogBody.item(itemStack, plainMessageBody.orElse(null), showDecorations, showTooltip, width, height))
);
private static final Registry<MapCodec<? extends DialogBody>> DIALOG_BODY_TYPES = Util.make(() -> {
final MappedRegistry<MapCodec<? extends DialogBody>> registry = new MappedRegistry<>(ResourceKey.createRegistryKey(ResourceLocation.fromNamespaceAndPath(ResourceLocation.PAPER_NAMESPACE, "dialog_body_type")), Lifecycle.experimental());
Registry.register(registry, "item", ITEM_BODY_CODEC);
Registry.register(registry, "plain_message", PLAIN_MESSAGE_BODY_CODEC);
return registry.freeze();
});
private static final Function<DialogBody, MapCodec<? extends DialogBody>> GET_DIALOG_BODY_TYPE = dialogAction -> switch (dialogAction) {
case PlainMessageDialogBody $ -> PLAIN_MESSAGE_BODY_CODEC;
case ItemDialogBody $ -> ITEM_BODY_CODEC;
};
private static final Codec<DialogBody> DIALOG_BODY_CODEC = DIALOG_BODY_TYPES.byNameCodec().dispatch(GET_DIALOG_BODY_TYPE, Function.identity());
private static final Codec<List<DialogBody>> DIALOG_BODY_LIST_CODEC = ExtraCodecs.compactListCodec(DIALOG_BODY_CODEC);
// input types
private static final MapCodec<BooleanDialogInput> BOOLEAN_DIALOG_INPUT_TYPE_MAP_CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
ParsedTemplate.VARIABLE_CODEC.fieldOf("key").forGetter(DialogInput::key),
AdventureCodecs.COMPONENT_CODEC.fieldOf("label").forGetter(BooleanDialogInput::label),
Codec.BOOL.optionalFieldOf("initial", false).forGetter(BooleanDialogInput::initial),
Codec.STRING.optionalFieldOf("on_true", "true").forGetter(BooleanDialogInput::onTrue),
Codec.STRING.optionalFieldOf("on_false", "false").forGetter(BooleanDialogInput::onFalse)
).apply(instance, DialogInput::bool));
private static final MapCodec<NumberRangeDialogInput> NUMBER_RANGE_INPUT_MAP_CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
ParsedTemplate.VARIABLE_CODEC.fieldOf("key").forGetter(DialogInput::key),
Dialog.WIDTH_CODEC.optionalFieldOf("width", PlainMessage.DEFAULT_WIDTH).forGetter(NumberRangeDialogInput::width),
AdventureCodecs.COMPONENT_CODEC.fieldOf("label").forGetter(NumberRangeDialogInput::label),
Codec.STRING.optionalFieldOf("label_format", "options.generic_value").forGetter(NumberRangeDialogInput::labelFormat),
Codec.FLOAT.fieldOf("start").forGetter(NumberRangeDialogInput::start),
Codec.FLOAT.fieldOf("end").forGetter(NumberRangeDialogInput::end),
Codec.FLOAT.optionalFieldOf("initial").forGetter(type -> Optional.ofNullable(type.initial())),
ExtraCodecs.POSITIVE_FLOAT.optionalFieldOf("step").forGetter(type -> Optional.ofNullable(type.step()))
).apply(instance, (key, width, label, labelFormat, start, end, initial, step) -> DialogInput.numberRange(key, width, label, labelFormat, start, end, initial.orElse(null), step.orElse(null))));
private static final Codec<SingleOptionDialogInput.OptionEntry> SINGLE_OPTION_DIALOG_INPUT_ENTRY_FULL_CODEC = RecordCodecBuilder.create(instance -> instance.group(
Codec.STRING.fieldOf("id").forGetter(SingleOptionDialogInput.OptionEntry::id),
AdventureCodecs.COMPONENT_CODEC.optionalFieldOf("display").forGetter(entry -> Optional.ofNullable(entry.display())),
Codec.BOOL.optionalFieldOf("initial", false).forGetter(SingleOptionDialogInput.OptionEntry::initial)
).apply(instance, (id, display, initial) -> SingleOptionDialogInput.OptionEntry.create(id, display.orElse(null), initial)));
private static final Codec<SingleOptionDialogInput.OptionEntry> SINGLE_OPTION_DIALOG_INPUT_ENTRY_CODEC = Codec.withAlternative(
SINGLE_OPTION_DIALOG_INPUT_ENTRY_FULL_CODEC, Codec.STRING, string -> SingleOptionDialogInput.OptionEntry.create(string, null, false)
);
private static final MapCodec<SingleOptionDialogInput> SINGLE_OPTION_DIALOG_INPUT_TYPE_MAP_CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
ParsedTemplate.VARIABLE_CODEC.fieldOf("key").forGetter(DialogInput::key),
Dialog.WIDTH_CODEC.optionalFieldOf("width", PlainMessage.DEFAULT_WIDTH).forGetter(SingleOptionDialogInput::width),
ExtraCodecs.nonEmptyList(SINGLE_OPTION_DIALOG_INPUT_ENTRY_CODEC.listOf()).fieldOf("options").forGetter(SingleOptionDialogInput::entries),
AdventureCodecs.COMPONENT_CODEC.fieldOf("label").forGetter(SingleOptionDialogInput::label),
Codec.BOOL.optionalFieldOf("label_visible", true).forGetter(SingleOptionDialogInput::labelVisible)
).apply(instance, DialogInput::singleOption));
private static final Codec<TextDialogInput.MultilineOptions> TEXT_DIALOG_INPUT_MULTILINE_OPTIONS_CODEC = RecordCodecBuilder.create(instance -> instance.group(
ExtraCodecs.POSITIVE_INT.optionalFieldOf("max_lines").forGetter(options -> Optional.ofNullable(options.maxLines())),
ExtraCodecs.intRange(1, TextInput.MultilineOptions.MAX_HEIGHT).optionalFieldOf("height").forGetter(options -> Optional.ofNullable(options.height()))
).apply(instance, (maxLines, height) -> TextDialogInput.MultilineOptions.create(maxLines.orElse(null), height.orElse(null))));
private static final MapCodec<TextDialogInput> TEXT_DIALOG_INPUT_TYPE_MAP_CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
ParsedTemplate.VARIABLE_CODEC.fieldOf("key").forGetter(DialogInput::key),
Dialog.WIDTH_CODEC.optionalFieldOf("width", PlainMessage.DEFAULT_WIDTH).forGetter(TextDialogInput::width),
AdventureCodecs.COMPONENT_CODEC.fieldOf("label").forGetter(TextDialogInput::label),
Codec.BOOL.optionalFieldOf("label_visible", true).forGetter(TextDialogInput::labelVisible),
Codec.STRING.optionalFieldOf("initial", "").forGetter(TextDialogInput::initial),
ExtraCodecs.POSITIVE_INT.optionalFieldOf("max_length", 32).forGetter(TextDialogInput::maxLength),
TEXT_DIALOG_INPUT_MULTILINE_OPTIONS_CODEC.optionalFieldOf("multiline").forGetter(inputType -> Optional.ofNullable(inputType.multiline()))
).apply(instance, (key, width, label, labelVisible, initial, maxLength, multilineOptions) ->
DialogInput.text(key, width, label, labelVisible, initial, maxLength, multilineOptions.orElse(null))
));
private static final Registry<MapCodec<? extends DialogInput>> DIALOG_INPUT_TYPES = Util.make(() -> {
final MappedRegistry<MapCodec<? extends DialogInput>> registry = new MappedRegistry<>(ResourceKey.createRegistryKey(ResourceLocation.fromNamespaceAndPath(ResourceLocation.PAPER_NAMESPACE, "dialog_input_type")), Lifecycle.experimental());
Registry.register(registry, "boolean", BOOLEAN_DIALOG_INPUT_TYPE_MAP_CODEC);
Registry.register(registry, "number_range", NUMBER_RANGE_INPUT_MAP_CODEC);
Registry.register(registry, "single_option", SINGLE_OPTION_DIALOG_INPUT_TYPE_MAP_CODEC);
Registry.register(registry, "text", TEXT_DIALOG_INPUT_TYPE_MAP_CODEC);
return registry.freeze();
});
private static final Function<DialogInput, MapCodec<? extends DialogInput>> GET_DIALOG_INPUT_TYPE_TYPE = dialogAction -> switch (dialogAction) {
case TextDialogInput $ -> TEXT_DIALOG_INPUT_TYPE_MAP_CODEC;
case SingleOptionDialogInput $ -> SINGLE_OPTION_DIALOG_INPUT_TYPE_MAP_CODEC;
case NumberRangeDialogInput $ -> NUMBER_RANGE_INPUT_MAP_CODEC;
case BooleanDialogInput $ -> BOOLEAN_DIALOG_INPUT_TYPE_MAP_CODEC;
};
private static final Codec<DialogInput> DIALOG_INPUT_CODEC = DIALOG_INPUT_TYPES.byNameCodec().dispatchMap(GET_DIALOG_INPUT_TYPE_TYPE, Function.identity()).codec();
// dialog base / common dialog data
public static final Codec<DialogBase> DIALOG_BASE_CODEC = RecordCodecBuilder.<DialogBase>mapCodec(instance -> instance.group(
AdventureCodecs.COMPONENT_CODEC.fieldOf("title").forGetter(DialogBase::title),
AdventureCodecs.COMPONENT_CODEC.optionalFieldOf("external_title").forGetter(base -> Optional.ofNullable(base.externalTitle())),
Codec.BOOL.optionalFieldOf("can_close_with_escape", true).forGetter(DialogBase::canCloseWithEscape),
Codec.BOOL.optionalFieldOf("pause", true).forGetter(DialogBase::pause),
AdventureCodecs.indexCodec(DialogBase.DialogAfterAction.NAMES).optionalFieldOf("after_action", DialogBase.DialogAfterAction.CLOSE).forGetter(DialogBase::afterAction),
DIALOG_BODY_LIST_CODEC.optionalFieldOf("body", List.of()).forGetter(DialogBase::body),
DIALOG_INPUT_CODEC.listOf().optionalFieldOf("inputs", List.of()).forGetter(DialogBase::inputs)
).apply(instance, (title, externalTitle, canCloseWithEsc, pause, afterAction, body, inputs) -> DialogBase.create(title, externalTitle.orElse(null), canCloseWithEsc, pause, afterAction, body, inputs))
).codec();
public static final Codec<io.papermc.paper.dialog.Dialog> DIALOG_CODEC = Codec.of(Dialog.CODEC.comap(PaperDialog::bukkitToMinecraftHolder), registryFileDecoderFor(Dialog.DIRECT_CODEC, PaperDialog::minecraftHolderToBukkit, RegistryKey.DIALOG, true));
}
@@ -0,0 +1,155 @@
package io.papermc.paper.registry.data.dialog;
import io.papermc.paper.adventure.PaperAdventure;
import io.papermc.paper.adventure.providers.ClickCallbackProviderImpl;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.registry.data.dialog.action.CommandTemplateActionImpl;
import io.papermc.paper.registry.data.dialog.action.CustomClickActionImpl;
import io.papermc.paper.registry.data.dialog.action.DialogAction;
import io.papermc.paper.registry.data.dialog.action.DialogActionCallback;
import io.papermc.paper.registry.data.dialog.action.StaticActionImpl;
import io.papermc.paper.registry.data.dialog.body.ItemDialogBody;
import io.papermc.paper.registry.data.dialog.body.ItemDialogBodyImpl;
import io.papermc.paper.registry.data.dialog.body.PlainMessageBodyImpl;
import io.papermc.paper.registry.data.dialog.body.PlainMessageDialogBody;
import io.papermc.paper.registry.data.dialog.input.BooleanDialogInput;
import io.papermc.paper.registry.data.dialog.input.BooleanDialogInputImpl;
import io.papermc.paper.registry.data.dialog.input.NumberRangeDialogInput;
import io.papermc.paper.registry.data.dialog.input.NumberRangeDialogInputImpl;
import io.papermc.paper.registry.data.dialog.input.SingleOptionDialogInput;
import io.papermc.paper.registry.data.dialog.input.SingleOptionDialogInputImpl;
import io.papermc.paper.registry.data.dialog.input.TextDialogInput;
import io.papermc.paper.registry.data.dialog.input.TextDialogInputImpl;
import io.papermc.paper.registry.data.dialog.type.ConfirmationType;
import io.papermc.paper.registry.data.dialog.type.ConfirmationTypeImpl;
import io.papermc.paper.registry.data.dialog.type.DialogListType;
import io.papermc.paper.registry.data.dialog.type.DialogListTypeImpl;
import io.papermc.paper.registry.data.dialog.type.MultiActionType;
import io.papermc.paper.registry.data.dialog.type.MultiActionTypeImpl;
import io.papermc.paper.registry.data.dialog.type.NoticeType;
import io.papermc.paper.registry.data.dialog.type.NoticeTypeImpl;
import io.papermc.paper.registry.data.dialog.type.ServerLinksType;
import io.papermc.paper.registry.data.dialog.type.ServerLinksTypeImpl;
import io.papermc.paper.registry.set.RegistrySet;
import java.util.List;
import java.util.UUID;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.nbt.api.BinaryTagHolder;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickCallback;
import net.kyori.adventure.text.event.ClickEvent;
import net.minecraft.core.UUIDUtil;
import net.minecraft.nbt.CompoundTag;
import org.bukkit.inventory.ItemStack;
import org.jspecify.annotations.Nullable;
public final class PaperDialogInstancesProvider implements DialogInstancesProvider {
@Override
public DialogBase.Builder dialogBaseBuilder(final Component title) {
return new DialogBaseImpl.BuilderImpl(title);
}
@Override
public ActionButton.Builder actionButtonBuilder(final Component label) {
return new ActionButtonImpl.BuilderImpl(label);
}
@Override
public DialogAction.CustomClickAction register(final DialogActionCallback callback, final ClickCallback.Options options) {
final UUID id = ClickCallbackProviderImpl.DIALOG_CLICK_MANAGER.addCallback(UUID.randomUUID(), callback, options);
final CompoundTag tag = new CompoundTag();
tag.store(ClickCallbackProviderImpl.ID_KEY, UUIDUtil.CODEC, id);
return DialogAction.customClick(ClickCallbackProviderImpl.DIALOG_CLICK_CALLBACK_KEY, BinaryTagHolder.encode(tag, PaperAdventure.NBT_CODEC));
}
@Override
public DialogAction.StaticAction staticAction(final ClickEvent value) {
return new StaticActionImpl(value);
}
@Override
public DialogAction.CommandTemplateAction commandTemplate(final String template) {
return new CommandTemplateActionImpl(template);
}
@Override
public DialogAction.CustomClickAction customClick(final Key id, final @Nullable BinaryTagHolder additions) {
return new CustomClickActionImpl(id, additions);
}
@Override
public ItemDialogBody.Builder itemDialogBodyBuilder(final ItemStack itemStack) {
return new ItemDialogBodyImpl.BuilderImpl(itemStack);
}
@Override
public PlainMessageDialogBody plainMessageDialogBody(final Component component) {
return new PlainMessageBodyImpl(component);
}
@Override
public PlainMessageDialogBody plainMessageDialogBody(final Component component, final int width) {
return new PlainMessageBodyImpl(component, width);
}
@Override
public BooleanDialogInput.Builder booleanBuilder(final String key, final Component label) {
return new BooleanDialogInputImpl.BuilderImpl(key, label);
}
@Override
public NumberRangeDialogInput.Builder numberRangeBuilder(final String key, final Component label, final float start, final float end) {
return new NumberRangeDialogInputImpl.BuilderImpl(key, label, start, end);
}
@Override
public SingleOptionDialogInput.Builder singleOptionBuilder(final String key, final Component label, final List<SingleOptionDialogInput.OptionEntry> entries) {
return new SingleOptionDialogInputImpl.BuilderImpl(key, entries, label);
}
@Override
public SingleOptionDialogInput.OptionEntry singleOptionEntry(final String id, final @Nullable Component display, final boolean initial) {
return new SingleOptionDialogInputImpl.SingleOptionEntryImpl(id, display, initial);
}
@Override
public TextDialogInput.Builder textBuilder(final String key, final Component label) {
return new TextDialogInputImpl.BuilderImpl(key, label);
}
@Override
public TextDialogInput.MultilineOptions multilineOptions(final @Nullable Integer maxLines, final @Nullable Integer height) {
return new TextDialogInputImpl.MultilineOptionsImpl(maxLines, height);
}
@Override
public ConfirmationType confirmation(final ActionButton yesButton, final ActionButton noButton) {
return new ConfirmationTypeImpl(yesButton, noButton);
}
@Override
public DialogListType.Builder dialogList(final RegistrySet<Dialog> dialogs) {
return new DialogListTypeImpl.BuilderImpl(dialogs);
}
@Override
public MultiActionType.Builder multiAction(final List<ActionButton> actions) {
return new MultiActionTypeImpl.BuilderImpl(actions);
}
@Override
public NoticeType notice() {
return new NoticeTypeImpl();
}
@Override
public NoticeType notice(final ActionButton action) {
return new NoticeTypeImpl(action);
}
@Override
public ServerLinksType serverLinks(final @Nullable ActionButton exitAction, final int columns, final int buttonWidth) {
return new ServerLinksTypeImpl(exitAction, columns, buttonWidth);
}
}
@@ -0,0 +1,74 @@
package io.papermc.paper.registry.data.dialog;
import io.papermc.paper.registry.PaperRegistryBuilder;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.dialog.type.DialogType;
import io.papermc.paper.registry.data.util.Conversions;
import io.papermc.paper.registry.set.RegistryValueSetBuilder;
import io.papermc.paper.registry.set.RegistryValueSetBuilderImpl;
import net.minecraft.server.dialog.CommonDialogData;
import net.minecraft.server.dialog.Dialog;
import org.jspecify.annotations.Nullable;
import static io.papermc.paper.registry.data.util.Checks.asArgument;
import static io.papermc.paper.registry.data.util.Checks.asConfigured;
public class PaperDialogRegistryEntry implements DialogRegistryEntry {
protected @Nullable DialogBase dialogBase;
protected @Nullable DialogType dialogType;
protected final Conversions conversions;
public PaperDialogRegistryEntry(
final Conversions conversions,
final @Nullable Dialog internal
) {
this.conversions = conversions;
if (internal == null) return;
final CommonDialogData common = internal.common();
this.dialogBase = conversions.convert(common, PaperDialogCodecs.DIALOG_BASE_CODEC, CommonDialogData.MAP_CODEC.codec());
this.dialogType = PaperDialogs.extractSpecialty(internal, conversions);
}
@Override
public DialogBase base() {
return asConfigured(this.dialogBase, "dialogBase");
}
@Override
public DialogType type() {
return asConfigured(this.dialogType, "dialogSpecialty");
}
public static final class PaperBuilder extends PaperDialogRegistryEntry implements Builder, PaperRegistryBuilder<Dialog, io.papermc.paper.dialog.Dialog> {
public PaperBuilder(final Conversions conversions, final @Nullable Dialog internal) {
super(conversions, internal);
}
@Override
public RegistryValueSetBuilder<io.papermc.paper.dialog.Dialog, Builder> registryValueSet() {
return new RegistryValueSetBuilderImpl<>(RegistryKey.DIALOG, this.conversions);
}
@Override
public Builder base(final DialogBase dialogBase) {
this.dialogBase = asArgument(dialogBase, "dialogBase");
return this;
}
@Override
public Builder type(final DialogType dialogType) {
this.dialogType = asArgument(dialogType, "dialogSpecialty");
return this;
}
@Override
public Dialog build() {
return PaperDialogs.constructDialog(this.base(), this.type(), this.conversions);
}
}
}
@@ -0,0 +1,84 @@
package io.papermc.paper.registry.data.dialog;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.dialog.type.ConfirmationType;
import io.papermc.paper.registry.data.dialog.type.DialogListType;
import io.papermc.paper.registry.data.dialog.type.DialogType;
import io.papermc.paper.registry.data.dialog.type.MultiActionType;
import io.papermc.paper.registry.data.dialog.type.NoticeType;
import io.papermc.paper.registry.data.dialog.type.ServerLinksType;
import io.papermc.paper.registry.data.util.Conversions;
import io.papermc.paper.registry.set.PaperRegistrySets;
import io.papermc.paper.registry.set.RegistrySet;
import java.util.Optional;
import java.util.function.Function;
import net.minecraft.core.registries.Registries;
import net.minecraft.server.dialog.CommonDialogData;
import net.minecraft.server.dialog.ConfirmationDialog;
import net.minecraft.server.dialog.Dialog;
import net.minecraft.server.dialog.DialogListDialog;
import net.minecraft.server.dialog.MultiActionDialog;
import net.minecraft.server.dialog.NoticeDialog;
import net.minecraft.server.dialog.ServerLinksDialog;
public final class PaperDialogs {
private PaperDialogs() {
}
public static DialogType extractSpecialty(final Dialog nmsDialog, final Conversions conversions) {
final Function<net.minecraft.server.dialog.ActionButton, ActionButton> convertButton = button -> conversions.convert(button, PaperDialogCodecs.ACTION_BUTTON_CODEC, net.minecraft.server.dialog.ActionButton.CODEC);
return switch (nmsDialog) {
case final ConfirmationDialog conf ->
DialogType.confirmation(convertButton.apply(conf.yesButton()), convertButton.apply(conf.noButton()));
case final DialogListDialog list -> {
final RegistrySet<io.papermc.paper.dialog.Dialog> apiSet = PaperRegistrySets.convertToApiWithDirects(RegistryKey.DIALOG, list.dialogs());
yield DialogType.dialogList(
apiSet,
list.exitAction().map(convertButton).orElse(null),
list.columns(),
list.buttonWidth()
);
}
case final MultiActionDialog multi ->
DialogType.multiAction(multi.actions().stream().map(convertButton).toList(), multi.exitAction().map(convertButton).orElse(null), multi.columns());
case final NoticeDialog notice -> DialogType.notice(convertButton.apply(notice.action()));
case final ServerLinksDialog links ->
DialogType.serverLinks(links.exitAction().map(convertButton).orElse(null), links.columns(), links.buttonWidth());
default -> throw new IllegalArgumentException("Unsupported dialog type: " + nmsDialog.getClass().getName());
};
}
public static Dialog constructDialog(final DialogBase dialogBase, final DialogType dialogType, final Conversions conversions) {
final Function<ActionButton, net.minecraft.server.dialog.ActionButton> convertButton = button -> conversions.convert(button, net.minecraft.server.dialog.ActionButton.CODEC, PaperDialogCodecs.ACTION_BUTTON_CODEC);
final CommonDialogData common = conversions.convert(dialogBase, CommonDialogData.MAP_CODEC.codec(), PaperDialogCodecs.DIALOG_BASE_CODEC);
switch (dialogType) {
case final ConfirmationType conf -> {
return new ConfirmationDialog(common, convertButton.apply(conf.yesButton()), convertButton.apply(conf.noButton()));
}
case final DialogListType list -> {
return new DialogListDialog(
common,
PaperRegistrySets.convertToNmsWithDirects(Registries.DIALOG, conversions.lookup(), list.dialogs()),
Optional.ofNullable(list.exitAction()).map(convertButton),
list.columns(),
list.buttonWidth()
);
}
case final MultiActionType multi -> {
return new MultiActionDialog(
common,
multi.actions().stream().map(convertButton).toList(),
Optional.ofNullable(multi.exitAction()).map(convertButton),
multi.columns()
);
}
case final NoticeType notice -> {
return new NoticeDialog(common, convertButton.apply(notice.action()));
}
case final ServerLinksType links -> {
return new ServerLinksDialog(common, Optional.ofNullable(links.exitAction()).map(convertButton), links.columns(), links.buttonWidth());
}
}
}
}
@@ -0,0 +1,10 @@
package io.papermc.paper.registry.data.dialog.action;
import net.minecraft.commands.functions.StringTemplate;
public record CommandTemplateActionImpl(String template) implements DialogAction.CommandTemplateAction {
public CommandTemplateActionImpl {
StringTemplate.fromString(template);
}
}
@@ -0,0 +1,24 @@
package io.papermc.paper.registry.data.dialog.action;
import com.google.common.base.Preconditions;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import io.papermc.paper.adventure.PaperAdventure;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.nbt.api.BinaryTagHolder;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import org.jspecify.annotations.Nullable;
public record CustomClickActionImpl(Key id, @Nullable BinaryTagHolder additions) implements DialogAction.CustomClickAction {
public CustomClickActionImpl {
if (additions != null) {
try {
final Tag tag = additions.get(PaperAdventure.NBT_CODEC);
Preconditions.checkArgument(tag instanceof CompoundTag, "Additions must be a compound tag");
} catch (final CommandSyntaxException e) {
throw new RuntimeException(e);
}
}
}
}
@@ -0,0 +1,11 @@
package io.papermc.paper.registry.data.dialog.action;
import com.google.common.base.Preconditions;
import net.kyori.adventure.text.event.ClickEvent;
public record StaticActionImpl(ClickEvent value) implements DialogAction.StaticAction {
public StaticActionImpl {
Preconditions.checkArgument(value.action().readable(), "action must be readable");
}
}
@@ -0,0 +1,63 @@
package io.papermc.paper.registry.data.dialog.body;
import com.google.common.base.Preconditions;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.inventory.ItemStack;
import org.jspecify.annotations.Nullable;
public record ItemDialogBodyImpl(
ItemStack item, @Nullable PlainMessageDialogBody description, boolean showDecorations, boolean showTooltip, int width, int height
) implements ItemDialogBody {
public static final class BuilderImpl implements ItemDialogBody.Builder {
private final ItemStack item;
private @Nullable PlainMessageDialogBody description;
private boolean showDecorations = true;
private boolean showTooltip = true;
private int width = 16;
private int height = 16;
public BuilderImpl(final ItemStack item) {
net.minecraft.world.item.ItemStack.validateStrict(CraftItemStack.unwrap(item)).getOrThrow();
this.item = item;
}
@Override
public ItemDialogBody.Builder description(final @Nullable PlainMessageDialogBody description) {
this.description = description;
return this;
}
@Override
public ItemDialogBody.Builder showDecorations(final boolean showDecorations) {
this.showDecorations = showDecorations;
return this;
}
@Override
public ItemDialogBody.Builder showTooltip(final boolean showTooltip) {
this.showTooltip = showTooltip;
return this;
}
@Override
public ItemDialogBody.Builder width(final int width) {
Preconditions.checkArgument(width >= 1 && width <= 256, "Width must be between 1 and 256");
this.width = width;
return this;
}
@Override
public ItemDialogBody.Builder height(final int height) {
Preconditions.checkArgument(this.width >= 1 && this.width <= 256, "Width must be between 1 and 256");
this.height = height;
return this;
}
@Override
public ItemDialogBody build() {
return new ItemDialogBodyImpl(this.item, this.description, this.showDecorations, this.showTooltip, this.width, this.height);
}
}
}
@@ -0,0 +1,16 @@
package io.papermc.paper.registry.data.dialog.body;
import com.google.common.base.Preconditions;
import net.kyori.adventure.text.Component;
import net.minecraft.server.dialog.body.PlainMessage;
public record PlainMessageBodyImpl(Component contents, int width) implements PlainMessageDialogBody {
public PlainMessageBodyImpl {
Preconditions.checkArgument(width >= 1 && width <= 1024, "Width must be between 1 and 1024");
}
public PlainMessageBodyImpl(final Component contents) {
this(contents, PlainMessage.DEFAULT_WIDTH);
}
}
@@ -0,0 +1,46 @@
package io.papermc.paper.registry.data.dialog.input;
import com.google.common.base.Preconditions;
import net.kyori.adventure.text.Component;
import net.minecraft.commands.functions.StringTemplate;
public record BooleanDialogInputImpl(String key, Component label, boolean initial, String onTrue, String onFalse) implements BooleanDialogInput {
public static final class BuilderImpl implements BooleanDialogInput.Builder {
private final String key;
private final Component label;
private boolean initial = false;
private String onTrue = "true";
private String onFalse = "false";
public BuilderImpl(final String key, final Component label) {
Preconditions.checkArgument(StringTemplate.isValidVariableName(key), "key must be a valid input name");
this.key = key;
this.label = label;
}
@Override
public BooleanDialogInput.Builder initial(final boolean initial) {
this.initial = initial;
return this;
}
@Override
public BooleanDialogInput.Builder onTrue(final String onTrue) {
this.onTrue = onTrue;
return this;
}
@Override
public BooleanDialogInput.Builder onFalse(final String onFalse) {
this.onFalse = onFalse;
return this;
}
@Override
public BooleanDialogInput build() {
return new BooleanDialogInputImpl(this.key, this.label, this.initial, this.onTrue, this.onFalse);
}
}
}
@@ -0,0 +1,73 @@
package io.papermc.paper.registry.data.dialog.input;
import com.google.common.base.Preconditions;
import net.kyori.adventure.text.Component;
import net.minecraft.commands.functions.StringTemplate;
import net.minecraft.server.dialog.body.PlainMessage;
import org.jspecify.annotations.Nullable;
public record NumberRangeDialogInputImpl(
String key,
int width,
Component label,
String labelFormat,
float start,
float end,
@Nullable Float initial,
@Nullable Float step
) implements NumberRangeDialogInput {
public static final class BuilderImpl implements NumberRangeDialogInput.Builder {
private final String key;
private final Component label;
private final float start;
private final float end;
private int width = PlainMessage.DEFAULT_WIDTH;
private String labelFormat = "options.generic_value";
private @Nullable Float initial = null;
private @Nullable Float step = null;
public BuilderImpl(final String key, final Component label, final float start, final float end) {
Preconditions.checkArgument(StringTemplate.isValidVariableName(key), "key must be a valid input name");
this.key = key;
this.label = label;
this.start = start;
this.end = end;
}
@Override
public BuilderImpl width(final int width) {
Preconditions.checkArgument(width >= 1 && width <= 1024, "width must be between 1 and 1024");
this.width = width;
return this;
}
@Override
public BuilderImpl labelFormat(final String labelFormat) {
this.labelFormat = labelFormat;
return this;
}
@Override
public BuilderImpl initial(final @Nullable Float initial) {
if (initial != null) {
Preconditions.checkArgument(initial >= this.start && initial <= this.end, "initial must be within the range");
}
this.initial = initial;
return this;
}
@Override
public BuilderImpl step(final @Nullable Float step) {
Preconditions.checkArgument(step == null || step > 0, "step must be null or greater than 0");
this.step = step;
return this;
}
@Override
public NumberRangeDialogInput build() {
return new NumberRangeDialogInputImpl(this.key, this.width, this.label, this.labelFormat, this.start, this.end, this.initial, this.step);
}
}
}
@@ -0,0 +1,60 @@
package io.papermc.paper.registry.data.dialog.input;
import com.google.common.base.Preconditions;
import java.util.List;
import net.kyori.adventure.text.Component;
import net.minecraft.commands.functions.StringTemplate;
import net.minecraft.server.dialog.body.PlainMessage;
import org.jetbrains.annotations.Nullable;
public record SingleOptionDialogInputImpl(
String key,
int width,
List<SingleOptionDialogInput.OptionEntry> entries,
Component label,
boolean labelVisible
) implements SingleOptionDialogInput {
public SingleOptionDialogInputImpl {
entries = List.copyOf(entries);
}
public record SingleOptionEntryImpl(String id, @Nullable Component display, boolean initial) implements OptionEntry {
}
public static final class BuilderImpl implements SingleOptionDialogInput.Builder {
private final String key;
private int width = PlainMessage.DEFAULT_WIDTH;
private final List<OptionEntry> entries;
private final Component label;
private boolean labelVisible = true;
public BuilderImpl(final String key, final List<OptionEntry> entries, final Component label) {
Preconditions.checkArgument(StringTemplate.isValidVariableName(key), "key must be a valid input name");
this.key = key;
Preconditions.checkArgument(!entries.isEmpty(), "entries must not be empty");
Preconditions.checkArgument(entries.stream().filter(OptionEntry::initial).count() <= 1, "only 1 option can be initially selected");
this.entries = entries;
this.label = label;
}
@Override
public BuilderImpl width(final int width) {
Preconditions.checkArgument(width >= 1 && width <= 1024, "width must be between 1 and 1024");
this.width = width;
return this;
}
@Override
public BuilderImpl labelVisible(final boolean labelVisible) {
this.labelVisible = labelVisible;
return this;
}
@Override
public SingleOptionDialogInput build() {
return new SingleOptionDialogInputImpl(this.key, this.width, this.entries, this.label, this.labelVisible);
}
}
}
@@ -0,0 +1,81 @@
package io.papermc.paper.registry.data.dialog.input;
import com.google.common.base.Preconditions;
import net.kyori.adventure.text.Component;
import net.minecraft.commands.functions.StringTemplate;
import net.minecraft.server.dialog.body.PlainMessage;
import net.minecraft.server.dialog.input.TextInput;
import org.jspecify.annotations.Nullable;
public record TextDialogInputImpl(
String key,
int width,
Component label,
boolean labelVisible,
String initial,
int maxLength,
TextDialogInput.@Nullable MultilineOptions multiline
) implements TextDialogInput {
public record MultilineOptionsImpl(@Nullable Integer maxLines, @Nullable Integer height) implements MultilineOptions {
public MultilineOptionsImpl {
Preconditions.checkArgument(maxLines == null || maxLines > 0, "maxLines must be null or greater than 0");
Preconditions.checkArgument(height == null || (height >= 1 && height <= TextInput.MultilineOptions.MAX_HEIGHT), "height must be null or between 1 and 512");
}
}
public static final class BuilderImpl implements TextDialogInput.Builder {
private final String key;
private int width = PlainMessage.DEFAULT_WIDTH;
private final Component label;
private boolean labelVisible = true;
private String initial = "";
private int maxLength = 32;
private TextDialogInput.@Nullable MultilineOptions multiline = null;
public BuilderImpl(final String key, final Component label) {
Preconditions.checkArgument(StringTemplate.isValidVariableName(key), "key must be a valid input name");
this.key = key;
this.label = label;
}
@Override
public TextDialogInput.Builder width(final int width) {
Preconditions.checkArgument(width >= 1 && width <= 1024, "width must be between 1 and 1024");
this.width = width;
return this;
}
@Override
public TextDialogInput.Builder labelVisible(final boolean labelVisible) {
this.labelVisible = labelVisible;
return this;
}
@Override
public TextDialogInput.Builder initial(final String initial) {
this.initial = initial;
return this;
}
@Override
public TextDialogInput.Builder maxLength(final int maxLength) {
Preconditions.checkArgument(maxLength > 0, "maxLength must be greater than 0");
this.maxLength = maxLength;
return this;
}
@Override
public TextDialogInput.Builder multiline(final TextDialogInput.@Nullable MultilineOptions multiline) {
this.multiline = multiline;
return this;
}
@Override
public TextDialogInput build() {
Preconditions.checkState(this.initial.length() <= this.maxLength, "The initial value must be less than or equal to the maximum length.");
return new TextDialogInputImpl(this.key, this.width, this.label, this.labelVisible, this.initial, this.maxLength, this.multiline);
}
}
}
@@ -0,0 +1,6 @@
package io.papermc.paper.registry.data.dialog.type;
import io.papermc.paper.registry.data.dialog.ActionButton;
public record ConfirmationTypeImpl(ActionButton yesButton, ActionButton noButton) implements ConfirmationType {
}
@@ -0,0 +1,53 @@
package io.papermc.paper.registry.data.dialog.type;
import com.google.common.base.Preconditions;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.registry.data.dialog.ActionButton;
import io.papermc.paper.registry.set.RegistrySet;
import net.minecraft.server.dialog.CommonButtonData;
import org.jspecify.annotations.Nullable;
public record DialogListTypeImpl(
RegistrySet<Dialog> dialogs,
@Nullable ActionButton exitAction,
int columns,
int buttonWidth
) implements DialogListType {
public static final class BuilderImpl implements DialogListType.Builder {
private final RegistrySet<Dialog> dialogs;
private @Nullable ActionButton exitAction;
private int columns = 2;
private int buttonWidth = CommonButtonData.DEFAULT_WIDTH;
public BuilderImpl(final RegistrySet<Dialog> dialogs) {
this.dialogs = dialogs;
}
@Override
public DialogListType.Builder exitAction(final @Nullable ActionButton exitAction) {
this.exitAction = exitAction;
return this;
}
@Override
public DialogListType.Builder columns(final int columns) {
Preconditions.checkArgument(columns > 0, "columns must be greater than 0");
this.columns = columns;
return this;
}
@Override
public DialogListType.Builder buttonWidth(final int buttonWidth) {
Preconditions.checkArgument(buttonWidth >= 1 && buttonWidth <= 1024, "buttonWidth must be between 1 and 1024");
this.buttonWidth = buttonWidth;
return this;
}
@Override
public DialogListType build() {
return new DialogListTypeImpl(this.dialogs, this.exitAction, this.columns, this.buttonWidth);
}
}
}
@@ -0,0 +1,46 @@
package io.papermc.paper.registry.data.dialog.type;
import com.google.common.base.Preconditions;
import io.papermc.paper.registry.data.dialog.ActionButton;
import java.util.List;
import org.jspecify.annotations.Nullable;
public record MultiActionTypeImpl(
List<ActionButton> actions,
@Nullable ActionButton exitAction,
int columns
) implements MultiActionType {
public MultiActionTypeImpl {
actions = List.copyOf(actions);
}
public static final class BuilderImpl implements MultiActionType.Builder {
private final List<ActionButton> actions;
private @Nullable ActionButton exitAction = null;
private int columns = 2;
public BuilderImpl(final List<ActionButton> actions) {
Preconditions.checkArgument(!actions.isEmpty(), "actions cannot be empty");
this.actions = actions;
}
@Override
public Builder exitAction(final @org.jetbrains.annotations.Nullable ActionButton exitAction) {
this.exitAction = exitAction;
return this;
}
@Override
public Builder columns(final int columns) {
Preconditions.checkArgument(columns > 0, "columns must be greater than 0");
this.columns = columns;
return this;
}
@Override
public MultiActionType build() {
return new MultiActionTypeImpl(this.actions, this.exitAction, this.columns);
}
}
}
@@ -0,0 +1,15 @@
package io.papermc.paper.registry.data.dialog.type;
import io.papermc.paper.registry.data.dialog.ActionButton;
import net.minecraft.server.dialog.CommonButtonData;
import static net.kyori.adventure.text.Component.translatable;
public record NoticeTypeImpl(ActionButton action) implements NoticeType {
public static final ActionButton DEFAULT_ACTION = ActionButton.builder(translatable("gui.ok")).width(CommonButtonData.DEFAULT_WIDTH).build();
public NoticeTypeImpl() {
this(DEFAULT_ACTION);
}
}
@@ -0,0 +1,17 @@
package io.papermc.paper.registry.data.dialog.type;
import com.google.common.base.Preconditions;
import io.papermc.paper.registry.data.dialog.ActionButton;
import org.jspecify.annotations.Nullable;
public record ServerLinksTypeImpl(
@Nullable ActionButton exitAction,
int columns,
int buttonWidth
) implements ServerLinksType {
public ServerLinksTypeImpl {
Preconditions.checkArgument(columns > 0, "columns must be greater than 0");
Preconditions.checkArgument(buttonWidth >= 1 && buttonWidth <= 1024, "buttonWidth must be between 1 and 1024");
}
}
@@ -1,12 +1,14 @@
package io.papermc.paper.registry.data.util;
import com.google.common.base.Preconditions;
import com.mojang.serialization.Codec;
import com.mojang.serialization.JavaOps;
import io.papermc.paper.adventure.PaperAdventure;
import io.papermc.paper.adventure.WrapperAwareSerializer;
import io.papermc.paper.registry.PaperRegistries;
import io.papermc.paper.registry.PaperRegistryBuilder;
import io.papermc.paper.registry.PaperRegistryBuilderFactory;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.client.ClientTextureAsset;
import io.papermc.paper.registry.entry.RegistryEntryMeta;
import java.util.function.Consumer;
@@ -36,10 +38,20 @@ public class Conversions {
private final RegistryOps.RegistryInfoLookup lookup;
private final WrapperAwareSerializer serializer;
private final RegistryOps<Object> javaOps;
public Conversions(final RegistryOps.RegistryInfoLookup lookup) {
this.lookup = lookup;
this.serializer = new WrapperAwareSerializer(() -> RegistryOps.create(JavaOps.INSTANCE, lookup));
this.javaOps = RegistryOps.create(JavaOps.INSTANCE, lookup);
}
public <OUT, IN> OUT convert(final IN in, final Codec<OUT> outCodec, final Codec<IN> inCodec) {
final Object obj = inCodec.encodeStart(this.javaOps, in)
.getOrThrow(s -> new RuntimeException("Failed to encode input: " + in + "; " + s));
return outCodec.decode(this.javaOps, obj)
.getOrThrow(s -> new RuntimeException("Failed to decode to output: " + obj + "; " + s))
.getFirst();
}
public RegistryOps.RegistryInfoLookup lookup() {
@@ -74,20 +86,20 @@ public class Conversions {
);
}
private static <M, A extends Keyed, B extends PaperRegistryBuilder<M, A>> RegistryEntryMeta.Buildable<M, A, B> getDirectHolderBuildableMeta(final ResourceKey<? extends Registry<M>> registryKey) {
private static <M, A extends Keyed, B extends PaperRegistryBuilder<M, A>> RegistryEntryMeta.Buildable<M, A, B> getDirectHolderBuildableMeta(final RegistryKey<A> registryKey) {
final RegistryEntryMeta.Buildable<M, A, B> buildableMeta = PaperRegistries.getBuildableMeta(registryKey);
Preconditions.checkArgument(buildableMeta.registryTypeMapper().supportsDirectHolders(), "Registry type mapper must support direct holders");
return buildableMeta;
}
public <M, A extends Keyed, B extends PaperRegistryBuilder<M, A>> A createApiInstanceFromBuilder(final ResourceKey<? extends Registry<M>> registryKey, final Consumer<? super PaperRegistryBuilderFactory<M, A, B>> value) {
public <M, A extends Keyed, B extends PaperRegistryBuilder<M, A>> A createApiInstanceFromBuilder(final RegistryKey<A> registryKey, final Consumer<? super PaperRegistryBuilderFactory<M, A, B>> value) {
final RegistryEntryMeta.Buildable<M, A, B> meta = getDirectHolderBuildableMeta(registryKey);
final PaperRegistryBuilderFactory<M, A, B> builderFactory = this.createRegistryBuilderFactory(registryKey, meta);
value.accept(builderFactory);
return meta.registryTypeMapper().createBukkit(Holder.direct(builderFactory.requireBuilder().build()));
}
public <M, A extends Keyed, B extends PaperRegistryBuilder<M, A>> Holder<M> createHolderFromBuilder(final ResourceKey<? extends Registry<M>> registryKey, final Consumer<? super PaperRegistryBuilderFactory<M, A, B>> value) {
public <M, A extends Keyed, B extends PaperRegistryBuilder<M, A>> Holder<M> createHolderFromBuilder(final RegistryKey<A> registryKey, final Consumer<? super PaperRegistryBuilderFactory<M, A, B>> value) {
final RegistryEntryMeta.Buildable<M, A, B> meta = getDirectHolderBuildableMeta(registryKey);
final PaperRegistryBuilderFactory<M, A, B> builderFactory = this.createRegistryBuilderFactory(registryKey, meta);
value.accept(builderFactory);
@@ -95,11 +107,12 @@ public class Conversions {
}
private <M, A extends Keyed, B extends PaperRegistryBuilder<M, A>> PaperRegistryBuilderFactory<M, A, B> createRegistryBuilderFactory(
final ResourceKey<? extends Registry<M>> registryKey,
final RegistryKey<A> registryKey,
final RegistryEntryMeta.Buildable<M, A, B> buildableMeta
) {
final HolderLookup.RegistryLookup<M> lookupForBuilders = this.lookup.lookupForValueCopyViaBuilders().lookupOrThrow(registryKey);
return new PaperRegistryBuilderFactory<>(registryKey, this, buildableMeta.builderFiller(), lookupForBuilders::getValueForCopying);
final ResourceKey<? extends Registry<M>> resourceRegistryKey = PaperRegistries.registryToNms(registryKey);
final HolderLookup.RegistryLookup<M> lookupForBuilders = this.lookup.lookupForValueCopyViaBuilders().lookupOrThrow(resourceRegistryKey);
return new PaperRegistryBuilderFactory<>(resourceRegistryKey, this, buildableMeta.builderFiller(), lookupForBuilders::getValueForCopying);
}
}
@@ -37,10 +37,6 @@ public class RegistryEntryBuilder<M, A extends Keyed> { // TODO remove Keyed
return new RegistryEntryImpl<>(new RegistryEntryMeta.ApiOnly<>(this.mcKey, this.apiKey, apiRegistrySupplier));
}
public CraftStage<M, A> craft(final Class<?> classToPreload, final BiFunction<? super NamespacedKey, M, ? extends A> minecraftToBukkit) {
return new CraftStage<>(this.mcKey, this.apiKey, classToPreload, new RegistryTypeMapper<>(minecraftToBukkit));
}
public CraftStage<M, A> craft(final Class<?> classToPreload, final Function<Holder<M>, ? extends A> minecraftToBukkit) {
return this.craft(classToPreload, minecraftToBukkit, false);
}
@@ -18,9 +18,7 @@ import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.NullMarked;
@NullMarked
public record NamedRegistryKeySetImpl<T extends Keyed, M>( // TODO remove Keyed
TagKey<T> tagKey,
HolderSet.Named<M> namedSet
@@ -3,6 +3,7 @@ package io.papermc.paper.registry.set;
import io.papermc.paper.registry.PaperRegistries;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.TypedKey;
import io.papermc.paper.util.Holderable;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.core.Holder;
@@ -11,6 +12,7 @@ import net.minecraft.core.Registry;
import net.minecraft.resources.RegistryOps;
import net.minecraft.resources.ResourceKey;
import org.bukkit.Keyed;
import org.bukkit.craftbukkit.CraftRegistry;
public final class PaperRegistrySets {
@@ -25,6 +27,30 @@ public final class PaperRegistrySets {
}
}
public static <A extends Keyed, M> HolderSet<M> convertToNmsWithDirects(final ResourceKey<? extends Registry<M>> resourceKey, final RegistryOps.RegistryInfoLookup lookup, final RegistrySet<A> registrySet) { // TODO remove Keyed
if (registrySet instanceof NamedRegistryKeySetImpl<A, ?>) {
return ((NamedRegistryKeySetImpl<A, M>) registrySet).namedSet();
} else if (registrySet.isEmpty()) {
return HolderSet.empty();
} else if (registrySet instanceof final RegistryValueSet<A> valueSet) {
final List<Holder<M>> directs = new ArrayList<>(valueSet.values().size());
for (final A value : valueSet) {
if (!(value instanceof final Holderable<?> holderable)) {
throw new UnsupportedOperationException("Cannot convert a registry set containing non-holderable values");
}
directs.add(((Holderable<M>) holderable).getHolder());
}
return HolderSet.direct(directs);
} else if (registrySet instanceof final RegistryKeySet<A> keySet) {
final RegistryOps.RegistryInfo<M> registryInfo = lookup.lookup(resourceKey).orElseThrow();
return HolderSet.direct(key -> {
return registryInfo.getter().getOrThrow(PaperRegistries.toNms(key));
}, keySet.values());
} else {
throw new UnsupportedOperationException("Cannot convert a registry set of type " + registrySet.getClass().getName());
}
}
public static <A extends Keyed, M> RegistryKeySet<A> convertToApi(final RegistryKey<A> registryKey, final HolderSet<M> holders) { // TODO remove Keyed
if (holders instanceof final HolderSet.Named<M> named) {
return new NamedRegistryKeySetImpl<>(PaperRegistries.fromNms(named.key()), named);
@@ -40,6 +66,30 @@ public final class PaperRegistrySets {
}
}
public static <A extends Keyed, M> RegistrySet<A> convertToApiWithDirects(final RegistryKey<A> registryKey, final HolderSet<M> holders) { // TODO remove Keyed
if (holders instanceof final HolderSet.Named<M> named) {
return new NamedRegistryKeySetImpl<>(PaperRegistries.fromNms(named.key()), named);
} else {
if (holders.size() == 0) {
return RegistrySet.keySet(registryKey);
}
if (holders.get(0) instanceof Holder.Direct<M>) {
final List<A> directs = new ArrayList<>(holders.size());
final ResourceKey<? extends Registry<M>> nmsRegistryKey = PaperRegistries.registryToNms(registryKey);
for (final Holder<M> holder : holders) {
directs.add(CraftRegistry.minecraftHolderToBukkit(holder, nmsRegistryKey));
}
return RegistrySet.valueSet(registryKey, directs);
} else {
final List<TypedKey<A>> keys = new ArrayList<>(holders.size());
for (final Holder<M> holder : holders) {
keys.add(PaperRegistries.fromNms(((Holder.Reference<M>) holder).key()));
}
return RegistrySet.keySet(registryKey, keys);
}
}
}
private PaperRegistrySets() {
}
}
@@ -0,0 +1,33 @@
package io.papermc.paper.registry.set;
import io.papermc.paper.registry.RegistryBuilder;
import io.papermc.paper.registry.RegistryBuilderFactory;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.util.Conversions;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import org.bukkit.Keyed;
public class RegistryValueSetBuilderImpl<M, API extends Keyed, BUILDER extends RegistryBuilder<API>> implements RegistryValueSetBuilder<API, BUILDER> { // TODO remove Keyed
private final RegistryKey<API> registryKey;
private final Conversions conversions;
private final List<API> instances = new ArrayList<>();
public RegistryValueSetBuilderImpl(final RegistryKey<API> registryKey, final Conversions conversions) {
this.registryKey = registryKey;
this.conversions = conversions;
}
@Override
public RegistryValueSetBuilder<API, BUILDER> add(final Consumer<RegistryBuilderFactory<API, ? extends BUILDER>> builder) {
this.instances.add(this.conversions.createApiInstanceFromBuilder(this.registryKey, builder));
return this;
}
@Override
public RegistryValueSet<API> build() {
return RegistrySet.valueSet(this.registryKey, this.instances);
}
}
@@ -0,0 +1,54 @@
package io.papermc.paper.util;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.Decoder;
import com.mojang.serialization.DynamicOps;
import io.papermc.paper.adventure.AdventureCodecs;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.TypedKey;
import java.util.function.Function;
import net.kyori.adventure.key.Key;
import net.minecraft.core.Holder;
import net.minecraft.resources.RegistryOps;
import org.bukkit.Keyed;
import org.bukkit.Registry;
import org.jspecify.annotations.NullMarked;
@NullMarked
public final class PaperCodecs {
public static <A extends Keyed, M> Decoder<A> registryFileDecoderFor(final Decoder<? extends M> directNmsDecoder, final Function<? super Holder<M>, A> directHolderConverter, final RegistryKey<A> registryKey, final boolean allowInline) { // TODO remove Keyed
final Decoder.Terminal<A> terminalDecoder = directNmsDecoder.map(nms -> directHolderConverter.apply(Holder.direct(nms))).terminal();
return Decoder.ofTerminal(new Decoder.Terminal<>() {
@Override
public <T> DataResult<A> decode(final DynamicOps<T> ops, final T input) {
// logic based on RegistryFileCodec
if (ops instanceof RegistryOps<?>) {
// Pretty sure we can just use our RegistryAccess here. These codecs aren't ever
// used for deserialization, so we don't need to rely on different implementations
// of HolderGetter like vanilla's RegistryFileCodec does. We are always just
// getting existing dialog elements, never creating empty reference holders.
final Registry<A> registry = RegistryAccess.registryAccess().getRegistry(registryKey);
final DataResult<Pair<Key, T>> keyDataResult = AdventureCodecs.KEY_CODEC.decode(ops, input);
if (keyDataResult.result().isEmpty()) {
return !allowInline ? DataResult.error(() -> "Inline definitions not allowed here") : terminalDecoder.decode(ops, input);
}
final Pair<Key, T> pair = keyDataResult.result().get();
final TypedKey<A> elementKey = TypedKey.create(registryKey, pair.getFirst());
final A value = registry.get(elementKey);
if (value == null) {
return DataResult.error(() -> "Failed to get element " + elementKey);
}
return DataResult.success(value);
} else {
return terminalDecoder.decode(ops, input);
}
}
});
}
private PaperCodecs() {
}
}
@@ -25,7 +25,7 @@ public class CraftArt extends OldEnumHolderable<Art, PaintingVariant> implements
}
public static Holder<PaintingVariant> bukkitToMinecraftHolder(Art bukkit) {
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.PAINTING_VARIANT);
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
}
public CraftArt(Holder<PaintingVariant> paintingVariant) {
@@ -21,7 +21,7 @@ public class CraftJukeboxSong extends HolderableBase<net.minecraft.world.item.Ju
}
public static Holder<net.minecraft.world.item.JukeboxSong> bukkitToMinecraftHolder(JukeboxSong bukkit) {
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.JUKEBOX_SONG);
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
}
public CraftJukeboxSong(final Holder<net.minecraft.world.item.JukeboxSong> holder) {
@@ -28,7 +28,7 @@ public class CraftMusicInstrument extends MusicInstrument implements io.papermc.
}
public static Holder<Instrument> bukkitToMinecraftHolder(MusicInstrument bukkit) {
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.INSTRUMENT);
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
}
public static Object bukkitToString(MusicInstrument bukkit) {
@@ -99,28 +99,18 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
* @param bukkit the bukkit representation
* @return the minecraft representation of the bukkit value
*/
public static <B extends Keyed, M> M bukkitToMinecraft(B bukkit) {
@SuppressWarnings("unchecked")
public static <B extends Keyed, M> M bukkitToMinecraft(final B bukkit) {
Preconditions.checkArgument(bukkit != null);
return ((Handleable<M>) bukkit).getHandle();
}
public static <B extends Keyed, M> Holder<M> bukkitToMinecraftHolder(B bukkit, ResourceKey<net.minecraft.core.Registry<M>> registryKey) {
@SuppressWarnings("unchecked")
public static <B extends Keyed, M> Holder<M> bukkitToMinecraftHolder(final B bukkit) {
Preconditions.checkArgument(bukkit != null);
// Paper start - support direct Holder
if (bukkit instanceof io.papermc.paper.util.Holderable<?>) {
return ((io.papermc.paper.util.Holderable<M>) bukkit).getHolder();
}
// Paper end - support direct Holder
net.minecraft.core.Registry<M> registry = CraftRegistry.getMinecraftRegistry(registryKey);
if (registry.wrapAsHolder(CraftRegistry.bukkitToMinecraft(bukkit)) instanceof Holder.Reference<M> holder) {
return holder;
}
throw new IllegalArgumentException("No Reference holder found for " + bukkit
+ ", this can happen if a plugin creates its own registry entry with out properly registering it.");
return ((Holderable<M>) bukkit).getHolder();
}
// Paper start - fixup upstream being dum
@@ -23,7 +23,7 @@ public class CraftSound extends OldEnumHolderable<Sound, SoundEvent> implements
}
public static Holder<SoundEvent> bukkitToMinecraftHolder(Sound bukkit) {
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.SOUND_EVENT);
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
}
public CraftSound(Holder<SoundEvent> soundEffect) {
@@ -43,7 +43,7 @@ public class CraftAttribute extends OldEnumHolderable<Attribute, net.minecraft.w
}
public static Holder<net.minecraft.world.entity.ai.attributes.Attribute> bukkitToMinecraftHolder(Attribute bukkit) {
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.ATTRIBUTE);
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
}
public static String bukkitToString(Attribute bukkit) {
@@ -36,7 +36,7 @@ public class CraftBiome extends OldEnumHolderable<Biome, net.minecraft.world.lev
if (bukkit == Biome.CUSTOM) {
return null;
}
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.BIOME);
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
}
public CraftBiome(final Holder<net.minecraft.world.level.biome.Biome> holder) {
@@ -24,7 +24,7 @@ public class CraftPatternType extends OldEnumHolderable<PatternType, BannerPatte
}
public static Holder<BannerPattern> bukkitToMinecraftHolder(PatternType bukkit) {
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.BANNER_PATTERN);
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
}
public CraftPatternType(Holder<BannerPattern> bannerPatternType) {
@@ -79,7 +79,7 @@ public class CraftDamageType extends HolderableBase<net.minecraft.world.damageso
}
public static Holder<net.minecraft.world.damagesource.DamageType> bukkitToMinecraftHolder(DamageType bukkitDamageType) {
return CraftRegistry.bukkitToMinecraftHolder(bukkitDamageType, Registries.DAMAGE_TYPE);
return CraftRegistry.bukkitToMinecraftHolder(bukkitDamageType);
}
public static net.minecraft.world.damagesource.DamageType bukkitToMinecraft(DamageType bukkitDamageType) {
@@ -33,7 +33,7 @@ public class CraftEnchantment extends Enchantment implements Holderable<net.mine
}
public static Holder<net.minecraft.world.item.enchantment.Enchantment> bukkitToMinecraftHolder(Enchantment bukkit) {
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.ENCHANTMENT);
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
}
public static String bukkitToString(Enchantment bukkit) {
@@ -59,7 +59,7 @@ public class CraftCat extends CraftTameableAnimal implements Cat {
}
public static Holder<CatVariant> bukkitToMinecraftHolder(Type bukkit) {
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.CAT_VARIANT);
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
}
public CraftType(final Holder<CatVariant> holder) {
@@ -49,7 +49,7 @@ public class CraftChicken extends CraftAnimals implements Chicken {
}
public static Holder<ChickenVariant> bukkitToMinecraftHolder(Variant bukkit) {
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.CHICKEN_VARIANT);
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
}
@@ -49,7 +49,7 @@ public class CraftCow extends CraftAbstractCow implements Cow {
}
public static Holder<CowVariant> bukkitToMinecraftHolder(Variant bukkit) {
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.COW_VARIANT);
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
}
public CraftVariant(final Holder<CowVariant> holder) {
@@ -63,7 +63,7 @@ public class CraftFrog extends CraftAnimals implements org.bukkit.entity.Frog {
}
public static Holder<FrogVariant> bukkitToMinecraftHolder(Variant bukkit) {
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.FROG_VARIANT);
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
}
public CraftVariant(final Holder<FrogVariant> holder) {
super(holder, count++);
@@ -97,7 +97,7 @@ public class CraftPig extends CraftAnimals implements Pig {
}
public static Holder<PigVariant> bukkitToMinecraftHolder(Variant bukkit) {
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.PIG_VARIANT);
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
}
public CraftVariant(final Holder<PigVariant> holder) {
File diff suppressed because it is too large Load Diff
@@ -178,7 +178,7 @@ public class CraftVillager extends CraftAbstractVillager implements Villager {
}
public static Holder<VillagerType> bukkitToMinecraftHolder(Type bukkit) {
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.VILLAGER_TYPE);
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
}
public CraftType(final Holder<VillagerType> holder){
@@ -194,7 +194,7 @@ public class CraftVillager extends CraftAbstractVillager implements Villager {
}
public static Holder<VillagerProfession> bukkitToMinecraftHolder(Profession bukkit) {
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.VILLAGER_PROFESSION);
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
}
public static Profession minecraftToBukkit(VillagerProfession minecraft) {
@@ -105,7 +105,7 @@ public class CraftWolf extends CraftTameableAnimal implements Wolf {
}
public static Holder<WolfVariant> bukkitToMinecraftHolder(Variant bukkit) {
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.WOLF_VARIANT);
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
}
public CraftVariant(final Holder<WolfVariant> holder) {
@@ -128,7 +128,7 @@ public class CraftWolf extends CraftTameableAnimal implements Wolf {
}
public static Holder<WolfSoundVariant> bukkitToMinecraftHolder(SoundVariant bukkit) {
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.WOLF_SOUND_VARIANT);
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
}
public CraftSoundVariant(final Holder<WolfSoundVariant> holder) {
@@ -25,7 +25,7 @@ public class CraftTrimMaterial extends HolderableBase<net.minecraft.world.item.e
}
public static Holder<net.minecraft.world.item.equipment.trim.TrimMaterial> bukkitToMinecraftHolder(TrimMaterial bukkit) {
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.TRIM_MATERIAL);
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
}
public static Object bukkitToObject(TrimMaterial bukkit) {
@@ -25,7 +25,7 @@ public class CraftTrimPattern extends HolderableBase<net.minecraft.world.item.eq
}
public static Holder<net.minecraft.world.item.equipment.trim.TrimPattern> bukkitToMinecraftHolder(TrimPattern bukkit) {
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.TRIM_PATTERN);
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
}
public static Object bukkitToObject(TrimPattern bukkit) {
@@ -26,7 +26,7 @@ public final class CraftMapCursor {
}
public static Holder<MapDecorationType> bukkitToMinecraftHolder(MapCursor.Type bukkit) {
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.MAP_DECORATION_TYPE);
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
}
public CraftType(final Holder<MapDecorationType> holder) {
@@ -38,7 +38,7 @@ public class CraftPotionEffectType extends PotionEffectType implements Holderabl
}
public static Holder<MobEffect> bukkitToMinecraftHolder(PotionEffectType bukkit) {
return CraftRegistry.bukkitToMinecraftHolder(bukkit, Registries.MOB_EFFECT);
return CraftRegistry.bukkitToMinecraftHolder(bukkit);
}
private final Holder<MobEffect> holder;
@@ -0,0 +1 @@
io.papermc.paper.registry.data.dialog.PaperDialogInstancesProvider
@@ -270,7 +270,7 @@ public class RegistryConversionTest {
Joiner.on('\n').withKeyValueSeparator(" got: ").join(notMatching)));
}
static final Set<RegistryKey<?>> IGNORE_FOR_DIRECT_HOLDER = Set.of(RegistryKey.TRIM_MATERIAL, RegistryKey.TRIM_PATTERN, RegistryKey.INSTRUMENT, RegistryKey.BANNER_PATTERN, RegistryKey.SOUND_EVENT); // Paper
static final Set<RegistryKey<?>> IGNORE_FOR_DIRECT_HOLDER = Set.of(RegistryKey.TRIM_MATERIAL, RegistryKey.TRIM_PATTERN, RegistryKey.INSTRUMENT, RegistryKey.BANNER_PATTERN, RegistryKey.SOUND_EVENT, RegistryKey.DIALOG); // Paper
/**
* Minecraft registry can return a default key / value
@@ -1,6 +1,8 @@
package org.bukkit.support.provider;
import com.google.common.collect.Lists;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.dialog.PaperDialog;
import io.papermc.paper.registry.RegistryKey;
import java.util.List;
import java.util.stream.Stream;
@@ -115,6 +117,7 @@ public class RegistriesArgumentProvider implements ArgumentsProvider {
register(RegistryKey.CHICKEN_VARIANT, Chicken.Variant.class, Registries.CHICKEN_VARIANT, CraftChicken.CraftVariant.class, ChickenVariant.class);
register(RegistryKey.COW_VARIANT, Cow.Variant.class, Registries.COW_VARIANT, CraftCow.CraftVariant.class, CowVariant.class);
register(RegistryKey.PIG_VARIANT, Pig.Variant.class, Registries.PIG_VARIANT, CraftPig.CraftVariant.class, PigVariant.class);
register(RegistryKey.DIALOG, Dialog.class, Registries.DIALOG, PaperDialog.class, net.minecraft.server.dialog.Dialog.class);
}
private static void register(RegistryKey registryKey, Class bukkit, ResourceKey registry, Class craft, Class minecraft) { // Paper