diff --git a/patches/api/0420-Tag-Modification-API.patch b/patches/api/0420-Tag-Modification-API.patch new file mode 100644 index 0000000000..990ecfd3f8 --- /dev/null +++ b/patches/api/0420-Tag-Modification-API.patch @@ -0,0 +1,144 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic +Date: Sat, 18 Mar 2023 14:36:47 -0700 +Subject: [PATCH] Tag Modification API + + +diff --git a/src/main/java/io/papermc/paper/tag/TagUpdate.java b/src/main/java/io/papermc/paper/tag/TagUpdate.java +new file mode 100644 +index 0000000000000000000000000000000000000000..8ec1289f25521f6c8bc1910c7c60e809a2ea08d1 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/tag/TagUpdate.java +@@ -0,0 +1,24 @@ ++package io.papermc.paper.tag; ++ ++import java.util.Map; ++import java.util.Set; ++import net.kyori.adventure.key.Key; ++import net.kyori.adventure.key.Keyed; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Unmodifiable; ++ ++/** ++ * Represents an update to a set of tags that can be ++ * sent to any number of players or a single player. ++ */ ++@ApiStatus.NonExtendable ++public interface TagUpdate { ++ ++ /** ++ * Get the set of updated tags. ++ * ++ * @return an immutable map describing tags ++ */ ++ @NotNull @Unmodifiable Map> updatedTags(); ++} +diff --git a/src/main/java/org/bukkit/Registry.java b/src/main/java/org/bukkit/Registry.java +index 0a3a41ae4c488b148266129d3663be3f8830d509..1c3e941c3322ab39a99e5c4f08fcb64bee8f32c5 100644 +--- a/src/main/java/org/bukkit/Registry.java ++++ b/src/main/java/org/bukkit/Registry.java +@@ -271,6 +271,20 @@ public interface Registry extends Iterable { + return (namespacedKey != null) ? get(namespacedKey) : null; + } + ++ // Paper start - tag updates ++ /** ++ * Edits the tags the server and client use to determine various behaviors. ++ * In the consumer parameter, the map and initial sets will be mutable to make ++ * changes and after the consumer, they will be sent to the client. ++ * ++ * @param editConsumer the consumer to edit the map of tags ++ * @throws UnsupportedOperationException if this registry doesn't support editing tags ++ */ ++ default io.papermc.paper.tag.TagUpdate createTagUpdate(final java.util.function.@NotNull Consumer>> editConsumer) { ++ throw new UnsupportedOperationException("This registry doesn't support editing tags"); ++ } ++ // Paper end ++ + static final class SimpleRegistry & Keyed> implements Registry { + + private final Map map; +diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java +index 2204336d8800311b65e894739ab1b27273e7c6f2..87cba207c54cb53e5491cdaf250ce751fb8e52c5 100644 +--- a/src/main/java/org/bukkit/Server.java ++++ b/src/main/java/org/bukkit/Server.java +@@ -2139,4 +2139,36 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi + */ + @NotNull org.bukkit.potion.PotionBrewer getPotionBrewer(); + // Paper end ++ // Paper start - tag updates ++ /** ++ * Applies this tag update to the tags on the server. After ++ * applying the changes, updates will be sent to all connected ++ * clients with the new tags. ++ *

++ * These changes will be reset when the server is restarted ++ * or reloaded through {@code /bukkit:reload} or {@code /minecraft:reload}. ++ * Listen to {@link io.papermc.paper.event.server.ServerResourcesReloadedEvent} if ++ * you want to preserve your changes through reloads. ++ * ++ * @param tagUpdate the tag update to send ++ * @see #applyTagUpdate(Iterable) for multiple updates at once ++ */ ++ default void applyTagUpdate(io.papermc.paper.tag.@NotNull TagUpdate tagUpdate) { ++ this.applyTagUpdate(java.util.Set.of(tagUpdate)); ++ } ++ ++ /** ++ * Applies this tag update to the tags on the server. After ++ * applying the changes, updates will be sent to all connected ++ * clients with the new tags. ++ *

++ * These changes will be reset when the server is restarted ++ * or reloaded through {@code /bukkit:reload} or {@code /minecraft:reload}. ++ * Listen to {@link io.papermc.paper.event.server.ServerResourcesReloadedEvent} if ++ * you want to preserve your changes through reloads. ++ * ++ * @param tagUpdates the tag updates to send ++ */ ++ void applyTagUpdate(@NotNull Iterable> tagUpdates); ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java +index 660f28e371176c62e38a84b187958aceb235c8e3..935fdae93a3a5abdb5643993895b22542a5bd97c 100644 +--- a/src/main/java/org/bukkit/entity/Player.java ++++ b/src/main/java/org/bukkit/entity/Player.java +@@ -3000,4 +3000,37 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM + @Override + Spigot spigot(); + // Spigot end ++ ++ // Paper start - tag updates ++ /** ++ * Send a tag update to this player. The player's client ++ * will apply this tag change and update its behavior ++ * accordingly. ++ *

