diff --git a/patches/minecraft/net/minecraft/core/Holder.java.patch b/patches/minecraft/net/minecraft/core/Holder.java.patch index 02911ffd32..2aeac31d4a 100644 --- a/patches/minecraft/net/minecraft/core/Holder.java.patch +++ b/patches/minecraft/net/minecraft/core/Holder.java.patch @@ -1,13 +1,32 @@ --- a/net/minecraft/core/Holder.java +++ b/net/minecraft/core/Holder.java -@@ -194,6 +_,10 @@ - return this.f_205749_.stream(); - } +@@ -11,7 +_,17 @@ + import net.minecraft.resources.ResourceLocation; + import net.minecraft.tags.TagKey; -+ public Type getType() { -+ return this.f_205750_; +-public interface Holder { ++public interface Holder extends net.minecraftforge.registries.tags.IReverseTag { ++ @Override ++ default boolean containsTag(TagKey key) { ++ return this.m_203656_(key); ++ } ++ ++ @Override ++ default Stream> getTagKeys() { ++ return this.m_203616_(); ++ } ++ + T m_203334_(); + + boolean m_203633_(); +@@ -192,6 +_,10 @@ + + public Stream> m_203616_() { + return this.f_205749_.stream(); + } + - public String toString() { - return "Reference{" + this.f_205751_ + "=" + this.f_205752_ + "}"; ++ public Type getType() { ++ return this.f_205750_; } + + public String toString() { diff --git a/patches/minecraft/net/minecraft/core/MappedRegistry.java.patch b/patches/minecraft/net/minecraft/core/MappedRegistry.java.patch index 24f8ca9661..6d8f8740e7 100644 --- a/patches/minecraft/net/minecraft/core/MappedRegistry.java.patch +++ b/patches/minecraft/net/minecraft/core/MappedRegistry.java.patch @@ -4,7 +4,7 @@ return this.f_205842_.containsKey(p_175392_); } -+ /** @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 + public void unfreeze() { + this.f_205845_ = false; diff --git a/patches/minecraft/net/minecraft/data/tags/BiomeTagsProvider.java.patch b/patches/minecraft/net/minecraft/data/tags/BiomeTagsProvider.java.patch new file mode 100644 index 0000000000..b0266f6dba --- /dev/null +++ b/patches/minecraft/net/minecraft/data/tags/BiomeTagsProvider.java.patch @@ -0,0 +1,16 @@ +--- a/net/minecraft/data/tags/BiomeTagsProvider.java ++++ b/net/minecraft/data/tags/BiomeTagsProvider.java +@@ -7,8 +_,13 @@ + import net.minecraft.world.level.biome.Biomes; + + public class BiomeTagsProvider extends TagsProvider { ++ /** @deprecated Forge: Use the {@link #BiomeTagsProvider(DataGenerator, String, net.minecraftforge.common.data.ExistingFileHelper) mod id variant} */ ++ @Deprecated + public BiomeTagsProvider(DataGenerator p_211094_) { + super(p_211094_, BuiltinRegistries.f_123865_); ++ } ++ public BiomeTagsProvider(DataGenerator p_211094_, String modId, @org.jetbrains.annotations.Nullable net.minecraftforge.common.data.ExistingFileHelper existingFileHelper) { ++ super(p_211094_, BuiltinRegistries.f_123865_, modId, existingFileHelper); + } + + protected void m_6577_() { diff --git a/patches/minecraft/net/minecraft/data/tags/BlockTagsProvider.java.patch b/patches/minecraft/net/minecraft/data/tags/BlockTagsProvider.java.patch index 087f0bbab8..39239167bd 100644 --- a/patches/minecraft/net/minecraft/data/tags/BlockTagsProvider.java.patch +++ b/patches/minecraft/net/minecraft/data/tags/BlockTagsProvider.java.patch @@ -1,14 +1,15 @@ --- a/net/minecraft/data/tags/BlockTagsProvider.java +++ b/net/minecraft/data/tags/BlockTagsProvider.java -@@ -7,8 +_,12 @@ +@@ -7,8 +_,13 @@ import net.minecraft.world.level.block.Blocks; public class BlockTagsProvider extends TagsProvider { ++ /** @deprecated Forge: Use the {@link #BlockTagsProvider(DataGenerator, String, net.minecraftforge.common.data.ExistingFileHelper) mod id variant} */ + @Deprecated public BlockTagsProvider(DataGenerator p_126511_) { super(p_126511_, Registry.f_122824_); + } -+ public BlockTagsProvider(DataGenerator p_126511_, String modId, @javax.annotation.Nullable net.minecraftforge.common.data.ExistingFileHelper existingFileHelper) { ++ public BlockTagsProvider(DataGenerator p_126511_, String modId, @org.jetbrains.annotations.Nullable net.minecraftforge.common.data.ExistingFileHelper existingFileHelper) { + super(p_126511_, Registry.f_122824_, modId, existingFileHelper); } diff --git a/patches/minecraft/net/minecraft/data/tags/ConfiguredStructureTagsProvider.java.patch b/patches/minecraft/net/minecraft/data/tags/ConfiguredStructureTagsProvider.java.patch new file mode 100644 index 0000000000..99f22735e0 --- /dev/null +++ b/patches/minecraft/net/minecraft/data/tags/ConfiguredStructureTagsProvider.java.patch @@ -0,0 +1,16 @@ +--- a/net/minecraft/data/tags/ConfiguredStructureTagsProvider.java ++++ b/net/minecraft/data/tags/ConfiguredStructureTagsProvider.java +@@ -7,8 +_,13 @@ + import net.minecraft.world.level.levelgen.structure.BuiltinStructures; + + public class ConfiguredStructureTagsProvider extends TagsProvider> { ++ /** @deprecated Forge: Use the {@link #ConfiguredStructureTagsProvider(DataGenerator, String, net.minecraftforge.common.data.ExistingFileHelper) mod id variant} */ ++ @Deprecated + public ConfiguredStructureTagsProvider(DataGenerator p_211098_) { + super(p_211098_, BuiltinRegistries.f_123862_); ++ } ++ public ConfiguredStructureTagsProvider(DataGenerator p_211098_, String modId, @org.jetbrains.annotations.Nullable net.minecraftforge.common.data.ExistingFileHelper existingFileHelper) { ++ super(p_211098_, BuiltinRegistries.f_123862_, modId, existingFileHelper); + } + + protected void m_6577_() { diff --git a/patches/minecraft/net/minecraft/data/tags/EntityTypeTagsProvider.java.patch b/patches/minecraft/net/minecraft/data/tags/EntityTypeTagsProvider.java.patch index 618c5cf79f..79a9824be0 100644 --- a/patches/minecraft/net/minecraft/data/tags/EntityTypeTagsProvider.java.patch +++ b/patches/minecraft/net/minecraft/data/tags/EntityTypeTagsProvider.java.patch @@ -1,14 +1,15 @@ --- a/net/minecraft/data/tags/EntityTypeTagsProvider.java +++ b/net/minecraft/data/tags/EntityTypeTagsProvider.java -@@ -6,8 +_,12 @@ +@@ -6,8 +_,13 @@ import net.minecraft.world.entity.EntityType; public class EntityTypeTagsProvider extends TagsProvider> { ++ /** @deprecated Forge: Use the {@link #EntityTypeTagsProvider(DataGenerator, String, net.minecraftforge.common.data.ExistingFileHelper) mod id variant} */ + @Deprecated public EntityTypeTagsProvider(DataGenerator p_126517_) { super(p_126517_, Registry.f_122826_); + } -+ public EntityTypeTagsProvider(DataGenerator p_126517_, String modId, @javax.annotation.Nullable net.minecraftforge.common.data.ExistingFileHelper existingFileHelper) { ++ public EntityTypeTagsProvider(DataGenerator p_126517_, String modId, @org.jetbrains.annotations.Nullable net.minecraftforge.common.data.ExistingFileHelper existingFileHelper) { + super(p_126517_, Registry.f_122826_, modId, existingFileHelper); } diff --git a/patches/minecraft/net/minecraft/data/tags/FluidTagsProvider.java.patch b/patches/minecraft/net/minecraft/data/tags/FluidTagsProvider.java.patch index 7a3dc27ab0..9e10827c9b 100644 --- a/patches/minecraft/net/minecraft/data/tags/FluidTagsProvider.java.patch +++ b/patches/minecraft/net/minecraft/data/tags/FluidTagsProvider.java.patch @@ -1,14 +1,15 @@ --- a/net/minecraft/data/tags/FluidTagsProvider.java +++ b/net/minecraft/data/tags/FluidTagsProvider.java -@@ -7,8 +_,12 @@ +@@ -7,8 +_,13 @@ import net.minecraft.world.level.material.Fluids; public class FluidTagsProvider extends TagsProvider { ++ /** @deprecated Forge: Use the {@link #FluidTagsProvider(DataGenerator, String, net.minecraftforge.common.data.ExistingFileHelper) mod id variant} */ + @Deprecated public FluidTagsProvider(DataGenerator p_126523_) { super(p_126523_, Registry.f_122822_); + } -+ public FluidTagsProvider(DataGenerator p_126523_, String modId, @javax.annotation.Nullable net.minecraftforge.common.data.ExistingFileHelper existingFileHelper) { ++ public FluidTagsProvider(DataGenerator p_126523_, String modId, @org.jetbrains.annotations.Nullable net.minecraftforge.common.data.ExistingFileHelper existingFileHelper) { + super(p_126523_, Registry.f_122822_, modId, existingFileHelper); } diff --git a/patches/minecraft/net/minecraft/data/tags/GameEventTagsProvider.java.patch b/patches/minecraft/net/minecraft/data/tags/GameEventTagsProvider.java.patch index 0cef54a6d7..eaa4932931 100644 --- a/patches/minecraft/net/minecraft/data/tags/GameEventTagsProvider.java.patch +++ b/patches/minecraft/net/minecraft/data/tags/GameEventTagsProvider.java.patch @@ -1,15 +1,16 @@ --- a/net/minecraft/data/tags/GameEventTagsProvider.java +++ b/net/minecraft/data/tags/GameEventTagsProvider.java -@@ -6,8 +_,12 @@ +@@ -6,8 +_,13 @@ import net.minecraft.world.level.gameevent.GameEvent; public class GameEventTagsProvider extends TagsProvider { ++ /** @deprecated Forge: Use the {@link #GameEventTagsProvider(DataGenerator, String, net.minecraftforge.common.data.ExistingFileHelper) mod id variant} */ + @Deprecated public GameEventTagsProvider(DataGenerator p_176826_) { super(p_176826_, Registry.f_175412_); + } -+ public GameEventTagsProvider(DataGenerator p_126517_, String modId, @javax.annotation.Nullable net.minecraftforge.common.data.ExistingFileHelper existingFileHelper) { -+ super(p_126517_, Registry.f_175412_, modId, existingFileHelper); ++ public GameEventTagsProvider(DataGenerator p_176826_, String modId, @org.jetbrains.annotations.Nullable net.minecraftforge.common.data.ExistingFileHelper existingFileHelper) { ++ super(p_176826_, Registry.f_175412_, modId, existingFileHelper); } protected void m_6577_() { diff --git a/patches/minecraft/net/minecraft/data/tags/ItemTagsProvider.java.patch b/patches/minecraft/net/minecraft/data/tags/ItemTagsProvider.java.patch index 5fb099ec47..69b6631381 100644 --- a/patches/minecraft/net/minecraft/data/tags/ItemTagsProvider.java.patch +++ b/patches/minecraft/net/minecraft/data/tags/ItemTagsProvider.java.patch @@ -1,15 +1,16 @@ --- a/net/minecraft/data/tags/ItemTagsProvider.java +++ b/net/minecraft/data/tags/ItemTagsProvider.java -@@ -14,8 +_,13 @@ +@@ -14,8 +_,14 @@ public class ItemTagsProvider extends TagsProvider { private final Function, Tag.Builder> f_126528_; ++ /** @deprecated Forge: Use the {@link #ItemTagsProvider(DataGenerator, BlockTagsProvider, String, net.minecraftforge.common.data.ExistingFileHelper) mod id variant} */ + @Deprecated public ItemTagsProvider(DataGenerator p_126530_, BlockTagsProvider p_126531_) { super(p_126530_, Registry.f_122827_); + this.f_126528_ = p_126531_::m_206426_; + } -+ public ItemTagsProvider(DataGenerator p_126530_, BlockTagsProvider p_126531_, String modId, @javax.annotation.Nullable net.minecraftforge.common.data.ExistingFileHelper existingFileHelper) { ++ public ItemTagsProvider(DataGenerator p_126530_, BlockTagsProvider p_126531_, String modId, @org.jetbrains.annotations.Nullable net.minecraftforge.common.data.ExistingFileHelper existingFileHelper) { + super(p_126530_, Registry.f_122827_, modId, existingFileHelper); this.f_126528_ = p_126531_::m_206426_; } diff --git a/patches/minecraft/net/minecraft/data/tags/TagsProvider.java.patch b/patches/minecraft/net/minecraft/data/tags/TagsProvider.java.patch index 064d7722e4..7706b728e4 100644 --- a/patches/minecraft/net/minecraft/data/tags/TagsProvider.java.patch +++ b/patches/minecraft/net/minecraft/data/tags/TagsProvider.java.patch @@ -1,32 +1,27 @@ --- a/net/minecraft/data/tags/TagsProvider.java +++ b/net/minecraft/data/tags/TagsProvider.java -@@ -32,10 +_,29 @@ +@@ -32,10 +_,24 @@ protected final DataGenerator f_126539_; protected final Registry f_126540_; protected final Map f_126543_ = Maps.newLinkedHashMap(); + protected final String modId; -+ protected final String folder; + protected final net.minecraftforge.common.data.ExistingFileHelper existingFileHelper; + private final net.minecraftforge.common.data.ExistingFileHelper.IResourceType resourceType; -+ /**@deprecated Forge: Use the ModID version**/ @Deprecated ++ /** ++ * @see #TagsProvider(DataGenerator, Registry, String, net.minecraftforge.common.data.ExistingFileHelper) ++ * @deprecated Forge: Use the mod id variant ++ */ ++ @Deprecated protected TagsProvider(DataGenerator p_126546_, Registry p_126547_) { + this(p_126546_, p_126547_, "vanilla", null); + } -+ protected TagsProvider(DataGenerator p_126546_, Registry p_126547_, String modId, @javax.annotation.Nullable net.minecraftforge.common.data.ExistingFileHelper existingFileHelper) { -+ this(p_126546_, p_126547_, modId, existingFileHelper, null); -+ } -+ protected TagsProvider(DataGenerator p_126546_, Registry p_126547_, String modId, @javax.annotation.Nullable net.minecraftforge.common.data.ExistingFileHelper existingFileHelper, @javax.annotation.Nullable String folder) { ++ protected TagsProvider(DataGenerator p_126546_, Registry p_126547_, String modId, @org.jetbrains.annotations.Nullable net.minecraftforge.common.data.ExistingFileHelper existingFileHelper) { this.f_126539_ = p_126546_; this.f_126540_ = p_126547_; + this.modId = modId; + this.existingFileHelper = existingFileHelper; -+ if (folder == null) { -+ //noinspection unchecked,rawtypes -+ folder = ((Registry) Registry.f_122897_).m_7981_(f_126540_).m_135815_() + "s"; -+ } -+ this.folder = folder; -+ this.resourceType = new net.minecraftforge.common.data.ExistingFileHelper.ResourceType(net.minecraft.server.packs.PackType.SERVER_DATA, ".json", "tags/" + this.folder); ++ this.resourceType = new net.minecraftforge.common.data.ExistingFileHelper.ResourceType(net.minecraft.server.packs.PackType.SERVER_DATA, ".json", TagManager.m_203918_(p_126547_.m_123023_())); } protected abstract void m_6577_(); diff --git a/patches/minecraft/net/minecraft/tags/TagManager.java.patch b/patches/minecraft/net/minecraft/tags/TagManager.java.patch index a67693a22a..97e57d980e 100644 --- a/patches/minecraft/net/minecraft/tags/TagManager.java.patch +++ b/patches/minecraft/net/minecraft/tags/TagManager.java.patch @@ -1,10 +1,12 @@ --- a/net/minecraft/tags/TagManager.java +++ b/net/minecraft/tags/TagManager.java -@@ -17,6 +_,7 @@ - public class TagManager implements PreparableReloadListener { - private static final Map>, String> f_203902_ = Map.of(Registry.f_122901_, "tags/blocks", Registry.f_122903_, "tags/entity_types", Registry.f_122899_, "tags/fluids", Registry.f_175423_, "tags/game_events", Registry.f_122904_, "tags/items"); - private final RegistryAccess f_144569_; -+ protected java.util.Map> customTagTypeReaders = net.minecraftforge.common.ForgeTagHandler.createCustomTagTypeReaders(); - private List> f_203903_ = List.of(); +@@ -29,7 +_,8 @@ - public TagManager(RegistryAccess p_144572_) { + public static String m_203918_(ResourceKey> p_203919_) { + String s = f_203902_.get(p_203919_); +- return s != null ? s : "tags/" + p_203919_.m_135782_().m_135815_(); ++ ResourceLocation registryName = p_203919_.m_135782_(); ++ return s != null ? s : "tags/" + (registryName.m_135827_().equals("minecraft") ? "" : registryName.m_135827_() + "/") + registryName.m_135815_(); + } + + public CompletableFuture m_5540_(PreparableReloadListener.PreparationBarrier p_13482_, ResourceManager p_13483_, ProfilerFiller p_13484_, ProfilerFiller p_13485_, Executor p_13486_, Executor p_13487_) { diff --git a/patches/minecraft/net/minecraft/world/level/levelgen/WorldGenSettings.java.patch b/patches/minecraft/net/minecraft/world/level/levelgen/WorldGenSettings.java.patch index 7494a84c9d..abfbb0d770 100644 --- a/patches/minecraft/net/minecraft/world/level/levelgen/WorldGenSettings.java.patch +++ b/patches/minecraft/net/minecraft/world/level/levelgen/WorldGenSettings.java.patch @@ -12,7 +12,7 @@ Registry registry2 = p_209717_.m_175515_(Registry.f_211073_); Registry registry3 = DimensionType.m_204490_(p_209717_, i); String s = p_209718_.f_207545_(); -+ net.minecraftforge.common.world.ForgeWorldPreset type = net.minecraftforge.registries.ForgeRegistries.WORLD_TYPES.getValue(new net.minecraft.resources.ResourceLocation(s)); ++ net.minecraftforge.common.world.ForgeWorldPreset type = net.minecraftforge.registries.ForgeRegistries.WORLD_TYPES.get().getValue(new net.minecraft.resources.ResourceLocation(s)); + if (type != null) return type.createSettings(p_209717_, i, p_209718_.f_207544_(), false, s); switch(s) { case "flat": diff --git a/src/main/java/net/minecraftforge/client/ForgeWorldPresetScreens.java b/src/main/java/net/minecraftforge/client/ForgeWorldPresetScreens.java index bdb52731e9..c6d22ef8ba 100644 --- a/src/main/java/net/minecraftforge/client/ForgeWorldPresetScreens.java +++ b/src/main/java/net/minecraftforge/client/ForgeWorldPresetScreens.java @@ -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); diff --git a/src/main/java/net/minecraftforge/common/ForgeHooks.java b/src/main/java/net/minecraftforge/common/ForgeHooks.java index 7bf3338179..fe03ceddf8 100644 --- a/src/main/java/net/minecraftforge/common/ForgeHooks.java +++ b/src/main/java/net/minecraftforge/common/ForgeHooks.java @@ -1096,7 +1096,7 @@ public class ForgeHooks } } - private static final Map, DataSerializerEntry> serializerEntries = GameData.getSerializerMap(); + private static Map, DataSerializerEntry> serializerEntries = GameData.getSerializerMap(); //private static final ForgeRegistry serializerRegistry = (ForgeRegistry) 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)ForgeRegistries.DATA_SERIALIZERS).getValue(id); + DataSerializerEntry entry = ((ForgeRegistry)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)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) ForgeRegistries.DATA_SERIALIZERS.get()).getID(entry); + } } return id; } diff --git a/src/main/java/net/minecraftforge/common/ForgeMod.java b/src/main/java/net/minecraftforge/common/ForgeMod.java index 797819fe36..51a118e70d 100644 --- a/src/main/java/net/minecraftforge/common/ForgeMod.java +++ b/src/main/java/net/minecraftforge/common/ForgeMod.java @@ -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 ATTRIBUTES = DeferredRegister.create(Attribute.class, "forge"); + private static final DeferredRegister ATTRIBUTES = DeferredRegister.create(ForgeRegistries.Keys.ATTRIBUTES, "forge"); public static final RegistryObject SWIM_SPEED = ATTRIBUTES.register("swim_speed", () -> new RangedAttribute("forge.swimSpeed", 1.0D, 0.0D, 1024.0D).setSyncable(true)); public static final RegistryObject 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()); diff --git a/src/main/java/net/minecraftforge/common/ForgeStatesProvider.java b/src/main/java/net/minecraftforge/common/ForgeStatesProvider.java index a1332ca828..62ab6103ad 100644 --- a/src/main/java/net/minecraftforge/common/ForgeStatesProvider.java +++ b/src/main/java/net/minecraftforge/common/ForgeStatesProvider.java @@ -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(Supplier>> eventStream, BiFunction, CompletableFuture>> preDispatchHook, BiFunction, CompletableFuture>> postDispatchHook, BiFunction>, CompletableFuture>> finalActivityGenerator) implements IModStateTransition { public static SerialTransition of(Supplier>> eventStream) { return new SerialTransition(eventStream, (t, f)->CompletableFuture.completedFuture(Collections.emptyList()), (t, f)->CompletableFuture.completedFuture(Collections.emptyList()), (e, prev) ->prev.thenApplyAsync(Function.identity(), e)); diff --git a/src/main/java/net/minecraftforge/common/ForgeTagHandler.java b/src/main/java/net/minecraftforge/common/ForgeTagHandler.java deleted file mode 100644 index 167d822f8b..0000000000 --- a/src/main/java/net/minecraftforge/common/ForgeTagHandler.java +++ /dev/null @@ -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 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 > StaticTagHelper getTagHelper(IForgeRegistry registry) -// { -// return (StaticTagHelper) 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 Type of the registry -// * @return A named tag -// */ -// public static > Named makeWrapperTag(IForgeRegistry registry, ResourceLocation name) -// { -// validateRegistrySupportsTags(registry); -// if (tagTypesSet) -// { -// StaticTagHelper 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 Type of the registry -// * @return An optional tag -// */ -// public static > IOptionalNamedTag createOptionalTag(IForgeRegistry 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 Type of the registry -// * @return An optional tag -// */ -// public static > IOptionalNamedTag createOptionalTag(IForgeRegistry registry, ResourceLocation name, @Nullable Set> defaults) -// { -// validateRegistrySupportsTags(registry); -// if (tagTypesSet) -// { -// StaticTagHelper 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 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 > Named makeWrapperTag(ResourceLocation registryName, ResourceLocation name) -// { -// if (tagTypesSet) -// { -// IForgeRegistry 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 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 > IOptionalNamedTag 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 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 > IOptionalNamedTag createOptionalTag(ResourceLocation registryName, ResourceLocation name, @Nullable Set> defaults) -// { -// if (tagTypesSet) -// { -// IForgeRegistry 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 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 WILL 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 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> createCustomTagTypeReaders() - { - LOGGER.debug("Gathering custom tag collection reader from types."); - ImmutableMap.Builder> 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 Optional> getWrapperRegistry(ResourceKey> key, Optional> 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) GameData.getWrapper(reg.getRegistryKey(), Lifecycle.stable())); -// return Optional.of((Registry) 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(); -// } -} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/common/data/ForgeRegistryTagsProvider.java b/src/main/java/net/minecraftforge/common/data/ForgeRegistryTagsProvider.java index 0d870fb4a9..c93fc251f3 100644 --- a/src/main/java/net/minecraftforge/common/data/ForgeRegistryTagsProvider.java +++ b/src/main/java/net/minecraftforge/common/data/ForgeRegistryTagsProvider.java @@ -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> extends TagsProvider { - //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, String> vanillaTypes = ImmutableMap., String>builder() - .put(ForgeRegistries.BLOCKS, "blocks") - .put(ForgeRegistries.ENTITIES, "entity_types") - .put(ForgeRegistries.FLUIDS, "fluids") - .put(ForgeRegistries.ITEMS, "items") - .build(); - - private static > Registry wrapRegistry(IForgeRegistry registryIn) + private static > Registry wrapRegistry(IForgeRegistry forgeRegistry) { - if (!(registryIn instanceof ForgeRegistry)) - throw new IllegalArgumentException("Forge registry " + registryIn.getRegistryName() + " is not an instance of a ForgeRegistry"); - ForgeRegistry forgeRegistry = (ForgeRegistry) 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 > String getTagFolder(IForgeRegistry registryIn) + public ForgeRegistryTagsProvider(DataGenerator generator, IForgeRegistry forgeRegistry, String modId, @Nullable ExistingFileHelper existingFileHelper) { - String tagFolder = ((ForgeRegistry) registryIn).getTagFolder(); - return tagFolder == null ? vanillaTypes.get(registryIn) : tagFolder; - } - - public ForgeRegistryTagsProvider(DataGenerator generatorIn, IForgeRegistry 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); } } diff --git a/src/main/java/net/minecraftforge/common/loot/LootModifierManager.java b/src/main/java/net/minecraftforge/common/loot/LootModifierManager.java index 7864aff671..538e352d28 100644 --- a/src/main/java/net/minecraftforge/common/loot/LootModifierManager.java +++ b/src/main/java/net/minecraftforge/common/loot/LootModifierManager.java @@ -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); } /** diff --git a/src/main/java/net/minecraftforge/common/world/ForgeWorldPreset.java b/src/main/java/net/minecraftforge/common/world/ForgeWorldPreset.java index a52fafe9fe..1b748a35b9 100644 --- a/src/main/java/net/minecraftforge/common/world/ForgeWorldPreset.java +++ b/src/main/java/net/minecraftforge/common/world/ForgeWorldPreset.java @@ -34,7 +34,7 @@ public class ForgeWorldPreset extends ForgeRegistryEntry 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); diff --git a/src/main/java/net/minecraftforge/event/RegistryEvent.java b/src/main/java/net/minecraftforge/event/RegistryEvent.java index 1d490d11d3..738ada0974 100644 --- a/src/main/java/net/minecraftforge/event/RegistryEvent.java +++ b/src/main/java/net/minecraftforge/event/RegistryEvent.java @@ -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> extends GenericEven RegistryEvent(Class 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. diff --git a/src/main/java/net/minecraftforge/network/filters/VanillaConnectionNetworkFilter.java b/src/main/java/net/minecraftforge/network/filters/VanillaConnectionNetworkFilter.java index 906c243ec3..1db6319b56 100644 --- a/src/main/java/net/minecraftforge/network/filters/VanillaConnectionNetworkFilter.java +++ b/src/main/java/net/minecraftforge/network/filters/VanillaConnectionNetworkFilter.java @@ -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>, 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)); + } } diff --git a/src/main/java/net/minecraftforge/registries/DeferredRegister.java b/src/main/java/net/minecraftforge/registries/DeferredRegister.java index eb66157eaf..2112c47cda 100644 --- a/src/main/java/net/minecraftforge/registries/DeferredRegister.java +++ b/src/main/java/net/minecraftforge/registries/DeferredRegister.java @@ -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> */ public static > DeferredRegister create(IForgeRegistry reg, String modid) { - return new DeferredRegister(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 > DeferredRegister create(Class base, String modid) { - return new DeferredRegister(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 > DeferredRegister create(ResourceKey> 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 > DeferredRegister create(ResourceLocation registryName, String modid) + { + return new DeferredRegister<>(registryName, null, modid); + } + + private final ResourceLocation registryName; private final Class superType; private final String modid; private final Map, Supplier> entries = new LinkedHashMap<>(); private final Set> entriesView = Collections.unmodifiableSet(entries.keySet()); private IForgeRegistry type; + private Supplier> typeSupplier; private Supplier> registryFactory; + private SetMultimap, Supplier> optionalTags; private boolean seenRegisterEvent = false; - private DeferredRegister(Class base, String modid) + private DeferredRegister(@Nullable ResourceLocation registryName, @Nullable Class base, String modid) { + this.registryName = registryName; this.superType = base; this.modid = modid; } private DeferredRegister(IForgeRegistry reg, String modid) { - this(reg.getRegistrySuperType(), modid); + this(reg.getRegistryName(), reg.getRegistrySuperType(), modid); this.type = reg; } @@ -96,8 +137,11 @@ public class DeferredRegister> final ResourceLocation key = new ResourceLocation(modid, name); RegistryObject ret; - if (this.type != null) - ret = RegistryObject.of(key, this.type); + IForgeRegistry 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> * @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> makeRegistry(final String name, final Supplier> 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> makeRegistry(final String name, final Supplier> 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> makeRegistry(final Class base, final Supplier> 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 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 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 createOptionalTagKey(@NotNull String path, @NotNull Set> 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 createOptionalTagKey(@NotNull ResourceLocation location, @NotNull Set> defaults) + { + TagKey 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 name, @NotNull Set> 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> 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> makeRegistry(final ResourceLocation registryName, final Class superType, final Supplier> 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 getForgeRegistry() + { + if (this.type == null && this.typeSupplier != null) + this.type = this.typeSupplier.get(); + + return this.type; + } + + private void onFill(IForgeRegistry registry) + { + if (this.optionalTags == null) + return; + + ITagManager 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 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> } } - 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) diff --git a/src/main/java/net/minecraftforge/registries/ForgeDeferredRegistriesSetup.java b/src/main/java/net/minecraftforge/registries/ForgeDeferredRegistriesSetup.java new file mode 100644 index 0000000000..593f301d90 --- /dev/null +++ b/src/main/java/net/minecraftforge/registries/ForgeDeferredRegistriesSetup.java @@ -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; + } +} diff --git a/src/main/java/net/minecraftforge/registries/ForgeRegistries.java b/src/main/java/net/minecraftforge/registries/ForgeRegistries.java index eeee8c579c..da9bd99be0 100644 --- a/src/main/java/net/minecraftforge/registries/ForgeRegistries.java +++ b/src/main/java/net/minecraftforge/registries/ForgeRegistries.java @@ -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 BLOCKS = RegistryManager.ACTIVE.getRegistry(Block.class); - public static final IForgeRegistry FLUIDS = RegistryManager.ACTIVE.getRegistry(Fluid.class); - public static final IForgeRegistry ITEMS = RegistryManager.ACTIVE.getRegistry(Item.class); - public static final IForgeRegistry MOB_EFFECTS = RegistryManager.ACTIVE.getRegistry(MobEffect.class); - public static final IForgeRegistry SOUND_EVENTS = RegistryManager.ACTIVE.getRegistry(SoundEvent.class); - public static final IForgeRegistry POTIONS = RegistryManager.ACTIVE.getRegistry(Potion.class); - public static final IForgeRegistry ENCHANTMENTS = RegistryManager.ACTIVE.getRegistry(Enchantment.class); - public static final IForgeRegistry> ENTITIES = RegistryManager.ACTIVE.getRegistry(EntityType.class); - public static final IForgeRegistry> BLOCK_ENTITIES = RegistryManager.ACTIVE.getRegistry(BlockEntityType.class); - public static final IForgeRegistry> PARTICLE_TYPES = RegistryManager.ACTIVE.getRegistry(ParticleType.class); - public static final IForgeRegistry> CONTAINERS = RegistryManager.ACTIVE.getRegistry(MenuType.class); - public static final IForgeRegistry PAINTING_TYPES = RegistryManager.ACTIVE.getRegistry(Motive.class); - public static final IForgeRegistry> RECIPE_SERIALIZERS = RegistryManager.ACTIVE.getRegistry(RecipeSerializer.class); - public static final IForgeRegistry ATTRIBUTES = RegistryManager.ACTIVE.getRegistry(Attribute.class); - public static final IForgeRegistry> STAT_TYPES = RegistryManager.ACTIVE.getRegistry(StatType.class); + public static final IForgeRegistry BLOCKS = RegistryManager.ACTIVE.getRegistry(Keys.BLOCKS); + public static final IForgeRegistry FLUIDS = RegistryManager.ACTIVE.getRegistry(Keys.FLUIDS); + public static final IForgeRegistry ITEMS = RegistryManager.ACTIVE.getRegistry(Keys.ITEMS); + public static final IForgeRegistry MOB_EFFECTS = RegistryManager.ACTIVE.getRegistry(Keys.MOB_EFFECTS); + public static final IForgeRegistry SOUND_EVENTS = RegistryManager.ACTIVE.getRegistry(Keys.SOUND_EVENTS); + public static final IForgeRegistry POTIONS = RegistryManager.ACTIVE.getRegistry(Keys.POTIONS); + public static final IForgeRegistry ENCHANTMENTS = RegistryManager.ACTIVE.getRegistry(Keys.ENCHANTMENTS); + public static final IForgeRegistry> ENTITIES = RegistryManager.ACTIVE.getRegistry(Keys.ENTITY_TYPES); + public static final IForgeRegistry> BLOCK_ENTITIES = RegistryManager.ACTIVE.getRegistry(Keys.BLOCK_ENTITY_TYPES); + public static final IForgeRegistry> PARTICLE_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.PARTICLE_TYPES); + public static final IForgeRegistry> CONTAINERS = RegistryManager.ACTIVE.getRegistry(Keys.CONTAINER_TYPES); + public static final IForgeRegistry PAINTING_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.PAINTING_TYPES); + public static final IForgeRegistry> RECIPE_SERIALIZERS = RegistryManager.ACTIVE.getRegistry(Keys.RECIPE_SERIALIZERS); + public static final IForgeRegistry ATTRIBUTES = RegistryManager.ACTIVE.getRegistry(Keys.ATTRIBUTES); + public static final IForgeRegistry> STAT_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.STAT_TYPES); // Villages - public static final IForgeRegistry PROFESSIONS = RegistryManager.ACTIVE.getRegistry(VillagerProfession.class); - public static final IForgeRegistry POI_TYPES = RegistryManager.ACTIVE.getRegistry(PoiType.class); - public static final IForgeRegistry> MEMORY_MODULE_TYPES = RegistryManager.ACTIVE.getRegistry(MemoryModuleType.class); - public static final IForgeRegistry> SENSOR_TYPES = RegistryManager.ACTIVE.getRegistry(SensorType.class); - public static final IForgeRegistry SCHEDULES = RegistryManager.ACTIVE.getRegistry(Schedule.class); - public static final IForgeRegistry ACTIVITIES = RegistryManager.ACTIVE.getRegistry(Activity.class); + public static final IForgeRegistry PROFESSIONS = RegistryManager.ACTIVE.getRegistry(Keys.VILLAGER_PROFESSIONS); + public static final IForgeRegistry POI_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.POI_TYPES); + public static final IForgeRegistry> MEMORY_MODULE_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.MEMORY_MODULE_TYPES); + public static final IForgeRegistry> SENSOR_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.SENSOR_TYPES); + public static final IForgeRegistry SCHEDULES = RegistryManager.ACTIVE.getRegistry(Keys.SCHEDULES); + public static final IForgeRegistry ACTIVITIES = RegistryManager.ACTIVE.getRegistry(Keys.ACTIVITIES); // Worldgen - public static final IForgeRegistry> WORLD_CARVERS = RegistryManager.ACTIVE.getRegistry(WorldCarver.class); - public static final IForgeRegistry> FEATURES = RegistryManager.ACTIVE.getRegistry(Feature.class); - public static final IForgeRegistry CHUNK_STATUS = RegistryManager.ACTIVE.getRegistry(ChunkStatus.class); - public static final IForgeRegistry> STRUCTURE_FEATURES = RegistryManager.ACTIVE.getRegistry(StructureFeature.class); - public static final IForgeRegistry> BLOCK_STATE_PROVIDER_TYPES = RegistryManager.ACTIVE.getRegistry(BlockStateProviderType.class); - public static final IForgeRegistry> FOLIAGE_PLACER_TYPES = RegistryManager.ACTIVE.getRegistry(FoliagePlacerType.class); - public static final IForgeRegistry> TREE_DECORATOR_TYPES = RegistryManager.ACTIVE.getRegistry(TreeDecoratorType.class); + public static final IForgeRegistry> WORLD_CARVERS = RegistryManager.ACTIVE.getRegistry(Keys.WORLD_CARVERS); + public static final IForgeRegistry> FEATURES = RegistryManager.ACTIVE.getRegistry(Keys.FEATURES); + public static final IForgeRegistry CHUNK_STATUS = RegistryManager.ACTIVE.getRegistry(Keys.CHUNK_STATUS); + public static final IForgeRegistry> STRUCTURE_FEATURES = RegistryManager.ACTIVE.getRegistry(Keys.STRUCTURE_FEATURES); + public static final IForgeRegistry> BLOCK_STATE_PROVIDER_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.BLOCK_STATE_PROVIDER_TYPES); + public static final IForgeRegistry> FOLIAGE_PLACER_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.FOLIAGE_PLACER_TYPES); + public static final IForgeRegistry> TREE_DECORATOR_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.TREE_DECORATOR_TYPES); // Dynamic/Data driven. public static final IForgeRegistry BIOMES = RegistryManager.ACTIVE.getRegistry(Keys.BIOMES); // Custom forge registries - public static final IForgeRegistry DATA_SERIALIZERS = RegistryManager.ACTIVE.getRegistry(DataSerializerEntry.class); - public static final IForgeRegistry> LOOT_MODIFIER_SERIALIZERS = RegistryManager.ACTIVE.getRegistry(GlobalLootModifierSerializer.class); - public static final IForgeRegistry WORLD_TYPES = RegistryManager.ACTIVE.getRegistry(ForgeWorldPreset.class); + static final DeferredRegister DEFERRED_DATA_SERIALIZERS = DeferredRegister.create(Keys.DATA_SERIALIZERS, Keys.DATA_SERIALIZERS.location().getNamespace()); + public static final Supplier> DATA_SERIALIZERS = DEFERRED_DATA_SERIALIZERS.makeRegistry(DataSerializerEntry.class, GameData::getDataSerializersRegistryBuilder); + static final DeferredRegister> DEFERRED_LOOT_MODIFIER_SERIALIZERS = DeferredRegister.create(Keys.LOOT_MODIFIER_SERIALIZERS, Keys.LOOT_MODIFIER_SERIALIZERS.location().getNamespace()); + public static final Supplier>> LOOT_MODIFIER_SERIALIZERS = DEFERRED_LOOT_MODIFIER_SERIALIZERS.makeRegistry(GameData.c(GlobalLootModifierSerializer.class), GameData::getGLMSerializersRegistryBuilder); + static final DeferredRegister DEFERRED_WORLD_TYPES = DeferredRegister.create(Keys.WORLD_TYPES, Keys.WORLD_TYPES.location().getNamespace()); + public static final Supplier> 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> BIOMES = key("worldgen/biome"); - //Forge + // Forge public static final ResourceKey> DATA_SERIALIZERS = key("data_serializers"); public static final ResourceKey>> LOOT_MODIFIER_SERIALIZERS = key("forge:loot_modifier_serializers"); public static final ResourceKey> WORLD_TYPES = key("forge:world_types"); diff --git a/src/main/java/net/minecraftforge/registries/ForgeRegistry.java b/src/main/java/net/minecraftforge/registries/ForgeRegistry.java index 2ac5ff71e4..f1d8bfdfaa 100644 --- a/src/main/java/net/minecraftforge/registries/ForgeRegistry.java +++ b/src/main/java/net/minecraftforge/registries/ForgeRegistry.java @@ -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> implements IForgeRegistryInternal, IForgeRegistryModifiable { @@ -70,14 +73,14 @@ public class ForgeRegistry> implements IForgeRe private final Set blocked = Sets.newHashSet(); private final Multimap overrides = ArrayListMultimap.create(); private final BiMap, V> owners = HashBiMap.create(); + private final ForgeRegistryTagManager tagManager; private final DummyFactory 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> 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> implements IForgeRe return this.name; } + @Override public ResourceKey> getRegistryKey() { return this.key; @@ -167,12 +172,7 @@ public class ForgeRegistry> implements IForgeRe return superType; } - @Nullable - public String getTagFolder() - { - return tagFolder; - } - + @NotNull public Codec getCodec() { return this.codec; @@ -234,6 +234,7 @@ public class ForgeRegistry> implements IForgeRe return getResourceKey(value).map(ResourceKey::location).orElse(this.defaultKey); } + @NotNull @Override public Optional> getResourceKey(V value) { @@ -241,19 +242,79 @@ public class ForgeRegistry> implements IForgeRe return Optional.ofNullable(this.owners.inverse().get(value)).map(OverrideOwner::key); } + @SuppressWarnings("unchecked") + @Nullable + Registry 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> getHolderHelper() + { + Registry wrapper = getWrapper(); + if (!(wrapper instanceof IHolderHelperHolder)) + return Optional.empty(); + + // Unsafe cast means we can't use pattern matching here + return Optional.of(((IHolderHelperHolder) wrapper).getHolderHelper()); + } + + void onBindTags(Map, HolderSet.Named> tags, Set> defaultedTags) + { + if (this.tagManager != null) + this.tagManager.bind(tags, defaultedTags); + } + + @NotNull + @Override + public Optional> getHolder(ResourceKey key) + { + return getHolderHelper().flatMap(h -> h.getHolder(key)); + } + + @NotNull + @Override + public Optional> getHolder(ResourceLocation location) + { + return getHolderHelper().flatMap(h -> h.getHolder(location)); + } + + @NotNull + @Override + public Optional> getHolder(V value) + { + return getHolderHelper().flatMap(h -> h.getHolder(value)); + } + + @Nullable + @Override + public ITagManager tags() + { + return this.tagManager; + } + + @NotNull @Override public Set getKeys() { return Collections.unmodifiableSet(this.names.keySet()); } - @Nonnull + @NotNull @Override public Collection getValues() { return Collections.unmodifiableSet(this.names.values()); } + @NotNull @Override public Set, V>> getEntries() { diff --git a/src/main/java/net/minecraftforge/registries/ForgeRegistryTag.java b/src/main/java/net/minecraftforge/registries/ForgeRegistryTag.java new file mode 100644 index 0000000000..c788dd1a71 --- /dev/null +++ b/src/main/java/net/minecraftforge/registries/ForgeRegistryTag.java @@ -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> implements ITag +{ + private final TagKey key; + @Nullable + private HolderSet holderSet; + @Nullable + private List contents; + + ForgeRegistryTag(TagKey key) + { + this.key = key; + } + + @Override + public TagKey getKey() + { + return this.key; + } + + @NotNull + @Override + public Iterator iterator() + { + return this.getContents().iterator(); + } + + @Override + public Spliterator spliterator() + { + return this.getContents().spliterator(); + } + + @Override + public boolean isEmpty() + { + return this.getContents().isEmpty(); + } + + @Override + public int size() + { + return this.getContents().size(); + } + + @Override + public Stream stream() + { + return this.getContents().stream(); + } + + @Override + public boolean contains(V value) + { + return this.getContents().contains(value); + } + + @Override + public Optional getRandomElement(Random random) + { + return Util.getRandomSafe(this.getContents(), random); + } + + @Override + public boolean isBound() + { + return this.holderSet != null; + } + + List 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 holderSet) + { + this.holderSet = holderSet; + this.contents = null; + } + + @Override + public String toString() + { + return "Tag[" + + "key=" + this.key + ", " + + "contents=" + getContents() + ']'; + } +} diff --git a/src/main/java/net/minecraftforge/registries/ForgeRegistryTagManager.java b/src/main/java/net/minecraftforge/registries/ForgeRegistryTagManager.java new file mode 100644 index 0000000000..79f59c036f --- /dev/null +++ b/src/main/java/net/minecraftforge/registries/ForgeRegistryTagManager.java @@ -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> implements ITagManager +{ + private final ForgeRegistry owner; + private volatile Map, ITag> tags = new IdentityHashMap<>(); + + ForgeRegistryTagManager(ForgeRegistry owner) + { + this.owner = owner; + } + + void bind(Map, HolderSet.Named> holderTags, Set> defaultedTags) + { + IdentityHashMap, ITag> newTags = new IdentityHashMap<>(this.tags); + + // Forcefully unbind all pre-existing tags + newTags.values().forEach(tag -> ((ForgeRegistryTag) tag).bind(null)); + + // Bind all tags that were loaded + holderTags.forEach((key, holderSet) -> + ((ForgeRegistryTag) newTags.computeIfAbsent(key, ForgeRegistryTag::new)).bind(holderSet)); + + this.tags = newTags; + } + + @NotNull + @Override + public ITag getTag(@NotNull TagKey name) + { + Objects.requireNonNull(name); + ITag 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, ITag> map = new IdentityHashMap<>(this.tags); + map.put(name, tag); + this.tags = map; + } + + return tag; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @NotNull + @Override + public Optional> getReverseTag(@NotNull V value) + { + Objects.requireNonNull(value); + // All Holders are implementors of IReverseTag + return (Optional>) (Optional) this.owner.getHolder(value); + } + + @Override + public boolean isKnownTagName(@NotNull TagKey name) + { + Objects.requireNonNull(name); + ITag tag = this.tags.get(name); + return tag != null && tag.isBound(); + } + + @NotNull + @Override + public Iterator> iterator() + { + return Iterators.unmodifiableIterator(this.tags.values().iterator()); + } + + @NotNull + @Override + public Stream> stream() + { + return this.tags.values().stream(); + } + + @NotNull + @Override + public Stream> getTagNames() + { + return this.tags.keySet().stream(); + } + + @NotNull + @Override + public TagKey createTagKey(@NotNull ResourceLocation location) + { + Objects.requireNonNull(location); + return TagKey.create(this.owner.getRegistryKey(), location); + } + + @NotNull + @Override + public TagKey createOptionalTagKey(@NotNull ResourceLocation location, @NotNull Set> defaults) + { + TagKey tagKey = createTagKey(location); + + addOptionalTagDefaults(tagKey, defaults); + + return tagKey; + } + + @Override + public void addOptionalTagDefaults(@NotNull TagKey name, @NotNull Set> defaults) + { + Objects.requireNonNull(name); + Objects.requireNonNull(defaults); + + this.owner.getHolderHelper().ifPresent(h -> h.addOptionalTag(name, defaults)); + } +} diff --git a/src/main/java/net/minecraftforge/registries/GameData.java b/src/main/java/net/minecraftforge/registries/GameData.java index 487fb75adc..a487a7a748 100644 --- a/src/main/java/net/minecraftforge/registries/GameData.java +++ b/src/main/java/net/minecraftforge/registries/GameData.java @@ -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 getDataSerializersRegistryBuilder() + { + return makeRegistry(DATA_SERIALIZERS, DataSerializerEntry.class, 256 /*vanilla space*/, MAX_VARINT).disableSaving().disableOverrides() + .addCallback(SerializerCallbacks.INSTANCE); + } + + static RegistryBuilder> getGLMSerializersRegistryBuilder() + { + return makeRegistry(LOOT_MODIFIER_SERIALIZERS, c(GlobalLootModifierSerializer.class)).disableSaving().disableSync(); + } + + static RegistryBuilder 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 Class c(Class cls) { return (Class)cls; } + static Class c(Class cls) { return (Class)cls; } private static > RegistryBuilder makeRegistry(ResourceKey> key, Class type) { - return new RegistryBuilder().setName(key.location()).setType(type).setMaxID(MAX_VARINT).addCallback(new NamespacedWrapper.Factory()); + return new RegistryBuilder().setName(key.location()).setType(type).setMaxID(MAX_VARINT).hasWrapper(); } private static > RegistryBuilder makeRegistry(ResourceKey> key, Class type, int min, int max) { @@ -199,31 +211,32 @@ public class GameData @SuppressWarnings("unchecked") public static Map 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 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 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, DataSerializerEntry> getSerializerMap() { - return RegistryManager.ACTIVE.getRegistry(DataSerializerEntry.class).getSlaveMap(SERIALIZER_TO_ENTRY, Map.class); + ForgeRegistry registry = RegistryManager.ACTIVE.getRegistry(DATA_SERIALIZERS); + return registry == null ? null : registry.getSlaveMap(SERIALIZER_TO_ENTRY, Map.class); } @SuppressWarnings("unchecked") public static BiMap> getStructureMap() { - return (BiMap>) RegistryManager.ACTIVE.getRegistry(Feature.class).getSlaveMap(STRUCTURES, BiMap.class); + return (BiMap>) RegistryManager.ACTIVE.getRegistry(FEATURES).getSlaveMap(STRUCTURES, BiMap.class); } public static > K register_impl(K value) diff --git a/src/main/java/net/minecraftforge/registries/IForgeRegistry.java b/src/main/java/net/minecraftforge/registries/IForgeRegistry.java index 7f41120513..40d5b27368 100644 --- a/src/main/java/net/minecraftforge/registries/IForgeRegistry.java +++ b/src/main/java/net/minecraftforge/registries/IForgeRegistry.java @@ -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> extends Iterable { + ResourceKey> getRegistryKey(); ResourceLocation getRegistryName(); Class getRegistrySuperType(); @@ -38,13 +42,39 @@ public interface IForgeRegistry> extends Iterab @Nullable V getValue(ResourceLocation key); @Nullable ResourceLocation getKey(V value); @Nullable ResourceLocation getDefaultKey(); - @Nullable Optional> getResourceKey(V value); + @NotNull Optional> getResourceKey(V value); - @Nonnull Set getKeys(); - @Nonnull Collection getValues(); - @Nonnull Set, V>> getEntries(); + @NotNull Set getKeys(); + @NotNull Collection getValues(); + @NotNull Set, V>> getEntries(); - @Nonnull Codec getCodec(); + /** + * @see Registry#byNameCodec() + */ + @NotNull Codec getCodec(); + + /** + * This method exists purely as a stopgap for vanilla compatibility. + * For anything tag related, use {@link #tags()}. + * + * @see Registry#getHolder(ResourceKey) + */ + @NotNull Optional> getHolder(ResourceKey key); + /** + * This method exists purely as a stopgap for vanilla compatibility. + * For anything tag related, use {@link #tags()}. + */ + @NotNull Optional> getHolder(ResourceLocation location); + /** + * This method exists purely as a stopgap for vanilla compatibility. + * For anything tag related, use {@link #tags()}. + */ + @NotNull Optional> getHolder(V value); + + /** + * @return an instance of {@link ITagManager} if this registry supports tags and/or has a wrapper registry, null otherwise + */ + @Nullable ITagManager tags(); /** * Retrieve the slave map of type T from the registry. diff --git a/src/main/java/net/minecraftforge/registries/IHolderHelperHolder.java b/src/main/java/net/minecraftforge/registries/IHolderHelperHolder.java new file mode 100644 index 0000000000..147d90a33b --- /dev/null +++ b/src/main/java/net/minecraftforge/registries/IHolderHelperHolder.java @@ -0,0 +1,11 @@ +/* + * Minecraft Forge - Forge Development LLC + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.registries; + +interface IHolderHelperHolder> +{ + NamespacedHolderHelper getHolderHelper(); +} diff --git a/src/main/java/net/minecraftforge/registries/NamespacedDefaultedWrapper.java b/src/main/java/net/minecraftforge/registries/NamespacedDefaultedWrapper.java index 809b68f482..f4a28aa5f7 100644 --- a/src/main/java/net/minecraftforge/registries/NamespacedDefaultedWrapper.java +++ b/src/main/java/net/minecraftforge/registries/NamespacedDefaultedWrapper.java @@ -30,7 +30,7 @@ import net.minecraft.core.DefaultedRegistry; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Lifecycle; -class NamespacedDefaultedWrapper> extends DefaultedRegistry implements ILockableRegistry +class NamespacedDefaultedWrapper> extends DefaultedRegistry implements ILockableRegistry, IHolderHelperHolder { private final ForgeRegistry delegate; private final NamespacedHolderHelper holders; @@ -42,7 +42,7 @@ class NamespacedDefaultedWrapper> 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> extends Defau return this.delegate.size(); } + @Override + public NamespacedHolderHelper getHolderHelper() + { + return this.holders; + } + @Override public Optional> getHolder(int id) { return this.holders.getHolder(id); } @Override public Optional> getHolder(ResourceKey key) { return this.holders.getHolder(key); } @Override public Holder getOrCreateHolder(ResourceKey key) { return this.holders.getOrCreateHolder(key); } @@ -202,7 +208,7 @@ class NamespacedDefaultedWrapper> 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; } diff --git a/src/main/java/net/minecraftforge/registries/NamespacedHolderHelper.java b/src/main/java/net/minecraftforge/registries/NamespacedHolderHelper.java index 530d9cfdc5..0b28d0efa8 100644 --- a/src/main/java/net/minecraftforge/registries/NamespacedHolderHelper.java +++ b/src/main/java/net/minecraftforge/registries/NamespacedHolderHelper.java @@ -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> { private static final Logger LOGGER = LogManager.getLogger(); + private final ForgeRegistry owner; private final Registry self; @Nullable private final ResourceLocation defaultKey; @Nullable private final Function> holderLookup; + private final Multimap, Supplier> 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> holdersSorted; @@ -52,8 +59,9 @@ class NamespacedHolderHelper> private volatile Map, HolderSet.Named> tags = new IdentityHashMap<>(); private Holder.Reference defaultHolder; - NamespacedHolderHelper(Registry self, @Nullable ResourceLocation defaultKey, @Nullable Function> holderLookup) + NamespacedHolderHelper(ForgeRegistry owner, Registry self, @Nullable ResourceLocation defaultKey, @Nullable Function> holderLookup) { + this.owner = owner; this.self = self; this.defaultKey = defaultKey; this.holderLookup = holderLookup; @@ -70,6 +78,16 @@ class NamespacedHolderHelper> return Optional.ofNullable(this.holdersByName.get(key.location())); } + Optional> getHolder(ResourceLocation location) + { + return Optional.ofNullable(this.holdersByName.get(location)); + } + + Optional> getHolder(T value) + { + return Optional.ofNullable(this.holders.get(value)); + } + Holder getOrCreateHolder(ResourceKey key) { return this.holdersByName.computeIfAbsent(key.location(), k -> @@ -120,6 +138,11 @@ class NamespacedHolderHelper> return named; } + void addOptionalTag(TagKey name, @NotNull Set> defaults) + { + this.optionalTags.putAll(name, defaults); + } + Stream> getTagNames() { return this.tags.keySet().stream(); @@ -165,18 +188,7 @@ class NamespacedHolderHelper> { Map, List>> holderToTag = new IdentityHashMap<>(); this.holdersByName.values().forEach(v -> holderToTag.put(v, new ArrayList<>())); - newTags.forEach((k, v) -> - { - for(Holder 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)) - throw new IllegalStateException("Found direct holder " + holder + " value in tag " + k); - holderToTag.get((Holder.Reference)holder).add(k); - } - }); + newTags.forEach((name, values) -> values.forEach(holder -> addTagToHolder(holderToTag, name, holder))); Set> set = Sets.difference(this.tags.keySet(), newTags.keySet()); if (!set.isEmpty()) @@ -184,8 +196,33 @@ class NamespacedHolderHelper> Map, HolderSet.Named> tmpTags = new IdentityHashMap<>(this.tags); newTags.forEach((k, v) -> tmpTags.computeIfAbsent(k, this::createTag).bind(v)); + + Set> defaultedTags = Sets.difference(this.optionalTags.keySet(), newTags.keySet()); + defaultedTags.forEach(name -> { + List> 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, List>> holderToTag, TagKey name, Holder 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)) + throw new IllegalStateException("Found direct holder " + holder + " value in tag " + name); + + holderToTag.get((Holder.Reference) holder).add(name); } void resetTags() diff --git a/src/main/java/net/minecraftforge/registries/NamespacedWrapper.java b/src/main/java/net/minecraftforge/registries/NamespacedWrapper.java index cea9721592..2103351c36 100644 --- a/src/main/java/net/minecraftforge/registries/NamespacedWrapper.java +++ b/src/main/java/net/minecraftforge/registries/NamespacedWrapper.java @@ -32,7 +32,7 @@ import org.apache.logging.log4j.Logger; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Lifecycle; -class NamespacedWrapper> extends MappedRegistry implements ILockableRegistry +class NamespacedWrapper> extends MappedRegistry implements ILockableRegistry, IHolderHelperHolder { private static final Logger LOGGER = LogManager.getLogger(); private final ForgeRegistry delegate; @@ -45,7 +45,7 @@ class NamespacedWrapper> 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> extends MappedRegistry return this.delegate.size(); } + @Override + public NamespacedHolderHelper getHolderHelper() + { + return this.holders; + } + @Override public Optional> getHolder(int id) { return this.holders.getHolder(id); } @Override public Optional> getHolder(ResourceKey key) { return this.holders.getHolder(key); } @Override public Holder getOrCreateHolder(ResourceKey key) { return this.holders.getOrCreateHolder(key); } @@ -200,7 +206,7 @@ class NamespacedWrapper> 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; } diff --git a/src/main/java/net/minecraftforge/registries/NewRegistryEvent.java b/src/main/java/net/minecraftforge/registries/NewRegistryEvent.java new file mode 100644 index 0000000000..141a805f2d --- /dev/null +++ b/src/main/java/net/minecraftforge/registries/NewRegistryEvent.java @@ -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> 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 > Supplier> create(RegistryBuilder 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 > Supplier> create(RegistryBuilder builder, @Nullable Consumer> onFill) + { + RegistryHolder registryHolder = new RegistryHolder<>(); + + registries.add(new RegistryData<>(builder, registryHolder, onFill)); + + return registryHolder; + } + + @SuppressWarnings("rawtypes") + void fill() + { + Map, 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>( + RegistryBuilder builder, + RegistryHolder registryHolder, + Consumer> onFill + ) {} + + private static class RegistryHolder> implements Supplier> + { + IForgeRegistry registry = null; + + @Override + public IForgeRegistry get() + { + return this.registry; + } + } + + @Override + public String toString() + { + return "RegistryEvent.NewRegistry"; + } +} diff --git a/src/main/java/net/minecraftforge/registries/RegistryBuilder.java b/src/main/java/net/minecraftforge/registries/RegistryBuilder.java index 19cd3446a9..84695df3d2 100644 --- a/src/main/java/net/minecraftforge/registries/RegistryBuilder.java +++ b/src/main/java/net/minecraftforge/registries/RegistryBuilder.java @@ -38,8 +38,6 @@ public class RegistryBuilder> private boolean allowOverrides = true; private boolean allowModifications = false; private boolean hasWrapper = false; - @Nullable - private String tagFolder; private DummyFactory dummyFactory; private MissingFactory missingFactory; private Set legacyNames = new HashSet<>(); @@ -201,15 +199,6 @@ public class RegistryBuilder> return this; } - public RegistryBuilder 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 legacyName(String name) { return legacyName(new ResourceLocation(name)); @@ -227,7 +216,24 @@ public class RegistryBuilder> return this; } - public IForgeRegistry 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 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 create() { if (hasWrapper) { @@ -345,12 +351,6 @@ public class RegistryBuilder> return allowModifications; } - @Nullable - public String getTagFolder() - { - return tagFolder; - } - @Nullable public DummyFactory getDummyFactory() { diff --git a/src/main/java/net/minecraftforge/registries/RegistryManager.java b/src/main/java/net/minecraftforge/registries/RegistryManager.java index eedb48d9e7..446a5a9023 100644 --- a/src/main/java/net/minecraftforge/registries/RegistryManager.java +++ b/src/main/java/net/minecraftforge/registries/RegistryManager.java @@ -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 vanillaRegistryKeys = Set.of(); BiMap>> registries = HashBiMap.create(); private BiMap>, 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 > IForgeRegistry getRegistry(Class 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 reg = new ForgeRegistry(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 > void registerToRootRegistry(ForgeRegistry forgeReg) + { + WritableRegistry> registry = (WritableRegistry>) Registry.REGISTRY; + Registry wrapper = forgeReg.getWrapper(); + if (wrapper != null) + registry.register(forgeReg.getRegistryKey(), wrapper, Lifecycle.experimental()); + } + + public static IModStateTransition.EventGenerator newRegistryEventGenerator() + { + NewRegistryEvent event = new NewRegistryEvent(); + return mc -> event; + } + + + public static CompletableFuture> preNewRegistryEvent(final Executor executor, + final IModStateTransition.EventGenerator eventGenerator) + { + return CompletableFuture.runAsync(() -> vanillaRegistryKeys = Set.copyOf(Registry.REGISTRY.keySet()), executor) + .handle((v, t) -> t != null ? Collections.singletonList(t) : Collections.emptyList()); + } + + public static CompletableFuture> postNewRegistryEvent(final Executor executor, + final IModStateTransition.EventGenerator 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 getVanillaRegistryKeys() + { + return vanillaRegistryKeys; + } } diff --git a/src/main/java/net/minecraftforge/registries/RegistryObject.java b/src/main/java/net/minecraftforge/registries/RegistryObject.java index 7b94419574..006a3fbc90 100644 --- a/src/main/java/net/minecraftforge/registries/RegistryObject.java +++ b/src/main/java/net/minecraftforge/registries/RegistryObject.java @@ -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> 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 , U extends T> RegistryObject of(final ResourceLocation name, Supplier> registryType) { return new RegistryObject<>(name, registryType); } @@ -32,10 +38,38 @@ public final class RegistryObject> 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 , U extends T> RegistryObject of(final ResourceLocation name, final Class 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 , U extends T> RegistryObject of(final ResourceLocation name, final ResourceKey> 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 , U extends T> RegistryObject of(final ResourceLocation name, final ResourceLocation registryName, String modid) { + return new RegistryObject<>(name, registryName, modid); + } + private static RegistryObject EMPTY = new RegistryObject<>(); private static > RegistryObject empty() { @@ -48,6 +82,7 @@ public final class RegistryObject> impl this.name = null; } + @Deprecated(forRemoval = true, since = "1.18.2") private > RegistryObject(ResourceLocation name, Supplier> registryType) { this(name, RegistryManager.ACTIVE.getRegistry(registryType.get())); @@ -68,6 +103,7 @@ public final class RegistryObject> impl } @SuppressWarnings("unchecked") + @Deprecated(forRemoval = true, since = "1.18.2") private > RegistryObject(final ResourceLocation name, final Class baseType, final String modid) { this.name = name; @@ -83,7 +119,7 @@ public final class RegistryObject> 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) registry); @@ -97,6 +133,36 @@ public final class RegistryObject> impl } } + @SuppressWarnings("unchecked") + private > 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 registry; + + @Override + public void accept(Predicate 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) registry); + } + }); + IForgeRegistry 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) 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. diff --git a/src/main/java/net/minecraftforge/registries/tags/IReverseTag.java b/src/main/java/net/minecraftforge/registries/tags/IReverseTag.java new file mode 100644 index 0000000000..fca05a77e7 --- /dev/null +++ b/src/main/java/net/minecraftforge/registries/tags/IReverseTag.java @@ -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 +{ + Stream> getTagKeys(); + + boolean containsTag(TagKey key); + + default boolean containsTag(ITag tag) + { + return containsTag(tag.getKey()); + } +} diff --git a/src/main/java/net/minecraftforge/registries/tags/ITag.java b/src/main/java/net/minecraftforge/registries/tags/ITag.java new file mode 100644 index 0000000000..9a31e98c71 --- /dev/null +++ b/src/main/java/net/minecraftforge/registries/tags/ITag.java @@ -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 extends Iterable +{ + TagKey getKey(); + + Stream stream(); + + boolean isEmpty(); + + int size(); + + boolean contains(V value); + + Optional 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(); +} diff --git a/src/main/java/net/minecraftforge/registries/tags/ITagManager.java b/src/main/java/net/minecraftforge/registries/tags/ITagManager.java new file mode 100644 index 0000000000..ca61e0d514 --- /dev/null +++ b/src/main/java/net/minecraftforge/registries/tags/ITagManager.java @@ -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> extends Iterable> +{ + /** + * 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 getTag(@NotNull TagKey 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> 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 not create the tag if it does not exist. + * + * @see ITag#isBound() + */ + boolean isKnownTagName(@NotNull TagKey name); + + /** + * @return A stream of all tags stored in this tag manager, bound or unbound. + */ + @NotNull Stream> stream(); + + /** + * @return A stream of all tag keys stored in this tag manager, bound or unbound. + */ + @NotNull Stream> 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 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 createOptionalTagKey(@NotNull ResourceLocation location, @NotNull Set> 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 name, @NotNull Set> defaults); +} diff --git a/src/main/java/net/minecraftforge/server/command/ForgeCommand.java b/src/main/java/net/minecraftforge/server/command/ForgeCommand.java index b744e73b9c..a6af43e4f3 100644 --- a/src/main/java/net/minecraftforge/server/command/ForgeCommand.java +++ b/src/main/java/net/minecraftforge/server/command/ForgeCommand.java @@ -21,6 +21,7 @@ public class ForgeCommand .then(GenerateCommand.register()) .then(DimensionsCommand.register()) .then(ModListCommand.register()) + .then(TagsCommand.register()) ); } } diff --git a/src/main/java/net/minecraftforge/server/command/TagsCommand.java b/src/main/java/net/minecraftforge/server/command/TagsCommand.java new file mode 100644 index 0000000000..9ef63116b5 --- /dev/null +++ b/src/main/java/net/minecraftforge/server/command/TagsCommand.java @@ -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. + * + *

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)

+ * + *

The command has three subcommands:

+ *
    + *
  • {@code /forge tags <registry> list [page]} - Lists all available tags in the given registry.
  • + *
  • {@code /forge tags <registry> get <tag> [page]} - Gets all elements of the given tag in the given registry.
  • + *
  • {@code /forge tags <registry> query <element> [page]} - Queries for all tags in the given registry which + * contain the given registry object.
  • + *
+ */ +class TagsCommand +{ + private static final long PAGE_SIZE = 8; + private static final ResourceKey>> 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 register() + { + /* + * /forge tags list [page] + * /forge tags get [page] + * /forge tags query [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 ctx, final int page) throws CommandSyntaxException + { + final ResourceKey> 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 ctx, final int page) throws CommandSyntaxException + { + final ResourceKey> 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 ctx, final int page) throws CommandSyntaxException + { + final ResourceKey> 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> 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 suggestRegistries(final CommandContext ctx, + final SuggestionsBuilder builder) + { + ctx.getSource().suggestRegistryElements(Registry.REGISTRY, + SharedSuggestionProvider.ElementSuggestionType.ELEMENTS, builder); + return builder.buildFuture(); + } + + private static SuggestionProvider suggestFromRegistry( + final Function, Iterable> 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 Optional> getResourceKey(final CommandContext ctx, + final String name, + final ResourceKey> 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 cast(final Object input) + { + return (O) input; + } +} + diff --git a/src/main/resources/assets/forge/lang/en_us.json b/src/main/resources/assets/forge/lang/en_us.json index a1d110943e..e6430a8c9e 100644 --- a/src/main/resources/assets/forge/lang/en_us.json +++ b/src/main/resources/assets/forge/lang/en_us.json @@ -112,6 +112,18 @@ "commands.forge.tracking.be.reset": "Block entity timings data has been cleared!", "commands.forge.tracking.timing_entry": "{0} - {1} [{2}, {3}, {4}]: {5}", "commands.forge.tracking.no_data": "No data has been recorded yet.", + "commands.forge.tags.error.unknown_registry": "Unknown registry '%s'", + "commands.forge.tags.error.unknown_tag": "Unknown tag '%s' in registry '%s'", + "commands.forge.tags.error.unknown_element": "Unknown element '%s' in registry '%s'", + "commands.forge.tags.registry_key": "%s", + "commands.forge.tags.tag_count": "Tags: %s", + "commands.forge.tags.copy_tag_names": "Click to copy all tag names to clipboard", + "commands.forge.tags.element_count": "Elements: %s", + "commands.forge.tags.copy_element_names": "Click to copy all element names to clipboard", + "commands.forge.tags.tag_key": "%s / %s", + "commands.forge.tags.containing_tag_count": "Containing tags: %s", + "commands.forge.tags.element": "%s : %s", + "commands.forge.tags.page_info": "%s ", "commands.config.getwithtype": "Config for %s of type %s found at %s", "commands.config.noconfig": "Config for %s of type %s not found", diff --git a/src/main/resources/forge.exc b/src/main/resources/forge.exc index 917888b4a9..ffaced65e9 100644 --- a/src/main/resources/forge.exc +++ b/src/main/resources/forge.exc @@ -13,13 +13,14 @@ net/minecraft/client/resources/model/ModelBakery.(Lnet/minecraft/server/pa net/minecraft/client/resources/model/ModelBakery.processLoading(Lnet/minecraft/util/profiling/ProfilerFiller;I)V=|p_119249_,p_119250_ net/minecraft/client/resources/model/MultiPartBakedModel.getQuads(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/Direction;Ljava/util/Random;Lnet/minecraftforge/client/model/data/IModelData;)Ljava/util/List;=|p_119465_,p_119466_,p_119467_,modelData net/minecraft/client/resources/model/WeightedBakedModel.getQuads(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/Direction;Ljava/util/Random;Lnet/minecraftforge/client/model/data/IModelData;)Ljava/util/List;=|p_119547_,p_119548_,p_119549_,modelData +net/minecraft/data/tags/BiomeTagsProvider.(Lnet/minecraft/data/DataGenerator;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_211094_,modId,existingFileHelper net/minecraft/data/tags/BlockTagsProvider.(Lnet/minecraft/data/DataGenerator;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_126511_,modId,existingFileHelper +net/minecraft/data/tags/ConfiguredStructureTagsProvider.(Lnet/minecraft/data/DataGenerator;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_211098_,modId,existingFileHelper net/minecraft/data/tags/EntityTypeTagsProvider.(Lnet/minecraft/data/DataGenerator;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_126517_,modId,existingFileHelper net/minecraft/data/tags/FluidTagsProvider.(Lnet/minecraft/data/DataGenerator;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_126523_,modId,existingFileHelper -net/minecraft/data/tags/GameEventTagsProvider.(Lnet/minecraft/data/DataGenerator;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_126517_,modId,existingFileHelper +net/minecraft/data/tags/GameEventTagsProvider.(Lnet/minecraft/data/DataGenerator;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_176826_,modId,existingFileHelper net/minecraft/data/tags/ItemTagsProvider.(Lnet/minecraft/data/DataGenerator;Lnet/minecraft/data/tags/BlockTagsProvider;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_126530_,p_126531_,modId,existingFileHelper net/minecraft/data/tags/TagsProvider.(Lnet/minecraft/data/DataGenerator;Lnet/minecraft/core/Registry;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_126546_,p_126547_,modId,existingFileHelper -net/minecraft/data/tags/TagsProvider.(Lnet/minecraft/data/DataGenerator;Lnet/minecraft/core/Registry;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;Ljava/lang/String;)V=|p_126546_,p_126547_,modId,existingFileHelper,folder net/minecraft/gametest/framework/GameTestRegistry.register(Ljava/lang/reflect/Method;)V=|p_177504_,allowedNamespaces net/minecraft/network/FriendlyByteBuf.writeItemStack(Lnet/minecraft/world/item/ItemStack;Z)Lnet/minecraft/network/FriendlyByteBuf;=|p_130056_,limitedTag net/minecraft/server/dedicated/DedicatedServer.sendMessage(Lnet/minecraft/network/chat/Component;Ljava/util/UUID;)V=|message,p_108776_ diff --git a/src/test/java/net/minecraftforge/debug/DeferredRegistryTest.java b/src/test/java/net/minecraftforge/debug/DeferredRegistryTest.java index 78445869f6..5dfc403eea 100644 --- a/src/test/java/net/minecraftforge/debug/DeferredRegistryTest.java +++ b/src/test/java/net/minecraftforge/debug/DeferredRegistryTest.java @@ -5,8 +5,11 @@ package net.minecraftforge.debug; +import java.util.Set; import java.util.function.Supplier; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -37,14 +40,15 @@ public class DeferredRegistryTest { private static final DeferredRegister BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MODID); private static final DeferredRegister ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MODID); - private static final DeferredRegister CUSTOMS = DeferredRegister.create(Custom.class, MODID); + private static final DeferredRegister CUSTOMS = DeferredRegister.create(new ResourceLocation(MODID, "test_registry"), MODID); private static final RegistryObject BLOCK = BLOCKS.register("test", () -> new Block(Block.Properties.of(Material.STONE))); private static final RegistryObject ITEM = ITEMS .register("test", () -> new BlockItem(BLOCK.get(), new Item.Properties().tab(CreativeModeTab.TAB_BUILDING_BLOCKS))); private static final RegistryObject CUSTOM = CUSTOMS.register("test", () -> new Custom(){}); - private static final Supplier> CUSTOM_REG = CUSTOMS.makeRegistry("test_registry", () -> - new RegistryBuilder().disableSaving().setMaxID(Integer.MAX_VALUE - 1) + private static final TagKey CUSTOM_TAG_KEY = CUSTOMS.createOptionalTagKey("test_tag", Set.of(CUSTOM)); + private static final Supplier> CUSTOM_REG = CUSTOMS.makeRegistry(Custom.class, () -> + new RegistryBuilder().disableSaving().setMaxID(Integer.MAX_VALUE - 1).hasTags() .onAdd((owner, stage, id, obj, old) -> LOGGER.info("Custom Added: " + id + " " + obj.foo())) ); diff --git a/src/test/java/net/minecraftforge/debug/DuplicateOptionalTagTest.java b/src/test/java/net/minecraftforge/debug/DuplicateOptionalTagTest.java index 0167ae0547..d9d1f104b3 100644 --- a/src/test/java/net/minecraftforge/debug/DuplicateOptionalTagTest.java +++ b/src/test/java/net/minecraftforge/debug/DuplicateOptionalTagTest.java @@ -7,20 +7,19 @@ package net.minecraftforge.debug; import com.google.common.collect.Sets; import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; -import net.minecraftforge.common.ForgeTagHandler; import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.common.Tags; import net.minecraftforge.event.server.ServerStartedEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.registries.ForgeRegistries; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.List; import java.util.Set; import java.util.function.Supplier; +import java.util.stream.Collectors; /** * Tests that the values for defaulted optional tags defined in multiple places are combined. @@ -41,46 +40,35 @@ public class DuplicateOptionalTagTest private static final Set> TAG_A_DEFAULTS = Set.of(Blocks.BEDROCK.delegate); private static final Set> TAG_B_DEFAULTS = Set.of(Blocks.WHITE_WOOL.delegate); - // TODO-PATCHING: fix or remove this accordingly with optional tags feature (specifically, default values for opt. tags) -// private static final Tags.IOptionalNamedTag TAG_A = ForgeTagHandler.createOptionalTag(ForgeRegistries.BLOCKS, TAG_NAME, -// TAG_A_DEFAULTS); -// private static final Tags.IOptionalNamedTag TAG_B = ForgeTagHandler.createOptionalTag(ForgeRegistries.BLOCKS, TAG_NAME, -// TAG_B_DEFAULTS); + private static final TagKey TAG_A = ForgeRegistries.BLOCKS.tags().createOptionalTagKey(TAG_NAME, TAG_A_DEFAULTS); + private static final TagKey TAG_B = ForgeRegistries.BLOCKS.tags().createOptionalTagKey(TAG_NAME, TAG_B_DEFAULTS); public DuplicateOptionalTagTest() { -// MinecraftForge.EVENT_BUS.addListener(this::onServerStarted); + MinecraftForge.EVENT_BUS.addListener(this::onServerStarted); } -// private void onServerStarted(ServerStartedEvent event) -// { -// if (!TAG_A.isDefaulted()) -// { -// LOGGER.warn("First instance of optional tag is not defaulted!"); -// } -// -// if (!TAG_B.isDefaulted()) -// { -// LOGGER.warn("Second instance of optional tag is not defaulted!"); -// } -// -// if (!TAG_A.getValues().equals(TAG_B.getValues())) -// { -// LOGGER.error("Values of both optional tag instances are not the same: first instance: {}, second instance: {}", -// TAG_A.getValues(), TAG_B.getValues()); -// return; -// } -// -// final List expected = Sets.union(TAG_A_DEFAULTS, TAG_B_DEFAULTS).stream() -// .map(Supplier::get) -// .toList(); -// if (!TAG_A.getValues().equals(expected)) -// { -// LOGGER.error("Values of the optional tag do not match the expected union of their defaults: expected {}, got {}", -// expected, TAG_A.getValues()); -// return; -// } -// -// LOGGER.info("Optional tag instances match each other and the expected union of their defaults"); -// } + private void onServerStarted(ServerStartedEvent event) + { + Set tagAValues = ForgeRegistries.BLOCKS.tags().getTag(TAG_A).stream().collect(Collectors.toUnmodifiableSet()); + Set tagBValues = ForgeRegistries.BLOCKS.tags().getTag(TAG_B).stream().collect(Collectors.toUnmodifiableSet()); + + if (!tagAValues.equals(tagBValues)) + { + LOGGER.error("Values of both optional tag instances are not the same: first instance: {}, second instance: {}", tagAValues, tagBValues); + return; + } + + final Set expected = Sets.union(TAG_A_DEFAULTS, TAG_B_DEFAULTS).stream() + .map(Supplier::get) + .collect(Collectors.toUnmodifiableSet()); + if (!tagAValues.equals(expected)) + { + IllegalStateException e = new IllegalStateException("Optional tag values do not match!"); + LOGGER.error("Values of the optional tag do not match the expected union of their defaults: expected {}, got {}", expected, tagAValues, e); + return; + } + + LOGGER.info("Optional tag instances match each other and the expected union of their defaults"); + } } diff --git a/src/test/java/net/minecraftforge/debug/RegistryObjectTest.java b/src/test/java/net/minecraftforge/debug/RegistryObjectTest.java index 416cdf4a96..28afe77ff2 100644 --- a/src/test/java/net/minecraftforge/debug/RegistryObjectTest.java +++ b/src/test/java/net/minecraftforge/debug/RegistryObjectTest.java @@ -41,6 +41,6 @@ public class RegistryObjectTest public void commonSetup(FMLCommonSetupEvent event) { LOGGER.info("Stone 1: {}", RegistryObject.of(new ResourceLocation("minecraft", "stone"), ForgeRegistries.BLOCKS).get()); - LOGGER.info("Stone 2: {}", RegistryObject.of(new ResourceLocation("minecraft", "stone"), Block.class, MODID).get()); + LOGGER.info("Stone 2: {}", RegistryObject.of(new ResourceLocation("minecraft", "stone"), ForgeRegistries.BLOCKS.getRegistryKey(), MODID).get()); } } diff --git a/src/test/java/net/minecraftforge/debug/entity/AddEntityAttributeTest.java b/src/test/java/net/minecraftforge/debug/entity/AddEntityAttributeTest.java index ea7fd445f2..39cd029db9 100644 --- a/src/test/java/net/minecraftforge/debug/entity/AddEntityAttributeTest.java +++ b/src/test/java/net/minecraftforge/debug/entity/AddEntityAttributeTest.java @@ -9,6 +9,7 @@ import net.minecraft.world.entity.ai.attributes.Attribute; import net.minecraft.world.entity.ai.attributes.RangedAttribute; import net.minecraftforge.event.entity.EntityAttributeModificationEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.RegistryObject; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; @@ -17,7 +18,7 @@ import net.minecraftforge.registries.DeferredRegister; @Mod("add_entity_attribute_test") public class AddEntityAttributeTest { public static final boolean ENABLE = true; - private static final DeferredRegister ATTRIBUTES = DeferredRegister.create(Attribute.class, "add_entity_attribute_test"); + private static final DeferredRegister ATTRIBUTES = DeferredRegister.create(ForgeRegistries.ATTRIBUTES, "add_entity_attribute_test"); public static final RegistryObject TEST_ATTR = ATTRIBUTES.register("test_attr", () -> new RangedAttribute("forge.test_attr", 1.0D, 0.0D, 1024.0D).setSyncable(true)); public AddEntityAttributeTest() { diff --git a/src/test/java/net/minecraftforge/debug/gameplay/loot/GlobalLootModifiersTest.java b/src/test/java/net/minecraftforge/debug/gameplay/loot/GlobalLootModifiersTest.java index 39045d2a4a..b503bc6e71 100644 --- a/src/test/java/net/minecraftforge/debug/gameplay/loot/GlobalLootModifiersTest.java +++ b/src/test/java/net/minecraftforge/debug/gameplay/loot/GlobalLootModifiersTest.java @@ -66,7 +66,7 @@ public class GlobalLootModifiersTest { } } - private static final DeferredRegister> GLM = DeferredRegister.create(ForgeRegistries.LOOT_MODIFIER_SERIALIZERS, MODID); + private static final DeferredRegister> GLM = DeferredRegister.create(ForgeRegistries.Keys.LOOT_MODIFIER_SERIALIZERS, MODID); private static final DeferredRegister ENCHANTS = DeferredRegister.create(ForgeRegistries.ENCHANTMENTS, MODID); private static final RegistryObject DUNGEON_LOOT = GLM.register("dungeon_loot", DungeonLootEnhancerModifier.Serializer::new); diff --git a/src/test/java/net/minecraftforge/debug/item/CustomShieldTest.java b/src/test/java/net/minecraftforge/debug/item/CustomShieldTest.java index 385b687fd0..b45ff354c4 100644 --- a/src/test/java/net/minecraftforge/debug/item/CustomShieldTest.java +++ b/src/test/java/net/minecraftforge/debug/item/CustomShieldTest.java @@ -17,13 +17,14 @@ import net.minecraftforge.common.ToolAction; import net.minecraftforge.common.ToolActions; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.RegistryObject; import net.minecraftforge.registries.DeferredRegister; @Mod("custom_shield_test") public class CustomShieldTest { - private static final DeferredRegister ITEMS = DeferredRegister.create(Item.class, "custom_shield_test"); + private static final DeferredRegister ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, "custom_shield_test"); private static final RegistryObject CUSTOM_SHIELD_ITEM = ITEMS.register("custom_shield", () -> new CustomShieldItem((new Item.Properties()).durability(336).tab(CreativeModeTab.TAB_COMBAT)));