Fabric API

Fabric API

106M Downloads

Tag.contains() doesn't work with dynamic registry tags

YanisBft opened this issue ยท 7 comments

commented

Here's a simple test:

  1. Write the following code
Tag<Biome> biomeTag = TagFactory.BIOME.create(new Identifier("modid", "test"));
System.out.println(biomeTag.values().size()); //expected "1"

Biome biome1 = BuiltinRegistries.BIOME.get(BiomeKeys.BADLANDS); //in the tag
Biome biome2 = BuiltinRegistries.BIOME.get(BiomeKeys.JUNGLE); //not in the tag
System.out.println(biomeTag.contains(biome1)); //expected "true"
System.out.println(biomeTag.contains(biome2)); //expected "false"

System.out.println(biomeTag.values().get(0)); //expected "minecraft:badlands"
//see Biome's implementation of toString()
  1. Put the test.json tag file in the right place
{
  "replace": false,
  "values": [
    "minecraft:badlands"
  ]
}
  1. Read the output
    image
commented

Seems to be an issue with all dynamic registries: a tag factory for configured features has the same issue, while a tag factory for enchantments works fine

commented

You can't use the static registries for world gen objects that can come from datapacks.
Minecraft makes copies of them to use at runtime.

You need to use the version that comes from:

Registry<Biome> registry = minecraftServer.getRegistryManager().get(Registry.BIOME_KEY);

or something like that (untested code).

commented

It worked, thanks!
Though idk if I let this issue open

commented

We need some protection mechanism against this. Perhaps we could throw an error if the wrong registry is used? Clearly this kind of issue will be frequent if we don't do anything about it.

commented

To be honest, having to use the actual dynamic registry to get the references sounds the intended behavior to me.

commented

That sounds like an expensive check for something that is used a lot.

Maybe you could do something like the following for SetTag:

@Inject(method = "contains", at = @At("RETURN"), cancellable=true)
public void contains(T entry, CallbackInfoReturnable<Boolean> callback) {

    // Trap misses in development environments and check it is not using wrong registry 
    if (isDevelopment() && callback.getReturnValue() == Boolean.FALSE) {

        // See if the object has dynamic registry overrides
        Registry<?> registry = determineStaticRegistryForObject(entry.getClass()); 

        // Check its not an object from the static registry
        if (registry != null && registry.contains(entry)) { // There is no such method
            throw new RuntimeException("Use of static registry for object that has dynamic overrides: " + entry);
        }
    }
}

The idea being the error will be trapped at development time and not introduce overhead in production.

commented

This issue is no longer applicable as of Minecraft 1.18.2.