Tag.contains() doesn't work with dynamic registry tags
YanisBft opened this issue ยท 7 comments
Here's a simple test:
- 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()
- Put the
test.json
tag file in the right place
{
"replace": false,
"values": [
"minecraft:badlands"
]
}
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
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).
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.
To be honest, having to use the actual dynamic registry to get the references sounds the intended behavior to me.
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.