Posted January 19, 20205 yr 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 January 19, 20205 yr 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.
January 19, 20205 yr 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.
January 19, 20205 yr Author 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.
January 19, 20205 yr 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.
January 19, 20205 yr Author 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()); } - 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.