Posted April 12, 20205 yr First of all, I'm quite new to modding, so I'm sorry if I'm doing many beginners' errors. Hello, I've been reading some stuff about capabilities, but most stuff I find is from 1.12 and it appears to be outdated. I think. I want to implement sort of a currency, so that each player would have its own amount registered to it. What I have so far is this: ICurrency.java public interface ICurrency { public int getAmount(); public void setAmount(int amount); public void addOrSubtractAmount(int amount); } Currency.java public class Currency implements ICurrency { private int amount = 0; @Override public int getAmount() { return this.amount; } @Override public void setAmount(int amount) { this.amount = amount; if(this.amount < 0) this.amount = 0; } @Override public void addOrSubtractAmount(int amount) { this.amount += amount; if(this.amount < 0) this.amount = 0; } } CurrencyStorage.java public class CurrencyStorage implements Capability.IStorage<ICurrency> { @Nullable @Override public INBT writeNBT(Capability<ICurrency> capability, ICurrency instance, Direction side) { return IntNBT.func_229692_a_(instance.getAmount()); } @Override public void readNBT(Capability<ICurrency> capability, ICurrency instance, Direction side, INBT nbt) { if (!(instance instanceof Currency)) throw new IllegalArgumentException("Can not deserialize to an instance that isn't the default implementation"); instance.setAmount(((IntNBT)nbt).getInt()); } } CurrencyCapability.java public class CurrencyCapability implements ICapabilitySerializable<IntNBT> { @CapabilityInject(ICurrency.class) public static final Capability<ICurrency> CURRENCY_CAPABILITY = null; private LazyOptional<ICurrency> instance = LazyOptional.of(CURRENCY_CAPABILITY::getDefaultInstance); public static void register() { CapabilityManager.INSTANCE.register(ICurrency.class, new CurrencyStorage(), Currency::new); } @Nonnull @Override public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) { return CURRENCY_CAPABILITY.orEmpty(cap, instance);; } @Override public IntNBT serializeNBT() { return (IntNBT) CURRENCY_CAPABILITY.getStorage().writeNBT(CURRENCY_CAPABILITY, instance.orElseThrow(() -> new IllegalArgumentException("LazyOptional cannot be empty!")), null);; } @Override public void deserializeNBT(IntNBT nbt) { CURRENCY_CAPABILITY.getStorage().readNBT(CURRENCY_CAPABILITY, instance.orElseThrow(() -> new IllegalArgumentException("LazyOptional cannot be empty!")), null, nbt); } } Where I'm registering the capability @Mod(Constants.MODID) public class Mod { public Mod() { FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup); //More events and stuff... } @SubscribeEvent public void attachCapabilitiesEntity(final AttachCapabilitiesEvent<Entity> event) { if(event.getObject() instanceof PlayerEntity) event.addCapability(new ResourceLocation(Constants.MODID, Constants.CURRENCY), new CurrencyCapability()); } private void setup(final FMLCommonSetupEvent event) { //Register the Currency capability CurrencyCapability.register(); } //More events and stuff... } My problems lie on various places: 1. CurrencyCapability throws errors: .orEmpty, and CURRENCY_CAPABILITY.getStorage() say "Method invocation 'orEmpty' will produce 'NullPointerException' " and can't use it 2. I'm not sure if I'm registering it correctly 3. I don't know, once I'm able to register it correctly, how to get the info (the currency amount, that it) from the player Thank you for your time
April 12, 20205 yr Author 2 hours ago, diesieben07 said: PlayerEntity (and all entities in fact) implement ICapabilityProvider, so you just call getCapability. Right now I'm just testing so that if I right click on the item, it shows the current amount, then prints out a message to the user. It works, but it looks like an awful lot of code to get the information. Would this be the way to access its information? Is there a better way? public class CopperIngot extends Item { public CopperIngot() { super(new Item.Properties().group(Constants.SMITEMGROUP)); setRegistryName(Constants.MODID, Constants.COPPERINGOT); } @Override public ActionResult<ItemStack> onItemRightClick(World worldIn, PlayerEntity playerIn, Hand handIn) { if(!worldIn.isRemote) playerIn.getCapability(CurrencyCapability.CURRENCY_CAPABILITY).ifPresent(new NonNullConsumer<ICurrency>() { @Override public void accept(@Nonnull ICurrency iCurrency) { playerIn.sendStatusMessage(new StringTextComponent("Currency: "+iCurrency.getAmount()), false); iCurrency.addOrSubtractAmount(1); } }); return super.onItemRightClick(worldIn, playerIn, handIn); } }
April 12, 20205 yr Author 24 minutes ago, diesieben07 said: You might also want to consider using orElseThrow instead of ifPresent, since you add the capability to all players it would be an error for it to not be there. Damn, that worked like a charm, and it also reduced the amount of lines, thanks a lot. One last thing. If the mod were to be used in multiplayer, would I have synchronization problems? I read that on https://mcforge.readthedocs.io/en/1.15.x/datastorage/capabilities/#synchronizing-data-with-clients, but I'm unsure if that still applies, or if it won't be a problem at all
April 12, 20205 yr Author Just now, diesieben07 said: If you need the data on the client then you need to do the synchronization yourself, that still applies. If you only need the data on the server the you don't need to do anything else. So, if I wanted to (which is what I want to do) show the current amount on the inventory. That's the client, so I would have to do synchronization, right?
April 12, 20205 yr Author 2 minutes ago, diesieben07 said: Correct, you'd have to send the data to the player it is attached to. Alright, thank you very much for your help. I'll try later on the day to do the showing on the inventory, but for now that's all I needed, thanks a lot again.
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.