[1.18.2] Fix tags for custom forge registries. (#8495)

Tag-enabled registries must now be registered to vanilla's root registry. See RegistryBuilder#hasTags.
Modded tag-enabled registries have to use the format `data/<namespace>/tags/<registrynamespace>/<registryname>/<tag>.json`
This format is to prevent conflicts for registries with the same path but different namespaces
EX: Registry name `examplemod:shoe`, tag name `blue_shoes` would be `data/<namespace>/tags/examplemod/shoe/blue_shoes.json`
RegistryEvent.NewRegistry has been moved and renamed to NewRegistryEvent.
RegistryBuilder#create has been made private. See NewRegistryEvent#create
Created new ITagManager system for looking up Forge tags. See IForgeRegistry#tags.
Add lookup methods for Holders from forge registries. See IForgeRegistry#getHolder.
This commit is contained in:
SizableShrimp authored and GitHub committed 2022-03-16 13:36:00 -07:00
1 parent 30f3a7dbba
commit ad75cb8644
51 files changed
+1621 -531

No files matched your search

@@ -65,7 +65,7 @@ public class ForgeWorldPresetScreens
static void registerPresets()
{
ForgeRegistries.WORLD_TYPES.forEach(wt -> {
ForgeRegistries.WORLD_TYPES.get().forEach(wt -> {
GeneratorPreset gen = new GeneratorPreset(wt);
GENERATORS.put(wt, gen);
WorldPreset.registerGenerator(gen);
@@ -1096,7 +1096,7 @@ public class ForgeHooks
}
}
private static final Map<EntityDataSerializer<?>, DataSerializerEntry> serializerEntries = GameData.getSerializerMap();
private static Map<EntityDataSerializer<?>, DataSerializerEntry> serializerEntries = GameData.getSerializerMap();
//private static final ForgeRegistry<DataSerializerEntry> serializerRegistry = (ForgeRegistry<DataSerializerEntry>) ForgeRegistries.DATA_SERIALIZERS;
// Do not reimplement this ^ it introduces a chicken-egg scenario by classloading registries during bootstrap
@@ -1106,7 +1106,7 @@ public class ForgeHooks
EntityDataSerializer<?> serializer = vanilla.byId(id);
if (serializer == null)
{
DataSerializerEntry entry = ((ForgeRegistry<DataSerializerEntry>)ForgeRegistries.DATA_SERIALIZERS).getValue(id);
DataSerializerEntry entry = ((ForgeRegistry<DataSerializerEntry>)ForgeRegistries.DATA_SERIALIZERS.get()).getValue(id);
if (entry != null) serializer = entry.getSerializer();
}
return serializer;
@@ -1117,8 +1117,16 @@ public class ForgeHooks
int id = vanilla.getId(serializer);
if (id < 0)
{
DataSerializerEntry entry = serializerEntries.get(serializer);
if (entry != null) id = ((ForgeRegistry<DataSerializerEntry>)ForgeRegistries.DATA_SERIALIZERS).getID(entry);
// ForgeRegistries.DATA_SERIALIZERS is a deferred register now, so if this method is called too early, the serializer map will be null
// This should keep checking until it's not null
if (serializerEntries == null)
serializerEntries = GameData.getSerializerMap();
if (serializerEntries != null)
{
DataSerializerEntry entry = serializerEntries.get(serializer);
if (entry != null)
id = ((ForgeRegistry<DataSerializerEntry>) ForgeRegistries.DATA_SERIALIZERS.get()).getID(entry);
}
}
return id;
}
@@ -89,7 +89,7 @@ public class ForgeMod
private static final Logger LOGGER = LogManager.getLogger();
private static final Marker FORGEMOD = MarkerManager.getMarker("FORGEMOD");
private static final DeferredRegister<Attribute> ATTRIBUTES = DeferredRegister.create(Attribute.class, "forge");
private static final DeferredRegister<Attribute> ATTRIBUTES = DeferredRegister.create(ForgeRegistries.Keys.ATTRIBUTES, "forge");
public static final RegistryObject<Attribute> SWIM_SPEED = ATTRIBUTES.register("swim_speed", () -> new RangedAttribute("forge.swimSpeed", 1.0D, 0.0D, 1024.0D).setSyncable(true));
public static final RegistryObject<Attribute> NAMETAG_DISTANCE = ATTRIBUTES.register("nametag_distance", () -> new RangedAttribute("forge.nameTagDistance", 64.0D, 0.0D, 64.0).setSyncable(true));
@@ -144,6 +144,7 @@ public class ForgeMod
ModLoadingContext.get().registerConfig(ModConfig.Type.SERVER, ForgeConfig.serverSpec);
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, ForgeConfig.commonSpec);
modEventBus.register(ForgeConfig.class);
ForgeDeferredRegistriesSetup.setup(modEventBus);
// Forge does not display problems when the remote is not matching.
ModLoadingContext.get().registerExtensionPoint(IExtensionPoint.DisplayTest.class, ()->new IExtensionPoint.DisplayTest(()->"ANY", (remote, isServer)-> true));
StartupMessageManager.addModMessage("Forge version "+ForgeVersion.getVersion());
@@ -13,6 +13,7 @@ import net.minecraftforge.fml.event.IModBusEvent;
import net.minecraftforge.network.NetworkRegistry;
import net.minecraftforge.registries.GameData;
import net.minecraftforge.registries.ObjectHolderRegistry;
import net.minecraftforge.registries.RegistryManager;
import java.util.Collections;
import java.util.List;
@@ -24,7 +25,7 @@ import java.util.function.Supplier;
import java.util.stream.Stream;
public class ForgeStatesProvider implements IModStateProvider {
final ModLoadingState CREATE_REGISTRIES = ModLoadingState.withTransition("CREATE_REGISTRIES", "CONSTRUCT", ModLoadingPhase.GATHER, SerialTransition.of(()-> Stream.of(IModStateTransition.EventGenerator.fromFunction(RegistryEvent.NewRegistry::new))));
final ModLoadingState CREATE_REGISTRIES = ModLoadingState.withTransition("CREATE_REGISTRIES", "CONSTRUCT", ModLoadingPhase.GATHER, new SerialTransition<>(() -> Stream.of(IModStateTransition.EventGenerator.fromFunction(RegistryManager.newRegistryEventGenerator())), RegistryManager::preNewRegistryEvent, RegistryManager::postNewRegistryEvent, (e, prev) -> prev.thenApplyAsync(Function.identity(), e)));
final ModLoadingState OBJECT_HOLDERS = ModLoadingState.withInline("OBJECT_HOLDERS", "CREATE_REGISTRIES", ModLoadingPhase.GATHER, ml-> ObjectHolderRegistry.findObjectHolders());
final ModLoadingState INJECT_CAPABILITIES = ModLoadingState.withInline("INJECT_CAPABILITIES", "OBJECT_HOLDERS", ModLoadingPhase.GATHER, ml-> CapabilityManager.INSTANCE.injectCapabilities(ml.getAllScanData()));
final ModLoadingState UNFREEZE = ModLoadingState.withInline("UNFREEZE_DATA", "INJECT_CAPABILITIES", ModLoadingPhase.GATHER, ml->GameData.unfreezeData());
@@ -36,6 +37,7 @@ public class ForgeStatesProvider implements IModStateProvider {
return List.of(CREATE_REGISTRIES, OBJECT_HOLDERS, INJECT_CAPABILITIES, UNFREEZE, LOAD_REGISTRIES, FREEZE, NETLOCK);
}
// TODO 1.19: Pass the event instances to the pre and post hooks rather than the event generators
static record SerialTransition<T extends Event & IModBusEvent>(Supplier<Stream<EventGenerator<?>>> eventStream, BiFunction<Executor, ? extends EventGenerator<T>, CompletableFuture<List<Throwable>>> preDispatchHook, BiFunction<Executor, ? extends EventGenerator<T>, CompletableFuture<List<Throwable>>> postDispatchHook, BiFunction<Executor, CompletableFuture<List<Throwable>>, CompletableFuture<List<Throwable>>> finalActivityGenerator) implements IModStateTransition {
public static <T extends Event & IModBusEvent> SerialTransition<T> of(Supplier<Stream<EventGenerator<?>>> eventStream) {
return new SerialTransition<T>(eventStream, (t, f)->CompletableFuture.completedFuture(Collections.emptyList()), (t, f)->CompletableFuture.completedFuture(Collections.emptyList()), (e, prev) ->prev.thenApplyAsync(Function.identity(), e));
@@ -1,245 +0,0 @@
/*
* Minecraft Forge - Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.common;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import com.mojang.serialization.Lifecycle;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagLoader;
import net.minecraftforge.registries.ForgeRegistry;
import net.minecraftforge.registries.GameData;
import net.minecraftforge.registries.IForgeRegistry;
import net.minecraftforge.registries.IForgeRegistryEntry;
import net.minecraftforge.registries.RegistryManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class ForgeTagHandler
{
private static final Logger LOGGER = LogManager.getLogger();
private static Set<ResourceLocation> customTagTypeNames = Collections.emptySet();
private static boolean tagTypesSet = false;
// TODO-PATCHING: remove the custom registry tags stuff (1.18.2 has universal tags) and rework how optional tags could work
// @Nullable
// private static <T extends IForgeRegistryEntry<T>> StaticTagHelper<T> getTagHelper(IForgeRegistry<T> registry)
// {
// return (StaticTagHelper<T>) StaticTags.get(registry.getRegistryName());
// }
//
// private static void validateRegistrySupportsTags(IForgeRegistry<?> registry)
// {
// //Note: We also check against getTagHelper in case someone decides to use the helpers for tag creation for types supported by vanilla
// if (getTagHelper(registry) == null && (!(registry instanceof ForgeRegistry) || ((ForgeRegistry<?>) registry).getTagFolder() == null))
// {
// throw new IllegalArgumentException("Registry " + registry.getRegistryName() + " does not support tag types.");
// }
// }
//
// /**
// * Helper method that creates a named tag for a forge registry, erroring if the registry doesn't support custom tag types. If the custom tag types
// * have not been set yet, this method falls back and creates the tag reference delaying adding it to the tag registry to allow for statically
// * initializing and referencing the tag.
// * @param registry Registry the tag is for
// * @param name Name of the tag
// * @param <T> Type of the registry
// * @return A named tag
// */
// public static <T extends IForgeRegistryEntry<T>> Named<T> makeWrapperTag(IForgeRegistry<T> registry, ResourceLocation name)
// {
// validateRegistrySupportsTags(registry);
// if (tagTypesSet)
// {
// StaticTagHelper<T> tagRegistry = getTagHelper(registry);
// if (tagRegistry == null) throw new IllegalArgumentException("Registry " + registry.getRegistryName() + " does not support tag types.");
// return tagRegistry.bind(name.toString());
// }
// return StaticTagHelper.createDelayedTag(registry.getRegistryName(), name);
// }
//
// /**
// * Helper method that creates an optional tag for a forge registry, erroring if the registry doesn't support custom tag types. If the custom tag types
// * have not been set yet, this method falls back and creates the tag reference delaying adding it to the tag registry to allow for statically
// * initializing and referencing the tag.
// * @param registry Registry the tag is for
// * @param name Name of the tag
// * @param <T> Type of the registry
// * @return An optional tag
// */
// public static <T extends IForgeRegistryEntry<T>> IOptionalNamedTag<T> createOptionalTag(IForgeRegistry<T> registry, ResourceLocation name)
// {
// return createOptionalTag(registry, name, null);
// }
//
// /**
// * Helper method that creates an optional tag for a forge registry, erroring if the registry doesn't support custom tag types. If the custom tag types
// * have not been set yet, this method falls back and creates the tag reference delaying adding it to the tag registry to allow for statically
// * initializing and referencing the tag.
// * @param registry Registry the tag is for
// * @param name Name of the tag
// * @param defaults Default values for the optional tag
// * @param <T> Type of the registry
// * @return An optional tag
// */
// public static <T extends IForgeRegistryEntry<T>> IOptionalNamedTag<T> createOptionalTag(IForgeRegistry<T> registry, ResourceLocation name, @Nullable Set<Supplier<T>> defaults)
// {
// validateRegistrySupportsTags(registry);
// if (tagTypesSet)
// {
// StaticTagHelper<T> tagRegistry = getTagHelper(registry);
// if (tagRegistry == null) throw new IllegalArgumentException("Registry " + registry.getRegistryName() + " does not support tag types.");
// return tagRegistry.createOptional(name, defaults);
// }
// return StaticTagHelper.createDelayedOptional(registry.getRegistryName(), name, defaults);
// }
//
// /**
// * Helper method for creating named tags for custom forge registries. If the custom tag types have not been set yet, this method falls back and creates
// * the tag reference delaying adding it to the tag registry to allow for statically initializing and referencing the tag.
// * @param registryName Name of the registry the tag is for
// * @param name Name of the tag
// * @param <T> Type of the registry
// * @return A named tag
// * @implNote This method only errors instantly if tag types have already been set, otherwise the error is delayed until after registries finish initializing
// * and we can validate if the custom registry really does support custom tags.
// */
// public static <T extends IForgeRegistryEntry<T>> Named<T> makeWrapperTag(ResourceLocation registryName, ResourceLocation name)
// {
// if (tagTypesSet)
// {
// IForgeRegistry<T> registry = RegistryManager.ACTIVE.getRegistry(registryName);
// if (registry == null) throw new IllegalArgumentException("Could not find registry named: " + registryName);
// return makeWrapperTag(registry, name);
// }
// return StaticTagHelper.createDelayedTag(registryName, name);
// }
//
// /**
// * Helper method for creating optional tags for custom forge registries. If the custom tag types have not been set yet, this method falls back and creates
// * the tag reference delaying adding it to the tag registry to allow for statically initializing and referencing the tag.
// * @param registryName Name of the registry the tag is for
// * @param name Name of the tag
// * @param <T> Type of the registry
// * @return An optional tag
// * @implNote This method only errors instantly if tag types have already been set, otherwise the error is delayed until after registries finish initializing
// * and we can validate if the custom registry really does support custom tags.
// */
// public static <T extends IForgeRegistryEntry<T>> IOptionalNamedTag<T> createOptionalTag(ResourceLocation registryName, ResourceLocation name)
// {
// return createOptionalTag(registryName, name, null);
// }
//
// /**
// * Helper method for creating optional tags for custom forge registries. If the custom tag types have not been set yet, this method falls back and creates
// * the tag reference delaying adding it to the tag registry to allow for statically initializing and referencing the tag.
// * @param registryName Name of the registry the tag is for
// * @param name Name of the tag
// * @param defaults Default values for the optional tag
// * @param <T> Type of the registry
// * @return An optional tag
// * @implNote This method only errors instantly if tag types have already been set, otherwise the error is delayed until after registries finish initializing
// * and we can validate if the custom registry really does support custom tags.
// */
// public static <T extends IForgeRegistryEntry<T>> IOptionalNamedTag<T> createOptionalTag(ResourceLocation registryName, ResourceLocation name, @Nullable Set<Supplier<T>> defaults)
// {
// if (tagTypesSet)
// {
// IForgeRegistry<T> registry = RegistryManager.ACTIVE.getRegistry(registryName);
// if (registry == null) throw new IllegalArgumentException("Could not find registry named: " + registryName);
// return createOptionalTag(registry, name, defaults);
// }
// return StaticTagHelper.createDelayedOptional(registryName, name, defaults);
// }
/**
* Gets the all the registry names of registries that support custom tag types.
*/
public static Set<ResourceLocation> getCustomTagTypeNames()
{
return customTagTypeNames;
}
// /**
// * Sets the set containing the resource locations representing the registry name of each forge registry that supports custom tag types.
// *
// * @apiNote Internal: Calling this manually <strong>WILL</strong> cause a crash to occur as it can only be called once, and is done so by
// * forge after all registries have been initialized.
// */
// public static void setCustomTagTypes(Set<ResourceLocation> customTagTypesNames)
// {
// if (tagTypesSet) throw new RuntimeException("Custom tag types have already been set, this method should only be called by forge, and after registries are initialized");
// tagTypesSet = true;
// customTagTypeNames = ImmutableSet.copyOf(customTagTypesNames);
// //Add the static references for custom tag types to the proper tag registries
// // Note: If this ends up being a hotspot due to lots of mods having lots of statically registered tags
// // that get loaded/registered before the new registry event is fired/processed everywhere then this
// // potentially should end up being moved into an async processor.
// StaticTagHelper.performDelayedAdd();
// }
/**
* Creates a map for custom tag type to tag reader
*
* @apiNote Internal: For use by TagManager
*/
public static Map<ResourceLocation, TagLoader<?>> createCustomTagTypeReaders()
{
LOGGER.debug("Gathering custom tag collection reader from types.");
ImmutableMap.Builder<ResourceLocation, TagLoader<?>> builder = ImmutableMap.builder();
for (ResourceLocation registryName : customTagTypeNames)
{
ForgeRegistry<?> registry = RegistryManager.ACTIVE.getRegistry(registryName);
if (registry != null && registry.getTagFolder() != null)
{
builder.put(registryName, new TagLoader<>(rl -> Optional.ofNullable(registry.getValue(rl)), "tags/" + registry.getTagFolder()));
}
}
return builder.build();
}
// /**
// * Wraps the forge registry if it supports tags into the internal registry for use in serialization
// *
// * @apiNote Internal: For use in TagContainer
// */
// @SuppressWarnings({"OptionalUsedAsFieldOrParameterType", "unchecked"})
// public static <T> Optional<? extends Registry<T>> getWrapperRegistry(ResourceKey<? extends Registry<T>> key, Optional<? extends Registry<T>> vanillaReg)
// {
// if (vanillaReg.isPresent())
// return vanillaReg;
//
// ForgeRegistry<?> reg = RegistryManager.ACTIVE.getRegistry(key.location());
// if (reg == null || reg.getTagFolder() == null)
// return Optional.empty();
//
// if (reg.getDefaultKey() == null)
// return Optional.of((Registry<T>) GameData.getWrapper(reg.getRegistryKey(), Lifecycle.stable()));
// return Optional.of((Registry<T>) GameData.getWrapper(reg.getRegistryKey(), Lifecycle.stable(), "ignored"));
// }
//
// /**
// * Helper to reinject missing optional tags.
// *
// * @apiNote Internal
// */
// public static TagContainer reinjectOptionalTags(TagContainer tagCollectionSupplier)
// {
// TagContainer.Builder builder = new TagContainer.Builder();
// //noinspection unchecked,rawtypes
// StaticTags.visitHelpers(h -> builder.add(h.getKey(), h.reinjectOptionalTags(tagCollectionSupplier.getOrEmpty((ResourceKey) h.getKey()))));
// return builder.build();
// }
}
@@ -5,59 +5,28 @@
package net.minecraftforge.common.data;
import com.google.common.collect.ImmutableMap;
import com.mojang.serialization.Lifecycle;
import java.nio.file.Path;
import java.util.Map;
import javax.annotation.Nullable;
import net.minecraft.core.Registry;
import net.minecraft.data.DataGenerator;
import net.minecraft.data.tags.TagsProvider;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.core.Registry;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.ForgeRegistry;
import net.minecraftforge.registries.GameData;
import net.minecraftforge.registries.IForgeRegistry;
import net.minecraftforge.registries.IForgeRegistryEntry;
import org.jetbrains.annotations.Nullable;
public abstract class ForgeRegistryTagsProvider<T extends IForgeRegistryEntry<T>> extends TagsProvider<T>
{
//TODO add game event tags here when they become a forge registry
//Special handling for vanilla tag types in case someone decides to use the ForgeRegistryTagsProvider instead of one of the vanilla subtypes
private static final Map<IForgeRegistry<?>, String> vanillaTypes = ImmutableMap.<IForgeRegistry<?>, String>builder()
.put(ForgeRegistries.BLOCKS, "blocks")
.put(ForgeRegistries.ENTITIES, "entity_types")
.put(ForgeRegistries.FLUIDS, "fluids")
.put(ForgeRegistries.ITEMS, "items")
.build();
private static <T extends IForgeRegistryEntry<T>> Registry<T> wrapRegistry(IForgeRegistry<T> registryIn)
private static <T extends IForgeRegistryEntry<T>> Registry<T> wrapRegistry(IForgeRegistry<T> forgeRegistry)
{
if (!(registryIn instanceof ForgeRegistry))
throw new IllegalArgumentException("Forge registry " + registryIn.getRegistryName() + " is not an instance of a ForgeRegistry");
ForgeRegistry<T> forgeRegistry = (ForgeRegistry<T>) registryIn;
if (forgeRegistry.getTagFolder() == null && !vanillaTypes.containsKey(registryIn))
throw new IllegalArgumentException("Forge registry " + registryIn.getRegistryName() + " does not have support for tags");
if (forgeRegistry.tags() == null)
throw new IllegalArgumentException("Forge registry " + forgeRegistry.getRegistryName() + " does not have support for tags");
if (forgeRegistry.getDefaultKey() == null)
return GameData.getWrapper(forgeRegistry.getRegistryKey(), Lifecycle.experimental());
return GameData.getWrapper(forgeRegistry.getRegistryKey(), Lifecycle.experimental(), "default");
}
private static <T extends IForgeRegistryEntry<T>> String getTagFolder(IForgeRegistry<T> registryIn)
public ForgeRegistryTagsProvider(DataGenerator generator, IForgeRegistry<T> forgeRegistry, String modId, @Nullable ExistingFileHelper existingFileHelper)
{
String tagFolder = ((ForgeRegistry<T>) registryIn).getTagFolder();
return tagFolder == null ? vanillaTypes.get(registryIn) : tagFolder;
}
public ForgeRegistryTagsProvider(DataGenerator generatorIn, IForgeRegistry<T> registryIn, String modId, @Nullable ExistingFileHelper existingFileHelper)
{
super(generatorIn, wrapRegistry(registryIn), modId, existingFileHelper, getTagFolder(registryIn));
}
@Override
protected Path getPath(ResourceLocation id)
{
//TODO-PATCHING: Mojang has introduced logic here to determine where the tags go, we need to replicate that logic possibly.
return generator.getOutputFolder().resolve("data/" + id.getNamespace() + "/tags/" + folder + "/" + id.getPath() + ".json");
super(generator, wrapRegistry(forgeRegistry), modId, existingFileHelper);
}
}
@@ -116,11 +116,11 @@ public class LootModifierManager extends SimpleJsonResourceReloadListener {
ResourceLocation serializer = new ResourceLocation(GsonHelper.getAsString(object, "type"));
return ForgeRegistries.LOOT_MODIFIER_SERIALIZERS.getValue(serializer).read(location, object, lootConditions);
return ForgeRegistries.LOOT_MODIFIER_SERIALIZERS.get().getValue(serializer).read(location, object, lootConditions);
}
public static GlobalLootModifierSerializer<?> getSerializerForName(ResourceLocation resourcelocation) {
return ForgeRegistries.LOOT_MODIFIER_SERIALIZERS.getValue(resourcelocation);
return ForgeRegistries.LOOT_MODIFIER_SERIALIZERS.get().getValue(resourcelocation);
}
/**
@@ -34,7 +34,7 @@ public class ForgeWorldPreset extends ForgeRegistryEntry<ForgeWorldPreset>
if (StringUtil.isNullOrEmpty(defaultWorldType) || "default".equals(defaultWorldType))
return null; // use vanilla
ForgeWorldPreset def = ForgeRegistries.WORLD_TYPES.getValue(new ResourceLocation(defaultWorldType));
ForgeWorldPreset def = ForgeRegistries.WORLD_TYPES.get().getValue(new ResourceLocation(defaultWorldType));
if (def == null)
{
LOGGER.error("The defaultWorldType '{}' specified in the forge config has not been registered. The vanilla default generator will be used.", defaultWorldType);
@@ -17,7 +17,6 @@ import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.fml.event.IModBusEvent;
import net.minecraftforge.registries.RegistryBuilder;
import org.apache.commons.lang3.Validate;
import com.google.common.collect.ImmutableList;
@@ -27,7 +26,6 @@ import net.minecraftforge.eventbus.api.GenericEvent;
import net.minecraftforge.registries.IForgeRegistry;
import net.minecraftforge.registries.IForgeRegistryEntry;
/**
* RegistryEvent supertype.
*/
@@ -36,17 +34,6 @@ public class RegistryEvent<T extends IForgeRegistryEntry<T>> extends GenericEven
RegistryEvent(Class<T> clazz) {
super(clazz);
}
/**
* Register new registries when you receive this event, through the {@link RegistryBuilder}
*/
public static class NewRegistry extends net.minecraftforge.eventbus.api.Event implements IModBusEvent
{
public NewRegistry(ModContainer mc) {}
@Override
public String toString() {
return "RegistryEvent.NewRegistry";
}
}
/**
* Register your objects for the appropriate registry type when you receive this event.
@@ -8,8 +8,10 @@ package net.minecraftforge.network.filters;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
@@ -17,6 +19,7 @@ import io.netty.channel.ChannelHandler;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.commands.synchronization.ArgumentTypes;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.Connection;
import net.minecraft.network.protocol.game.ClientboundCommandsPacket;
@@ -25,15 +28,15 @@ import net.minecraft.network.protocol.game.ClientboundUpdateTagsPacket;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagNetworkSerialization;
import net.minecraftforge.common.ForgeTagHandler;
import net.minecraftforge.network.NetworkHooks;
import net.minecraftforge.registries.ForgeRegistries;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.minecraftforge.registries.RegistryManager;
import org.slf4j.Logger;
import com.google.common.collect.ImmutableMap;
import com.mojang.brigadier.tree.RootCommandNode;
import com.mojang.logging.LogUtils;
/**
* A filter for impl packets, used to filter/modify parts of vanilla impl messages that
@@ -42,8 +45,7 @@ import com.mojang.brigadier.tree.RootCommandNode;
@ChannelHandler.Sharable
public class VanillaConnectionNetworkFilter extends VanillaPacketFilter
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LogUtils.getLogger();
public VanillaConnectionNetworkFilter()
{
@@ -100,8 +102,15 @@ public class VanillaConnectionNetworkFilter extends VanillaPacketFilter
*/
private static ClientboundUpdateTagsPacket filterCustomTagTypes(ClientboundUpdateTagsPacket packet) {
Map<ResourceKey<? extends Registry<?>>, TagNetworkSerialization.NetworkPayload> tags = packet.getTags()
.entrySet().stream().filter(e -> !ForgeTagHandler.getCustomTagTypeNames().contains(e.getKey().location()))
.entrySet().stream().filter(e -> isVanillaRegistry(e.getKey().location()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
return new ClientboundUpdateTagsPacket(tags);
}
private static boolean isVanillaRegistry(ResourceLocation location)
{
// Checks if the registry name is contained within the static view of vanilla registries both in Registry and builtin registries
return RegistryManager.getVanillaRegistryKeys().contains(location)
|| RegistryAccess.REGISTRIES.keySet().stream().anyMatch(k -> k.location().equals(location));
}
}
@@ -5,13 +5,23 @@
package net.minecraftforge.registries;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.registries.tags.ITagManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
@@ -47,35 +57,66 @@ public class DeferredRegister<T extends IForgeRegistryEntry<T>>
*/
public static <B extends IForgeRegistryEntry<B>> DeferredRegister<B> create(IForgeRegistry<B> reg, String modid)
{
return new DeferredRegister<B>(reg, modid);
return new DeferredRegister<>(reg, modid);
}
/**
* Use for custom registries that are made during the NewRegistry event.
* Use for custom registries that are made during the NewRegistry event.
*
* @deprecated Use {@link #create(ResourceLocation, String)} and {@link #makeRegistry(Class, Supplier)} instead
*/
@Deprecated(forRemoval = true, since = "1.18.2")
public static <B extends IForgeRegistryEntry<B>> DeferredRegister<B> create(Class<B> base, String modid)
{
return new DeferredRegister<B>(base, modid);
return new DeferredRegister<>(null, base, modid);
}
/**
* DeferredRegister factory for custom registries that are made during the {@link NewRegistryEvent} event
* or with {@link #makeRegistry(Class, Supplier)}.
*
* @param key The key of the registry to reference
* @param modid The namespace for all objects registered to this DeferredRegister
*/
public static <B extends IForgeRegistryEntry<B>> DeferredRegister<B> create(ResourceKey<? extends Registry<B>> key, String modid)
{
return new DeferredRegister<>(key.location(), null, modid);
}
/**
* DeferredRegister factory for custom registries that are made during the {@link NewRegistryEvent} event
* or with {@link #makeRegistry(Class, Supplier)}.
*
* @param registryName The name of the registry, should include namespace. May come from another DeferredRegister through {@link #getRegistryName()}.
* @param modid The namespace for all objects registered to this DeferredRegister
*/
public static <B extends IForgeRegistryEntry<B>> DeferredRegister<B> create(ResourceLocation registryName, String modid)
{
return new DeferredRegister<>(registryName, null, modid);
}
private final ResourceLocation registryName;
private final Class<T> superType;
private final String modid;
private final Map<RegistryObject<T>, Supplier<? extends T>> entries = new LinkedHashMap<>();
private final Set<RegistryObject<T>> entriesView = Collections.unmodifiableSet(entries.keySet());
private IForgeRegistry<T> type;
private Supplier<IForgeRegistry<T>> typeSupplier;
private Supplier<RegistryBuilder<T>> registryFactory;
private SetMultimap<TagKey<T>, Supplier<T>> optionalTags;
private boolean seenRegisterEvent = false;
private DeferredRegister(Class<T> base, String modid)
private DeferredRegister(@Nullable ResourceLocation registryName, @Nullable Class<T> base, String modid)
{
this.registryName = registryName;
this.superType = base;
this.modid = modid;
}
private DeferredRegister(IForgeRegistry<T> reg, String modid)
{
this(reg.getRegistrySuperType(), modid);
this(reg.getRegistryName(), reg.getRegistrySuperType(), modid);
this.type = reg;
}
@@ -96,8 +137,11 @@ public class DeferredRegister<T extends IForgeRegistryEntry<T>>
final ResourceLocation key = new ResourceLocation(modid, name);
RegistryObject<I> ret;
if (this.type != null)
ret = RegistryObject.of(key, this.type);
IForgeRegistry<T> reg = getForgeRegistry();
if (reg != null)
ret = RegistryObject.of(key, reg);
else if (this.registryName != null)
ret = RegistryObject.of(key, this.registryName, this.modid);
else if (this.superType != null)
ret = RegistryObject.of(key, this.superType, this.modid);
else
@@ -118,15 +162,122 @@ public class DeferredRegister<T extends IForgeRegistryEntry<T>>
* @param name Path of the registry's {@link ResourceLocation}
* @param sup Supplier of the RegistryBuilder that is called to fill {@link #type} during the NewRegistry event
* @return A supplier of the {@link IForgeRegistry} created by the builder.
*
* @deprecated Use {@link #create(ResourceLocation, String)} and {@link #makeRegistry(Class, Supplier)} instead
*/
public Supplier<IForgeRegistry<T>> makeRegistry(final String name, final Supplier<RegistryBuilder<T>> sup) {
if (this.superType == null)
throw new IllegalStateException("Cannot create a registry without specifying a base type");
if (this.type != null || this.registryFactory != null)
throw new IllegalStateException("Cannot create a registry for a type that already exists");
@Deprecated(forRemoval = true, since = "1.18.2")
public Supplier<IForgeRegistry<T>> makeRegistry(final String name, final Supplier<RegistryBuilder<T>> sup)
{
return makeRegistry(new ResourceLocation(modid, name), this.superType, sup);
}
this.registryFactory = () -> sup.get().setName(new ResourceLocation(modid, name)).setType(this.superType);
return () -> this.type;
/**
* Only used for custom registries to fill the forge registry held in this DeferredRegister.
*
* Calls {@link RegistryBuilder#setName} and {@link RegistryBuilder#setType} automatically.
*
* @param base The base type to use in the created {@link IForgeRegistry}
* @param sup Supplier of a RegistryBuilder that initializes a {@link IForgeRegistry} during the {@link NewRegistryEvent} event
* @return A supplier of the {@link IForgeRegistry} created by the builder.
* Will always return null until after the {@link NewRegistryEvent} event fires.
*/
public Supplier<IForgeRegistry<T>> makeRegistry(final Class<T> base, final Supplier<RegistryBuilder<T>> sup)
{
return makeRegistry(this.registryName, base, sup);
}
/**
* Creates a tag key based on the current modid and provided path as the location and the registry name linked to this DeferredRegister.
* To control the namespace, use {@link #createTagKey(ResourceLocation)}.
*
* @throws IllegalStateException If the registry name was not set.
* Use the factories that take {@link #create(ResourceLocation, String) a registry name} or {@link #create(IForgeRegistry, String) forge registry}.
* @see #createTagKey(ResourceLocation)
* @see #createOptionalTagKey(String, Set)
*/
@NotNull
public TagKey<T> createTagKey(@NotNull String path)
{
Objects.requireNonNull(path);
return createTagKey(new ResourceLocation(this.modid, path));
}
/**
* Creates a tag key based on the provided resource location and the registry name linked to this DeferredRegister.
* To use the current modid as the namespace, use {@link #createTagKey(String)}.
*
* @throws IllegalStateException If the registry name was not set.
* Use the factories that take {@link #create(ResourceLocation, String) a registry name} or {@link #create(IForgeRegistry, String) forge registry}.
* @see #createTagKey(String)
* @see #createOptionalTagKey(ResourceLocation, Set)
*/
@NotNull
public TagKey<T> createTagKey(@NotNull ResourceLocation location)
{
if (this.registryName == null)
throw new IllegalStateException("The registry name was not set, cannot create a tag key");
Objects.requireNonNull(location);
return TagKey.create(ResourceKey.createRegistryKey(this.registryName), location);
}
/**
* Creates a tag key with the current modid and provided path that will use the set of defaults if the tag is not loaded from any datapacks.
* Useful on the client side when a server may not provide a specific tag.
* To control the namespace, use {@link #createOptionalTagKey(ResourceLocation, Set)}.
*
* @throws IllegalStateException If the registry name was not set.
* Use the factories that take {@link #create(ResourceLocation, String) a registry name} or {@link #create(IForgeRegistry, String) forge registry}.
* @see #createTagKey(String)
* @see #createTagKey(ResourceLocation)
* @see #createOptionalTagKey(ResourceLocation, Set)
* @see #addOptionalTagDefaults(TagKey, Set)
*/
@NotNull
public TagKey<T> createOptionalTagKey(@NotNull String path, @NotNull Set<? extends Supplier<T>> defaults)
{
Objects.requireNonNull(path);
return createOptionalTagKey(new ResourceLocation(this.modid, path), defaults);
}
/**
* Creates a tag key with the provided location that will use the set of defaults if the tag is not loaded from any datapacks.
* Useful on the client side when a server may not provide a specific tag.
* To use the current modid as the namespace, use {@link #createOptionalTagKey(String, Set)}.
*
* @throws IllegalStateException If the registry name was not set.
* Use the factories that take {@link #create(ResourceLocation, String) a registry name} or {@link #create(IForgeRegistry, String) forge registry}.
* @see #createTagKey(String)
* @see #createTagKey(ResourceLocation)
* @see #createOptionalTagKey(String, Set)
* @see #addOptionalTagDefaults(TagKey, Set)
*/
@NotNull
public TagKey<T> createOptionalTagKey(@NotNull ResourceLocation location, @NotNull Set<? extends Supplier<T>> defaults)
{
TagKey<T> tagKey = createTagKey(location);
addOptionalTagDefaults(tagKey, defaults);
return tagKey;
}
/**
* Adds defaults to an existing tag key.
* The set of defaults will be bound to the tag if the tag is not loaded from any datapacks.
* Useful on the client side when a server may not provide a specific tag.
*
* @throws IllegalStateException If the registry name was not set.
* Use the factories that take {@link #create(ResourceLocation, String) a registry name} or {@link #create(IForgeRegistry, String) forge registry}.
* @see #createOptionalTagKey(String, Set)
* @see #createOptionalTagKey(ResourceLocation, Set)
*/
public void addOptionalTagDefaults(@NotNull TagKey<T> name, @NotNull Set<? extends Supplier<T>> defaults)
{
Objects.requireNonNull(defaults);
if (optionalTags == null)
optionalTags = Multimaps.newSetMultimap(new IdentityHashMap<>(), HashSet::new);
optionalTags.putAll(name, defaults);
}
/**
@@ -162,17 +313,60 @@ public class DeferredRegister<T extends IForgeRegistryEntry<T>>
return entriesView;
}
/**
* @return The registry name stored in this deferred register. Useful for creating new deferred registers based on an existing one.
*/
@Nullable
public ResourceLocation getRegistryName()
{
return this.registryName;
}
private Supplier<IForgeRegistry<T>> makeRegistry(final ResourceLocation registryName, final Class<T> superType, final Supplier<RegistryBuilder<T>> sup) {
if (registryName == null)
throw new IllegalStateException("Cannot create a registry without specifying a registry name");
if (superType == null)
throw new IllegalStateException("Cannot create a registry without specifying a base type");
if (this.type != null || this.registryFactory != null)
throw new IllegalStateException("Cannot create a registry for a type that already exists");
this.registryFactory = () -> sup.get().setName(registryName).setType(superType);
return this::getForgeRegistry;
}
private IForgeRegistry<T> getForgeRegistry()
{
if (this.type == null && this.typeSupplier != null)
this.type = this.typeSupplier.get();
return this.type;
}
private void onFill(IForgeRegistry<T> registry)
{
if (this.optionalTags == null)
return;
ITagManager<T> tagManager = registry.tags();
if (tagManager == null)
throw new IllegalStateException("The forge registry " + registry.getRegistryName() + " does not support tags, but optional tags were registered!");
Multimaps.asMap(this.optionalTags).forEach(tagManager::addOptionalTagDefaults);
}
private void addEntries(RegistryEvent.Register<?> event)
{
if (this.type == null && this.registryFactory == null)
IForgeRegistry<T> storedType = this.getForgeRegistry();
if (storedType == null && this.registryFactory == null)
{
//If there is no type yet and we don't have a registry factory, attempt to capture the registry
//Note: This will only ever get run on the first registry event, as after that time,
// the type will no longer be null. This is needed here rather than during the NewRegistry event
// to ensure that mods can properly use deferred registers for custom registries added by other mods
captureRegistry();
storedType = this.type;
}
if (this.type != null && event.getGenericType() == this.type.getRegistrySuperType())
if (storedType != null && event.getGenericType() == storedType.getRegistrySuperType())
{
this.seenRegisterEvent = true;
@SuppressWarnings("unchecked")
@@ -185,14 +379,20 @@ public class DeferredRegister<T extends IForgeRegistryEntry<T>>
}
}
private void createRegistry(RegistryEvent.NewRegistry event)
private void createRegistry(NewRegistryEvent event)
{
this.type = this.registryFactory.get().create();
this.typeSupplier = event.create(this.registryFactory.get(), this::onFill);
}
private void captureRegistry()
{
if (this.superType != null)
if (this.registryName != null)
{
this.type = RegistryManager.ACTIVE.getRegistry(this.registryName);
if (this.type == null)
throw new IllegalStateException("Unable to find registry with key " + this.registryName + " for modid \"" + modid + "\" after NewRegistry event");
}
else if (this.superType != null)
{
this.type = RegistryManager.ACTIVE.getRegistry(this.superType);
if (this.type == null)
@@ -0,0 +1,28 @@
/*
* Minecraft Forge - Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.registries;
import net.minecraftforge.eventbus.api.IEventBus;
public class ForgeDeferredRegistriesSetup
{
private static boolean setup = false;
/**
* Internal forge method. Modders do not call.
*/
public static void setup(IEventBus modEventBus)
{
if (setup)
throw new IllegalStateException("Setup has already been called!");
ForgeRegistries.DEFERRED_DATA_SERIALIZERS.register(modEventBus);
ForgeRegistries.DEFERRED_LOOT_MODIFIER_SERIALIZERS.register(modEventBus);
ForgeRegistries.DEFERRED_WORLD_TYPES.register(modEventBus);
setup = true;
}
}
@@ -42,6 +42,8 @@ import net.minecraftforge.common.Tags;
import net.minecraftforge.common.loot.GlobalLootModifierSerializer;
import net.minecraftforge.common.world.ForgeWorldPreset;
import java.util.function.Supplier;
/**
* A class that exposes static references to all vanilla and Forge registries.
* Created to have a central place to access the registries directly if modders need.
@@ -52,46 +54,49 @@ public class ForgeRegistries
static { init(); } // This must be above the fields so we guarantee it's run before getRegistry is called. Yay static inializers
// Game objects
public static final IForgeRegistry<Block> BLOCKS = RegistryManager.ACTIVE.getRegistry(Block.class);
public static final IForgeRegistry<Fluid> FLUIDS = RegistryManager.ACTIVE.getRegistry(Fluid.class);
public static final IForgeRegistry<Item> ITEMS = RegistryManager.ACTIVE.getRegistry(Item.class);
public static final IForgeRegistry<MobEffect> MOB_EFFECTS = RegistryManager.ACTIVE.getRegistry(MobEffect.class);
public static final IForgeRegistry<SoundEvent> SOUND_EVENTS = RegistryManager.ACTIVE.getRegistry(SoundEvent.class);
public static final IForgeRegistry<Potion> POTIONS = RegistryManager.ACTIVE.getRegistry(Potion.class);
public static final IForgeRegistry<Enchantment> ENCHANTMENTS = RegistryManager.ACTIVE.getRegistry(Enchantment.class);
public static final IForgeRegistry<EntityType<?>> ENTITIES = RegistryManager.ACTIVE.getRegistry(EntityType.class);
public static final IForgeRegistry<BlockEntityType<?>> BLOCK_ENTITIES = RegistryManager.ACTIVE.getRegistry(BlockEntityType.class);
public static final IForgeRegistry<ParticleType<?>> PARTICLE_TYPES = RegistryManager.ACTIVE.getRegistry(ParticleType.class);
public static final IForgeRegistry<MenuType<?>> CONTAINERS = RegistryManager.ACTIVE.getRegistry(MenuType.class);
public static final IForgeRegistry<Motive> PAINTING_TYPES = RegistryManager.ACTIVE.getRegistry(Motive.class);
public static final IForgeRegistry<RecipeSerializer<?>> RECIPE_SERIALIZERS = RegistryManager.ACTIVE.getRegistry(RecipeSerializer.class);
public static final IForgeRegistry<Attribute> ATTRIBUTES = RegistryManager.ACTIVE.getRegistry(Attribute.class);
public static final IForgeRegistry<StatType<?>> STAT_TYPES = RegistryManager.ACTIVE.getRegistry(StatType.class);
public static final IForgeRegistry<Block> BLOCKS = RegistryManager.ACTIVE.getRegistry(Keys.BLOCKS);
public static final IForgeRegistry<Fluid> FLUIDS = RegistryManager.ACTIVE.getRegistry(Keys.FLUIDS);
public static final IForgeRegistry<Item> ITEMS = RegistryManager.ACTIVE.getRegistry(Keys.ITEMS);
public static final IForgeRegistry<MobEffect> MOB_EFFECTS = RegistryManager.ACTIVE.getRegistry(Keys.MOB_EFFECTS);
public static final IForgeRegistry<SoundEvent> SOUND_EVENTS = RegistryManager.ACTIVE.getRegistry(Keys.SOUND_EVENTS);
public static final IForgeRegistry<Potion> POTIONS = RegistryManager.ACTIVE.getRegistry(Keys.POTIONS);
public static final IForgeRegistry<Enchantment> ENCHANTMENTS = RegistryManager.ACTIVE.getRegistry(Keys.ENCHANTMENTS);
public static final IForgeRegistry<EntityType<?>> ENTITIES = RegistryManager.ACTIVE.getRegistry(Keys.ENTITY_TYPES);
public static final IForgeRegistry<BlockEntityType<?>> BLOCK_ENTITIES = RegistryManager.ACTIVE.getRegistry(Keys.BLOCK_ENTITY_TYPES);
public static final IForgeRegistry<ParticleType<?>> PARTICLE_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.PARTICLE_TYPES);
public static final IForgeRegistry<MenuType<?>> CONTAINERS = RegistryManager.ACTIVE.getRegistry(Keys.CONTAINER_TYPES);
public static final IForgeRegistry<Motive> PAINTING_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.PAINTING_TYPES);
public static final IForgeRegistry<RecipeSerializer<?>> RECIPE_SERIALIZERS = RegistryManager.ACTIVE.getRegistry(Keys.RECIPE_SERIALIZERS);
public static final IForgeRegistry<Attribute> ATTRIBUTES = RegistryManager.ACTIVE.getRegistry(Keys.ATTRIBUTES);
public static final IForgeRegistry<StatType<?>> STAT_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.STAT_TYPES);
// Villages
public static final IForgeRegistry<VillagerProfession> PROFESSIONS = RegistryManager.ACTIVE.getRegistry(VillagerProfession.class);
public static final IForgeRegistry<PoiType> POI_TYPES = RegistryManager.ACTIVE.getRegistry(PoiType.class);
public static final IForgeRegistry<MemoryModuleType<?>> MEMORY_MODULE_TYPES = RegistryManager.ACTIVE.getRegistry(MemoryModuleType.class);
public static final IForgeRegistry<SensorType<?>> SENSOR_TYPES = RegistryManager.ACTIVE.getRegistry(SensorType.class);
public static final IForgeRegistry<Schedule> SCHEDULES = RegistryManager.ACTIVE.getRegistry(Schedule.class);
public static final IForgeRegistry<Activity> ACTIVITIES = RegistryManager.ACTIVE.getRegistry(Activity.class);
public static final IForgeRegistry<VillagerProfession> PROFESSIONS = RegistryManager.ACTIVE.getRegistry(Keys.VILLAGER_PROFESSIONS);
public static final IForgeRegistry<PoiType> POI_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.POI_TYPES);
public static final IForgeRegistry<MemoryModuleType<?>> MEMORY_MODULE_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.MEMORY_MODULE_TYPES);
public static final IForgeRegistry<SensorType<?>> SENSOR_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.SENSOR_TYPES);
public static final IForgeRegistry<Schedule> SCHEDULES = RegistryManager.ACTIVE.getRegistry(Keys.SCHEDULES);
public static final IForgeRegistry<Activity> ACTIVITIES = RegistryManager.ACTIVE.getRegistry(Keys.ACTIVITIES);
// Worldgen
public static final IForgeRegistry<WorldCarver<?>> WORLD_CARVERS = RegistryManager.ACTIVE.getRegistry(WorldCarver.class);
public static final IForgeRegistry<Feature<?>> FEATURES = RegistryManager.ACTIVE.getRegistry(Feature.class);
public static final IForgeRegistry<ChunkStatus> CHUNK_STATUS = RegistryManager.ACTIVE.getRegistry(ChunkStatus.class);
public static final IForgeRegistry<StructureFeature<?>> STRUCTURE_FEATURES = RegistryManager.ACTIVE.getRegistry(StructureFeature.class);
public static final IForgeRegistry<BlockStateProviderType<?>> BLOCK_STATE_PROVIDER_TYPES = RegistryManager.ACTIVE.getRegistry(BlockStateProviderType.class);
public static final IForgeRegistry<FoliagePlacerType<?>> FOLIAGE_PLACER_TYPES = RegistryManager.ACTIVE.getRegistry(FoliagePlacerType.class);
public static final IForgeRegistry<TreeDecoratorType<?>> TREE_DECORATOR_TYPES = RegistryManager.ACTIVE.getRegistry(TreeDecoratorType.class);
public static final IForgeRegistry<WorldCarver<?>> WORLD_CARVERS = RegistryManager.ACTIVE.getRegistry(Keys.WORLD_CARVERS);
public static final IForgeRegistry<Feature<?>> FEATURES = RegistryManager.ACTIVE.getRegistry(Keys.FEATURES);
public static final IForgeRegistry<ChunkStatus> CHUNK_STATUS = RegistryManager.ACTIVE.getRegistry(Keys.CHUNK_STATUS);
public static final IForgeRegistry<StructureFeature<?>> STRUCTURE_FEATURES = RegistryManager.ACTIVE.getRegistry(Keys.STRUCTURE_FEATURES);
public static final IForgeRegistry<BlockStateProviderType<?>> BLOCK_STATE_PROVIDER_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.BLOCK_STATE_PROVIDER_TYPES);
public static final IForgeRegistry<FoliagePlacerType<?>> FOLIAGE_PLACER_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.FOLIAGE_PLACER_TYPES);
public static final IForgeRegistry<TreeDecoratorType<?>> TREE_DECORATOR_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.TREE_DECORATOR_TYPES);
// Dynamic/Data driven.
public static final IForgeRegistry<Biome> BIOMES = RegistryManager.ACTIVE.getRegistry(Keys.BIOMES);
// Custom forge registries
public static final IForgeRegistry<DataSerializerEntry> DATA_SERIALIZERS = RegistryManager.ACTIVE.getRegistry(DataSerializerEntry.class);
public static final IForgeRegistry<GlobalLootModifierSerializer<?>> LOOT_MODIFIER_SERIALIZERS = RegistryManager.ACTIVE.getRegistry(GlobalLootModifierSerializer.class);
public static final IForgeRegistry<ForgeWorldPreset> WORLD_TYPES = RegistryManager.ACTIVE.getRegistry(ForgeWorldPreset.class);
static final DeferredRegister<DataSerializerEntry> DEFERRED_DATA_SERIALIZERS = DeferredRegister.create(Keys.DATA_SERIALIZERS, Keys.DATA_SERIALIZERS.location().getNamespace());
public static final Supplier<IForgeRegistry<DataSerializerEntry>> DATA_SERIALIZERS = DEFERRED_DATA_SERIALIZERS.makeRegistry(DataSerializerEntry.class, GameData::getDataSerializersRegistryBuilder);
static final DeferredRegister<GlobalLootModifierSerializer<?>> DEFERRED_LOOT_MODIFIER_SERIALIZERS = DeferredRegister.create(Keys.LOOT_MODIFIER_SERIALIZERS, Keys.LOOT_MODIFIER_SERIALIZERS.location().getNamespace());
public static final Supplier<IForgeRegistry<GlobalLootModifierSerializer<?>>> LOOT_MODIFIER_SERIALIZERS = DEFERRED_LOOT_MODIFIER_SERIALIZERS.makeRegistry(GameData.c(GlobalLootModifierSerializer.class), GameData::getGLMSerializersRegistryBuilder);
static final DeferredRegister<ForgeWorldPreset> DEFERRED_WORLD_TYPES = DeferredRegister.create(Keys.WORLD_TYPES, Keys.WORLD_TYPES.location().getNamespace());
public static final Supplier<IForgeRegistry<ForgeWorldPreset>> WORLD_TYPES = DEFERRED_WORLD_TYPES.makeRegistry(ForgeWorldPreset.class, GameData::getWorldTypesRegistryBuilder);
public static final class Keys {
//Vanilla
@@ -127,7 +132,7 @@ public class ForgeRegistries
// Vanilla Dynamic
public static final ResourceKey<Registry<Biome>> BIOMES = key("worldgen/biome");
//Forge
// Forge
public static final ResourceKey<Registry<DataSerializerEntry>> DATA_SERIALIZERS = key("data_serializers");
public static final ResourceKey<Registry<GlobalLootModifierSerializer<?>>> LOOT_MODIFIER_SERIALIZERS = key("forge:loot_modifier_serializers");
public static final ResourceKey<Registry<ForgeWorldPreset>> WORLD_TYPES = key("forge:world_types");
@@ -8,16 +8,17 @@ package net.minecraftforge.registries;
import java.util.*;
import java.util.Map.Entry;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.DynamicOps;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderSet;
import net.minecraft.tags.TagKey;
import net.minecraftforge.common.util.LogMessageAdapter;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
import net.minecraftforge.registries.tags.ITagManager;
import org.apache.commons.lang3.Validate;
import com.google.common.base.Preconditions;
@@ -44,6 +45,8 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRegistryInternal<V>, IForgeRegistryModifiable<V>
{
@@ -70,14 +73,14 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
private final Set<Integer> blocked = Sets.newHashSet();
private final Multimap<ResourceLocation, V> overrides = ArrayListMultimap.create();
private final BiMap<OverrideOwner<V>, V> owners = HashBiMap.create();
private final ForgeRegistryTagManager<V> tagManager;
private final DummyFactory<V> dummyFactory;
private final boolean isDelegated;
private final int min;
private final int max;
private final boolean allowOverrides;
private final boolean isModifiable;
@Nullable
private final String tagFolder;
private final boolean hasWrapper;
private V defaultValue = null;
boolean isFrozen = false;
@@ -111,7 +114,8 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
this.isDelegated = ForgeRegistryEntry.class.isAssignableFrom(superType); //TODO: Make this IDelegatedRegistryEntry?
this.allowOverrides = builder.getAllowOverrides();
this.isModifiable = builder.getAllowModifications();
this.tagFolder = builder.getTagFolder();
this.hasWrapper = builder.getHasWrapper();
this.tagManager = this.hasWrapper ? new ForgeRegistryTagManager<>(this) : null;
if (this.create != null)
this.create.onCreate(this, stage);
}
@@ -156,6 +160,7 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
return this.name;
}
@Override
public ResourceKey<Registry<V>> getRegistryKey()
{
return this.key;
@@ -167,12 +172,7 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
return superType;
}
@Nullable
public String getTagFolder()
{
return tagFolder;
}
@NotNull
public Codec<V> getCodec()
{
return this.codec;
@@ -234,6 +234,7 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
return getResourceKey(value).map(ResourceKey::location).orElse(this.defaultKey);
}
@NotNull
@Override
public Optional<ResourceKey<V>> getResourceKey(V value)
{
@@ -241,19 +242,79 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
return Optional.ofNullable(this.owners.inverse().get(value)).map(OverrideOwner::key);
}
@SuppressWarnings("unchecked")
@Nullable
Registry<V> getWrapper()
{
if (!this.hasWrapper)
return null;
return this.defaultKey != null
? this.getSlaveMap(NamespacedDefaultedWrapper.Factory.ID, NamespacedDefaultedWrapper.class)
: this.getSlaveMap(NamespacedWrapper.Factory.ID, NamespacedWrapper.class);
}
@SuppressWarnings("unchecked")
@NotNull
Optional<NamespacedHolderHelper<V>> getHolderHelper()
{
Registry<V> wrapper = getWrapper();
if (!(wrapper instanceof IHolderHelperHolder))
return Optional.empty();
// Unsafe cast means we can't use pattern matching here
return Optional.of(((IHolderHelperHolder<V>) wrapper).getHolderHelper());
}
void onBindTags(Map<TagKey<V>, HolderSet.Named<V>> tags, Set<TagKey<V>> defaultedTags)
{
if (this.tagManager != null)
this.tagManager.bind(tags, defaultedTags);
}
@NotNull
@Override
public Optional<Holder<V>> getHolder(ResourceKey<V> key)
{
return getHolderHelper().flatMap(h -> h.getHolder(key));
}
@NotNull
@Override
public Optional<Holder<V>> getHolder(ResourceLocation location)
{
return getHolderHelper().flatMap(h -> h.getHolder(location));
}
@NotNull
@Override
public Optional<Holder<V>> getHolder(V value)
{
return getHolderHelper().flatMap(h -> h.getHolder(value));
}
@Nullable
@Override
public ITagManager<V> tags()
{
return this.tagManager;
}
@NotNull
@Override
public Set<ResourceLocation> getKeys()
{
return Collections.unmodifiableSet(this.names.keySet());
}
@Nonnull
@NotNull
@Override
public Collection<V> getValues()
{
return Collections.unmodifiableSet(this.names.values());
}
@NotNull
@Override
public Set<Entry<ResourceKey<V>, V>> getEntries()
{
@@ -0,0 +1,112 @@
/*
* Minecraft Forge - Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.registries;
import net.minecraft.Util;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderSet;
import net.minecraft.tags.TagKey;
import net.minecraftforge.registries.tags.ITag;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.Spliterator;
import java.util.stream.Stream;
class ForgeRegistryTag<V extends IForgeRegistryEntry<V>> implements ITag<V>
{
private final TagKey<V> key;
@Nullable
private HolderSet<V> holderSet;
@Nullable
private List<V> contents;
ForgeRegistryTag(TagKey<V> key)
{
this.key = key;
}
@Override
public TagKey<V> getKey()
{
return this.key;
}
@NotNull
@Override
public Iterator<V> iterator()
{
return this.getContents().iterator();
}
@Override
public Spliterator<V> spliterator()
{
return this.getContents().spliterator();
}
@Override
public boolean isEmpty()
{
return this.getContents().isEmpty();
}
@Override
public int size()
{
return this.getContents().size();
}
@Override
public Stream<V> stream()
{
return this.getContents().stream();
}
@Override
public boolean contains(V value)
{
return this.getContents().contains(value);
}
@Override
public Optional<V> getRandomElement(Random random)
{
return Util.getRandomSafe(this.getContents(), random);
}
@Override
public boolean isBound()
{
return this.holderSet != null;
}
List<V> getContents()
{
if (this.contents == null && this.holderSet != null)
this.contents = this.holderSet.stream().map(Holder::value).toList();
return this.contents == null ? List.of() : this.contents;
}
void bind(@Nullable HolderSet<V> holderSet)
{
this.holderSet = holderSet;
this.contents = null;
}
@Override
public String toString()
{
return "Tag[" +
"key=" + this.key + ", " +
"contents=" + getContents() + ']';
}
}
@@ -0,0 +1,138 @@
/*
* Minecraft Forge - Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.registries;
import com.google.common.collect.Iterators;
import net.minecraft.core.HolderSet;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraftforge.registries.tags.IReverseTag;
import net.minecraftforge.registries.tags.ITag;
import net.minecraftforge.registries.tags.ITagManager;
import org.jetbrains.annotations.NotNull;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Stream;
class ForgeRegistryTagManager<V extends IForgeRegistryEntry<V>> implements ITagManager<V>
{
private final ForgeRegistry<V> owner;
private volatile Map<TagKey<V>, ITag<V>> tags = new IdentityHashMap<>();
ForgeRegistryTagManager(ForgeRegistry<V> owner)
{
this.owner = owner;
}
void bind(Map<TagKey<V>, HolderSet.Named<V>> holderTags, Set<TagKey<V>> defaultedTags)
{
IdentityHashMap<TagKey<V>, ITag<V>> newTags = new IdentityHashMap<>(this.tags);
// Forcefully unbind all pre-existing tags
newTags.values().forEach(tag -> ((ForgeRegistryTag<V>) tag).bind(null));
// Bind all tags that were loaded
holderTags.forEach((key, holderSet) ->
((ForgeRegistryTag<V>) newTags.computeIfAbsent(key, ForgeRegistryTag::new)).bind(holderSet));
this.tags = newTags;
}
@NotNull
@Override
public ITag<V> getTag(@NotNull TagKey<V> name)
{
Objects.requireNonNull(name);
ITag<V> tag = this.tags.get(name);
if (tag == null)
{
// Create empty tag
tag = new ForgeRegistryTag<>(name);
// Mojang uses volatile and sets the tag map this way to not have the performance penalties of synced read access.
// However, this can generate a lot of new maps. We should look into performance alternatives.
IdentityHashMap<TagKey<V>, ITag<V>> map = new IdentityHashMap<>(this.tags);
map.put(name, tag);
this.tags = map;
}
return tag;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@NotNull
@Override
public Optional<IReverseTag<V>> getReverseTag(@NotNull V value)
{
Objects.requireNonNull(value);
// All Holders are implementors of IReverseTag
return (Optional<IReverseTag<V>>) (Optional) this.owner.getHolder(value);
}
@Override
public boolean isKnownTagName(@NotNull TagKey<V> name)
{
Objects.requireNonNull(name);
ITag<V> tag = this.tags.get(name);
return tag != null && tag.isBound();
}
@NotNull
@Override
public Iterator<ITag<V>> iterator()
{
return Iterators.unmodifiableIterator(this.tags.values().iterator());
}
@NotNull
@Override
public Stream<ITag<V>> stream()
{
return this.tags.values().stream();
}
@NotNull
@Override
public Stream<TagKey<V>> getTagNames()
{
return this.tags.keySet().stream();
}
@NotNull
@Override
public TagKey<V> createTagKey(@NotNull ResourceLocation location)
{
Objects.requireNonNull(location);
return TagKey.create(this.owner.getRegistryKey(), location);
}
@NotNull
@Override
public TagKey<V> createOptionalTagKey(@NotNull ResourceLocation location, @NotNull Set<? extends Supplier<V>> defaults)
{
TagKey<V> tagKey = createTagKey(location);
addOptionalTagDefaults(tagKey, defaults);
return tagKey;
}
@Override
public void addOptionalTagDefaults(@NotNull TagKey<V> name, @NotNull Set<? extends Supplier<V>> defaults)
{
Objects.requireNonNull(name);
Objects.requireNonNull(defaults);
this.owner.getHolderHelper().ifPresent(h -> h.addOptionalTag(name, defaults));
}
}
@@ -121,13 +121,13 @@ public class GameData
makeRegistry(BLOCKS, Block.class, "air").addCallback(BlockCallbacks.INSTANCE).legacyName("blocks").vanillaHolder(Block::builtInRegistryHolder).create();
makeRegistry(FLUIDS, Fluid.class, "empty").vanillaHolder(Fluid::builtInRegistryHolder).create();
makeRegistry(ITEMS, Item.class, "air").addCallback(ItemCallbacks.INSTANCE).legacyName("items").vanillaHolder(Item::builtInRegistryHolder).create();
makeRegistry(MOB_EFFECTS, MobEffect.class).legacyName("potions").tagFolder("mob_effects").create();
makeRegistry(MOB_EFFECTS, MobEffect.class).legacyName("potions").create();
//makeRegistry(BIOMES, Biome.class).legacyName("biomes").create();
makeRegistry(SOUND_EVENTS, SoundEvent.class).legacyName("soundevents").create();
makeRegistry(POTIONS, Potion.class, "empty").legacyName("potiontypes").tagFolder("potions").create();
makeRegistry(ENCHANTMENTS, Enchantment.class).legacyName("enchantments").tagFolder("enchantments").create();
makeRegistry(POTIONS, Potion.class, "empty").legacyName("potiontypes").create();
makeRegistry(ENCHANTMENTS, Enchantment.class).legacyName("enchantments").create();
makeRegistry(ENTITY_TYPES, c(EntityType.class), "pig").legacyName("entities").vanillaHolder(EntityType::builtInRegistryHolder).create();
makeRegistry(BLOCK_ENTITY_TYPES, c(BlockEntityType.class)).disableSaving().legacyName("blockentities").tagFolder("block_entity_types").create();
makeRegistry(BLOCK_ENTITY_TYPES, c(BlockEntityType.class)).disableSaving().legacyName("blockentities").create();
makeRegistry(PARTICLE_TYPES, c(ParticleType.class)).disableSaving().create();
makeRegistry(CONTAINER_TYPES, c(MenuType.class)).disableSaving().create();
makeRegistry(PAINTING_TYPES, Motive.class, "kebab").create();
@@ -147,25 +147,37 @@ public class GameData
makeRegistry(WORLD_CARVERS, c(WorldCarver.class)).disableSaving().disableSync().create();
makeRegistry(FEATURES, c(Feature.class)).addCallback(FeatureCallbacks.INSTANCE).disableSaving().disableSync().create();
makeRegistry(CHUNK_STATUS, ChunkStatus.class, "empty").disableSaving().disableSync().create();
makeRegistry(STRUCTURE_FEATURES, c(StructureFeature.class)).disableSaving().disableSync().tagFolder("structure_features").create();
makeRegistry(STRUCTURE_FEATURES, c(StructureFeature.class)).disableSaving().disableSync().create();
makeRegistry(BLOCK_STATE_PROVIDER_TYPES, c(BlockStateProviderType.class)).disableSaving().disableSync().create();
makeRegistry(FOLIAGE_PLACER_TYPES, c(FoliagePlacerType.class)).disableSaving().disableSync().create();
makeRegistry(TREE_DECORATOR_TYPES, c(TreeDecoratorType.class)).disableSaving().disableSync().create();
// Dynamic Worldgen
makeRegistry(BIOMES, Biome.class).disableSync().create();
// Custom forge registries
makeRegistry(DATA_SERIALIZERS, DataSerializerEntry.class, 256 /*vanilla space*/, MAX_VARINT).disableSaving().disableOverrides().addCallback(SerializerCallbacks.INSTANCE).create();
makeRegistry(LOOT_MODIFIER_SERIALIZERS, c(GlobalLootModifierSerializer.class)).disableSaving().disableSync().create();
makeRegistry(WORLD_TYPES, ForgeWorldPreset.class).disableSaving().disableSync().create();
}
static RegistryBuilder<DataSerializerEntry> getDataSerializersRegistryBuilder()
{
return makeRegistry(DATA_SERIALIZERS, DataSerializerEntry.class, 256 /*vanilla space*/, MAX_VARINT).disableSaving().disableOverrides()
.addCallback(SerializerCallbacks.INSTANCE);
}
static RegistryBuilder<GlobalLootModifierSerializer<?>> getGLMSerializersRegistryBuilder()
{
return makeRegistry(LOOT_MODIFIER_SERIALIZERS, c(GlobalLootModifierSerializer.class)).disableSaving().disableSync();
}
static RegistryBuilder<ForgeWorldPreset> getWorldTypesRegistryBuilder()
{
return makeRegistry(WORLD_TYPES, ForgeWorldPreset.class).disableSaving().disableSync();
}
@SuppressWarnings("unchecked") //Ugly hack to let us pass in a typed Class object. Remove when we remove type specific references.
private static <T> Class<T> c(Class<?> cls) { return (Class<T>)cls; }
static <T> Class<T> c(Class<?> cls) { return (Class<T>)cls; }
private static <T extends IForgeRegistryEntry<T>> RegistryBuilder<T> makeRegistry(ResourceKey<? extends Registry<T>> key, Class<T> type)
{
return new RegistryBuilder<T>().setName(key.location()).setType(type).setMaxID(MAX_VARINT).addCallback(new NamespacedWrapper.Factory<T>());
return new RegistryBuilder<T>().setName(key.location()).setType(type).setMaxID(MAX_VARINT).hasWrapper();
}
private static <T extends IForgeRegistryEntry<T>> RegistryBuilder<T> makeRegistry(ResourceKey<? extends Registry<T>> key, Class<T> type, int min, int max)
{
@@ -199,31 +211,32 @@ public class GameData
@SuppressWarnings("unchecked")
public static Map<Block,Item> getBlockItemMap()
{
return RegistryManager.ACTIVE.getRegistry(Item.class).getSlaveMap(BLOCK_TO_ITEM, Map.class);
return RegistryManager.ACTIVE.getRegistry(ITEMS).getSlaveMap(BLOCK_TO_ITEM, Map.class);
}
@SuppressWarnings("unchecked")
public static IdMapper<BlockState> getBlockStateIDMap()
{
return RegistryManager.ACTIVE.getRegistry(Block.class).getSlaveMap(BLOCKSTATE_TO_ID, IdMapper.class);
return RegistryManager.ACTIVE.getRegistry(BLOCKS).getSlaveMap(BLOCKSTATE_TO_ID, IdMapper.class);
}
@SuppressWarnings("unchecked")
public static Map<BlockState, PoiType> getBlockStatePointOfInterestTypeMap()
{
return RegistryManager.ACTIVE.getRegistry(PoiType.class).getSlaveMap(BLOCKSTATE_TO_POINT_OF_INTEREST_TYPE, Map.class);
return RegistryManager.ACTIVE.getRegistry(POI_TYPES).getSlaveMap(BLOCKSTATE_TO_POINT_OF_INTEREST_TYPE, Map.class);
}
@SuppressWarnings("unchecked")
public static Map<EntityDataSerializer<?>, DataSerializerEntry> getSerializerMap()
{
return RegistryManager.ACTIVE.getRegistry(DataSerializerEntry.class).getSlaveMap(SERIALIZER_TO_ENTRY, Map.class);
ForgeRegistry<DataSerializerEntry> registry = RegistryManager.ACTIVE.getRegistry(DATA_SERIALIZERS);
return registry == null ? null : registry.getSlaveMap(SERIALIZER_TO_ENTRY, Map.class);
}
@SuppressWarnings("unchecked")
public static BiMap<String, StructureFeature<?>> getStructureMap()
{
return (BiMap<String, StructureFeature<?>>) RegistryManager.ACTIVE.getRegistry(Feature.class).getSlaveMap(STRUCTURES, BiMap.class);
return (BiMap<String, StructureFeature<?>>) RegistryManager.ACTIVE.getRegistry(FEATURES).getSlaveMap(STRUCTURES, BiMap.class);
}
public static <K extends IForgeRegistryEntry<K>> K register_impl(K value)
@@ -5,16 +5,19 @@
package net.minecraftforge.registries;
import com.mojang.serialization.Codec;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.registries.tags.ITagManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.mojang.serialization.Codec;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
/**
* Main interface for the registry system. Use this to query the registry system.
@@ -23,6 +26,7 @@ import net.minecraft.resources.ResourceLocation;
*/
public interface IForgeRegistry<V extends IForgeRegistryEntry<V>> extends Iterable<V>
{
ResourceKey<Registry<V>> getRegistryKey();
ResourceLocation getRegistryName();
Class<V> getRegistrySuperType();
@@ -38,13 +42,39 @@ public interface IForgeRegistry<V extends IForgeRegistryEntry<V>> extends Iterab
@Nullable V getValue(ResourceLocation key);
@Nullable ResourceLocation getKey(V value);
@Nullable ResourceLocation getDefaultKey();
@Nullable Optional<ResourceKey<V>> getResourceKey(V value);
@NotNull Optional<ResourceKey<V>> getResourceKey(V value);
@Nonnull Set<ResourceLocation> getKeys();
@Nonnull Collection<V> getValues();
@Nonnull Set<Entry<ResourceKey<V>, V>> getEntries();
@NotNull Set<ResourceLocation> getKeys();
@NotNull Collection<V> getValues();
@NotNull Set<Entry<ResourceKey<V>, V>> getEntries();
@Nonnull Codec<V> getCodec();
/**
* @see Registry#byNameCodec()
*/
@NotNull Codec<V> getCodec();
/**
* This method exists purely as a stopgap for vanilla compatibility.
* For anything tag related, use {@link #tags()}.
*
* @see Registry#getHolder(ResourceKey)
*/
@NotNull Optional<Holder<V>> getHolder(ResourceKey<V> key);
/**
* This method exists purely as a stopgap for vanilla compatibility.
* For anything tag related, use {@link #tags()}.
*/
@NotNull Optional<Holder<V>> getHolder(ResourceLocation location);
/**
* This method exists purely as a stopgap for vanilla compatibility.
* For anything tag related, use {@link #tags()}.
*/
@NotNull Optional<Holder<V>> getHolder(V value);
/**
* @return an instance of {@link ITagManager} if this registry supports tags and/or has a wrapper registry, null otherwise
*/
@Nullable ITagManager<V> tags();
/**
* Retrieve the slave map of type T from the registry.
@@ -0,0 +1,11 @@
/*
* Minecraft Forge - Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.registries;
interface IHolderHelperHolder<T extends IForgeRegistryEntry<T>>
{
NamespacedHolderHelper<T> getHolderHelper();
}
@@ -30,7 +30,7 @@ import net.minecraft.core.DefaultedRegistry;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Lifecycle;
class NamespacedDefaultedWrapper<T extends IForgeRegistryEntry<T>> extends DefaultedRegistry<T> implements ILockableRegistry
class NamespacedDefaultedWrapper<T extends IForgeRegistryEntry<T>> extends DefaultedRegistry<T> implements ILockableRegistry, IHolderHelperHolder<T>
{
private final ForgeRegistry<T> delegate;
private final NamespacedHolderHelper<T> holders;
@@ -42,7 +42,7 @@ class NamespacedDefaultedWrapper<T extends IForgeRegistryEntry<T>> extends Defau
{
super("empty", owner.getRegistryKey(), Lifecycle.experimental(), holderLookup);
this.delegate = owner;
this.holders = new NamespacedHolderHelper<>(this, this.delegate.getDefaultKey(), holderLookup);
this.holders = new NamespacedHolderHelper<>(owner, this, this.delegate.getDefaultKey(), holderLookup);
}
@Override
@@ -186,6 +186,12 @@ class NamespacedDefaultedWrapper<T extends IForgeRegistryEntry<T>> extends Defau
return this.delegate.size();
}
@Override
public NamespacedHolderHelper<T> getHolderHelper()
{
return this.holders;
}
@Override public Optional<Holder<T>> getHolder(int id) { return this.holders.getHolder(id); }
@Override public Optional<Holder<T>> getHolder(ResourceKey<T> key) { return this.holders.getHolder(key); }
@Override public Holder<T> getOrCreateHolder(ResourceKey<T> key) { return this.holders.getOrCreateHolder(key); }
@@ -202,7 +208,7 @@ class NamespacedDefaultedWrapper<T extends IForgeRegistryEntry<T>> extends Defau
@Override public void resetTags() { this.holders.resetTags(); }
@Deprecated @Override public void unfreeze() { this.holders.unfreeze(); }
/** @Depreciated Forge: For internal use only. Use the Register events when registering values. */
/** @deprecated Forge: For internal use only. Use the Register events when registering values. */
@Deprecated @Override public void lock(){ this.locked = true; }
@@ -7,6 +7,7 @@ package net.minecraftforge.registries;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
@@ -15,11 +16,14 @@ import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -34,15 +38,18 @@ import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import org.jetbrains.annotations.NotNull;
class NamespacedHolderHelper<T extends IForgeRegistryEntry<T>>
{
private static final Logger LOGGER = LogManager.getLogger();
private final ForgeRegistry<T> owner;
private final Registry<T> self;
@Nullable
private final ResourceLocation defaultKey;
@Nullable
private final Function<T, Holder.Reference<T>> holderLookup;
private final Multimap<TagKey<T>, Supplier<T>> optionalTags = Multimaps.newSetMultimap(new IdentityHashMap<>(), HashSet::new);
private boolean frozen = false; // Frozen is vanilla's variant of locked, but it can be unfrozen
private List<Holder.Reference<T>> holdersSorted;
@@ -52,8 +59,9 @@ class NamespacedHolderHelper<T extends IForgeRegistryEntry<T>>
private volatile Map<TagKey<T>, HolderSet.Named<T>> tags = new IdentityHashMap<>();
private Holder.Reference<T> defaultHolder;
NamespacedHolderHelper(Registry<T> self, @Nullable ResourceLocation defaultKey, @Nullable Function<T, Holder.Reference<T>> holderLookup)
NamespacedHolderHelper(ForgeRegistry<T> owner, Registry<T> self, @Nullable ResourceLocation defaultKey, @Nullable Function<T, Holder.Reference<T>> holderLookup)
{
this.owner = owner;
this.self = self;
this.defaultKey = defaultKey;
this.holderLookup = holderLookup;
@@ -70,6 +78,16 @@ class NamespacedHolderHelper<T extends IForgeRegistryEntry<T>>
return Optional.ofNullable(this.holdersByName.get(key.location()));
}
Optional<Holder<T>> getHolder(ResourceLocation location)
{
return Optional.ofNullable(this.holdersByName.get(location));
}
Optional<Holder<T>> getHolder(T value)
{
return Optional.ofNullable(this.holders.get(value));
}
Holder<T> getOrCreateHolder(ResourceKey<T> key)
{
return this.holdersByName.computeIfAbsent(key.location(), k ->
@@ -120,6 +138,11 @@ class NamespacedHolderHelper<T extends IForgeRegistryEntry<T>>
return named;
}
void addOptionalTag(TagKey<T> name, @NotNull Set<? extends Supplier<T>> defaults)
{
this.optionalTags.putAll(name, defaults);
}
Stream<TagKey<T>> getTagNames()
{
return this.tags.keySet().stream();
@@ -165,18 +188,7 @@ class NamespacedHolderHelper<T extends IForgeRegistryEntry<T>>
{
Map<Holder.Reference<T>, List<TagKey<T>>> holderToTag = new IdentityHashMap<>();
this.holdersByName.values().forEach(v -> holderToTag.put(v, new ArrayList<>()));
newTags.forEach((k, v) ->
{
for(Holder<T> holder : v)
{
if (!holder.isValidInRegistry(this.self))
throw new IllegalStateException("Can't create named set " + k + " containing value " + holder + " from outside registry " + this);
if (!(holder instanceof Holder.Reference<T>))
throw new IllegalStateException("Found direct holder " + holder + " value in tag " + k);
holderToTag.get((Holder.Reference<T>)holder).add(k);
}
});
newTags.forEach((name, values) -> values.forEach(holder -> addTagToHolder(holderToTag, name, holder)));
Set<TagKey<T>> set = Sets.difference(this.tags.keySet(), newTags.keySet());
if (!set.isEmpty())
@@ -184,8 +196,33 @@ class NamespacedHolderHelper<T extends IForgeRegistryEntry<T>>
Map<TagKey<T>, HolderSet.Named<T>> tmpTags = new IdentityHashMap<>(this.tags);
newTags.forEach((k, v) -> tmpTags.computeIfAbsent(k, this::createTag).bind(v));
Set<TagKey<T>> defaultedTags = Sets.difference(this.optionalTags.keySet(), newTags.keySet());
defaultedTags.forEach(name -> {
List<Holder<T>> defaults = this.optionalTags.get(name).stream()
.map(valueSupplier -> getHolder(valueSupplier.get()).orElse(null))
.filter(Objects::nonNull)
.distinct()
.toList();
defaults.forEach(holder -> addTagToHolder(holderToTag, name, holder));
tmpTags.computeIfAbsent(name, this::createTag).bind(defaults);
});
holderToTag.forEach(Holder.Reference::bindTags);
this.tags = tmpTags;
this.owner.onBindTags(this.tags, defaultedTags);
}
private void addTagToHolder(Map<Holder.Reference<T>, List<TagKey<T>>> holderToTag, TagKey<T> name, Holder<T> holder)
{
if (!holder.isValidInRegistry(this.self))
throw new IllegalStateException("Can't create named set " + name + " containing value " + holder + " from outside registry " + this);
if (!(holder instanceof Holder.Reference<T>))
throw new IllegalStateException("Found direct holder " + holder + " value in tag " + name);
holderToTag.get((Holder.Reference<T>) holder).add(name);
}
void resetTags()
@@ -32,7 +32,7 @@ import org.apache.logging.log4j.Logger;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Lifecycle;
class NamespacedWrapper<T extends IForgeRegistryEntry<T>> extends MappedRegistry<T> implements ILockableRegistry
class NamespacedWrapper<T extends IForgeRegistryEntry<T>> extends MappedRegistry<T> implements ILockableRegistry, IHolderHelperHolder<T>
{
private static final Logger LOGGER = LogManager.getLogger();
private final ForgeRegistry<T> delegate;
@@ -45,7 +45,7 @@ class NamespacedWrapper<T extends IForgeRegistryEntry<T>> extends MappedRegistry
{
super(owner.getRegistryKey(), Lifecycle.experimental(), holderLookup);
this.delegate = owner;
this.holders = new NamespacedHolderHelper<>(this, null, holderLookup);
this.holders = new NamespacedHolderHelper<>(owner, this, null, holderLookup);
}
@Override
@@ -184,6 +184,12 @@ class NamespacedWrapper<T extends IForgeRegistryEntry<T>> extends MappedRegistry
return this.delegate.size();
}
@Override
public NamespacedHolderHelper<T> getHolderHelper()
{
return this.holders;
}
@Override public Optional<Holder<T>> getHolder(int id) { return this.holders.getHolder(id); }
@Override public Optional<Holder<T>> getHolder(ResourceKey<T> key) { return this.holders.getHolder(key); }
@Override public Holder<T> getOrCreateHolder(ResourceKey<T> key) { return this.holders.getOrCreateHolder(key); }
@@ -200,7 +206,7 @@ class NamespacedWrapper<T extends IForgeRegistryEntry<T>> extends MappedRegistry
@Override public void resetTags() { this.holders.resetTags(); }
@Deprecated @Override public void unfreeze() { this.holders.unfreeze(); }
/** @Depreciated Forge: For internal use only. Use the Register events when registering values. */
/** @deprecated Forge: For internal use only. Use the Register events when registering values. */
@Deprecated @Override public void lock(){ this.locked = true; }
@@ -0,0 +1,103 @@
/*
* Minecraft Forge - Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.registries;
import net.minecraft.core.Registry;
import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.fml.event.IModBusEvent;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* Register new registries when you receive this event through {@link RegistryBuilder} and {@link #create(RegistryBuilder)}.
*/
public class NewRegistryEvent extends Event implements IModBusEvent
{
private final List<RegistryData<?>> registries = new ArrayList<>();
public NewRegistryEvent() {}
/**
* Adds a registry builder to be created.
*
* @param builder The builder to turn into a {@link IForgeRegistry}
* @return A supplier of the {@link IForgeRegistry} created by the builder. Resolving too early will return null.
*/
public <V extends IForgeRegistryEntry<V>> Supplier<IForgeRegistry<V>> create(RegistryBuilder<V> builder)
{
return create(builder, null);
}
/**
* Adds a registry builder to be created.
*
* @param builder The builder to turn into a {@link IForgeRegistry}
* @param onFill Called when the returned supplier is filled with the registry
* @return a supplier of the {@link IForgeRegistry} created by the builder. Resolving too early will return null.
*/
public <V extends IForgeRegistryEntry<V>> Supplier<IForgeRegistry<V>> create(RegistryBuilder<V> builder, @Nullable Consumer<IForgeRegistry<V>> onFill)
{
RegistryHolder<V> registryHolder = new RegistryHolder<>();
registries.add(new RegistryData<>(builder, registryHolder, onFill));
return registryHolder;
}
@SuppressWarnings("rawtypes")
void fill()
{
Map<RegistryBuilder<?>, IForgeRegistry<?>> builtRegistries = this.registries
.stream()
.collect(Collectors.toMap(RegistryData::builder, d -> d.builder().create(), (a, b) -> a, IdentityHashMap::new));
builtRegistries.forEach((builder, reg) -> {
if (builder.getHasWrapper() && !Registry.REGISTRY.containsKey(reg.getRegistryName()))
RegistryManager.registerToRootRegistry((ForgeRegistry<?>) reg);
});
for (RegistryData data : this.registries)
{
IForgeRegistry<?> registry = builtRegistries.get(data.builder);
if (registry != null)
{
data.registryHolder.registry = registry;
if (data.onFill != null)
data.onFill.accept(registry);
}
}
}
private record RegistryData<V extends IForgeRegistryEntry<V>>(
RegistryBuilder<V> builder,
RegistryHolder<V> registryHolder,
Consumer<IForgeRegistry<V>> onFill
) {}
private static class RegistryHolder<V extends IForgeRegistryEntry<V>> implements Supplier<IForgeRegistry<V>>
{
IForgeRegistry<V> registry = null;
@Override
public IForgeRegistry<V> get()
{
return this.registry;
}
}
@Override
public String toString()
{
return "RegistryEvent.NewRegistry";
}
}
@@ -38,8 +38,6 @@ public class RegistryBuilder<T extends IForgeRegistryEntry<T>>
private boolean allowOverrides = true;
private boolean allowModifications = false;
private boolean hasWrapper = false;
@Nullable
private String tagFolder;
private DummyFactory<T> dummyFactory;
private MissingFactory<T> missingFactory;
private Set<ResourceLocation> legacyNames = new HashSet<>();
@@ -201,15 +199,6 @@ public class RegistryBuilder<T extends IForgeRegistryEntry<T>>
return this;
}
public RegistryBuilder<T> tagFolder(String tagFolder)
{
if (tagFolder == null || !tagFolder.matches("[a-z_/]+")) throw new IllegalArgumentException("Non [a-z_/] character in tag folder " + tagFolder);
this.tagFolder = tagFolder;
//Also mark this registry as having a wrapper to a vanilla registry so that it can be used in data generators properly
hasWrapper();
return this;
}
public RegistryBuilder<T> legacyName(String name)
{
return legacyName(new ResourceLocation(name));
@@ -227,7 +216,24 @@ public class RegistryBuilder<T extends IForgeRegistryEntry<T>>
return this;
}
public IForgeRegistry<T> create()
/**
* Enables tags for this registry if not already.
* All forge registries with wrappers inherently support tags.
*
* @return this builder
* @see RegistryBuilder#hasWrapper()
*/
public RegistryBuilder<T> hasTags()
{
// Tag system heavily relies on Registry<?> objects, so we need a wrapper for this registry to take advantage
this.hasWrapper();
return this;
}
/**
* Modders: Use {@link NewRegistryEvent#create(RegistryBuilder)} instead
*/
IForgeRegistry<T> create()
{
if (hasWrapper)
{
@@ -345,12 +351,6 @@ public class RegistryBuilder<T extends IForgeRegistryEntry<T>>
return allowModifications;
}
@Nullable
public String getTagFolder()
{
return tagFolder;
}
@Nullable
public DummyFactory<T> getDummyFactory()
{
@@ -7,9 +7,13 @@ package net.minecraftforge.registries;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.Function;
import java.util.stream.Collectors;
import com.google.common.collect.BiMap;
@@ -18,9 +22,13 @@ import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.collect.Sets.SetView;
import com.mojang.serialization.Lifecycle;
import net.minecraft.core.WritableRegistry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.core.Registry;
import net.minecraftforge.fml.IModStateTransition;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.network.HandshakeMessages;
import net.minecraftforge.registries.ForgeRegistry.Snapshot;
import org.apache.commons.lang3.tuple.Pair;
@@ -33,6 +41,7 @@ public class RegistryManager
public static final RegistryManager ACTIVE = new RegistryManager("ACTIVE");
public static final RegistryManager VANILLA = new RegistryManager("VANILLA");
public static final RegistryManager FROZEN = new RegistryManager("FROZEN");
private static Set<ResourceLocation> vanillaRegistryKeys = Set.of();
BiMap<ResourceLocation, ForgeRegistry<? extends IForgeRegistryEntry<?>>> registries = HashBiMap.create();
private BiMap<Class<? extends IForgeRegistryEntry<?>>, ResourceLocation> superTypes = HashBiMap.create();
@@ -68,6 +77,12 @@ public class RegistryManager
return getRegistry(key.location());
}
/**
* @see #getRegistry(ResourceLocation)
* @see #getRegistry(ResourceKey)
* @deprecated The uniqueness of registry super types will not be guaranteed starting in 1.19.
*/
@Deprecated(forRemoval = true, since = "1.18.2")
public <V extends IForgeRegistryEntry<V>> IForgeRegistry<V> getRegistry(Class<? super V> cls)
{
return getRegistry(superTypes.get(cls));
@@ -124,6 +139,9 @@ public class RegistryManager
foundType, superTypes.get(foundType), name, builder.getType(), builder.getType());
throw new IllegalArgumentException("Duplicate registry parent type found - you can only have one registry for a particular super type");
}
if (registries.containsKey(name))
throw new IllegalArgumentException("Attempted to register a registry for " + name + " with type "
+ builder.getType().getName() + " but it already exists as " + registries.get(name).getRegistrySuperType().getName());
ForgeRegistry<V> reg = new ForgeRegistry<V>(this, name, builder);
registries.put(name, reg);
superTypes.put(builder.getType(), name);
@@ -136,6 +154,36 @@ public class RegistryManager
return getRegistry(name);
}
@SuppressWarnings("unchecked")
static <V extends IForgeRegistryEntry<V>> void registerToRootRegistry(ForgeRegistry<V> forgeReg)
{
WritableRegistry<Registry<V>> registry = (WritableRegistry<Registry<V>>) Registry.REGISTRY;
Registry<V> wrapper = forgeReg.getWrapper();
if (wrapper != null)
registry.register(forgeReg.getRegistryKey(), wrapper, Lifecycle.experimental());
}
public static IModStateTransition.EventGenerator<NewRegistryEvent> newRegistryEventGenerator()
{
NewRegistryEvent event = new NewRegistryEvent();
return mc -> event;
}
public static CompletableFuture<List<Throwable>> preNewRegistryEvent(final Executor executor,
final IModStateTransition.EventGenerator<? extends NewRegistryEvent> eventGenerator)
{
return CompletableFuture.runAsync(() -> vanillaRegistryKeys = Set.copyOf(Registry.REGISTRY.keySet()), executor)
.handle((v, t) -> t != null ? Collections.singletonList(t) : Collections.emptyList());
}
public static CompletableFuture<List<Throwable>> postNewRegistryEvent(final Executor executor,
final IModStateTransition.EventGenerator<? extends NewRegistryEvent> eventGenerator)
{
return CompletableFuture.runAsync(() -> eventGenerator.apply(null).fill(), executor)
.handle((v, t) -> t != null ? Collections.singletonList(t) : Collections.emptyList());
}
private void addLegacyName(ResourceLocation legacyName, ResourceLocation name)
{
if (this.legacyNames.containsKey(legacyName))
@@ -189,4 +237,9 @@ public class RegistryManager
filter(resloc -> ACTIVE.synced.contains(resloc)).
collect(Collectors.toList());
}
public static Set<ResourceLocation> getVanillaRegistryKeys()
{
return vanillaRegistryKeys;
}
}
@@ -5,6 +5,8 @@
package net.minecraftforge.registries;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import javax.annotation.Nonnull;
@@ -24,6 +26,10 @@ public final class RegistryObject<T extends IForgeRegistryEntry<? super T>> impl
@Nullable
private T value;
/**
* @deprecated The uniqueness of registry super types will not be guaranteed starting in 1.19. Use {@link #of(ResourceLocation, ResourceLocation, String)}.
*/
@Deprecated(forRemoval = true, since = "1.18.2")
public static <T extends IForgeRegistryEntry<T>, U extends T> RegistryObject<U> of(final ResourceLocation name, Supplier<Class<? super T>> registryType) {
return new RegistryObject<>(name, registryType);
}
@@ -32,10 +38,38 @@ public final class RegistryObject<T extends IForgeRegistryEntry<? super T>> impl
return new RegistryObject<>(name, registry);
}
/**
* @deprecated The uniqueness of registry super types will not be guaranteed starting in 1.19. Use {@link #of(ResourceLocation, ResourceLocation, String)}.
*/
@Deprecated(forRemoval = true, since = "1.18.2")
public static <T extends IForgeRegistryEntry<T>, U extends T> RegistryObject<U> of(final ResourceLocation name, final Class<T> baseType, String modid) {
return new RegistryObject<>(name, baseType, modid);
}
/**
* Factory for a {@link RegistryObject} that stores the value of an object in a registry once it is ready.
*
* @param name The name of the object to look up in a forge registry
* @param key The key of the forge registry to grab from {@link RegistryManager#ACTIVE}
* @param modid The mod id calling context
* @return A {@link RegistryObject} that stores the value of an object in a registry once it is ready
*/
public static <T extends IForgeRegistryEntry<T>, U extends T> RegistryObject<U> of(final ResourceLocation name, final ResourceKey<? extends Registry<T>> key, String modid) {
return new RegistryObject<>(name, key.location(), modid);
}
/**
* Factory for a {@link RegistryObject} that stores the value of an object in a registry once it is ready.
*
* @param name The name of the object to look up in a forge registry
* @param registryName The name of the forge registry to grab from {@link RegistryManager#ACTIVE}
* @param modid The mod id calling context
* @return A {@link RegistryObject} that stores the value of an object in a registry once it is ready
*/
public static <T extends IForgeRegistryEntry<T>, U extends T> RegistryObject<U> of(final ResourceLocation name, final ResourceLocation registryName, String modid) {
return new RegistryObject<>(name, registryName, modid);
}
private static RegistryObject<?> EMPTY = new RegistryObject<>();
private static <T extends IForgeRegistryEntry<? super T>> RegistryObject<T> empty() {
@@ -48,6 +82,7 @@ public final class RegistryObject<T extends IForgeRegistryEntry<? super T>> impl
this.name = null;
}
@Deprecated(forRemoval = true, since = "1.18.2")
private <V extends IForgeRegistryEntry<V>> RegistryObject(ResourceLocation name, Supplier<Class<? super V>> registryType)
{
this(name, RegistryManager.ACTIVE.<V>getRegistry(registryType.get()));
@@ -68,6 +103,7 @@ public final class RegistryObject<T extends IForgeRegistryEntry<? super T>> impl
}
@SuppressWarnings("unchecked")
@Deprecated(forRemoval = true, since = "1.18.2")
private <V extends IForgeRegistryEntry<V>> RegistryObject(final ResourceLocation name, final Class<V> baseType, final String modid)
{
this.name = name;
@@ -83,7 +119,7 @@ public final class RegistryObject<T extends IForgeRegistryEntry<? super T>> impl
{
this.registry = RegistryManager.ACTIVE.getRegistry(baseType);
if (registry == null)
throw new IllegalStateException("Unable to find registry for type " + baseType.getName() + " for mod \"" + modid + "\". Check the 'caused by' to see futher stack.", callerStack);
throw new IllegalStateException("Unable to find registry for type " + baseType.getName() + " for mod \"" + modid + "\". Check the 'caused by' to see further stack.", callerStack);
}
if (pred.test(registry.getRegistryName()))
RegistryObject.this.updateReference((IForgeRegistry<? extends T>) registry);
@@ -97,6 +133,36 @@ public final class RegistryObject<T extends IForgeRegistryEntry<? super T>> impl
}
}
@SuppressWarnings("unchecked")
private <V extends IForgeRegistryEntry<V>> RegistryObject(final ResourceLocation name, final ResourceLocation registryName, final String modid)
{
this.name = name;
final Throwable callerStack = new Throwable("Calling Site from mod: " + modid);
ObjectHolderRegistry.addHandler(new Consumer<>()
{
private IForgeRegistry<V> registry;
@Override
public void accept(Predicate<ResourceLocation> pred)
{
if (registry == null)
{
this.registry = RegistryManager.ACTIVE.getRegistry(registryName);
if (registry == null)
throw new IllegalStateException("Unable to find registry with key " + registryName + " for mod \"" + modid + "\". Check the 'caused by' to see further stack.", callerStack);
}
if (pred.test(registry.getRegistryName()))
RegistryObject.this.updateReference((IForgeRegistry<? extends T>) registry);
}
});
IForgeRegistry<V> registry = RegistryManager.ACTIVE.getRegistry(registryName);
// allow registry to be null, this might be for a custom registry that does not exist yet
if (registry != null)
{
this.updateReference(((IForgeRegistry<? extends T>) registry));
}
}
/**
* Directly retrieves the wrapped Registry Object. This value will automatically be updated when the backing registry is updated.
* Will throw NPE if the value is null, use isPresent to check first. Or use any of the other guarded functions.
@@ -0,0 +1,29 @@
/*
* Minecraft Forge - Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.registries.tags;
import net.minecraft.core.Holder;
import net.minecraft.tags.TagKey;
import java.util.stream.Stream;
/**
* A reverse tag is an object aware of what tags it is contained in.
* {@link Holder}s implement this interface.
* A reverse tag makes no guarantees about its persistence relative to a registry value.
* Modders should look up a reverse tag every time they need it from a {@link ITagManager} rather than storing it somewhere.
*/
public interface IReverseTag<V>
{
Stream<TagKey<V>> getTagKeys();
boolean containsTag(TagKey<V> key);
default boolean containsTag(ITag<V> tag)
{
return containsTag(tag.getKey());
}
}
@@ -0,0 +1,41 @@
/*
* Minecraft Forge - Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.registries.tags;
import net.minecraft.tags.TagKey;
import java.util.Optional;
import java.util.Random;
import java.util.stream.Stream;
/**
* A tag is a collection of elements with an identifying {@link TagKey tag key}.
* For Forge, these are bound on world load.
* Tags will always be empty until they are bound.
* A tag instance provided for a given {@link TagKey} from a given {@link ITagManager} will always return the same instance on future invocations.
* This means that the same tag instance will be rebound across reloads assuming the same registry instance is in use.
* It is safe to store instances of this class for long periods of time.
*/
public interface ITag<V> extends Iterable<V>
{
TagKey<V> getKey();
Stream<V> stream();
boolean isEmpty();
int size();
boolean contains(V value);
Optional<V> getRandomElement(Random random);
/**
* @return {@code true} if this tag was loaded with a value (including empty),
* otherwise the tag is always empty and this returns {@code false}
*/
boolean isBound();
}
@@ -0,0 +1,92 @@
/*
* Minecraft Forge - Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.registries.tags;
import net.minecraft.core.Holder;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.IForgeRegistryEntry;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Stream;
/**
* A tag manager holds information about all tags currently bound to a forge registry.
* This should be preferred to any {@link Holder}-related methods.
*/
public interface ITagManager<V extends IForgeRegistryEntry<V>> extends Iterable<ITag<V>>
{
/**
* Queries this tag manager for a tag with the given tag key.
* If it does not exist, this will create an empty tag and return it.
*
* @apiNote This method guarantees that all future calls to this method on the same tag manager instance with the same tag key will return the same tag.
* @see #isKnownTagName(TagKey)
*/
@NotNull ITag<V> getTag(@NotNull TagKey<V> name);
/**
* Queries a reverse tag for a given value from the forge registry linked to this tag manager.
* A reverse tag stores all tags that the given {@code value} is contained in.
*
* @param value A value currently registered to the forge registry linked to this tag manager
* @return A reverse tag for the given value, or an empty optional if the value is not registered
*/
@NotNull Optional<IReverseTag<V>> getReverseTag(@NotNull V value);
/**
* Checks whether the given tag key exists in this tag manager and is bound.
* Unlike {@link #getTag(TagKey)}, this method will <b>not</b> create the tag if it does not exist.
*
* @see ITag#isBound()
*/
boolean isKnownTagName(@NotNull TagKey<V> name);
/**
* @return A stream of all tags stored in this tag manager, bound or unbound.
*/
@NotNull Stream<ITag<V>> stream();
/**
* @return A stream of all tag keys stored in this tag manager, bound or unbound.
*/
@NotNull Stream<TagKey<V>> getTagNames();
/**
* Creates a tag key based on the location and the forge registry linked to this tag manager.
* Custom registries can use {@link DeferredRegister#createTagKey(ResourceLocation)} to create tag keys before the tag manager is created.
*
* @see #createOptionalTagKey(ResourceLocation, Set)
* @see DeferredRegister#createTagKey(ResourceLocation)
*/
@NotNull TagKey<V> createTagKey(@NotNull ResourceLocation location);
/**
* Creates a tag key that will use the set of defaults if the tag is not loaded from any datapacks.
* Useful on the client side when a server may not provide a specific tag.
* Custom registries can use {@link DeferredRegister#addOptionalTagDefaults(TagKey, Set)} to create tag keys before the tag manager is created.
*
* @see #createTagKey(ResourceLocation)
* @see #addOptionalTagDefaults(TagKey, Set)
* @see DeferredRegister#createOptionalTagKey(ResourceLocation, Set)
*/
@NotNull TagKey<V> createOptionalTagKey(@NotNull ResourceLocation location, @NotNull Set<? extends Supplier<V>> defaults);
/**
* Adds defaults to an existing tag key.
* The set of defaults will be bound to the tag if the tag is not loaded from any datapacks.
* Useful on the client side when a server may not provide a specific tag.
* Custom registries can use {@link DeferredRegister#addOptionalTagDefaults(TagKey, Set)} to add defaults before the tag manager is created.
*
* @see #createOptionalTagKey(ResourceLocation, Set)
* @see DeferredRegister#addOptionalTagDefaults(TagKey, Set)
*/
void addOptionalTagDefaults(@NotNull TagKey<V> name, @NotNull Set<? extends Supplier<V>> defaults);
}
@@ -21,6 +21,7 @@ public class ForgeCommand
.then(GenerateCommand.register())
.then(DimensionsCommand.register())
.then(ModListCommand.register())
.then(TagsCommand.register())
);
}
}
@@ -0,0 +1,267 @@
/*
* Minecraft Forge - Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.server.command;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import com.mojang.datafixers.util.Pair;
import net.minecraft.ChatFormatting;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.commands.arguments.ResourceKeyArgument;
import net.minecraft.commands.arguments.ResourceLocationArgument;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderSet;
import net.minecraft.core.Registry;
import net.minecraft.network.chat.ClickEvent;
import net.minecraft.network.chat.ComponentUtils;
import net.minecraft.network.chat.HoverEvent;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.util.Mth;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* The {@code /forge tags} command for listing a registry's tags, getting the elements of tags, and querying the tags of a
* registry object.
*
* <p>Each command is paginated, showing {@value PAGE_SIZE} entries at a time. When there are more than 0 entries,
* the text indicating the amount of entries is highlighted and can be clicked to copy the list of all entries (across
* all pages) to the clipboard. (This is reflected by the use of green text in brackets, mimicking the clickable
* coordinates in the {@code /locate} command's message)</p>
*
* <p>The command has three subcommands:</p>
* <ul>
* <li>{@code /forge tags &lt;registry> list [page]} - Lists all available tags in the given registry.</li>
* <li>{@code /forge tags &lt;registry> get &lt;tag> [page]} - Gets all elements of the given tag in the given registry.</li>
* <li>{@code /forge tags &lt;registry> query &lt;element> [page]} - Queries for all tags in the given registry which
* contain the given registry object.</li>
* </ul>
*/
class TagsCommand
{
private static final long PAGE_SIZE = 8;
private static final ResourceKey<Registry<Registry<?>>> ROOT_REGISTRY_KEY =
ResourceKey.createRegistryKey(new ResourceLocation("root"));
private static final DynamicCommandExceptionType UNKNOWN_REGISTRY = new DynamicCommandExceptionType(key ->
new TranslatableComponent("commands.forge.tags.error.unknown_registry", key));
private static final Dynamic2CommandExceptionType UNKNOWN_TAG = new Dynamic2CommandExceptionType((tag, registry) ->
new TranslatableComponent("commands.forge.tags.error.unknown_tag", tag, registry));
private static final Dynamic2CommandExceptionType UNKNOWN_ELEMENT = new Dynamic2CommandExceptionType((tag, registry) ->
new TranslatableComponent("commands.forge.tags.error.unknown_element", tag, registry));
public static ArgumentBuilder<CommandSourceStack, ?> register()
{
/*
* /forge tags <registry> list [page]
* /forge tags <registry> get <tag> [page]
* /forge tags <registry> query <element> [page]
*/
return Commands.literal("tags")
.requires(cs -> cs.hasPermission(2))
.then(Commands.argument("registry", ResourceKeyArgument.key(ROOT_REGISTRY_KEY))
.suggests(TagsCommand::suggestRegistries)
.then(Commands.literal("list")
.executes(ctx -> listTags(ctx, 1))
.then(Commands.argument("page", IntegerArgumentType.integer(1))
.executes(ctx -> listTags(ctx, IntegerArgumentType.getInteger(ctx, "page")))
)
)
.then(Commands.literal("get")
.then(Commands.argument("tag", ResourceLocationArgument.id())
.suggests(suggestFromRegistry(r -> r.getTagNames().map(TagKey::location)::iterator))
.executes(ctx -> listTagElements(ctx, 1))
.then(Commands.argument("page", IntegerArgumentType.integer(1))
.executes(ctx -> listTagElements(ctx, IntegerArgumentType.getInteger(ctx, "page")))
)
)
)
.then(Commands.literal("query")
.then(Commands.argument("element", ResourceLocationArgument.id())
.suggests(suggestFromRegistry(Registry::keySet))
.executes(ctx -> queryElementTags(ctx, 1))
.then(Commands.argument("page", IntegerArgumentType.integer(1))
.executes(ctx -> queryElementTags(ctx, IntegerArgumentType.getInteger(ctx, "page")))
)
)
)
);
}
private static int listTags(final CommandContext<CommandSourceStack> ctx, final int page) throws CommandSyntaxException
{
final ResourceKey<? extends Registry<?>> registryKey = getResourceKey(ctx, "registry", ROOT_REGISTRY_KEY)
.orElseThrow(); // Expect to be always retrieve a resource key for the root registry (registry key)
final Registry<?> registry = ctx.getSource().getServer().registryAccess().registry(registryKey)
.orElseThrow(() -> UNKNOWN_REGISTRY.create(registryKey.location()));
final long tagCount = registry.getTags().count();
ctx.getSource().sendSuccess(createMessage(
new TranslatableComponent("commands.forge.tags.registry_key", new TextComponent(registryKey.location().toString()).withStyle(ChatFormatting.GOLD)),
"commands.forge.tags.tag_count",
"commands.forge.tags.copy_tag_names",
tagCount,
page,
ChatFormatting.DARK_GREEN,
() -> registry.getTags()
.map(Pair::getSecond)
.map(s -> s.unwrap().map(k -> k.location().toString(), Object::toString))
), false);
return (int) tagCount;
}
private static int listTagElements(final CommandContext<CommandSourceStack> ctx, final int page) throws CommandSyntaxException
{
final ResourceKey<? extends Registry<?>> registryKey = getResourceKey(ctx, "registry", ROOT_REGISTRY_KEY)
.orElseThrow(); // Expect to be always retrieve a resource key for the root registry (registry key)
final Registry<?> registry = ctx.getSource().getServer().registryAccess().registry(registryKey)
.orElseThrow(() -> UNKNOWN_REGISTRY.create(registryKey.location()));
final ResourceLocation tagLocation = ResourceLocationArgument.getId(ctx, "tag");
final TagKey<?> tagKey = TagKey.create(cast(registryKey), tagLocation);
final HolderSet.Named<?> tag = registry.getTag(cast(tagKey))
.orElseThrow(() -> UNKNOWN_TAG.create(tagKey.location(), registryKey.location()));
ctx.getSource().sendSuccess(createMessage(
new TranslatableComponent("commands.forge.tags.tag_key",
new TextComponent(tagKey.registry().location().toString()).withStyle(ChatFormatting.GOLD),
new TextComponent(tagKey.location().toString()).withStyle(ChatFormatting.DARK_GREEN)),
"commands.forge.tags.element_count",
"commands.forge.tags.copy_element_names",
tag.size(),
page,
ChatFormatting.YELLOW,
() -> tag.stream().map(s -> s.unwrap().map(k -> k.location().toString(), Object::toString))
), false);
return tag.size();
}
private static int queryElementTags(final CommandContext<CommandSourceStack> ctx, final int page) throws CommandSyntaxException
{
final ResourceKey<? extends Registry<?>> registryKey = getResourceKey(ctx, "registry", ROOT_REGISTRY_KEY)
.orElseThrow(); // Expect to be always retrieve a resource key for the root registry (registry key)
final Registry<?> registry = ctx.getSource().getServer().registryAccess().registry(registryKey)
.orElseThrow(() -> UNKNOWN_REGISTRY.create(registryKey.location()));
final ResourceLocation elementLocation = ResourceLocationArgument.getId(ctx, "element");
final ResourceKey<?> elementKey = ResourceKey.create(cast(registryKey), elementLocation);
final Holder<?> elementHolder = registry.getHolder(cast(elementKey))
.orElseThrow(() -> UNKNOWN_ELEMENT.create(elementLocation, registryKey.location()));
final long containingTagsCount = elementHolder.tags().count();
ctx.getSource().sendSuccess(createMessage(
new TranslatableComponent("commands.forge.tags.element",
new TextComponent(registryKey.location().toString()).withStyle(ChatFormatting.GOLD),
new TextComponent(elementLocation.toString()).withStyle(ChatFormatting.YELLOW)),
"commands.forge.tags.containing_tag_count",
"commands.forge.tags.copy_tag_names",
containingTagsCount,
page,
ChatFormatting.DARK_GREEN,
() -> elementHolder.tags().map(k -> k.location().toString())
), false);
return (int) containingTagsCount;
}
private static MutableComponent createMessage(final TranslatableComponent header,
final String containsText,
final String copyHoverText,
final long count,
final long currentPage,
final ChatFormatting elementColor,
final Supplier<Stream<String>> names)
{
final String allElementNames = names.get().sorted().collect(Collectors.joining("\n"));
final long totalPages = (count - 1) / PAGE_SIZE + 1;
final long actualPage = Mth.clamp(currentPage, 1, totalPages);
MutableComponent containsComponent = new TranslatableComponent(containsText, count);
if (count > 0) // Highlight the count text, make it clickable, and append page counters
{
containsComponent = ComponentUtils.wrapInSquareBrackets(containsComponent.withStyle(s -> s
.withColor(ChatFormatting.GREEN)
.withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, allElementNames))
.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
new TranslatableComponent(copyHoverText)))));
containsComponent = new TranslatableComponent("commands.forge.tags.page_info",
containsComponent, actualPage, totalPages);
}
final MutableComponent tagElements = new TextComponent("").append(containsComponent);
names.get()
.sorted()
.skip(PAGE_SIZE * (actualPage - 1))
.limit(PAGE_SIZE)
.map(TextComponent::new)
.map(t -> t.withStyle(elementColor))
.map(t -> new TextComponent("\n - ").append(t))
.forEach(tagElements::append);
return header.append("\n").append(tagElements);
}
private static CompletableFuture<Suggestions> suggestRegistries(final CommandContext<CommandSourceStack> ctx,
final SuggestionsBuilder builder)
{
ctx.getSource().suggestRegistryElements(Registry.REGISTRY,
SharedSuggestionProvider.ElementSuggestionType.ELEMENTS, builder);
return builder.buildFuture();
}
private static SuggestionProvider<CommandSourceStack> suggestFromRegistry(
final Function<Registry<?>, Iterable<ResourceLocation>> namesFunction)
{
return (ctx, builder) -> getResourceKey(ctx, "registry", ROOT_REGISTRY_KEY)
.flatMap(key -> ctx.getSource().registryAccess().registry(key).map(registry -> {
SharedSuggestionProvider.suggestResource(namesFunction.apply(registry), builder);
return builder.buildFuture();
}))
.orElseGet(builder::buildFuture);
}
@SuppressWarnings("SameParameterValue")
private static <T> Optional<ResourceKey<T>> getResourceKey(final CommandContext<CommandSourceStack> ctx,
final String name,
final ResourceKey<Registry<T>> registryKey)
{
// Don't inline to avoid an unchecked cast warning due to raw types
final ResourceKey<?> key = ctx.getArgument(name, ResourceKey.class);
return key.cast(registryKey);
}
@SuppressWarnings("unchecked")
private static <O> O cast(final Object input)
{
return (O) input;
}
}