diff --git a/build.gradle b/build.gradle index 080fba041a..4bf79c48ba 100644 --- a/build.gradle +++ b/build.gradle @@ -1040,6 +1040,7 @@ project(':forge') { '--mod', 'custom_tag_types_test', '--mod', 'new_model_loader_test', '--mod', 'remove_tag_datagen_test', + '--mod', 'tag_based_tool_types', '--output', rootProject.file('src/generated_test/resources/'), '--existing', sourceSets.main.resources.srcDirs[0], '--existing', sourceSets.test.resources.srcDirs[0] diff --git a/patches/minecraft/net/minecraft/core/Holder.java.patch b/patches/minecraft/net/minecraft/core/Holder.java.patch new file mode 100644 index 0000000000..02911ffd32 --- /dev/null +++ b/patches/minecraft/net/minecraft/core/Holder.java.patch @@ -0,0 +1,13 @@ +--- a/net/minecraft/core/Holder.java ++++ b/net/minecraft/core/Holder.java +@@ -194,6 +_,10 @@ + return this.f_205749_.stream(); + } + ++ public Type getType() { ++ return this.f_205750_; ++ } ++ + public String toString() { + return "Reference{" + this.f_205751_ + "=" + this.f_205752_ + "}"; + } diff --git a/patches/minecraft/net/minecraft/core/MappedRegistry.java.patch b/patches/minecraft/net/minecraft/core/MappedRegistry.java.patch index 1a316297e3..24f8ca9661 100644 --- a/patches/minecraft/net/minecraft/core/MappedRegistry.java.patch +++ b/patches/minecraft/net/minecraft/core/MappedRegistry.java.patch @@ -1,10 +1,11 @@ --- a/net/minecraft/core/MappedRegistry.java +++ b/net/minecraft/core/MappedRegistry.java -@@ -279,6 +_,13 @@ +@@ -279,6 +_,14 @@ return this.f_205842_.containsKey(p_175392_); } -+ // Forge: For internal use only. Use the Register events when registering values. ++ /** @Depreciated Forge: For internal use only. Use the Register events when registering values. */ ++ @Deprecated + public void unfreeze() { + this.f_205845_ = false; + if (this.f_205846_ != null && this.f_205847_ == null) diff --git a/patches/minecraft/net/minecraft/tags/TagLoader.java.patch b/patches/minecraft/net/minecraft/tags/TagLoader.java.patch new file mode 100644 index 0000000000..6509e76383 --- /dev/null +++ b/patches/minecraft/net/minecraft/tags/TagLoader.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/tags/TagLoader.java ++++ b/net/minecraft/tags/TagLoader.java +@@ -156,7 +_,7 @@ + p_203899_.keySet().forEach((p_144522_) -> { + m_144523_(p_203899_, multimap, set, p_144522_, (p_144537_, p_144538_) -> { + p_144538_.m_144371_(function, function1).ifLeft((p_144543_) -> { +- f_13445_.error("Couldn't load tag {} as it is missing following references: {}", p_144537_, p_144543_.stream().map(Objects::toString).collect(Collectors.joining(", "))); ++ f_13445_.error("Couldn't load tag {} as it is missing following references: {}", p_144537_, p_144543_.stream().map(Objects::toString).collect(Collectors.joining(", \n\t"))); + }).ifRight((p_144532_) -> { + map.put(p_144537_, p_144532_); + }); diff --git a/src/generated_test/resources/.cache/cache b/src/generated_test/resources/.cache/cache index d762f8cd43..30377b61fe 100644 --- a/src/generated_test/resources/.cache/cache +++ b/src/generated_test/resources/.cache/cache @@ -84,6 +84,10 @@ bab7cd04569c8aa63320772acdc3c91a5ceda14a assets/new_model_loader_test/models/blo d2c8e860521c5e738ed0798337f5728d610825ff assets/piston_event_test/models/block/shiftonmove.json f51f026a75e27a0a53a76771d553c0e3385a2966 assets/piston_event_test/models/item/shiftonmove.json cf16f861eaf5815238c2278eb48bde0688cb73b7 assets/scaffolding_test/blockstates/scaffolding_method_test.json +7b03828e34fedcdea09e689bea11e16fc849f080 assets/tag_based_tool_types/blockstates/test_stone.json +79167e317e2f247baca693404b657252157b2349 assets/tag_based_tool_types/models/block/test_stone.json +ba49dc1068fd9c93a458f4267896ea4476187411 assets/tag_based_tool_types/models/item/test_stone.json +15c4780e8ea8e13179a35f045ac7975c76f4471d assets/tag_based_tool_types/models/item/test_tool.json 806659ba3e9c12797df0fe7211a222795834fdfd data/data_gen_test/advancements/conditional.json 0001aa55877ddee50e3180db7337e926797d7e4d data/data_gen_test/advancements/obtain_dirt.json 76e760c8e8a1bbd3fb5538bc2d773c117016c0ce data/data_gen_test/advancements/recipes/conditional2.json @@ -102,3 +106,6 @@ be30fbd8ad753b35166ef534123df4e852515cc5 data/minecraft/advancements/good_parent d344b06ab85a35c448ea3fa1561c0be2978f3f1a data/minecraft/advancements/obtain_diamond_block.json 7c42eede6fe98d1291c00b084e4c1be8642102cd data/minecraft/advancements/story/root.json f10f0cf9694892eccfddfa67e0ef4aa1f24fb6db data/minecraft/tags/blocks/test_tag.json +04db4e62a40a90a43f53c0b971f5f8b33941e380 data/tag_based_tool_types/loot_tables/blocks/test_stone.json +ea47ac90d561c40f01a29110f0dbf189671d390e data/tag_based_tool_types/tags/blocks/minable/my_tool.json +ea47ac90d561c40f01a29110f0dbf189671d390e data/tag_based_tool_types/tags/blocks/needs_my_tier_tool.json diff --git a/src/generated_test/resources/assets/tag_based_tool_types/blockstates/test_stone.json b/src/generated_test/resources/assets/tag_based_tool_types/blockstates/test_stone.json new file mode 100644 index 0000000000..4331b202d8 --- /dev/null +++ b/src/generated_test/resources/assets/tag_based_tool_types/blockstates/test_stone.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "tag_based_tool_types:block/test_stone" + } + } +} \ No newline at end of file diff --git a/src/generated_test/resources/assets/tag_based_tool_types/models/block/test_stone.json b/src/generated_test/resources/assets/tag_based_tool_types/models/block/test_stone.json new file mode 100644 index 0000000000..1b45c04047 --- /dev/null +++ b/src/generated_test/resources/assets/tag_based_tool_types/models/block/test_stone.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { + "all": "minecraft:block/debug" + } +} \ No newline at end of file diff --git a/src/generated_test/resources/assets/tag_based_tool_types/models/item/test_stone.json b/src/generated_test/resources/assets/tag_based_tool_types/models/item/test_stone.json new file mode 100644 index 0000000000..e74daa5bae --- /dev/null +++ b/src/generated_test/resources/assets/tag_based_tool_types/models/item/test_stone.json @@ -0,0 +1,3 @@ +{ + "parent": "tag_based_tool_types:block/test_stone" +} \ No newline at end of file diff --git a/src/generated_test/resources/assets/tag_based_tool_types/models/item/test_tool.json b/src/generated_test/resources/assets/tag_based_tool_types/models/item/test_tool.json new file mode 100644 index 0000000000..fbf16c745d --- /dev/null +++ b/src/generated_test/resources/assets/tag_based_tool_types/models/item/test_tool.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "minecraft:item/wooden_pickaxe" + } +} \ No newline at end of file diff --git a/src/generated_test/resources/data/tag_based_tool_types/loot_tables/blocks/test_stone.json b/src/generated_test/resources/data/tag_based_tool_types/loot_tables/blocks/test_stone.json new file mode 100644 index 0000000000..d3be5d73ed --- /dev/null +++ b/src/generated_test/resources/data/tag_based_tool_types/loot_tables/blocks/test_stone.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1.0, + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "name": "tag_based_tool_types:test_stone" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/generated_test/resources/data/tag_based_tool_types/tags/blocks/minable/my_tool.json b/src/generated_test/resources/data/tag_based_tool_types/tags/blocks/minable/my_tool.json new file mode 100644 index 0000000000..ea937c7be7 --- /dev/null +++ b/src/generated_test/resources/data/tag_based_tool_types/tags/blocks/minable/my_tool.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "tag_based_tool_types:test_stone" + ] +} \ No newline at end of file diff --git a/src/generated_test/resources/data/tag_based_tool_types/tags/blocks/needs_my_tier_tool.json b/src/generated_test/resources/data/tag_based_tool_types/tags/blocks/needs_my_tier_tool.json new file mode 100644 index 0000000000..ea937c7be7 --- /dev/null +++ b/src/generated_test/resources/data/tag_based_tool_types/tags/blocks/needs_my_tier_tool.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "tag_based_tool_types:test_stone" + ] +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/registries/ForgeRegistry.java b/src/main/java/net/minecraftforge/registries/ForgeRegistry.java index 53ca032ed5..2ac5ff71e4 100644 --- a/src/main/java/net/minecraftforge/registries/ForgeRegistry.java +++ b/src/main/java/net/minecraftforge/registries/ForgeRegistry.java @@ -88,6 +88,7 @@ public class ForgeRegistry> implements IForgeRe private final Codec codec = new RegistryCodec(); + @SuppressWarnings("unchecked") ForgeRegistry(RegistryManager stage, ResourceLocation name, RegistryBuilder builder) { this.name = name; @@ -209,6 +210,11 @@ public class ForgeRegistry> implements IForgeRe return this.names.isEmpty(); } + int size() + { + return this.names.size(); + } + @Override public V getValue(ResourceLocation key) { @@ -392,12 +398,6 @@ public class ForgeRegistry> implements IForgeRe } } - if (this.builder.getVanillaHolder() != null) - { - var ref = this.builder.getVanillaHolder().apply(value); - ref.bind(rkey, value); - } - if (this.add != null) this.add.onAdd(this, this.stage, idToUse, value, oldEntry); diff --git a/src/main/java/net/minecraftforge/registries/NamespacedDefaultedWrapper.java b/src/main/java/net/minecraftforge/registries/NamespacedDefaultedWrapper.java index 5b56dc0ac2..809b68f482 100644 --- a/src/main/java/net/minecraftforge/registries/NamespacedDefaultedWrapper.java +++ b/src/main/java/net/minecraftforge/registries/NamespacedDefaultedWrapper.java @@ -6,35 +6,43 @@ package net.minecraftforge.registries; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.OptionalInt; +import java.util.Random; import java.util.Set; import java.util.function.Function; +import java.util.stream.Stream; import javax.annotation.Nullable; import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.core.Registry; + import org.apache.commons.lang3.Validate; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; import net.minecraft.core.DefaultedRegistry; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - +import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Lifecycle; class NamespacedDefaultedWrapper> extends DefaultedRegistry implements ILockableRegistry { - private static final Logger LOGGER = LogManager.getLogger(); + private final ForgeRegistry delegate; + private final NamespacedHolderHelper holders; + private boolean locked = false; - private ForgeRegistry delegate; + private Lifecycle elementsLifecycle = Lifecycle.experimental(); private NamespacedDefaultedWrapper(ForgeRegistry owner, Function> holderLookup) { super("empty", owner.getRegistryKey(), Lifecycle.experimental(), holderLookup); this.delegate = owner; + this.holders = new NamespacedHolderHelper<>(this, this.delegate.getDefaultKey(), holderLookup); } @Override @@ -42,16 +50,18 @@ class NamespacedDefaultedWrapper> extends Defau { if (locked) throw new IllegalStateException("Can not register to a locked registry. Modder should use Forge Register methods."); + Validate.notNull(value); + this.elementsLifecycle = this.elementsLifecycle.add(lifecycle); if (value.getRegistryName() == null) value.setRegistryName(key.location()); - int realId = this.delegate.add(id, value); - if (realId != id && id != -1) - LOGGER.warn("Registered object did not get ID it asked for. Name: {} Type: {} Expected: {} Got: {}", key, value.getRegistryType().getName(), id, realId); + T oldValue = this.delegate.getRaw(value.getRegistryName()); - return super.registerMapping(realId, key, value, lifecycle); + int realId = this.delegate.add(id, value); + + return this.holders.onAdded(RegistryManager.ACTIVE, realId, value, oldValue); } @Override @@ -109,6 +119,12 @@ class NamespacedDefaultedWrapper> extends Defau return this.delegate.containsKey(key); } + @Override + public boolean containsKey(ResourceKey key) + { + return this.delegate.getRegistryName().equals(key.registry()) && containsKey(key.location()); + } + @Override public int getId(@Nullable T value) { @@ -123,10 +139,23 @@ class NamespacedDefaultedWrapper> extends Defau } @Override - public Lifecycle lifecycle(T p_122764_) { + public ResourceLocation getDefaultKey() + { + return this.delegate.getDefaultKey(); + } + + @Override + public Lifecycle lifecycle(T value) + { return Lifecycle.stable(); } + @Override + public Lifecycle elementsLifecycle() + { + return this.elementsLifecycle; + } + @Override public Iterator iterator() { @@ -145,28 +174,54 @@ class NamespacedDefaultedWrapper> extends Defau return this.delegate.getEntries(); } - //TODO-PATCHING: Figure out if getRandom needs to be overriden. - @Override - public ResourceLocation getDefaultKey() + public boolean isEmpty() { - return this.delegate.getDefaultKey(); + return this.delegate.isEmpty(); } - //internal @Override - public void lock(){ this.locked = true; } + public int size() + { + return this.delegate.size(); + } - //@Override public void unfreeze() {} + @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); } + @Override public Optional> getRandom(Random rand) { return this.holders.getRandom(rand); } + @Override public Stream> holders() { return this.holders.holders(); } + @Override public boolean isKnownTagName(TagKey name) { return this.holders.isKnownTagName(name); } + @Override public Stream, HolderSet.Named>> getTags() { return this.holders.getTags(); } + @Override public HolderSet.Named getOrCreateTag(TagKey name) { return this.holders.getOrCreateTag(name); } + @Override public Stream> getTagNames() { return this.holders.getTagNames(); } + @Override public Registry freeze() { return this.holders.freeze(); } + @Override public Holder.Reference createIntrusiveHolder(T value) { return this.holders.createIntrusiveHolder(value); } + @Override public Optional> getTag(TagKey name) { return this.holders.getTag(name); } + @Override public void bindTags(Map, List>> newTags) { this.holders.bindTags(newTags); } + @Override public void resetTags() { this.holders.resetTags(); } + @Deprecated @Override public void unfreeze() { this.holders.unfreeze(); } - public static class Factory> implements IForgeRegistry.CreateCallback + /** @Depreciated Forge: For internal use only. Use the Register events when registering values. */ + @Deprecated @Override public void lock(){ this.locked = true; } + + + public static class Factory> implements IForgeRegistry.CreateCallback, IForgeRegistry.AddCallback { public static final ResourceLocation ID = new ResourceLocation("forge", "registry_defaulted_wrapper"); + @Override public void onCreate(IForgeRegistryInternal owner, RegistryManager stage) { ForgeRegistry fowner = (ForgeRegistry)owner; owner.setSlaveMap(ID, new NamespacedDefaultedWrapper(fowner, fowner.getBuilder().getVanillaHolder())); } + + @Override + @SuppressWarnings("unchecked") + public void onAdd(IForgeRegistryInternal owner, RegistryManager stage, int id, V value, V oldValue) + { + owner.getSlaveMap(ID, NamespacedDefaultedWrapper.class).holders.onAdded(stage, id, value, oldValue); + } } } diff --git a/src/main/java/net/minecraftforge/registries/NamespacedHolderHelper.java b/src/main/java/net/minecraftforge/registries/NamespacedHolderHelper.java new file mode 100644 index 0000000000..530d9cfdc5 --- /dev/null +++ b/src/main/java/net/minecraftforge/registries/NamespacedHolderHelper.java @@ -0,0 +1,252 @@ +/* + * Minecraft Forge - Forge Development LLC + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.registries; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Random; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.annotation.Nullable; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import com.google.common.collect.Sets; +import com.mojang.datafixers.util.Pair; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectList; +import net.minecraft.Util; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; + +class NamespacedHolderHelper> +{ + private static final Logger LOGGER = LogManager.getLogger(); + private final Registry self; + @Nullable + private final ResourceLocation defaultKey; + @Nullable + private final Function> holderLookup; + + private boolean frozen = false; // Frozen is vanilla's variant of locked, but it can be unfrozen + private List> holdersSorted; + private ObjectList> holdersById = new ObjectArrayList<>(256); + private Map> holdersByName = new HashMap<>(); + private Map> holders = new IdentityHashMap<>(); + private volatile Map, HolderSet.Named> tags = new IdentityHashMap<>(); + private Holder.Reference defaultHolder; + + NamespacedHolderHelper(Registry self, @Nullable ResourceLocation defaultKey, @Nullable Function> holderLookup) + { + this.self = self; + this.defaultKey = defaultKey; + this.holderLookup = holderLookup; + } + + + Optional> getHolder(int id) + { + return id >= 0 && id < this.holdersById.size() ? Optional.ofNullable(this.holdersById.get(id)) : Optional.empty(); + } + + Optional> getHolder(ResourceKey key) + { + return Optional.ofNullable(this.holdersByName.get(key.location())); + } + + Holder getOrCreateHolder(ResourceKey key) + { + return this.holdersByName.computeIfAbsent(key.location(), k -> + { + if (this.holderLookup != null) + throw new IllegalStateException("This registry can't create new holders without value"); + if (this.frozen) + throw new IllegalStateException("Registry is already frozen (trying to add key " + k + ")"); + return Holder.Reference.createStandAlone(this.self, key); + }); + } + + Optional> getRandom(Random rand) + { + Optional> ret = Util.getRandomSafe(this.getSortedHolders(), rand).map(Holder::hackyErase); + if (this.defaultKey != null) + ret = ret.or(() -> Optional.of(this.defaultHolder)); + return ret; + } + + Stream> holders() + { + return this.getSortedHolders().stream(); + } + + boolean isKnownTagName(TagKey name) + { + return this.tags.containsKey(name); + } + + Stream, HolderSet.Named>> getTags() + { + return this.tags.entrySet().stream().map(e -> Pair.of(e.getKey(), e.getValue())); + } + + HolderSet.Named getOrCreateTag(TagKey name) + { + HolderSet.Named named = this.tags.get(name); + if (named == null) + { + named = createTag(name); + // They use volatile and set it this way to not have the performance penalties of synced read access. But this makes a lot of new maps.. We need to look into performance alternatives. + Map, HolderSet.Named> map = new IdentityHashMap<>(this.tags); + map.put(name, named); + this.tags = map; + } + + return named; + } + + Stream> getTagNames() + { + return this.tags.keySet().stream(); + } + + //TODO: Move this to ValidateCallback? + Registry freeze() + { + this.frozen = true; + List unregisterd = this.holdersByName.entrySet().stream() + .filter(e -> !e.getValue().isBound()) + .map(e -> e.getKey()).sorted().toList(); + + if (!unregisterd.isEmpty()) + throw new IllegalStateException("Unbound values in registry " + this.self.key() + ": " + unregisterd.stream().map(ResourceLocation::toString).collect(Collectors.joining(", \n\t"))); + + if (this.holderLookup != null) + { + List> intrusive = this.holders.values().stream().filter(h -> h.getType() == Holder.Reference.Type.INTRUSIVE && !h.isBound()).toList(); + if (!intrusive.isEmpty()) + throw new IllegalStateException("Some intrusive holders were not added to registry: " + intrusive); + } + return this.self; + } + + @SuppressWarnings("deprecation") + Holder.Reference createIntrusiveHolder(T value) + { + if (this.holderLookup == null) + throw new IllegalStateException("This registry can't create intrusive holders"); + if (this.frozen) + throw new IllegalStateException("Registry is already frozen"); + + return this.holders.computeIfAbsent(value, k -> Holder.Reference.createIntrusive(this.self, value)); + } + + Optional> getTag(TagKey name) + { + return Optional.ofNullable(this.tags.get(name)); + } + + void bindTags(Map, List>> newTags) + { + 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); + } + }); + + Set> set = Sets.difference(this.tags.keySet(), newTags.keySet()); + if (!set.isEmpty()) + LOGGER.warn("Not all defined tags for registry {} are present in data pack: {}", this.self.key(), set.stream().map(k -> k.location().toString()).sorted().collect(Collectors.joining(", \n\t"))); + + Map, HolderSet.Named> tmpTags = new IdentityHashMap<>(this.tags); + newTags.forEach((k, v) -> tmpTags.computeIfAbsent(k, this::createTag).bind(v)); + holderToTag.forEach(Holder.Reference::bindTags); + this.tags = tmpTags; + } + + void resetTags() + { + this.tags.values().forEach(t -> t.bind(List.of())); + this.holders.values().forEach(v -> v.bindTags(Set.of())); + } + + void unfreeze() + { + this.frozen = false; + } + + @Nullable + Holder onAdded(RegistryManager stage, int id, T newValue, T oldValue) + { + if (stage != RegistryManager.ACTIVE) + return null; // Only do holder shit on the active registries, not pending or snapshots. + + ResourceKey key = ResourceKey.create(this.self.key(), newValue.getRegistryName()); + + //Holder.Reference oldHolder = oldValue == null ? null : getHolder(key, oldValue); + // Do we need to do anything with the old holder? We cant update its pointer unless its non-intrusive... + // And if thats the case, then we *should* get its reference in newHolder + + Holder.Reference newHolder = getHolder(key, newValue); + + this.holdersById.size(Math.max(this.holdersById.size(), id + 1)); + this.holdersById.set(id, newHolder); + this.holdersByName.put(newValue.getRegistryName(), newHolder); + this.holders.put(newValue, newHolder); + newHolder.bind(key, newValue); + this.holdersSorted = null; + + + if (this.defaultKey != null && this.defaultKey.equals(key.location())) + this.defaultHolder = newHolder; + + return newHolder; + } + + private HolderSet.Named createTag(TagKey name) + { + return new HolderSet.Named<>(this.self, name); + } + + + private Holder.Reference getHolder(ResourceKey key, T value) + { + if (this.holderLookup != null) + return this.holderLookup.apply(value); + + return this.holdersByName.computeIfAbsent(value.getRegistryName(), k -> Holder.Reference.createStandAlone(this.self, key)); + } + + private List> getSortedHolders() + { + if (this.holdersSorted == null) + this.holdersSorted = this.holdersById.stream().filter(Objects::nonNull).toList(); + + return this.holdersSorted; + } + +} diff --git a/src/main/java/net/minecraftforge/registries/NamespacedWrapper.java b/src/main/java/net/minecraftforge/registries/NamespacedWrapper.java index 566a25dcb6..cea9721592 100644 --- a/src/main/java/net/minecraftforge/registries/NamespacedWrapper.java +++ b/src/main/java/net/minecraftforge/registries/NamespacedWrapper.java @@ -6,33 +6,46 @@ package net.minecraftforge.registries; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.OptionalInt; +import java.util.Random; import java.util.Set; import java.util.function.Function; +import java.util.stream.Stream; + import javax.annotation.Nullable; import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; import net.minecraft.core.MappedRegistry; +import net.minecraft.core.Registry; + import org.apache.commons.lang3.Validate; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Lifecycle; class NamespacedWrapper> extends MappedRegistry implements ILockableRegistry { private static final Logger LOGGER = LogManager.getLogger(); + private final ForgeRegistry delegate; + private final NamespacedHolderHelper holders; + private boolean locked = false; - private ForgeRegistry delegate; + private Lifecycle elementsLifecycle = Lifecycle.experimental(); public NamespacedWrapper(ForgeRegistry owner, Function> holderLookup) { super(owner.getRegistryKey(), Lifecycle.experimental(), holderLookup); this.delegate = owner; + this.holders = new NamespacedHolderHelper<>(this, null, holderLookup); } @Override @@ -42,14 +55,18 @@ class NamespacedWrapper> extends MappedRegistry throw new IllegalStateException("Can not register to a locked registry. Modder should use Forge Register methods."); Validate.notNull(value); + this.elementsLifecycle = this.elementsLifecycle.add(lifecycle); if (value.getRegistryName() == null) value.setRegistryName(key.location()); + T oldValue = get(value.getRegistryName()); + int realId = this.delegate.add(id, value); if (realId != id && id != -1) LOGGER.warn("Registered object did not get ID it asked for. Name: {} Type: {} Expected: {} Got: {}", key, value.getRegistryType().getName(), id, realId); - return super.registerMapping(realId, key, value, lifecycle); + + return this.holders.onAdded(RegistryManager.ACTIVE, realId, value, oldValue); } @Override @@ -106,6 +123,12 @@ class NamespacedWrapper> extends MappedRegistry return this.delegate.containsKey(key); } + @Override + public boolean containsKey(ResourceKey key) + { + return this.delegate.getRegistryName().equals(key.registry()) && containsKey(key.location()); + } + @Override public int getId(@Nullable T value) { @@ -120,10 +143,17 @@ class NamespacedWrapper> extends MappedRegistry } @Override - public Lifecycle lifecycle(T p_122764_) { + public Lifecycle lifecycle(T value) + { return Lifecycle.stable(); } + @Override + public Lifecycle elementsLifecycle() + { + return this.elementsLifecycle; + } + @Override public Iterator iterator() { @@ -142,30 +172,54 @@ class NamespacedWrapper> extends MappedRegistry return this.delegate.getEntries(); } - // TODO-PATCHING: Figure out if getRandom needs to be overriden. - - /* @Override public boolean isEmpty() { return this.delegate.isEmpty(); } - */ - //internal @Override - public void lock(){ this.locked = true; } + public int size() + { + return this.delegate.size(); + } - //@Override public void unfreeze() {} + @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); } + @Override public Optional> getRandom(Random rand) { return this.holders.getRandom(rand); } + @Override public Stream> holders() { return this.holders.holders(); } + @Override public boolean isKnownTagName(TagKey name) { return this.holders.isKnownTagName(name); } + @Override public Stream, HolderSet.Named>> getTags() { return this.holders.getTags(); } + @Override public HolderSet.Named getOrCreateTag(TagKey name) { return this.holders.getOrCreateTag(name); } + @Override public Stream> getTagNames() { return this.holders.getTagNames(); } + @Override public Registry freeze() { return this.holders.freeze(); } + @Override public Holder.Reference createIntrusiveHolder(T value) { return this.holders.createIntrusiveHolder(value); } + @Override public Optional> getTag(TagKey name) { return this.holders.getTag(name); } + @Override public void bindTags(Map, List>> newTags) { this.holders.bindTags(newTags); } + @Override public void resetTags() { this.holders.resetTags(); } + @Deprecated @Override public void unfreeze() { this.holders.unfreeze(); } - public static class Factory> implements IForgeRegistry.CreateCallback + /** @Depreciated Forge: For internal use only. Use the Register events when registering values. */ + @Deprecated @Override public void lock(){ this.locked = true; } + + + public static class Factory> implements IForgeRegistry.CreateCallback, IForgeRegistry.AddCallback { public static final ResourceLocation ID = new ResourceLocation("forge", "registry_defaulted_wrapper"); + @Override public void onCreate(IForgeRegistryInternal owner, RegistryManager stage) { ForgeRegistry fowner = (ForgeRegistry)owner; owner.setSlaveMap(ID, new NamespacedWrapper(fowner, fowner.getBuilder().getVanillaHolder())); } + + @Override + @SuppressWarnings("unchecked") + public void onAdd(IForgeRegistryInternal owner, RegistryManager stage, int id, V value, V oldValue) + { + owner.getSlaveMap(ID, NamespacedWrapper.class).holders.onAdded(stage, id, value, oldValue); + } } } diff --git a/src/main/java/net/minecraftforge/registries/RegistryBuilder.java b/src/main/java/net/minecraftforge/registries/RegistryBuilder.java index 08cce7aaab..19cd3446a9 100644 --- a/src/main/java/net/minecraftforge/registries/RegistryBuilder.java +++ b/src/main/java/net/minecraftforge/registries/RegistryBuilder.java @@ -382,4 +382,9 @@ public class RegistryBuilder> { return this.vanillaHolder; } + + boolean getHasWrapper() + { + return this.hasWrapper; + } } diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 126a1a8f0a..519a7ea8b6 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -137,7 +137,11 @@ public net.minecraft.client.sounds.SoundEngine f_120217_ # soundManager public net.minecraft.commands.arguments.selector.EntitySelectorParser m_121229_()V # finalizePredicates public net.minecraft.commands.arguments.selector.EntitySelectorParser m_121317_()V # parseOptions public net.minecraft.commands.arguments.selector.options.EntitySelectorOptions m_121453_(Ljava/lang/String;Lnet/minecraft/commands/arguments/selector/options/EntitySelectorOptions$Modifier;Ljava/util/function/Predicate;Lnet/minecraft/network/chat/Component;)V # register +public net.minecraft.core.Holder$Reference m_205769_(Ljava/util/Collection;)V # bindTags public net.minecraft.core.Holder$Reference m_205775_(Lnet/minecraft/resources/ResourceKey;Ljava/lang/Object;)V # bind +public net.minecraft.core.Holder$Reference$Type +public net.minecraft.core.HolderSet$Named (Lnet/minecraft/core/Registry;Lnet/minecraft/tags/TagKey;)V # constructor +public net.minecraft.core.HolderSet$Named m_205835_(Ljava/util/List;)V # bind protected net.minecraft.core.IdMapper f_122653_ # nextId protected net.minecraft.core.IdMapper f_122654_ # tToId - internal map protected net.minecraft.core.IdMapper f_122655_ # idToT - internal index list diff --git a/src/test/java/net/minecraftforge/debug/item/TagBasedToolTypesTest.java b/src/test/java/net/minecraftforge/debug/item/TagBasedToolTypesTest.java index 35031565b3..4a19e46bed 100644 --- a/src/test/java/net/minecraftforge/debug/item/TagBasedToolTypesTest.java +++ b/src/test/java/net/minecraftforge/debug/item/TagBasedToolTypesTest.java @@ -5,39 +5,74 @@ package net.minecraftforge.debug.item; +import net.minecraft.data.DataGenerator; +import net.minecraft.data.loot.BlockLoot; +import net.minecraft.data.loot.LootTableProvider; +import net.minecraft.data.tags.BlockTagsProvider; import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.BlockTags; -import net.minecraft.tags.Tag; import net.minecraft.tags.TagKey; import net.minecraft.world.item.*; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.common.ForgeHooks; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.level.material.MaterialColor; +import net.minecraft.world.level.storage.loot.LootTable; +import net.minecraft.world.level.storage.loot.LootTables; +import net.minecraft.world.level.storage.loot.ValidationContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParamSet; +import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets; +import net.minecraftforge.client.model.generators.BlockStateProvider; +import net.minecraftforge.client.model.generators.ItemModelProvider; +import net.minecraftforge.client.model.generators.ModelFile; +import net.minecraftforge.client.model.generators.ModelFile.UncheckedModelFile; import net.minecraftforge.common.ForgeTier; import net.minecraftforge.common.TierSortingRegistry; -import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.common.data.ExistingFileHelper; +import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.forge.event.lifecycle.GatherDataEvent; +import net.minecraftforge.registries.DeferredRegister; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.RegistryObject; -import javax.annotation.Nullable; import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.stream.Collectors; -@Mod("tag_based_tool_types") -@Mod.EventBusSubscriber(bus= Mod.EventBusSubscriber.Bus.MOD) +import com.google.common.collect.ImmutableList; +import com.mojang.datafixers.util.Pair; + +@Mod(TagBasedToolTypesTest.MODID) public class TagBasedToolTypesTest { - public static final TagKey MY_TOOL_TAG = BlockTags.create(new ResourceLocation("tag_based_tool_types:minable/my_tool")); - public static final TagKey MY_TIER_TAG = BlockTags.create(new ResourceLocation("tag_based_tool_types:needs_my_tier_tool")); - public static final Tier MY_TIER = TierSortingRegistry.registerTier( - new ForgeTier(5, 5000, 10, 100, 0, MY_TIER_TAG, () -> Ingredient.of(Items.BEDROCK)), - new ResourceLocation("tag_based_tool_types:my_tier"), - List.of(Tiers.DIAMOND), List.of()); + static final String MODID = "tag_based_tool_types"; - @SubscribeEvent - public static void registerItems(RegistryEvent.Register event) + private static final TagKey MINEABLE_TAG = BlockTags.create(new ResourceLocation(MODID, "minable/my_tool")); + private static final TagKey REQUIRES_TAG = BlockTags.create(new ResourceLocation(MODID, "needs_my_tier_tool")); + + private static final Tier MY_TIER = TierSortingRegistry.registerTier( + new ForgeTier(5, 5000, 10, 100, 0, REQUIRES_TAG, () -> Ingredient.of(Items.BEDROCK)), + new ResourceLocation(MODID, "my_tier"), + List.of(Tiers.DIAMOND), List.of() + ); + + private static final DeferredRegister BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MODID); + private static final RegistryObject STONE = BLOCKS.register("test_stone", () -> new Block(BlockBehaviour.Properties.of(Material.STONE, MaterialColor.DIRT).requiresCorrectToolForDrops().strength(1.5F, 6.0F))); + + private static final DeferredRegister ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MODID); + @SuppressWarnings("unused") + private static final RegistryObject ORE_ITEM = ITEMS.register(STONE.getId().getPath(), () -> new BlockItem(STONE.get(), (new Item.Properties()).tab(CreativeModeTab.TAB_BUILDING_BLOCKS))); + private static final RegistryObject TOOL = ITEMS.register("test_tool", () -> { - event.getRegistry().register(new DiggerItem(1, 1, MY_TIER, MY_TOOL_TAG, new Item.Properties().tab(CreativeModeTab.TAB_TOOLS)) + return new DiggerItem(1, 1, MY_TIER, MINEABLE_TAG, new Item.Properties().tab(CreativeModeTab.TAB_TOOLS)) { @Override public float getDestroySpeed(ItemStack stack, BlockState state) @@ -54,10 +89,94 @@ public class TagBasedToolTypesTest return TierSortingRegistry.isCorrectTierForDrops(Tiers.WOOD, state); if (state.is(BlockTags.MINEABLE_WITH_AXE)) return TierSortingRegistry.isCorrectTierForDrops(MY_TIER, state); - if (state.is(MY_TOOL_TAG)) + if (state.is(MINEABLE_TAG)) return TierSortingRegistry.isCorrectTierForDrops(MY_TIER, state); return false; } - }.setRegistryName("test_item")); + }; + }); + + public TagBasedToolTypesTest() + { + final IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); + BLOCKS.register(modEventBus); + ITEMS.register(modEventBus); + modEventBus.addListener(this::gatherData); + } + + @SubscribeEvent + public void gatherData(GatherDataEvent event) + { + DataGenerator gen = event.getGenerator(); + ExistingFileHelper existing = event.getExistingFileHelper(); + + if (event.includeServer()) + { + gen.addProvider(new BlockTagsProvider(gen, MODID, existing) + { + @Override + protected void addTags() + { + this.tag(MINEABLE_TAG).add(STONE.get()); + this.tag(REQUIRES_TAG).add(STONE.get()); + } + }); + + gen.addProvider(new LootTableProvider(gen) + { + @Override + protected void validate(Map map, ValidationContext validationtracker) + { + map.forEach((name, table) -> LootTables.validate(validationtracker, name, table)); + } + + @Override + protected List>>, LootContextParamSet>> getTables() + { + return ImmutableList.of(Pair.of(() -> + { + return new BlockLoot() + { + + @Override + protected Iterable getKnownBlocks() + { + return BLOCKS.getEntries().stream().map(Supplier::get).collect(Collectors.toList()); + } + + @Override + protected void addTables() + { + this.dropSelf(STONE.get()); + } + }; + }, LootContextParamSets.BLOCK)); + } + }); + } + + if (event.includeClient()) + { + gen.addProvider(new BlockStateProvider(gen, MODID, existing) + { + @Override + protected void registerStatesAndModels() + { + ModelFile model = models().cubeAll(STONE.get().getRegistryName().getPath(), mcLoc("block/debug")); + simpleBlock(STONE.get(), model); + simpleBlockItem(STONE.get(), model); + } + }); + gen.addProvider(new ItemModelProvider(gen, MODID, existing) + { + @Override + protected void registerModels() + { + getBuilder(TOOL.get().getRegistryName().getPath()) + .parent(new UncheckedModelFile("item/generated")) + .texture("layer0", mcLoc("item/wooden_pickaxe")); + } + }); + } } }