Jump to content

[1.14.4] [Solved] Capabilities: What are they and how do I use them?


Recommended Posts

Posted (edited)

I cannot for the life of me understand how capabilities work. It may just be the way I'm needing to implement them, so I'll be talking about that specifically.

 

I'm currently making a mod that adds a modifier to the player's health to allow for more than just the generic 20 max health. Previously I used a class that contained a player cache and had functions to store and modify the NBT data of said player. Well, I was recently told to use capabilities now, but I have absolutely no idea how to do that. From what I understand, capabilities are supposed to be an easy way to store data alongside Minecraft classes. For example, an easy way to store data for a furnace or something. What I don't understand is how the hell this is supposed to work; what is a capability? Is it a modification to the way things work? Or is it supposed to be an easy interface between NBT data and Minecraft objects? How the hell do you attach a capability to an Entity? Do I need to provide my own Capability Provider, or use one of Forge's? If I need to use one of Forge's, which one do i use? How do I provide my own, in the case I need to do that (the forge documentation is useless and outdated). What does it mean to "expose" a capability? What is a capability type? What does it mean to "attach" a capability?  Capabilities do not make sense at all!

 

I will attempt to break this down so you can understand what I don't: "In general terms, each capability provides a feature in the form of an interface, alongside with a default implementation which can be requested, and a storage handler for at least this default implementation. " "each capability provides a feature in the form of an interface," what does this even mean? I've completed my college course on Java and still have no idea what this implies. A Java interface is used to easily describe classes and provides a layer of abstraction, it is not supposed to provide features. You cannot "implement" a feature using an interface. "alongside with a default implementation which can be requested," what? A default implementation versus... what, a custom one? Someone else's? Why not just implement what you need instead of attempting to provide an interface? "a storage handler for at least this default implementation," again, what is this 'default implementation', and what other types of implementations are there? Also, what in the world is this implementation?

 

I, alongside many others, learn best by example, but the examples on the Forge docs don't provide any context whatsoever. The text refers to things that haven't been talked about, and implies that you have already created things that you didn't even know existed. How am I supposed to have made an "instance of the underlying capability type" if I didn't even know I needed to make one? Maybe I'm just having a hard time understanding Forge's documentation, but I have no idea what capabilities are supposed to be, what they provide in functionality, and how they're supposed to be "easier" than just directly interfacing with a player's NBT data. And yes, I have looked in the source, and no, it doesn't help whatsoever because if I don't even know what they do in the first place how am I supposed to understand an implementation?

Note: Once I truly understand the purpose of a capability and how to implement one, I would love to make a PR to help improve the wording of the docs.

Edited by FireController1847
  • Like 3

I am on my journey of making a remake of matmos, as explained here.

Posted
1 minute ago, FireController1847 said:

what is a capability?

A capability is a way to attach data to an object that doesn't belong to you. IE the player.

 

3 minutes ago, FireController1847 said:

how to implement one

It's actually incredibly simple.

You need a Capability instance with the @CapabilityInject annotation above it.

You need to register your Capability using CapabilityManager.INSTANCE.register(Class<Some Base class where your capability data will be stored>, new IStorageInstance, new Callable)

The IStorageInstance has two methods that save and load data for the capability. The new Callable needs to create an instance of your base class.

Then you also need to use the AttachCapabilitiesEvent to attach it to the object you want to attach it to. You use
AttachCapabilitiesEvent#addCapability(ID, new ICapabilityProvider)

The ICapabilityProvider is the object that provides the way to get the Capability data from the object it is attached to.

  • Like 2

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Posted
13 minutes ago, FireController1847 said:

"each capability provides a feature in the form of an interface," what does this even mean?

This tells me that the capability provides some "feature" or "data" that you can interact with in the form of the Class type interface.

 

14 minutes ago, FireController1847 said:

alongside with a default implementation which can be requested," what? A default implementation versus... what, a custom one? Someone else's? Why not just implement what you need instead of attempting to provide an interface?

What if you want this data to behave differently for different objects? You don't want to have to create another capability do you? Like case in point the IItemHandler capability. It needs to perform differently for a chest than it does to a furnace. The furnace will not allow the player to insert items into the output slot. This needs to be handled. Whereas all slots are accessible in a chest.
 

