ContainerPlayer и кастомные ячейки

Версия Minecraft
1.7.10
355
2
17
Около недели провозился с этой проблемой и пришел к выводу, что стоит обратится к знающим, кто сталкивался с кастомным интерфейсом игрока.

Основная задача
Заменить ванильный интерфейс на свой, которые содержит:
  • 4 слота для брони
  • 2-4 уникальных слота (Маска, рюкзак.. )
  • 5-30 уникальных слотов рюкзака
  • 5 ванильных слотов хот-бара.
Визуально все выглядит так:
823x94DIJ74W7A.png


Проблема
Когда происходит клик по вещи с хот-бара, да и с рюкзака тоже, вылетает с ошибкой:
(Зачастую после перезахода в одиночный мир, когда в первый раз одеваешь рюкзак и тыкаешь предметы, вроде как все нормально).


java.lang.IndexOutOfBoundsException: Index: 42, Size: 16
Мол пытаюсь вызвать слот, которого нет в массиве.
Хотя длина массива этих слотов 46.

И тут стоит вопрос, откуда он еще берет эти значения, где еще регистрируются слота, уже все перекопал.

P.S.
Заранее уточню, что гуглил везде где только можно, статью coolAlias тоже смотрел, все по сути работает если делать на основе ванильных слотов, но мне нужно вызывать содержимое и слота рюкзака и создавать собственно под них различные исключения.

Ну и в коде существуют переменные которые скорее не используются, это из-за различных вариаций замены инвентаря.



Java:
public class PlayerContainerCustom
        extends ContainerPlayer
{

    protected static EntityPlayer thePlayer;

    private InventoryPlayer inventoryPlayer;
    private PlayerInventoryCustom inventoryCustom;
    private InventoryBackpack inventoryBackpack;

    ItemStack prevIt = null;
    private int backslots = 0;


    public PlayerContainerCustom(EntityPlayer player, InventoryPlayer inventoryPlayer, PlayerInventoryCustom inventoryCustom)
    {
        super(inventoryPlayer, player.worldObj.isRemote, player);

        thePlayer = player;

        this.inventoryPlayer = inventoryPlayer;
        this.inventoryCustom = inventoryCustom;
        inventoryCustom.setPlayerContainerCustom(this);

        initContainer(player);
    }

    public void initContainer(EntityPlayer player)
    {
        this.inventorySlots.clear();
        this.inventoryItemStacks.clear();


        this.addSlotToContainer(new SlotCrafting(player, inventoryPlayer, craftResult, 0, 68, 100));

            /*
            * 4x4 craft matrix
            * */

        for(int i = 0; i < 2; ++i)
            for(int createdinv = 0; createdinv < 2; ++createdinv)
                this.addSlotToContainer(new Slot(this.craftMatrix, createdinv + i * 2, 10 + createdinv * 20, 90 + i * 20));


        for (int i = 0; i < 4; ++i)
            this.addSlotToContainer(new SlotArmor(player, inventoryPlayer, inventoryPlayer.getSizeInventory() - 1 - i, 8, 8 + i * 20, i));
        

        this.addSlotToContainer(new SlotMask(player, inventoryCustom, 0, 80, 8, 8));
        this.addSlotToContainer(new SlotBackpack(player, inventoryCustom, 1, 80, 28, 8));

        inventoryCustom.editing = true;

        ExtendedPlayer extendedPlayer = ExtendedPlayer.get(player);

        if ((extendedPlayer.getBackpack() != null) && (extendedPlayer.getBackpack().getItem() instanceof ItemBackpack))
        {
            ItemBackpack backpack = (ItemBackpack) extendedPlayer.getBackpack().getItem();
            InventoryBackpack invBackpack = new InventoryBackpack(extendedPlayer, this);

            invBackpack.adding = true;

            int backpackSize = ((ItemBackpack) extendedPlayer.getBackpack().getItem()).backpackSize;

            backslots = backpackSize;

            int rows = (int) Math.ceil(backpackSize / 5.0);
            int row = 0;
            int indexSlot = 0;

            while (backpackSize != 0)
            {
                if (indexSlot >= 5)
                {
                    row++;
                    indexSlot = 0;
                }

                this.addSlotToContainer(new Slot(invBackpack, backpackSize, 104 + indexSlot * 20, ((rows == 1) ? 28 + row * 20 : 10 + row * 20))
                {
                    public boolean isItemValid(ItemStack itemStack)
                    {
                        if (itemStack == null) return false;

                        return !(itemStack.getItem() instanceof ItemBackpack);
                    }
                });

                indexSlot++;
                backpackSize--;

            }

            invBackpack.adding = false;

        }

        inventoryCustom.editing = false;

        for (int i = 0; i < 5; ++i)
            this.addSlotToContainer(new SlotCustom(inventoryPlayer, i, 40 + i * 20, 142));
        

        this.onCraftMatrixChanged(this.craftMatrix);


        MainClass.log("Inventory Slots:");
        MainClass.log("  Length: " + inventorySlots.size());

        for(Object s : inventorySlots)
        {
            Slot slot = (Slot) s;
            MainClass.log(" > index: " + slot.getSlotIndex() + " II: " + slot.inventory.toString());
        }

    }



    @Override
    public boolean canInteractWith(EntityPlayer player)
    {
        return true;
    }


    @Override
    public ItemStack slotClick(int slotId, int clickedButton, int mode, EntityPlayer player)
    {

        ItemStack itemstack = super.slotClick(slotId, clickedButton, mode, player);

        if ((player.inventory.getItemStack() != this.prevIt) && (!player.worldObj.isRemote) && (slotId < 16 + this.backslots))
        {
            MainClass.networkWrapper.sendTo(new MSG_InvStack(player.inventory.getItemStack()), (EntityPlayerMP) player);
        }

        return itemstack;
    }


    // Shift-click отключен, сначала нужно решить проблему с индексацией слотов.
    public ItemStack transferStackInSlot(EntityPlayer player, int par2)
    {
        return null;
    }
}