++ * These changes will be reset when the server is restarted ++ * or reloaded through {@code /bukkit:reload} or {@code /minecraft:reload}. ++ * Listen to {@link io.papermc.paper.event.server.ServerResourcesReloadedEvent} if ++ * you want to preserve your changes through reloads. ++ * ++ * @param tagUpdate the tag update to send ++ * @see #sendTagUpdates(Iterable) for multiple updates at once ++ */ ++ default void sendTagUpdate(io.papermc.paper.tag.@NotNull TagUpdate tagUpdate) { ++ this.sendTagUpdates(java.util.Set.of(tagUpdate)); ++ } ++ ++ /** ++ * Send a group of tag updates to this player. The player's client ++ * will apply these tag changes and update its behavior ++ * accordingly. ++ *

++ * These changes will be reset when the server is restarted ++ * or reloaded through {@code /bukkit:reload} or {@code /minecraft:reload}. ++ * Listen to {@link io.papermc.paper.event.server.ServerResourcesReloadedEvent} if ++ * you want to preserve your changes through reloads. ++ * ++ * @param tagUpdates the tag updates to send ++ */ ++ void sendTagUpdates(@NotNull Iterable> tagUpdates); ++ // Paper end + } diff --git a/patches/server/0010-Adventure.patch b/patches/server/0010-Adventure.patch index ecf70ed5d8..f256704264 100644 --- a/patches/server/0010-Adventure.patch +++ b/patches/server/0010-Adventure.patch @@ -735,10 +735,10 @@ index 0000000000000000000000000000000000000000..2fd6c3e65354071af71c7d8ebb97b559 +} diff --git a/src/main/java/io/papermc/paper/adventure/PaperAdventure.java b/src/main/java/io/papermc/paper/adventure/PaperAdventure.java new file mode 100644 -index 0000000000000000000000000000000000000000..01e424792f68bac73ec41726031ebbb53df13da7 +index 0000000000000000000000000000000000000000..3e45741833b8652951beb61ced05630507541a88 --- /dev/null +++ b/src/main/java/io/papermc/paper/adventure/PaperAdventure.java -@@ -0,0 +1,354 @@ +@@ -0,0 +1,361 @@ +package io.papermc.paper.adventure; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; @@ -783,6 +783,7 @@ index 0000000000000000000000000000000000000000..01e424792f68bac73ec41726031ebbb5 +import org.bukkit.craftbukkit.command.VanillaCommandWrapper; +import org.bukkit.craftbukkit.entity.CraftEntity; +import org.bukkit.entity.Entity; ++import org.intellij.lang.annotations.Subst; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + @@ -871,6 +872,12 @@ index 0000000000000000000000000000000000000000..01e424792f68bac73ec41726031ebbb5 + return asVanilla(key); + } + ++ public static Key asAdventure(final ResourceLocation location) { ++ @Subst("minecraft") final String namespace = location.getNamespace(); ++ @Subst("some_path") final String path = location.getPath(); ++ return Key.key(namespace, path); ++ } ++ + // Component + + public static Component asAdventure(final net.minecraft.network.chat.Component component) { diff --git a/patches/server/0969-Tag-Modification-API.patch b/patches/server/0969-Tag-Modification-API.patch new file mode 100644 index 0000000000..83ec09831f --- /dev/null +++ b/patches/server/0969-Tag-Modification-API.patch @@ -0,0 +1,135 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic +Date: Sat, 18 Mar 2023 14:37:29 -0700 +Subject: [PATCH] Tag Modification API + +== AT == +public net.minecraft.tags.TagNetworkSerialization$NetworkPayload (Ljava/util/Map;)V + +diff --git a/src/main/java/io/papermc/paper/tag/PaperTagUpdate.java b/src/main/java/io/papermc/paper/tag/PaperTagUpdate.java +new file mode 100644 +index 0000000000000000000000000000000000000000..c744e7bcf59fbd3007f8506d024546e0d7f2b11d +--- /dev/null ++++ b/src/main/java/io/papermc/paper/tag/PaperTagUpdate.java +@@ -0,0 +1,48 @@ ++package io.papermc.paper.tag; ++ ++import com.google.common.collect.ImmutableMap; ++import it.unimi.dsi.fastutil.ints.IntArrayList; ++import it.unimi.dsi.fastutil.ints.IntList; ++import java.util.Map; ++import java.util.Set; ++import net.kyori.adventure.key.Key; ++import net.kyori.adventure.key.Keyed; ++import net.minecraft.core.Holder; ++import net.minecraft.core.Registry; ++import net.minecraft.core.RegistryAccess; ++import net.minecraft.resources.ResourceKey; ++import net.minecraft.resources.ResourceLocation; ++import net.minecraft.tags.TagNetworkSerialization; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.framework.qual.DefaultQualifier; ++ ++@DefaultQualifier(NonNull.class) ++public record PaperTagUpdate( ++ ResourceKey> registryKey, ++ Map> updatedTags, ++ Map, java.util.List>> nmsTags ++) implements TagUpdate { ++ ++ // logic was adapted from TagNetworkSerialization#serializeToNetwork ++ public void addToNetworkPayloadMap(final Map>, TagNetworkSerialization.NetworkPayload> networkPayload, final RegistryAccess registryAccess) { ++ final ImmutableMap.Builder map = ImmutableMap.builder(); ++ final Registry registry = registryAccess.registryOrThrow(this.registryKey); ++ this.nmsTags.forEach((key, tagValues) -> { ++ final IntList idList = new IntArrayList(tagValues.size()); ++ for(Holder holder : tagValues) { ++ if (holder.kind() != Holder.Kind.REFERENCE) { ++ throw new IllegalStateException("Can't serialize unregistered value " + holder); ++ } ++ ++ idList.add(registry.getId(holder.value())); ++ } ++ ++ map.put(key.location(), idList); ++ }); ++ networkPayload.put(this.registryKey, new TagNetworkSerialization.NetworkPayload(map.build())); ++ } ++ ++ public void applyToRegistry(final RegistryAccess registryAccess) { ++ registryAccess.registryOrThrow(this.registryKey).bindTags(this.nmsTags); ++ } ++} +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java b/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java +index 34888b525fd35ac64e6e5e66036ad965a6769959..59ddba449d49c7f9d447b665eb52c765ff6b15ec 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java +@@ -78,4 +78,25 @@ public class CraftRegistry implements Registry { + public Stream values() { + return this.minecraftRegistry.keySet().stream().map(minecraftKey -> this.get(CraftNamespacedKey.fromMinecraft(minecraftKey))); + } ++ // Paper start - tag updates ++ @Override ++ public io.papermc.paper.tag.TagUpdate createTagUpdate(final java.util.function.Consumer>> editConsumer) { ++ final Map> map = this.minecraftRegistry.getTags().collect( ++ java.util.stream.Collectors.toMap( ++ pair -> io.papermc.paper.adventure.PaperAdventure.asAdventure(pair.getFirst().location()), ++ pair -> pair.getSecond().stream().map(holder -> java.util.Objects.requireNonNull(this.get(CraftNamespacedKey.fromMinecraft(holder.unwrapKey().orElseThrow().location())), () -> "Couldn't get bukkit object for key " + holder.unwrapKey())).collect(java.util.stream.Collectors.toSet()), ++ (bs, bs2) -> { throw new IllegalStateException("No duplicate tag keys"); }, ++ HashMap::new ++ ) ++ ); ++ editConsumer.accept(map); ++ final Map, java.util.List>> newTags = map.entrySet().stream().collect( ++ java.util.stream.Collectors.toMap( ++ entry -> net.minecraft.tags.TagKey.create(this.minecraftRegistry.key(), io.papermc.paper.adventure.PaperAdventure.asVanilla(entry.getKey())), ++ entry -> entry.getValue().stream().map(b -> (net.minecraft.core.Holder) this.minecraftRegistry.getHolderOrThrow(net.minecraft.resources.ResourceKey.create(this.minecraftRegistry.key(), io.papermc.paper.adventure.PaperAdventure.asVanilla(b.key())))).toList() ++ ) ++ ); ++ return new io.papermc.paper.tag.PaperTagUpdate<>(this.minecraftRegistry.key(), map, newTags); ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index f9a9d2bb7b6d1bf4a0931438de4d8c7ee0757479..ddddfdb95e9936a560e435b807d9cc3e7ac0b234 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -2919,4 +2919,22 @@ public final class CraftServer implements Server { + } + + // Paper end ++ // Paper start - tag updates ++ @Override ++ public void applyTagUpdate(final Iterable> tagUpdates) { ++ for (final io.papermc.paper.tag.TagUpdate update : tagUpdates) { ++ ((io.papermc.paper.tag.PaperTagUpdate) update).applyToRegistry(this.console.registryAccess()); ++ } ++ net.minecraft.world.level.block.Blocks.rebuildCache(); ++ this.playerList.broadcastAll(createTagUpdatePacket(tagUpdates, this.console.registryAccess())); ++ } ++ ++ public static net.minecraft.network.protocol.game.ClientboundUpdateTagsPacket createTagUpdatePacket(final Iterable> tagUpdates, final net.minecraft.core.RegistryAccess registryAccess) { ++ final Map>, net.minecraft.tags.TagNetworkSerialization.NetworkPayload> map = new java.util.HashMap<>(); ++ for (final io.papermc.paper.tag.TagUpdate update : tagUpdates) { ++ ((io.papermc.paper.tag.PaperTagUpdate) update).addToNetworkPayloadMap(map, registryAccess); ++ } ++ return new net.minecraft.network.protocol.game.ClientboundUpdateTagsPacket(map); ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index eca5e6b93dd84307bf9dbadf32414d6f506e69dc..cf350b2d6969f20cc0d1933a9ba51475f9d942d8 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -3148,4 +3148,11 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + return this.spigot; + } + // Spigot end ++ ++ // Paper start - tag updates ++ @Override ++ public void sendTagUpdates(final Iterable> tagUpdates) { ++ this.getHandle().connection.send(CraftServer.createTagUpdatePacket(tagUpdates, this.getHandle().getLevel().registryAccess())); ++ } ++ // Paper end + }