18 minutes ago, FireController1847 said:

What does it mean to "expose" a capability?

It means to make is accessible. The term refers to the ICapabilityProvider object you attached in the AttachCapabilitiesEvent. You need to make the method getCapability return your data object stored in the ICapabilityProvider instance you have made.

 

19 minutes ago, FireController1847 said:

What does it mean to "attach" a capability?

Attaching it means you have given it to the Entity/World/ItemStack/TileEntity/etc.

  • Like 1

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Posted

Thank you, these helped a lot. One thing I couldn't figure out when I was implementing the ICapabilityProvider was what a LazyOption or something was, and how to properly return the capability. I do not understand how that works; does it create a new instance of my capability every time the getCapability is called or is the one instance I make in my provider uses for everything?

I am on my journey of making a remake of matmos, as explained here.

Posted
Just now, FireController1847 said:

Thank you, these helped a lot. One thing I couldn't figure out when I was implementing the ICapabilityProvider was what a LazyOption or something was, and how to properly return the capability. I do not understand how that works; does it create a new instance of my capability every time the getCapability is called or is the one instance I make in my provider uses for everything?

That's been confusing a lot of people. So a LazyOptional is an object that stores a value optionally and has many methods to allow you to interact with is such as LazyOptional#ifPresent which will run a NonNullConsumer (which you can just pass in a lambda) that will run only if the LazyOptional holds something.

So what I do for the LazyOptional is just do
private LazyOptional<SomeData> instance = LazyOptional.of(CAPABILITY_INSTANCE::getDefaultInstance);

This will create one instance of the default instance when it is first needed. IE the first time you try to access it.

  • Thanks 1

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Posted

1) Don't create LazyOptionals in getCap, violates the whole point and doesn't allow you to mark the LazyOptional as invalid when its no longer needed. This is an easy mistake to make and if you do it, Lex will find you and come murder you in your sleep.

2) All it is is a wrapper around your capability instance (which may or may not necessarily exist yet) and when your cap does exist, the LazyOptional will automatically get updated from holding null to holding your cap instance (because its actually providing a pointer to your cap field, rather than pointing at what your cap field contained when the LazyOptional was created).

11 hours ago, Animefan8888 said:

allow you to interact with is such as LazyOptional#ifPresent which will run a NonNullConsumer (which you can just pass in a lambda) that will run only if the LazyOptional holds something.

Annoyingly, I've found situations where this isn't possible.

https://github.com/Draco18s/ReasonableRealism/blob/1.14.4/src/main/java/com/draco18s/industry/entity/AbstractHopper.java#L118-L121

There's no way to return the boolean required for the hopper updateHopper's Supplier parameter from inside the ifPresent lambda. Trust me, I tried.

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
1 hour ago, diesieben07 said:

Yes, which is why orElse, orElseGet and orElseThrow exist.

Oh I know. Its more that it's irksome that having to do orElse(null) and having check for null.

(I'm not sure what the difference between orElse and orElseGet is, and should probably use orElseThrow, but I'm a little confused by what to pass in).

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
1 hour ago, diesieben07 said:

orElseThrow(() -> new IllegalArgumentException("Invalid LazyOptional, must not be empty")).

That's what I needed. I hadn't looked at it in a while and I've become more comfortable with Supplies and Consumers lately and I just hadn't gone back to see if I could figure it out again. I knew it was better than what I was doing, but that I'd gotten stuck and went with what I knew.

Thanks.

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
On 9/9/2019 at 10:05 PM, Animefan8888 said:

That's been confusing a lot of people. So a LazyOptional is an object that stores a value optionally and has many methods to allow you to interact with is such as LazyOptional#ifPresent which will run a NonNullConsumer (which you can just pass in a lambda) that will run only if the LazyOptional holds something.

So what I do for the LazyOptional is just do
private LazyOptional<SomeData> instance = LazyOptional.of(CAPABILITY_INSTANCE::getDefaultInstance);

This will create one instance of the default instance when it is first needed. IE the first time you try to access it.

So sorry for my very late response. Gotta love the beginning of the week, have no freetime haha. This is interesting... If I extend the ICapabilitySerializable or something like that, and my instance is a lazy optional, what do I do for the writeNBT and the readNBT functions? I need to be able to have the instance to write and read the NBT data to it, no?