Java:
public class PlayerInventoryCustom
        implements IInventory
{
    private final String name = "Inventory";
    private final String tagName = "CustomInventory";

    private EntityPlayer player;
    public boolean editing = false;
    private PlayerContainerCustom playerContainerCustom;

    public static final int INV_SIZE = 48;
    public ItemStack[] inventory = new ItemStack[INV_SIZE];


    public PlayerInventoryCustom(EntityPlayer player)
    {
        this.player = player;
    }

    public void setPlayerContainerCustom(PlayerContainerCustom playerContainerCustom)
    {
        this.playerContainerCustom = playerContainerCustom;
    }

    @Override
    public int getSizeInventory()
    {
        return this.inventory.length;
    }

    @Override
    public ItemStack getStackInSlot(int slot)
    {
        return this.inventory[slot];
    }

    @Override
    public ItemStack decrStackSize(int slot, int amount)
    {
        ItemStack stack = this.getStackInSlot(slot);

        if(stack != null)
        {
            if(stack.stackSize > amount)
            {
                stack = stack.splitStack(amount);

                if(stack.stackSize == 0)
                    this.setInventorySlotContents(slot, (ItemStack) null);

            } else {
                this.setInventorySlotContents(slot, (ItemStack) null);
            }

            this.markDirty();
        }

        return stack;
    }

    @Override
    public ItemStack getStackInSlotOnClosing(int slot)
    {

        ItemStack stack = this.getStackInSlot(slot);
        if(stack != null) {
            this.setInventorySlotContents(slot, (ItemStack)null);
        }

        return stack;
    }


    public void dropAllItems(EntityPlayer player)
    {
        ItemStack[] items = this.inventory;
        int itemsLength = items.length;

        for(int i = 0; i < itemsLength; ++i)
        {

            ItemStack is = items[i];

            if(is != null)
            {
                player.func_146097_a(is, false, false);
                is = null;
            }
        }

        this.markDirty();
    }

    public ItemStack removeStackFromSlot(int slot)
    {
        ItemStack stack = this.getStackInSlot(slot);

        if(stack != null)
            this.setInventorySlotContents(slot, (ItemStack)null);

        return stack;
    }

    @Override
    public void setInventorySlotContents(int slot, ItemStack itemStack)
    {

        this.inventory[slot] = itemStack;

        if(itemStack != null && itemStack.stackSize > this.getInventoryStackLimit())
            itemStack.stackSize = this.getInventoryStackLimit();

        this.markDirty();
    }

    @Override
    public String getInventoryName()
    {
        return "Inventory";
    }

    @Override
    public boolean hasCustomInventoryName()
    {
        return true;
    }

    @Override
    public int getInventoryStackLimit() {
        return 1;
    }

    @Override
    public void markDirty()
    {

        for(int i = 0; i < this.getSizeInventory(); ++i) {
            if(this.getStackInSlot(i) != null && this.getStackInSlot(i).stackSize == 0) {
                this.setInventorySlotContents(i, (ItemStack)null);
            }
        }

        if (!this.editing)
        {

            ExtendedPlayer extendedPlayer = ExtendedPlayer.get(player);

            if ((extendedPlayer.getBackpack() != getStackInSlot(1)) && ((getStackInSlot(1) == null) || getStackInSlot(1).getItem() instanceof ItemBackpack))
            {
                extendedPlayer.setBackpack(getStackInSlot(1));
                this.player.inventory.setItemStack(null);

                this.playerContainerCustom.initContainer(player);
            }
        }

    }

    @Override
    public boolean isUseableByPlayer(EntityPlayer entityPlayer)
    {
        return true;
    }

    @Override
    public void openInventory()
    {}

    @Override
    public void closeInventory()
    {}

    @Override
    public boolean isItemValidForSlot(int slot, ItemStack itemStack)
    {
        return true;
    }

    public void writeToNBT(NBTTagCompound compound)
    {
        NBTTagList items = new NBTTagList();

        for(int i = 0; i < this.getSizeInventory(); ++i)
        {
            if(this.getStackInSlot(i) != null)
            {
                NBTTagCompound item = new NBTTagCompound();
                item.setByte("Slot", (byte)i);

                this.getStackInSlot(i).writeToNBT(item);
                items.appendTag(item);
            }
        }

        compound.setTag("MainInventory", items);
    }

    public void readFromNBT(NBTTagCompound compound)
    {

        NBTTagList items = compound.getTagList("MainInventory", compound.getId());
        for (int i = 0; i < items.tagCount(); ++i)
        {

            NBTTagCompound item = items.getCompoundTagAt(i);
            byte slot = item.getByte("Slot");
            if (slot >= 0 && slot < getSizeInventory())
            {
                inventory[slot] = ItemStack.loadItemStackFromNBT(item);
            }
        }
    }
}
 
