From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Thu, 20 Jun 2024 09:40:57 -0700 Subject: [PATCH] Tag Lifecycle Events == AT == public net/minecraft/tags/TagEntry id public net/minecraft/tags/TagEntry tag public net/minecraft/tags/TagEntry required diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProviderImpl.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProviderImpl.java index 7e84e0acd50ef04fa1ee2577f49e51c63f5ba5c3..05ceb6a0556c5e958237cd8e17525343dc3e8458 100644 --- a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProviderImpl.java +++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProviderImpl.java @@ -13,6 +13,8 @@ public final class LifecycleEventTypeProviderImpl implements LifecycleEventTypeP return (LifecycleEventTypeProviderImpl) LifecycleEventTypeProvider.provider(); } + private final PaperTagEventTypeProvider provider = new PaperTagEventTypeProvider(); + @Override public LifecycleEventType.Monitorable monitor(final String name, final Class ownerType) { return LifecycleEventRunner.INSTANCE.addEventType(new MonitorableLifecycleEventType<>(name, ownerType)); @@ -22,4 +24,9 @@ public final class LifecycleEventTypeProviderImpl implements LifecycleEventTypeP public LifecycleEventType.Prioritizable prioritized(final String name, final Class ownerType) { return LifecycleEventRunner.INSTANCE.addEventType(new PrioritizableLifecycleEventType.Simple<>(name, ownerType)); } + + @Override + public PaperTagEventTypeProvider tagProvider() { + return this.provider; + } } diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/PaperTagEventTypeProvider.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/PaperTagEventTypeProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..1cf4a3caa8d630e95eb569eef2cd577bba1a97a9 --- /dev/null +++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/PaperTagEventTypeProvider.java @@ -0,0 +1,24 @@ +package io.papermc.paper.plugin.lifecycle.event.types; + +import io.papermc.paper.plugin.bootstrap.BootstrapContext; +import io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.tag.PaperTagListenerManager; +import io.papermc.paper.tag.PostFlattenTagRegistrar; +import io.papermc.paper.tag.PreFlattenTagRegistrar; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; + +@DefaultQualifier(NonNull.class) +public class PaperTagEventTypeProvider implements TagEventTypeProvider { + + @Override + public PrioritizableLifecycleEventType.Simple>> preFlatten(final RegistryKey registryKey) { + return PaperTagListenerManager.INSTANCE.getPreFlattenType(registryKey); + } + + @Override + public PrioritizableLifecycleEventType.Simple>> postFlatten(final RegistryKey registryKey) { + return PaperTagListenerManager.INSTANCE.getPostFlattenType(registryKey); + } +} diff --git a/src/main/java/io/papermc/paper/tag/PaperPostFlattenTagRegistrar.java b/src/main/java/io/papermc/paper/tag/PaperPostFlattenTagRegistrar.java new file mode 100644 index 0000000000000000000000000000000000000000..cc37618cbbad7f21d65c7753f4dd4416042d2146 --- /dev/null +++ b/src/main/java/io/papermc/paper/tag/PaperPostFlattenTagRegistrar.java @@ -0,0 +1,118 @@ +package io.papermc.paper.tag; + +import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableMap; +import io.papermc.paper.adventure.PaperAdventure; +import io.papermc.paper.plugin.bootstrap.BootstrapContext; +import io.papermc.paper.plugin.lifecycle.event.registrar.PaperRegistrar; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.TypedKey; +import io.papermc.paper.registry.tag.TagKey; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.function.Function; +import net.minecraft.resources.ResourceLocation; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.framework.qual.DefaultQualifier; + +@SuppressWarnings("BoundedWildcard") +@DefaultQualifier(NonNull.class) +public class PaperPostFlattenTagRegistrar implements PaperRegistrar, PostFlattenTagRegistrar { + + public final Map> tags; + private final Function> fromIdConverter; + private final Function toIdConverter; + private final RegistryKey registryKey; + + public PaperPostFlattenTagRegistrar( + final Map> tags, + final TagEventConfig config + ) { + this.tags = tags; + this.fromIdConverter = config.fromIdConverter(); + this.toIdConverter = config.toIdConverter(); + this.registryKey = config.apiRegistryKey(); + } + + @Override + public void setCurrentContext(final @Nullable BootstrapContext owner) { + } + + @Override + public RegistryKey registryKey() { + return this.registryKey; + } + + @Override + public Map, Collection>> getAllTags() { + final ImmutableMap.Builder, Collection>> tags = ImmutableMap.builderWithExpectedSize(this.tags.size()); + for (final Map.Entry> entry : this.tags.entrySet()) { + final TagKey key = TagKey.create(this.registryKey, CraftNamespacedKey.fromMinecraft(entry.getKey())); + tags.put(key, this.convert(entry.getValue())); + } + return tags.build(); + } + + private Collection> convert(final Collection nms) { + return Collections.unmodifiableCollection( + Collections2.transform(nms, m -> this.convert(this.toIdConverter.apply(m))) + ); + } + + private TypedKey convert(final ResourceLocation location) { + return TypedKey.create(this.registryKey, CraftNamespacedKey.fromMinecraft(location)); + } + + private M convert(final TypedKey key) { + final Optional optional = this.fromIdConverter.apply(PaperAdventure.asVanilla(key.key())); + if (optional.isEmpty()) { + throw new IllegalArgumentException(key + " doesn't exist"); + } + return optional.get(); + } + + @Override + public boolean hasTag(final TagKey tagKey) { + return this.tags.containsKey(PaperAdventure.asVanilla(tagKey.key())); + } + + private Collection getNmsTag(final TagKey tagKey, final boolean create) { + final ResourceLocation vanillaKey = PaperAdventure.asVanilla(tagKey.key()); + Collection tag = this.tags.get(vanillaKey); + if (tag == null) { + if (create) { + tag = this.tags.computeIfAbsent(vanillaKey, k -> new ArrayList<>()); + } else { + throw new NoSuchElementException("Tag " + tagKey + " is not present"); + } + } + return tag; + } + + @Override + public Collection> getTag(final TagKey tagKey) { + return this.convert(this.getNmsTag(tagKey, false)); + } + + @Override + public void addToTag(final TagKey tagKey, final Collection> values) { + final Collection nmsTag = new ArrayList<>(this.getNmsTag(tagKey, true)); + for (final TypedKey key : values) { + nmsTag.add(this.convert(key)); + } + this.tags.put(PaperAdventure.asVanilla(tagKey.key()), nmsTag); + } + + @Override + public void setTag(final TagKey tagKey, final Collection> values) { + final List newList = List.copyOf(Collections2.transform(values, this::convert)); + this.tags.put(PaperAdventure.asVanilla(tagKey.key()), newList); + } +} diff --git a/src/main/java/io/papermc/paper/tag/PaperPreFlattenTagRegistrar.java b/src/main/java/io/papermc/paper/tag/PaperPreFlattenTagRegistrar.java new file mode 100644 index 0000000000000000000000000000000000000000..44111b55eaa6d1cc93e2c556b23bb5c97953caac --- /dev/null +++ b/src/main/java/io/papermc/paper/tag/PaperPreFlattenTagRegistrar.java @@ -0,0 +1,128 @@ +package io.papermc.paper.tag; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import io.papermc.paper.adventure.PaperAdventure; +import io.papermc.paper.plugin.bootstrap.BootstrapContext; +import io.papermc.paper.plugin.lifecycle.event.registrar.PaperRegistrar; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.tag.TagKey; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagLoader; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.framework.qual.DefaultQualifier; + +@SuppressWarnings("BoundedWildcard") +@DefaultQualifier(NonNull.class) +public class PaperPreFlattenTagRegistrar implements PaperRegistrar, PreFlattenTagRegistrar { + + public final Map> tags; + private final RegistryKey registryKey; + + private @Nullable BootstrapContext owner; + + public PaperPreFlattenTagRegistrar( + final Map> tags, + final TagEventConfig config + ) { + this.tags = new HashMap<>(tags); + this.registryKey = config.apiRegistryKey(); + } + + @Override + public void setCurrentContext(final @Nullable BootstrapContext owner) { + this.owner = owner; + } + + @Override + public RegistryKey registryKey() { + return this.registryKey; + } + + @Override + public Map, Collection>> getAllTags() { + final ImmutableMap.Builder, Collection>> builder = ImmutableMap.builderWithExpectedSize(this.tags.size()); + for (final Map.Entry> entry : this.tags.entrySet()) { + final TagKey key = TagKey.create(this.registryKey, CraftNamespacedKey.fromMinecraft(entry.getKey())); + builder.put(key, convert(entry.getValue())); + } + return builder.build(); + } + + private static List> convert(final List nmsEntries) { + return Collections.unmodifiableList(Lists.transform(nmsEntries, PaperPreFlattenTagRegistrar::convert)); + } + + private static io.papermc.paper.tag.TagEntry convert(final TagLoader.EntryWithSource nmsEntry) { + return new TagEntryImpl<>(CraftNamespacedKey.fromMinecraft(nmsEntry.entry().id), nmsEntry.entry().tag, nmsEntry.entry().required); + } + + private TagLoader.EntryWithSource convert(final TagEntry entry) { + Preconditions.checkState(this.owner != null, "Owner is not set"); + final ResourceLocation vanilla = PaperAdventure.asVanilla(entry.key()); + final net.minecraft.tags.TagEntry nmsEntry; + if (entry.isTag()) { + if (entry.isRequired()) { + nmsEntry = net.minecraft.tags.TagEntry.tag(vanilla); + } else { + nmsEntry = net.minecraft.tags.TagEntry.optionalTag(vanilla); + } + } else { + if (entry.isRequired()) { + nmsEntry = net.minecraft.tags.TagEntry.element(vanilla); + } else { + nmsEntry = net.minecraft.tags.TagEntry.optionalElement(vanilla); + } + } + return new TagLoader.EntryWithSource(nmsEntry, this.owner.getPluginMeta().getDisplayName()); + } + + @Override + public boolean hasTag(final TagKey tagKey) { + return this.tags.containsKey(PaperAdventure.asVanilla(tagKey.key())); + } + + private List getNmsTag(final TagKey tagKey, boolean create) { + final ResourceLocation vanillaKey = PaperAdventure.asVanilla(tagKey.key()); + List tag = this.tags.get(vanillaKey); + if (tag == null) { + if (create) { + tag = this.tags.computeIfAbsent(vanillaKey, k -> new ArrayList<>()); + } else { + throw new NoSuchElementException("Tag " + tagKey + " is not present"); + } + } + return tag; + } + + @Override + public List> getTag(final TagKey tagKey) { + return convert(this.getNmsTag(tagKey, false)); + } + + @Override + public void addToTag(final TagKey tagKey, final Collection> entries) { + final List nmsTag = new ArrayList<>(this.getNmsTag(tagKey, true)); + for (final TagEntry tagEntry : entries) { + nmsTag.add(this.convert(tagEntry)); + } + this.tags.put(PaperAdventure.asVanilla(tagKey.key()), nmsTag); + } + + @Override + public void setTag(final TagKey tagKey, final Collection> entries) { + final List newList = List.copyOf(Collections2.transform(entries, this::convert)); + this.tags.put(PaperAdventure.asVanilla(tagKey.key()), newList); + } +} diff --git a/src/main/java/io/papermc/paper/tag/PaperTagListenerManager.java b/src/main/java/io/papermc/paper/tag/PaperTagListenerManager.java new file mode 100644 index 0000000000000000000000000000000000000000..f3e4019f9d55d3cce34248a806bf0ccb37c8e7b1 --- /dev/null +++ b/src/main/java/io/papermc/paper/tag/PaperTagListenerManager.java @@ -0,0 +1,105 @@ +package io.papermc.paper.tag; + +import io.papermc.paper.plugin.bootstrap.BootstrapContext; +import io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner; +import io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent; +import io.papermc.paper.plugin.lifecycle.event.types.AbstractLifecycleEventType; +import io.papermc.paper.plugin.lifecycle.event.types.PrioritizableLifecycleEventType; +import io.papermc.paper.registry.PaperRegistries; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.event.RegistryEventMap; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagLoader; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.framework.qual.DefaultQualifier; + +@DefaultQualifier(NonNull.class) +public class PaperTagListenerManager { + + public static final String PRE_FLATTEN_EVENT_NAME = "pre-flatten"; + public static final String POST_FLATTEN_EVENT_NAME = "post-flatten"; + + public static final PaperTagListenerManager INSTANCE = new PaperTagListenerManager(); + + public final RegistryEventMap preFlatten = new RegistryEventMap(PRE_FLATTEN_EVENT_NAME); + public final RegistryEventMap postFlatten = new RegistryEventMap(POST_FLATTEN_EVENT_NAME); + + private PaperTagListenerManager() { + } + + public Map> firePreFlattenEvent( + final Map> initial, + final @Nullable TagEventConfig config + ) { + if (config == null || config.preFlatten() == null || !config.preFlatten().hasHandlers()) { + return initial; + } + final PaperPreFlattenTagRegistrar registrar = new PaperPreFlattenTagRegistrar<>(initial, config); + LifecycleEventRunner.INSTANCE.callReloadableRegistrarEvent( + config.preFlatten(), + registrar, + BootstrapContext.class, + config.cause() + ); + return Map.copyOf(registrar.tags); + } + + public Map> firePostFlattenEvent( + final Map> initial, + final @Nullable TagEventConfig config + ) { + if (config == null || config.postFlatten() == null || !config.postFlatten().hasHandlers()) { + return initial; + } + final PaperPostFlattenTagRegistrar registrar = new PaperPostFlattenTagRegistrar<>(initial, config); + LifecycleEventRunner.INSTANCE.callReloadableRegistrarEvent( + config.postFlatten(), + registrar, + BootstrapContext.class, + config.cause() + ); + return Map.copyOf(registrar.tags); + } + + public @Nullable TagEventConfig, B> createEventConfig(final Registry registry, final ReloadableRegistrarEvent.Cause cause) { + if (PaperRegistries.getEntry(registry.key()) == null) { + // TODO probably should be able to modify every registry + return null; + } + final RegistryKey registryKey = PaperRegistries.registryFromNms(registry.key()); + @Nullable AbstractLifecycleEventType>, ?> preFlatten = null; + if (this.preFlatten.hasHandlers(registryKey)) { + preFlatten = (AbstractLifecycleEventType>, ?>) this.preFlatten.>>getEventType(registryKey); + } + @Nullable AbstractLifecycleEventType>, ?> postFlatten = null; + if (this.postFlatten.hasHandlers(registryKey)) { + postFlatten = (AbstractLifecycleEventType>, ?>) this.postFlatten.>>getEventType(registryKey); + } + return new TagEventConfig<>( + preFlatten, + postFlatten, + cause, + registry::getHolder, + h -> ((Holder.Reference) h).key().location(), + PaperRegistries.registryFromNms(registry.key()) + ); + } + + public PrioritizableLifecycleEventType.Simple>> getPreFlattenType(final RegistryKey registryKey) { + return this.preFlatten.getOrCreate(registryKey, (ignored, name) -> { + return new PrioritizableLifecycleEventType.Simple<>(name, BootstrapContext.class); + }); + } + + public PrioritizableLifecycleEventType.Simple>> getPostFlattenType(final RegistryKey registryKey) { + return this.postFlatten.getOrCreate(registryKey, (ignored, name) -> { + return new PrioritizableLifecycleEventType.Simple<>(name, BootstrapContext.class); + }); + } +} diff --git a/src/main/java/io/papermc/paper/tag/TagEventConfig.java b/src/main/java/io/papermc/paper/tag/TagEventConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..d6d4bfc6f45d646afeace422a038c670e10aaf80 --- /dev/null +++ b/src/main/java/io/papermc/paper/tag/TagEventConfig.java @@ -0,0 +1,23 @@ +package io.papermc.paper.tag; + +import io.papermc.paper.plugin.bootstrap.BootstrapContext; +import io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent; +import io.papermc.paper.plugin.lifecycle.event.types.AbstractLifecycleEventType; +import io.papermc.paper.registry.RegistryKey; +import java.util.Optional; +import java.util.function.Function; +import net.minecraft.resources.ResourceLocation; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.framework.qual.DefaultQualifier; + +@DefaultQualifier(NonNull.class) +public record TagEventConfig( + @Nullable AbstractLifecycleEventType>, ?> preFlatten, + @Nullable AbstractLifecycleEventType>, ?> postFlatten, + ReloadableRegistrarEvent.Cause cause, + Function> fromIdConverter, + Function toIdConverter, + RegistryKey apiRegistryKey +) { +} diff --git a/src/main/java/net/minecraft/server/ServerFunctionLibrary.java b/src/main/java/net/minecraft/server/ServerFunctionLibrary.java index 8ae2ca2435d84fee930d2931e45ad440245cbe0f..9fff5ca1e9707e4e6be5ecb92be936332d24bb96 100644 --- a/src/main/java/net/minecraft/server/ServerFunctionLibrary.java +++ b/src/main/java/net/minecraft/server/ServerFunctionLibrary.java @@ -118,7 +118,7 @@ public class ServerFunctionLibrary implements PreparableReloadListener { return null; }).join()); this.functions = builder.build(); - this.tags = this.tagsLoader.build((Map>)intermediate.getFirst()); + this.tags = this.tagsLoader.build((Map>)intermediate.getFirst(), null); // Paper - command function tags are not implemented yet }, applyExecutor ); diff --git a/src/main/java/net/minecraft/tags/TagLoader.java b/src/main/java/net/minecraft/tags/TagLoader.java index b47afb0ce8d9517ac7ee9c651c160f99d70f8a98..0bde4f7ae7d1897c630513261d1d5fb75a762634 100644 --- a/src/main/java/net/minecraft/tags/TagLoader.java +++ b/src/main/java/net/minecraft/tags/TagLoader.java @@ -79,7 +79,10 @@ public class TagLoader { return list.isEmpty() ? Either.right(builder.build()) : Either.left(list); } - public Map> build(Map> tags) { + // Paper start - fire tag registrar events + public Map> build(Map> tags, @Nullable io.papermc.paper.tag.TagEventConfig eventConfig) { + tags = io.papermc.paper.tag.PaperTagListenerManager.INSTANCE.firePreFlattenEvent(tags, eventConfig); + // Paper end - fire tag registrar events final Map> map = Maps.newHashMap(); TagEntry.Lookup lookup = new TagEntry.Lookup() { @Nullable @@ -107,11 +110,13 @@ public class TagLoader { ) .ifRight(resolvedEntries -> map.put(id, (Collection)resolvedEntries)) ); - return map; + return io.papermc.paper.tag.PaperTagListenerManager.INSTANCE.firePostFlattenEvent(map, eventConfig); // Paper - fire tag registrar events } - public Map> loadAndBuild(ResourceManager manager) { - return this.build(this.load(manager)); + // Paper start - fire tag registrar events + public Map> loadAndBuild(ResourceManager manager, @Nullable io.papermc.paper.tag.TagEventConfig eventConfig) { + return this.build(this.load(manager), eventConfig); + // Paper end - fire tag registrar events } public static record EntryWithSource(TagEntry entry, String source) { diff --git a/src/main/java/net/minecraft/tags/TagManager.java b/src/main/java/net/minecraft/tags/TagManager.java index 7116362d798d7c28a4880c873a60507afa4673e6..2188274de5d1fe1aa5419be6247da6a3a2414a3b 100644 --- a/src/main/java/net/minecraft/tags/TagManager.java +++ b/src/main/java/net/minecraft/tags/TagManager.java @@ -39,7 +39,7 @@ public class TagManager implements PreparableReloadListener { ) { List>> list = this.registryAccess .registries() - .map(registry -> this.createLoader(manager, prepareExecutor, (RegistryAccess.RegistryEntry)registry)) + .map(registry -> this.createLoader(manager, prepareExecutor, (RegistryAccess.RegistryEntry)registry, applyExecutor instanceof net.minecraft.server.MinecraftServer ? io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL : io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.RELOAD)) // Paper - add registrar event cause .toList(); return CompletableFuture.allOf(list.toArray(CompletableFuture[]::new)) .thenCompose(synchronizer::wait) @@ -48,11 +48,15 @@ public class TagManager implements PreparableReloadListener { private CompletableFuture> createLoader( ResourceManager resourceManager, Executor prepareExecutor, RegistryAccess.RegistryEntry requirement + , io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause cause // Paper - add registrar event cause ) { ResourceKey> resourceKey = requirement.key(); Registry registry = requirement.value(); TagLoader> tagLoader = new TagLoader<>(registry::getHolder, Registries.tagsDirPath(resourceKey)); - return CompletableFuture.supplyAsync(() -> new TagManager.LoadResult<>(resourceKey, tagLoader.loadAndBuild(resourceManager)), prepareExecutor); + // Paper start - fire tag registrar events + final io.papermc.paper.tag.TagEventConfig, ?> config = io.papermc.paper.tag.PaperTagListenerManager.INSTANCE.createEventConfig(registry, cause); + return CompletableFuture.supplyAsync(() -> new TagManager.LoadResult<>(resourceKey, tagLoader.loadAndBuild(resourceManager, config)), prepareExecutor); + // Paper end - fire tag registrar events } public static record LoadResult(ResourceKey> key, Map>> tags) {