Also, I don't quite think one of my questions was answered.... Does the instance exist for every entity it is attached to, or is there one global instance that is used between all of them? Similarly, if my mod only applies to players, wouldn't that be a huge waste of space adding it to every entity? The way my mod works now (by caching PlayerData and manually updating their NBT data) would be much more efficient in this case, and I don't see any reason why I would want to or need to switch to capabilities.

I am on my journey of making a remake of matmos, as explained here.

Posted (edited)
Quote

You attach the ICapabilityProvider to the entity. You decide how the instance is constructed. In theory you could attach the same instance to all entities. Or you can make a new one for each of them (this is what's usually done).

Apologies if I'm misunderstanding, I'm having a hard time wrapping my head around this. So the "instance" is the object that is attached to each entity, correct? Say, if I have a capability called MoreHealth, the "instance" is an instance of MoreHealth, right? Also, to create a capability on a per-entity basis, does Forge do that for me when I attach it? If the instance is a static on the provider class, wouldn't the same instance be used if you call getCapability on different entities?

On a similar note, I have no idea what to do in the if statement for the getCapability function. I've seen examples of calling a "cast" function, but none of them appear to work on the latest forge. This is my class currently:
 

package com.firecontroller1847.levelhearts.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 MoreHealthProvider implements ICapabilitySerializable<INBT> {

	@CapabilityInject(IMoreHealth.class)
	public static Capability<IMoreHealth> MORE_HEALTH_CAPABILITY;

	private LazyOptional<IMoreHealth> instance = LazyOptional.of(MORE_HEALTH_CAPABILITY::getDefaultInstance);

	@Override
	public <T> LazyOptional<T> getCapability(Capability<T> cap, Direction side) {
		return cap == MORE_HEALTH_CAPABILITY ? ???????? : LazyOptional.empty(); // I have no idea what to return here
	}

	@Override
	public INBT serializeNBT() {
		// TODO
		return null;
	}

	@Override
	public void deserializeNBT(INBT nbt) {
		// TODO
	}

}

 

Edited by FireController1847

I am on my journey of making a remake of matmos, as explained here.

Posted
Just now, Animefan8888 said:

instance.cast()

Okay, I swear that didn't show up when I tried it before. Alright, I think I understand it enough to get a prototype working, I'll probably be back at some point to ask more questions haha. Thanks for everyone's help! :)

I am on my journey of making a remake of matmos, as explained here.

Posted
4 minutes ago, FireController1847 said:

If the instance is a static on the provider class, wouldn't the same instance be used if you call getCapability on different entities?

Yes it would be.

 

4 minutes ago, FireController1847 said:

So the "instance" is the object that is attached to each entity, correct?

Technically. The ICapabilityProvider is attached to the entity and it stores the "instance" field.

 

5 minutes ago, FireController1847 said:

Also, to create a capability on a per-entity basis, does Forge do that for me when I attach it?

Yes every time an entity is created it attaches a new ICapabilityProvider to that entity.

  • Thanks 1

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Posted (edited)

Okay, I am now to the point where I got stuck before. When calling the serializeNBT and deserializeNBT functions, I need to call the IStorage instance from my capability. What I don't understand is how I am supposed to provide the instance if it is a LazyOptional...
 

package com.firecontroller1847.levelhearts.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 MoreHealthProvider implements ICapabilitySerializable<INBT> {

	@CapabilityInject(IMoreHealth.class)
	public static Capability<IMoreHealth> MORE_HEALTH_CAPABILITY;

	private LazyOptional<IMoreHealth> instance = LazyOptional.of(MORE_HEALTH_CAPABILITY::getDefaultInstance);

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

	@Override
	public INBT serializeNBT() {
		// using this.instance errors, expects IMoreHealth not Lazy Optional
		return MORE_HEALTH_CAPABILITY.getStorage().writeNBT(MORE_HEALTH_CAPABILITY, this.instance, null);
	}

	@Override
	public void deserializeNBT(INBT nbt) {
		// using this.instance errors, expects IMoreHealth not Lazy Optional
		MORE_HEALTH_CAPABILITY.getStorage().readNBT(MORE_HEALTH_CAPABILITY, this.instance, null, nbt);
	}

}


If I were to use LazyOptional.orElse, would I do something like this?

 

	@Override
	public INBT serializeNBT() {
		// using this.instance errors, expects IMoreHealth not Lazy Optional
		return MORE_HEALTH_CAPABILITY.getStorage().writeNBT(MORE_HEALTH_CAPABILITY, this.instance.orElse(MORE_HEALTH_CAPABILITY.getDefaultInstance()), null);
	}


If I do, then what's the point of doing the LazyOptional.of with the getDefaultInstance method in the first place?

EDIT: I read this comment on another thread, and it's starting to make more sense. So, in this situation, if the instance is null then it's an error condition, right? Or is it not...? Is it allowed to be null here? I'd assume not since the IStorage requires it...

Edited by FireController1847

I am on my journey of making a remake of matmos, as explained here.

Posted (edited)

Okay, I think I've got it. Hopefully this looks right...

https://gist.github.com/FireController1847/c7a50144f45806a996d13efcff468d1b

EDIT:

So I got this crash report, and I have no idea how to fix it.
 

java.lang.NullPointerException: null
	at net.minecraftforge.common.capabilities.CapabilityManager.register(CapabilityManager.java:79) ~[?:?]
	at com.firecontroller1847.levelhearts.LevelHearts.<init>(LevelHearts.java:32) ~[?:?]
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:1.8.0_221]
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source) ~[?:1.8.0_221]
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source) ~[?:1.8.0_221]
	at java.lang.reflect.Constructor.newInstance(Unknown Source) ~[?:1.8.0_221]
	at java.lang.Class.newInstance(Unknown Source) ~[?:1.8.0_221]
	at net.minecraftforge.fml.javafmlmod.FMLModContainer.constructMod(FMLModContainer.java:131) ~[?:28.0]
	at java.util.function.Consumer.lambda$andThen$0(Unknown Source) ~[?:1.8.0_221]
	at java.util.function.Consumer.lambda$andThen$0(Unknown Source) ~[?:1.8.0_221]
	at net.minecraftforge.fml.ModContainer.transitionState(ModContainer.java:112) ~[?:?]
	at net.minecraftforge.fml.ModList.lambda$null$10(ModList.java:133) ~[?:?]
	at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(Unknown Source) ~[?:1.8.0_221]
	at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(Unknown Source) ~[?:1.8.0_221]
	at java.util.stream.AbstractPipeline.copyInto(Unknown Source) ~[?:1.8.0_221]
	at java.util.stream.ForEachOps$ForEachTask.compute(Unknown Source) ~[?:1.8.0_221]
	at java.util.concurrent.CountedCompleter.exec(Unknown Source) ~[?:1.8.0_221]
	at java.util.concurrent.ForkJoinTask.doExec(Unknown Source) ~[?:1.8.0_221]
	at java.util.concurrent.ForkJoinTask.doInvoke(Unknown Source) ~[?:1.8.0_221]
	at java.util.concurrent.ForkJoinTask.invoke(Unknown Source) ~[?:1.8.0_221]
	at java.util.stream.ForEachOps$ForEachOp.evaluateParallel(Unknown Source) ~[?:1.8.0_221]
	at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateParallel(Unknown Source) ~[?:1.8.0_221]
	at java.util.stream.AbstractPipeline.evaluate(Unknown Source) ~[?:1.8.0_221]
	at java.util.stream.ReferencePipeline.forEach(Unknown Source) ~[?:1.8.0_221]
	at java.util.stream.ReferencePipeline$Head.forEach(Unknown Source) ~[?:1.8.0_221]
	at net.minecraftforge.fml.ModList.lambda$dispatchParallelEvent$11(ModList.java:133) ~[?:?]
	at java.util.concurrent.ForkJoinTask$AdaptedRunnableAction.exec(Unknown Source) [?:1.8.0_221]
	at java.util.concurrent.ForkJoinTask.doExec(Unknown Source) [?:1.8.0_221]
	at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(Unknown Source) [?:1.8.0_221]
	at java.util.concurrent.ForkJoinPool.runWorker(Unknown Source) [?:1.8.0_221]
	at java.util.concurrent.ForkJoinWorkerThread.run(Unknown Source) [?:1.8.0_221]

 

Edited by FireController1847

I am on my journey of making a remake of matmos, as explained here.

Posted
7 minutes ago, FireController1847 said:

So I got this crash report, and I have no idea how to fix it.

Don't register your Capability in the @Mod files constructor instead do it in the FMLCommonSetupEvent.

  • Like 1

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Posted (edited)

Arrg! So close! I got one more error and this time it's with my ResourceLocation...

 

	public static final String NBT_ID = "levelHearts";
	public static final String MOD_ID = "levelhearts";

	@SubscribeEvent
	public static void onAttachCapabilities(AttachCapabilitiesEvent<Entity> event) {
		if (event.getObject() instanceof PlayerEntity) {
			event.addCapability(new ResourceLocation(LevelHearts.MOD_ID, LevelHearts.NBT_ID), new MoreHealthProvider());
		}
	}
net.minecraft.util.ResourceLocationException: Non [a-z0-9/._-] character in path of location: levelhearts:levelHearts
	at net.minecraft.util.ResourceLocation.<init>(ResourceLocation.java:30) ~[forge-1.14.4-28.0.95_mapped_snapshot_20190907-1.14.3-recomp.jar:?] {}
	at net.minecraft.util.ResourceLocation.<init>(ResourceLocation.java:39) ~[forge-1.14.4-28.0.95_mapped_snapshot_20190907-1.14.3-recomp.jar:?] {}
	at com.firecontroller1847.levelhearts.LevelHearts.onAttachCapabilities(LevelHearts.java:44) ~[main/:?] {}
	at net.minecraftforge.eventbus.ASMEventHandler_0_LevelHearts_onAttachCapabilities_AttachCapabilitiesEvent.invoke(.dynamic) ~[?:?] {}
	at net.minecraftforge.eventbus.ASMEventHandler.invoke(ASMEventHandler.java:80) ~[eventbus-1.0.0-service.jar:?] {}
	at net.minecraftforge.eventbus.EventBus.post(EventBus.java:258) ~[eventbus-1.0.0-service.jar:?] {}
	at net.minecraftforge.event.ForgeEventFactory.gatherCapabilities(ForgeEventFactory.java:560) ~[?:?] {}
	at net.minecraftforge.event.ForgeEventFactory.gatherCapabilities(ForgeEventFactory.java:554) ~[?:?] {}
	at net.minecraftforge.common.capabilities.CapabilityProvider.gatherCapabilities(CapabilityProvider.java:48) ~[?:?] {}
	at net.minecraftforge.common.capabilities.CapabilityProvider.gatherCapabilities(CapabilityProvider.java:44) ~[?:?] {}
	at net.minecraft.entity.Entity.<init>(Entity.java:222) ~[?:?] {pl:accesstransformer:B}
	at net.minecraft.entity.LivingEntity.<init>(LivingEntity.java:192) ~[?:?] {}
	at net.minecraft.entity.player.PlayerEntity.<init>(PlayerEntity.java:162) ~[?:?] {pl:accesstransformer:B}
	at net.minecraft.entity.player.ServerPlayerEntity.<init>(ServerPlayerEntity.java:163) ~[?:?] {pl:accesstransformer:B}
	at net.minecraft.server.management.PlayerList.createPlayerForUser(PlayerList.java:390) ~[?:?] {}
	at net.minecraft.network.login.ServerLoginNetHandler.tryAcceptPlayer(ServerLoginNetHandler.java:119) ~[?:?] {}
	at net.minecraft.network.login.ServerLoginNetHandler.tick(ServerLoginNetHandler.java:63) ~[?:?] {}
	at net.minecraft.network.NetworkManager.tick(NetworkManager.java:241) ~[?:?] {}
	at net.minecraft.network.NetworkSystem.tick(NetworkSystem.java:148) ~[?:?] {}
	at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:882) ~[?:?] {pl:accesstransformer:B}
	at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:800) ~[?:?] {pl:accesstransformer:B}
	at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:118) ~[?:?] {pl:runtimedistcleaner:A}
	at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:646) ~[?:?] {pl:accesstransformer:B}
	at java.lang.Thread.run(Unknown Source) ~[?:1.8.0_221] {}