3,005
192
592
Где-то нету проверки на рюкзак (без рюкзака - ровно 16 слотов).
P.S. Но это не точно.
 
355
2
17
Я предполагал такое, но ведь количество ячеек для инвентаря уже формируется с проверкой рюкзака.
И при вызове onClick он пытается вытянуть вещь из InventorySlots

В net.minecraft.inventory.Container; (Родитель)

Slot slot = (Slot) this.inventorySlots.get(p_75144_1_);

И собственно длина этого массива в моем методе равен 46 (После индексации ячеек проверяю). А оно тащит черт пойми откуда..
Причем ошибка появляется именно при нажатии на предмет из ХотБара, где индек слотов 0, 1, 2, 3, 4
 
2,505
81
397
Ты унаследовался от ContainerPlayer, а там итак уже слоты добавлены. Удаляй их или перемещай (чтобы ванильный креатив работал).
Еще интересно, как ты открываешь это сооружение.
На самом деле, сделать все нормально без хуклибы - не получится.
 
355
2
17
Ванильные слоты я очищаю, перед тем как добавляю все заново

В методе initContainer:
this.inventorySlots.clear();
this.inventoryItemStacks.clear();


А вызывается он действительно извращенным методом:
(Полный пример есть в статье coolAlies)

Java:
CommonProxy:

    @Override
    public Object getServerGuiElement(int guiId, EntityPlayer player, World world, int x, int y, int z)
    {
        if (guiId == CustomSurvival.GUI_CUSTOM_INV) {
            return new PlayerContainerCustom(player, player.inventory, ExtendedPlayer.get(player).inventory);
        } else {
            return null;
        }
    }

    @Override
    public Object getClientGuiElement(int guiId, EntityPlayer player, World world, int x, int y, int z)
    {
        if (guiId == CustomSurvival.GUI_CUSTOM_INV) {
            return new GuiPlayerInventoryCustom(player, player.inventory, ExtendedPlayer.get(player).inventory);
        } else {
            return null;
        }
    }


