Jump to content

[Solved]Adding IItemHandler Capability to player makes it use items from the hotbar. - PlayerEntity + IItemHandler


Recommended Posts

Posted (edited)

I've been trying to figure out how to add a Capability to the player. I've used the Forge documentation so far. 

 

I'm trying to register the IItemhandler capability to the player. I've created a custom class called CustomItemBar with that registers an Event in my PreInit with an attachCapabilitiesEvent that checks if the entity is a player, then adds the capability.

 

I've created the following class to call events and contain a basic wrapper to call my capability from other locations: 

public class CustomItemBar {

    public static void register(){
        MinecraftForge.EVENT_BUS.register(new CustomItemBar());

    }
    @SubscribeEvent
    public void attachCapabilities(AttachCapabilitiesEvent<Entity> event){
        if(event.getObject() instanceof EntityPlayer) {
            event.addCapability(new ResourceLocation("InventoryCapability"), new AbilityProvider());
            System.out.println("Capability added");
        }
    }

    public static IItemHandler getHandler(Entity entity){
        if(entity.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null)){
            return entity.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null);
        }
        return null;
    }

}

 

I add the capability through AbilityProvider which is as follows: 

public class AbilityProvider implements ICapabilitySerializable<NBTTagCompound>,ICapabilityProvider {

    public static ItemStackHandler handler = new ItemStackHandler(5);

    @Override
    public boolean hasCapability(Capability<?> capability, EnumFacing facing) {
        if (capability == ITEM_HANDLER_CAPABILITY) {

            System.out.println("has item handler capability");
            return true;
        }
        return false;
    }

    @Override
    public <T> T getCapability(Capability<T> capability, EnumFacing facing) {
        if(capability == ITEM_HANDLER_CAPABILITY){
            System.out.println("We returned the handler");
            return (T)this.handler;
        }
        System.out.println("We returned null");
        return null;
    }


    @Override
    public NBTTagCompound serializeNBT() {
        return handler.serializeNBT();
    }

    @Override
    public void deserializeNBT(NBTTagCompound nbt) {
        handler.deserializeNBT(nbt);
    }

}

As you can see I'm using ItemStackHandler with a size of 5, 

 

When I launch the client everything works fine. When I log in to my world I get the proper 

System.out.println("Custom ability added");

 

line returned to me in the logs. 

 

However, this is where it goes wrong: When I try to use 

    public static IItemHandler getHandler(Entity entity){
        if(entity.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null)){
            return entity.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null);
        }
        return null;
    }

in my items. I don't get the 

    @Override
    public <T> T getCapability(Capability<T> capability, EnumFacing facing) {
        if(capability == ITEM_HANDLER_CAPABILITY){
            System.out.println("We returned the handler");
            return (T)this.handler;
        }
        System.out.println("We returned null");
        return null;
    }

 

Message that pops up here. Neither of them.

 

My implementation is as such: 

     final IItemHandler handler2 = CustomItemBar.getHandler(player);
	 handler2.insertItem(4, new ItemStack(RPVPItems.testitem, 5), false);

//and then for the item to tell me what's going on: 

        final IItemHandler handlerAbility = CustomItemBar.getHandler(player);
		if(handlerAbility.extractItem(4, 1, true) != null) {
            ItemStack information = handlerAbility.getStackInSlot(4);
            ItemStack stack = handlerAbility.extractItem(4, 1, false);
            System.out.println("slot 2 has " + information.getItem() + " and amount:" + information.stackSize);
            world.spawnEntityInWorld(new EntityItem(world, player.posX, player.posY, player.posZ, stack));
        }

 

 

The big issue I'm having here is that instead of adding and substracting items from my new Capability storage, the items get added to my hotbar in slot 5 (which makes sense, since slot 1 is id 0)

The commandlines will tell me that there's 4 items in slot 4, but this is obviously an issue since it's reading from my hotbar slot on my actual inventory, not from my Capability. 

 

 

TL:DR:

- The Capability seems to register just fine, no errors are thrown up.

- I try to add the pre-existing IItemhandler to my player, so I don't need to create another Interface.

- When I call the entity.getCapabilities it DOES detect the capability ITEM_HANDLER_CAPABILITY but it doesn't call the code in my overwritten getCapability in my provider.

- When I attempt to use the capability I instead interact with the items from my regular inventory in the same slot that I gave to the code.

 

 

For future people who're googling sollutions:

 

Like Choonster helpfully pointed out, I've created a new Interface extending IItemHandler, then I've created a new implementation of this interface extending ItemStackHandler and implementing my new IItemHandler copy.  I've injected it and registered it using conventional methods and added it to my EntityPlayer on login. For this to work I've had to create an additional Storage class, but that wasn't much of a problem. The code works perfectly fine now and my player has a custom Inventory of the slots I've dictated in my AbilityProvider.

Edited by oldcheese
Solved, many thanks to Choonster.
Posted

EntityPlayer already provides CapabilityItemHandler.ITEM_HANDLER_CAPABILITY itself, so it never delegates this to the attached ICapabilityProviders. You need to create your own interface that extends IItemHandler, register a capability for it and provide that from AbilityProvider instead.

 

AbilityProvider.handler is a static field, which means it's shared between all instances of AbilityProvider (and thus all instances of EntityPlayer you attach it to). It needs to be an instance field instead.

 

Your IDE should warn you about accessing a static field through this.

  • Like 1

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted
4 hours ago, Choonster said:

AbilityProvider.handler is a static field, which means it's shared between all instances of AbilityProvider (and thus all instances of EntityPlayer you attach it to). It needs to be an instance field instead.

 

Your IDE should warn you about accessing a static field through this.

 

Ah. My bad. I hadn't thought about that, the other Capability I made used this, but on a server I see why that'd be horrid.

 

So basically ITEM_HANDLER_CAPABILITY is already registered to the player. Does this mean that the player's inventory is stored in CapabilityItemHandler? Would using ItemHandlerSlot yield the same results in this case as using regular slots? I don't see a reason to use that, I'm just curious.  Basically this entire thread revolves around me thinking I can expose the already existing IItemhandler again.

 

For future people who're googling sollutions:

 

Like Choonster helpfully pointed out, I've created a new Interface extending IItemHandler, then I've created a new implementation of this interface extending ItemStackHandler and implementing my new IItemHandler copy.  I've injected it and registered it using conventional methods and added it to my EntityPlayer on login. For this to work I've had to create an additional Storage class, but that wasn't much of a problem. The code works perfectly fine now and my player has a custom Inventory of the slots I've dictated in my AbilityProvider.

 

 

Posted (edited)
39 minutes ago, oldcheese said:

So basically ITEM_HANDLER_CAPABILITY is already registered to the player. Does this mean that the player's inventory is stored in CapabilityItemHandler? Would using ItemHandlerSlot yield the same results in this case as using regular slots? I don't see a reason to use that, I'm just curious.  Basically this entire thread revolves around me thinking I can expose the already existing IItemhandler again.

 

The player's inventory is stored in the EntityPlayer#inventory field, which is an instance of InventoryPlayer (an implementation of vanilla's IInventory interface).

 

Forge patches EntityPlayer to add the EntityPlayer#getCapability method, which exposes various IItemHandler wrappers of the InventoryPlayer (main, equipment or joined depending on the EnumFacing). These don't store the inventory contents themselves, they simply provide access to the underlying InventoryPlayer through the IItemHandler interface.

 

You can use a SlotItemHandler with one of these IItemHandler wrappers instead of a regular Slot with the InventoryPlayer to achieve the same result (access a player inventory slot in a GUI).

Edited by Choonster
  • Like 1

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

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.