EDIT: Nevermind, I'm just dumb and missed the fact the error says no capital letters.

Edited by FireController1847

I am on my journey of making a remake of matmos, as explained here.

Posted

Okay, sorry about that last error report. I was just dumb and couldn't read. Here's one that might be more worthy of some help...

MoreHeathProvider

package com.firecontroller1847.levelhearts.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 MoreHealthProvider implements ICapabilitySerializable<INBT> {

	@CapabilityInject(IMoreHealth.class)
	public static Capability<IMoreHealth> MORE_HEALTH_CAPABILITY;

	private LazyOptional<IMoreHealth> instance = LazyOptional.of(MORE_HEALTH_CAPABILITY::getDefaultInstance);

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

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

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

}


The Erroring Event (on addCapability)

	@SubscribeEvent
	public static void onAttachCapabilities(AttachCapabilitiesEvent<Entity> event) {
		if (event.getObject() instanceof PlayerEntity) {
			event.addCapability(new ResourceLocation(LevelHearts.MOD_ID, "morehealth"), new MoreHealthProvider());
		}
	}

 

java.lang.NullPointerException
	at com.firecontroller1847.levelhearts.capabilities.MoreHealthProvider.<init>(MoreHealthProvider.java:15)
	at com.firecontroller1847.levelhearts.LevelHearts.onAttachCapabilities(LevelHearts.java:43)
	at net.minecraftforge.eventbus.ASMEventHandler_2_LevelHearts_onAttachCapabilities_AttachCapabilitiesEvent.invoke(.dynamic)
	at net.minecraftforge.eventbus.ASMEventHandler.invoke(ASMEventHandler.java:80)
	at net.minecraftforge.eventbus.EventBus.post(EventBus.java:258)
	at net.minecraftforge.event.ForgeEventFactory.gatherCapabilities(ForgeEventFactory.java:560)
	at net.minecraftforge.event.ForgeEventFactory.gatherCapabilities(ForgeEventFactory.java:554)
	at net.minecraftforge.common.capabilities.CapabilityProvider.gatherCapabilities(CapabilityProvider.java:48)
	at net.minecraftforge.common.capabilities.CapabilityProvider.gatherCapabilities(CapabilityProvider.java:44)
	at net.minecraft.entity.Entity.<init>(Entity.java:222)
	at net.minecraft.entity.LivingEntity.<init>(LivingEntity.java:192)
	at net.minecraft.entity.player.PlayerEntity.<init>(PlayerEntity.java:162)
	at net.minecraft.entity.player.ServerPlayerEntity.<init>(ServerPlayerEntity.java:163)
	at net.minecraft.server.management.PlayerList.createPlayerForUser(PlayerList.java:390)
	at net.minecraft.network.login.ServerLoginNetHandler.tryAcceptPlayer(ServerLoginNetHandler.java:119)
	at net.minecraft.network.login.ServerLoginNetHandler.tick(ServerLoginNetHandler.java:63)
	at net.minecraft.network.NetworkManager.tick(NetworkManager.java:241)
	at net.minecraft.network.NetworkSystem.tick(NetworkSystem.java:148)
	at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:882)
	at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:800)
	at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:118)
	at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:646)
	at java.lang.Thread.run(Unknown Source)

 

I am on my journey of making a remake of matmos, as explained here.

Posted
5 minutes ago, FireController1847 said:

public static Capability<IMoreHealth> MORE_HEALTH_CAPABILITY;

Make it public static final and set it equal to null. I think.

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Posted
2 minutes ago, Animefan8888 said:

Make it public static final and set it equal to null. I think.

That didn't make a difference, unfortunately. I think the error is lying in line 15. I need to go be back in a bit.

I am on my journey of making a remake of matmos, as explained here.

Posted

Sorry about the abrupt leave. I think the cause of the error is down to the LazyOptional.of method. I'm not sure if getInstance() is returning null upon creation, but the error makes no sense... Does anyone have an idea?

I am on my journey of making a remake of matmos, as explained here.

Posted
1 minute ago, FireController1847 said:

I've updated the following gist with my latest modifications to the code.

	@SubscribeEvent
	public static void onCommonSetup(FMLCommonSetupEvent event) {
		CapabilityManager.INSTANCE.register(IMoreHealth.class, new MoreHealthStorage(), MoreHealth::new);
	}

This won't run I don't think. You should register it in your constructor like so.
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onCommonSetup);

  • Thanks 1

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

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.