Jump to content

Recommended Posts

Posted (edited)

Hello, I'm working on implementing a capability for living entities for a mod I'm working on and I've run into some trouble. I believe I've gotten most of it right, from what I've gathered from other threads here.

 

The trouble I'm running into is when the AttachCapabilitiesEvent<Entity> I'm hooking into fires. When that event fires, I'm calling event.addCapability(), and the game crashes while it's attempting to create the new instance of my capability provider (ICapabilitySerializable), throwing a null pointer exception.

 

Here, you can see my event listener for the AttachCapabilitiesEvent<Entity>, as well as the registration for my capability:

@SubscribeEvent
public static void onCommonSetup(FMLCommonSetupEvent event) {
    CapabilityManager.INSTANCE.register(ILifeForce.class, new LifeForceStorage(), LifeForce::new);
}

@SubscribeEvent
public static void onAttachCapabilities(AttachCapabilitiesEvent<Entity> event) {
    if(event.getObject() instanceof LivingEntity) {
        event.addCapability(new ResourceLocation(LifePower.MODID, "lifeforce"), new LifeForceProvider());
        event.getObject().getCapability(LifeForceProvider.LIFE_FORCE_CAP).ifPresent((capability) -> {
            capability.set(Math.round(((LivingEntity) event.getObject()).getHealth()));
        });
    }
}

 

Here's the gist of the crash report:

Description: Ticking memory connection

java.lang.NullPointerException: Ticking memory connection
	at com.networkoverflow.lifepower.content.capabilities.LifeForceProvider.<init>(LifeForceProvider.java:14) ~[classes/:?] {pl:capability_inject_definalize:A}
	at com.networkoverflow.lifepower.content.capabilities.CapabilityHandler.onAttachCapabilities(CapabilityHandler.java:25) ~[classes/:?] {}
	at net.minecraftforge.eventbus.ASMEventHandler_0_CapabilityHandler_onAttachCapabilities_AttachCapabilitiesEvent.invoke(.dynamic) ~[?:?] {}

(Full Crash Report)

 

Here is my LifeForceProvider.java (with line 14, the crash line, marked):

package com.networkoverflow.lifepower.content.capabilities;

import net.minecraft.nbt.INBT;
import net.minecraft.util.Direction;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.CapabilityInject;
import net.minecraftforge.common.capabilities.ICapabilitySerializable;
import net.minecraftforge.common.util.LazyOptional;

public class LifeForceProvider implements ICapabilitySerializable<INBT> {
    @CapabilityInject(ILifeForce.class)
    public static final Capability<ILifeForce> LIFE_FORCE_CAP = null;

    private LazyOptional<ILifeForce> instance = LazyOptional.of(LIFE_FORCE_CAP::getDefaultInstance); //THIS IS WHAT CRASHES <-----

    @Override
    public <T> LazyOptional<T> getCapability(Capability<T> capability, Direction direction) {
        return capability == LIFE_FORCE_CAP ? instance.cast() : LazyOptional.empty();
    }

    @Override
    public INBT serializeNBT() {
        return LIFE_FORCE_CAP.getStorage().writeNBT(LIFE_FORCE_CAP, this.instance.orElseThrow(() -> new IllegalArgumentException("LazyOptional must not be empty!")), null);
    }

    @Override
    public void deserializeNBT(INBT nbt) {
        LIFE_FORCE_CAP.getStorage().readNBT(LIFE_FORCE_CAP, this.instance.orElseThrow(() -> new IllegalArgumentException("LazyOptional must not be empty!")), null, nbt);
    }
}

 

So, as you can see, the LazyOptional.of() that I'm calling is raising a NullPointerException, which I think refers to LIFE_FORCE_CAP being null. However, I thought that because that variable has the @CapabilityInject annotation, its null value would be replaced with whatever forge generates.

 

One reason I could think of for this happening is if I'm hooking into the wrong place to register my capability. In this thread, @Animefan8888 mentioned that the call to CapabilityManager.INSTANCE.register() should happen in the FMLCommonSetupEvent. Is this the wrong place to register my capability, or am I doing something else wrong?

 

All code related to this capability

Edited by AnZaNaMa

- Just because things are the way they are doesn't mean they can't be the way you want them to be. Unless they're aspen trees. You can tell they're aspens 'cause the way they are.

Posted

You have told it to call a method called "getDefaultInstance" but this method does not exist anywhere I can find.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Posted

LIFE_FORCE_CAP is a Capability<ILifeForce>, so the call to LIFE_FORCE_CAP::getDefaultInstance will resolve to Capability::getDefaultInstance which does exist. I was under the impression that getDefultInstance would return the instance of LifeForce that I passed into CapabilityManager.INSTANCE.register()

- Just because things are the way they are doesn't mean they can't be the way you want them to be. Unless they're aspen trees. You can tell they're aspens 'cause the way they are.

Posted

Your capability registered object and the capability data are not the same thing.

For example, this:

https://github.com/Draco18s/ReasonableRealism/blob/1.12.1/src/main/java/com/draco18s/farming/entities/capabilities/MilkStorage.java#L28-L33

Returns a new instance of the class that holds the capability data.

 

My capability attach event:

https://github.com/Draco18s/ReasonableRealism/blob/1.12.1/src/main/java/com/draco18s/farming/FarmingEventHandler.java#L761

 

(Note: that is 1.12 code and there have been changes, but it should at least work as a template to understand what's going on)

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Posted

I managed to figure it out. I had my @EventBusSubscriber wrong (had to subscribe to the client-specific bus for the FMLCommonSetupEvent). Because of that, the event handler wasn't firing, so the instance of the capability was never being injected with a value, so it was still null when I tried to use it.

 

@Draco18s also, you were right. I was just passing an instance of my ILifeForce, when I needed to pass it a supplier. I ended up using a lambda supplier:

@SubscribeEvent
public static void onCommonSetupEvent(FMLCommonSetupEvent event) {
    CapabilityManager.INSTANCE.register(ILifeForce.class, new LifeForceStorage(), () -> new LifeForce());
}

 

  • Like 1

- Just because things are the way they are doesn't mean they can't be the way you want them to be. Unless they're aspen trees. You can tell they're aspens 'cause the way they are.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.