Fabric API

Fabric API

108M Downloads

Cannot invoke "FluidRenderHandlerRegistry.get()" because "FluidRenderHandlerRegistry.INSTANCE" is null

Fourmisain opened this issue ยท 2 comments

commented

I ran into this weird issue that FluidRenderHandlerRegistry.INSTANCE is null when searching for water in REI with Fabric API 0.42.0 and I think I know why this is happening.

Here's the relevant code:

public interface FluidRenderHandlerRegistry {
FluidRenderHandlerRegistry INSTANCE = FluidRenderHandlerRegistryImpl.INSTANCE;

public class FluidRenderHandlerRegistryImpl implements FluidRenderHandlerRegistry {
public static final FluidRenderHandlerRegistryImpl INSTANCE = new FluidRenderHandlerRegistryImpl();

On first glance, this looks good, REI (Architectury, actually) calls FluidRenderHandlerRegistry.INSTANCE.get() which should statically initialize FluidRenderHandlerRegistry, which should then statically initialize FluidRenderHandlerRegistryImpl before setting the FluidRenderHandlerRegistry.INSTANCE field, so how can it be null?

This issue happens when FluidRenderHandlerRegistryImpl gets statically initialized first, because before Impl is getting initialized, the superinterface FluidRenderHandlerRegistry will be initialized (reference), so FluidRenderHandlerRegistry.INSTANCE will be set to FluidRenderHandlerRegistryImpl.INSTANCE, which at that point is null!

Okay, but why does this happen, where is Impl getting initialized?

There are currently only 4 references to Impl:

FluidRenderHandlerRegistryImpl.INSTANCE.onFluidRendererReload(self, waterSprites, lavaSprites, waterOverlaySprite);

FluidRenderHandler handler = FluidRenderHandlerRegistryImpl.INSTANCE.getOverride(state.getFluid());

if (FluidRenderHandlerRegistryImpl.INSTANCE.isBlockTransparent(block)) {

return FluidRenderHandlerRegistryImpl.INSTANCE.renderFluid(pos, world, vertexConsumer, state);

The first one of these will run on resource reload, so basically when reaching the title screen, this explains what I found while debugging: FluidRenderHandlerRegistry.INSTANCE.get() will work when run in the client mod initializer (and will fix the issue) but it won't work when run ingame.

A Bunch of Solutions

I think one quick way to fix this would be to eliminate any direct reference to FluidRenderHandlerRegistryImpl, e.g. by replacing
FluidRenderHandlerRegistryImpl.INSTANCE with (FluidRenderHandlerRegistryImpl) FluidRenderHandlerRegistry.INSTANCE, but this is a very brittle solution.

A more robust fix would be to replace the FluidRenderHandlerRegistry.INSTANCE field with a default getInstance() method, but this is also an API breaking change.

A non-API-breaking fix could be to remove the FluidRenderHandlerRegistryImpl.INSTANCE field altogether and create the instance directly in the interface - this will need a public Impl constructor.

A non-structural fix could be to statically initialize FluidRenderHandlerRegistry as early as possible e.g. running it in Fabric API's client mod initializer, but this could theoretically still break if e.g. another mod tries to access Impl even earlier (mod load order, hacks, etc).

commented

I can confirm this problem in the 1.18 snapshot version of the fabric api. The game doesn't load.

commented

A non-API-breaking fix could be to remove the FluidRenderHandlerRegistryImpl.INSTANCE field altogether and create the instance directly in the interface - this will need a public Impl constructor.

This seems like the best solution to me, most other APIs do this, while a small handful have this possibly breaking pattern.