KeybindingRegistry:

@SubscribeEvent
    public void onKeyInput(InputEvent.KeyInputEvent event) {
        int i = 0;

        for(Iterator i$ = keyList.iterator(); i$.hasNext(); ++i) {
            KeyBinding kb = (KeyBinding)i$.next();
            keypressed[i] = Boolean.valueOf(kb.isPressed());
        }

        if(keypressed[3].booleanValue() && this.mc.inGameHasFocus && this.mc.thePlayer != null) {
            if(!isKeyDown) {
                if(!this.mc.thePlayer.capabilities.isCreativeMode)
                {
                    CustomSurvival.networkWrapper.sendToServer(new MSG_OpenGui(0, CustomSurvival.GUI_CUSTOM_INV));
                } else {
                    this.mc.getNetHandler().addToSendQueue(new C16PacketClientStatus(C16PacketClientStatus.EnumState.OPEN_INVENTORY_ACHIEVEMENT));
                    this.mc.displayGuiScreen(new GuiInventory(this.mc.thePlayer));
                }

                isKeyDown = true;
                return;
            }

            if(!this.mc.thePlayer.capabilities.isCreativeMode) {
                CustomSurvival.networkWrapper.sendToServer(new MSG_OpenGui(1, CustomSurvival.GUI_CUSTOM_INV));
            } else {
                this.mc.displayGuiScreen((GuiScreen) null);
            }

            isKeyDown = false;
        }

    }

Ну а до креатива еще дело дойдет, да и он работает отдельно.
 
2,505
81
397
Ой, не заметил.

Костыли от форжа. Кайф. Тебе не поможет этот гайд.
Во-первых, ты выкинул все ванильные слоты. Но на самом деле они остались, и это не есть хорошо. Нужно переприсвоить inventoryContainer, чтобы именно твой контейнер инвентаря был всегда открыт, а не ванильный. (Да, если нет открытого контейнера, значит открывается player.inventoryContainer - ванильная механика). Креатив работает не отдельно. Гуи креатива двигает слоты от player.inventoryContainer. Естественно все по индексам, поэтому высок шанс, что будет падать или работать некорректно.
Во-вторых, у тебя измененный хот бар. А это вообще дичь. Если у тебя эти слоты будут расположены на индексах 0-9 (т.е. 0-4), то может с горем пополам еще будет работать. У маджонгов проверка на диапазон от 0 до 9 всунута минимум в 4ех местах.

Лично я полностью подменил player.inventoryContainer на свой PlayerContainer и player.inventory на свой InventoryPlayer. Зато теперь могу полностью все изменять, как хочу. И не нужны костыли в виде IEEP, пакетов и прочего. Совместимость с другими модами ровно такая же, как и в этом гайде - её нет. Но она и не нужна. Проблему с индексами хот бара решал хуками. Проблему с креативом - тоже хуками, но потом полностью изменил креатив.
 
355
2
17
Слишком много переписывать придется а мне так далеко не нужно, мне бы эту проблему с индексацией решить. IEEP тоже использую, собственно там и указываю этот инвентарь. В пример аналогичных GUI могу привести мод DayM. Тоже идет 5 слотов хотбара и контейнеры в виде одежды.

Но чет по аналогии у меня лыжи не едут =)
 
Сверху