Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Getting Started with Capabilities: a practical example
public interface MyCapability extends INBTSerializable<CompoundNBT> {
int getValue();
void setValue(int value);
}
// This also provides an example for serialization, though it is ignored
public final class MyCapabilityImpl implements MyCapability {
private static final String INFO_NBT_KEY = "nfo";
private int value;
public MyCapabilityImpl() {
this(0);
}
public MyCapabilityImpl(final int value) {
this.value = value;
}
@Override
public int getValue() {
return this.value;
}
@Override
public void setValue(final int value) {
if (this.value > value) {
System.err.println("Nope");
return;
}
this.value = value;
}
@Nonnull
@Override
public CompoundNBT serializeNBT() {
final CompoundNBT nbt = new CompoundNBT();
nbt.putInt(INFO_NBT_KEY, this.getValue());
return nbt;
}
@Override
public void deserializeNBT(@Nonnull final CompoundNBT nbt) {
this.setValue(nbt.getInt(INFO_NBT_KEY));
}
}
public final class MyCapabilityManager {
@CapabilityInject(MyCapability.class)
@Nonnull
@SuppressWarnings("ConstantConditions")
public static Capability<MyCapability> MY_CAPABILITY = null;
public static final ResourceLocation MY_CAPABILITY_NAME = new ResourceLocation("mymod", "my_capability");
private MyCapabilityManager() {}
public static void registerCapabilities() {
CapabilityManager.INSTANCE.register(
MyCapability.class,
SimpleCapabilityStorage.create(() -> MY_CAPABILITY, Constants.NBT.TAG_COMPOUND),
MyCapabilityImpl::new
);
}
// This attaches the capability to every ItemStack as an example
// You can also do this for other capability providers
public static void onStackAttachCapabilities(@Nonnull final AttachCapabilitiesEvent<ItemStack> e) {
final ItemStack obj = e.getObject();
final MyCapability capability = new MyCapabilityImpl(ThreadLocalRandom.current().nextInt(10)); // Default Storage could be used
e.addCapability(
MY_CAPABILITY_NAME,
// Use SimplePersistentCapabilityProvider if in need of persisting data
SimpleVolatileCapabilityProvider.from(MY_CAPABILITY, () -> capability)
);
}
}
public class SimpleCapabilityStorage<S extends INBT, C extends INBTSerializable<S>> implements Capability.IStorage<C> {
private final Lazy<Capability<C>> capability;
private final int expectedTagType;
protected SimpleCapabilityStorage(final int expectedTagType, @Nonnull final Lazy<Capability<C>> capability) {
this.expectedTagType = expectedTagType;
this.capability = capability;
}
@Nonnull
public static <S extends INBT, C extends INBTSerializable<S>> SimpleCapabilityStorage<S, C> create(@Nonnull final Supplier<Capability<C>> capability) {
return create(capability, -1);
}
@Nonnull
public static <S extends INBT, C extends INBTSerializable<S>> SimpleCapabilityStorage<S, C> create(@Nonnull final Supplier<Capability<C>> capability, final int expectedTagType) {
return new SimpleCapabilityStorage<>(expectedTagType, Lazy.of(capability));
}
@Nullable
@Override
public INBT writeNBT(@Nonnull final Capability<C> capability, @Nonnull final C instance, @Nullable final Direction side) {
if (capability != this.capability.get()) return null;
return instance.serializeNBT();
}
@Override
@SuppressWarnings("unchecked")
public void readNBT(@Nonnull final Capability<C> capability, @Nonnull final C instance, @Nullable final Direction side, @Nonnull final INBT nbt) {
if (capability != this.capability.get()) return;
if (this.expectedTagType != -1 && nbt.getId() != this.expectedTagType) {
throw new IllegalStateException("The NBT type " + nbt.getClass().getSimpleName() + " is not suitable for the capability " + capability);
}
instance.deserializeNBT((S) nbt);
}
}
public class SimplePersistentCapabilityProvider<C, S extends INBT> implements ICapabilityProvider, INBTSerializable<S> {
private final Capability<C> capability;
private final LazyOptional<C> implementation;
private final Direction direction;
protected SimplePersistentCapabilityProvider(@Nonnull final Capability<C> capability, @Nonnull final LazyOptional<C> implementation, @Nullable final Direction direction) {
this.capability = capability;
this.implementation = implementation;
this.direction = direction;
}
@Nonnull
public static <C> SimplePersistentCapabilityProvider<C, INBT> from(@Nonnull final Capability<C> cap, @Nonnull final NonNullSupplier<C> impl) {
return from(cap, null, impl);
}
@Nonnull
public static <C> SimplePersistentCapabilityProvider<C, INBT> from(@Nonnull final Capability<C> cap, @Nullable final Direction direction, @Nonnull final NonNullSupplier<C> impl) {
return new SimplePersistentCapabilityProvider<>(cap, LazyOptional.of(impl), direction);
}
@Nonnull
@Override
public <T> LazyOptional<T> getCapability(@Nonnull final Capability<T> cap, @Nullable final Direction side) {
if (cap == this.capability) return this.implementation.cast();
return LazyOptional.empty();
}
@Nullable
@Override
@SuppressWarnings("unchecked")
public S serializeNBT() {
return (S) this.capability.writeNBT(this.getInstance(), this.direction);
}
@Override
public void deserializeNBT(@Nonnull final S nbt) {
this.capability.readNBT(this.getInstance(), this.direction, nbt);
}
@Nonnull
private C getInstance() {
return this.implementation.orElseThrow(() -> new IllegalStateException("Unable to obtain capability instance"));
}
}
public class SimpleVolatileCapabilityProvider<C> implements ICapabilityProvider {
private final Capability<C> capability;
private final LazyOptional<C> implementation;
protected SimpleVolatileCapabilityProvider(@Nonnull final Capability<C> capability, @Nonnull final LazyOptional<C> implementation) {
this.capability = capability;
this.implementation = implementation;
}
@Nonnull
public static <C> SimpleVolatileCapabilityProvider<C> from(@Nonnull final Capability<C> cap, @Nonnull final NonNullSupplier<C> impl) {
return new SimpleVolatileCapabilityProvider<>(cap, LazyOptional.of(impl));
}
@Nonnull
@Override
public <T> LazyOptional<T> getCapability(@Nonnull final Capability<T> cap, @Nullable final Direction side) {
if (cap == this.capability) return this.implementation.cast();
return LazyOptional.empty();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment