Работа с контейнером

Версия Minecraft
1.7.10
API
Forge
Доброго времени суток. Захотел поиграться с тайлами и контейнерами для них. Проблема в том, что после того, как я положил в слот предмет, забрать я его больше не могу, только через shift-клик(для шифта понятное дело, я забацал метод transferStackInSlot), а как забрать предмет через обычный клик, где я что-то упустил?
TraderContainer:
public class TraderContainer extends Container {

    private TileTrader tileTrader;

    public TraderContainer(EntityPlayer player, TileTrader tileTrader) {
        this.tileTrader = tileTrader;
        addSlotToContainer(new Slot(tileTrader, 0, 111, 25));
        addSlotToContainer(new Slot(tileTrader, 1, 111, 50));
        addSlotToContainer(new Slot(tileTrader, 2, 151, 25));
        addSlotToContainer(new Slot(tileTrader, 3, 151, 50));

        //inventory
        for(int i = 0 ; i< 3; i++) {
            for(int j = 0; j < 9; j++) {
                addSlotToContainer(new Slot(player.inventory, j + i * 9 + 9,  8 + j * 18, 101 + i * 18));
            }
        }
        //hotbar
        for(int i = 0; i < 9; i++) {
            addSlotToContainer(new Slot(player.inventory, i, 8 + i * 18, 159));
        }
    }

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

    @Override
    public void onContainerClosed(EntityPlayer p_75134_1_) {
        this.tileTrader.closeInventory();
        super.onContainerClosed(p_75134_1_);
    }

    @Override
    public ItemStack transferStackInSlot(EntityPlayer player, int i) {
        ItemStack itemstack = null;
        Slot slot = (Slot)this.inventorySlots.get(i);

        if (slot != null && slot.getHasStack()) {
            ItemStack itemstack1 = slot.getStack();
            itemstack = itemstack1.copy();

            if (i <= 3) {
                if (!this.mergeItemStack(itemstack1, 4, 40, true)) {
                    return null;
                }
                slot.onSlotChange(itemstack1, itemstack);
            }
            else if (i >= 4 && i < 31) {
                if (!this.mergeItemStack(itemstack1, 31, 40, false)) {
                    return null;
                }
            }
            else if (i >= 31 && i < 40) {
                if (!this.mergeItemStack(itemstack1, 4, 31, false)) {
                    return null;
                }
            }
            else if (!this.mergeItemStack(itemstack1, 4, 40, false)) {
                return null;
            }
            if (itemstack1.stackSize == 0) {
                slot.putStack(null);
            }
            else {
                slot.onSlotChanged();
            }
            if (itemstack1.stackSize == itemstack.stackSize) {
                return null;
            }

            slot.onPickupFromSlot(player, itemstack1);
        }
        return itemstack;
    }
}
TilETrader:
public class TileTrader extends TileEntity implements ISidedInventory {

    private ItemStack[] slots = new ItemStack[4];
    private GameProfile owner;
    private EntityPlayer entityPlayer;

    public TileTrader() {

    }

    @Override
    public boolean canUpdate() {
        return false;
    }

    @Override
    public void writeToNBT(NBTTagCompound nbtTag) {
        super.writeToNBT(nbtTag);
        if(owner != null) {
            NBTTagCompound ownerNbt = new NBTTagCompound();
            NBTUtil.func_152460_a(ownerNbt, owner);
            nbtTag.setTag("owner", ownerNbt);
        }
        NBTTagList list = new NBTTagList();
        for(int i = 0; i < slots.length; i++) {
            if(slots[i] != null) {
                NBTTagCompound item = new NBTTagCompound();
                item.setByte("index", (byte) i);
                slots[i].writeToNBT(item);
                list.appendTag(item);
            }
        }
        nbtTag.setTag("slots", list);
    }

    @Override
    public void readFromNBT(NBTTagCompound nbtTag) {
        super.readFromNBT(nbtTag);
        this.owner = NBTUtil.func_152459_a(nbtTag.getCompoundTag("owner"));
        NBTTagList list = nbtTag.getTagList("slots", 10);
        for(int i = 0; i < list.tagCount(); i++) {
            NBTTagCompound tag = list.getCompoundTagAt(i);
            byte index = tag.getByte("index");
            if(index >= 0 && index < this.slots.length) {
                slots[index] = ItemStack.loadItemStackFromNBT(tag);
            }
        }
    }

    public void setOpeners(GameProfile gameProfile, EntityPlayer player) {
        if(owner == null) {
            this.owner = gameProfile;
            markDirty();
        }
        this.entityPlayer = player;
    }

    public GameProfile getOwner() {
        return owner;
    }

    public boolean isOwner(EntityPlayer player) {
        return player.getGameProfile().getId() == getOwner().getId();
    }

    public EntityPlayer getEntityPlayer() {
        return entityPlayer;
    }

    @Override
    public int[] getAccessibleSlotsFromSide(int i) {
        return new int[] {0, 1, 2, 3};
    }

    @Override
    public boolean canInsertItem(int i, ItemStack itemStack, int j) {
        return false;
    }

    @Override
    public boolean canExtractItem(int i, ItemStack itemStack, int j) {
        return true;
    }

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

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

    @Override
    public ItemStack decrStackSize(int p_70298_1_, int p_70298_2_) {
        return null;
    }

    @Override
    public ItemStack getStackInSlotOnClosing(int p_70304_1_) {
        return null;
    }

    @Override
    public void setInventorySlotContents(int i, ItemStack itemstack) {
        this.slots[i] = itemstack;
        if ((itemstack != null) && (itemstack.stackSize > getInventoryStackLimit())) {
            itemstack.stackSize = getInventoryStackLimit();
        }
    }

    @Override
    public String getInventoryName() {
        return "Торговый аппарат";
    }

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

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

    @Override
    public boolean isUseableByPlayer(EntityPlayer player) {
        return player.worldObj.getTileEntity(this.xCoord, this.yCoord, this.zCoord) == this && player.getDistanceSq(this.xCoord, this.yCoord, this.zCoord) <= 64;
    }

    @Override
    public void openInventory() {
    }

    @Override
    public void closeInventory() {
        entityPlayer = null;
        markDirty();
    }

    @Override
    public boolean isItemValidForSlot(int i, ItemStack stack) {
        System.out.println(i + " valid");
        return true;
    }

}
И еще один вопрос, как можно реализовать примерно такую вещь: кладу в слот предмет, он помещается, но при этом из курсора он не пропадает, ПКМ увеличивает кол-во предметов, ЛКМ уменьшает.
 

VeniVidiVici

Санта Барбарис
327
15
198
Ну, наверное потому что у тебя метод decrStackSize тупо нулл возвращает?
Открой печку, например, и скопируй реализации методов IInventory.
где я что-то упустил
Типо такого
decrStackSize:
public ItemStack decrStackSize(int slotId, int stackCount)
{
    if (this.slots[slotId] != null)
    {
        ItemStack itemstack;

        if (this.slots[slotId].stackSize <= stackCount)
        {
            itemstack = this.slots[slotId];
            this.slots[slotId] = null;
            return itemstack;
        }
        else
        {
            itemstack = this.slots[slotId].splitStack(stackCount);

            if (this.slots[slotId].stackSize == 0)
            {
                this.slots[slotId] = null;
            }

            return itemstack;
        }
    }
    else
    {
        return null;
    }
}
 
Ну, наверное потому что у тебя метод decrStackSize тупо нулл возвращает?
Открой печку, например, и скопируй реализации методов IInventory.
Да, сработало, спасибо. А что на счет второго вопроса выше? Код не прошу, хотя бы толчек в какую сторону копать, а то идей вообще нет.

Появился еще один вопрос, как можно проверить может ли игрок забирать или класть предметы в слот? Сделал проверку в методах setInventorySlotContents и decrStackSize, но оказывается они еще и на клиенте отрабатывают, на сервере получаю краш.
Java:
 @Override
    public void setInventorySlotContents(int i, ItemStack itemstack) {
        if(!isOwner(entityPlayer) && i < 2) {
            return;
        }
        this.slots[i] = itemstack;
        if ((itemstack != null) && (itemstack.stackSize > getInventoryStackLimit())) {
            itemstack.stackSize = getInventoryStackLimit();
        }
    }

public ItemStack decrStackSize(int i, int j) {
        if(!isOwner(entityPlayer) && i < 2) {
            return null;
        }
        if (this.slots[i] != null) {
            ItemStack itemstack;
            if (this.slots[i].stackSize <= j) {
                itemstack = this.slots[i];
                this.slots[i] = null;
                return itemstack;
            }
            else {
                itemstack = this.slots[i].splitStack(j);
                if (this.slots[i].stackSize == 0) {
                    this.slots[i] = null;
                }
                return itemstack;
            }
        }
        return null;
    }

public boolean isOwner(EntityPlayer player) {
        return player.getGameProfile().getId() == getOwner().getId();
    }

Владелец и человек. который открыл инвентарь устанавливаются в методе onBlockActivated.
Юзать пакеты ?
 

VeniVidiVici

Санта Барбарис
327
15
198
Появился еще один вопрос, как можно проверить может ли игрок забирать или класть предметы в слот
1. Тайл должен хранить инфу о том что, кому и как можно (запись того же owner)
2. Окружающие должны знать инфу о том, что хранит тайл, хотя бы перед взаимодействием с ним
3. Никто не отменял if(!world.isRemote) для обработки только на сервере в общих методах
 

tox1cozZ

aka Agravaine
8,454
598
2,890
Появился еще один вопрос, как можно проверить может ли игрок забирать или класть предметы в слот
Напиши свой класс extends Slot, в методах isItemValid и canTakeStack проверяй можно ли туда класть или забирать твой предмет. Игрока, тайл и тд передавай в конструктор слота. Ну и сам слот уже добавляй в контейнере.
 
Напиши свой класс extends Slot, в методах isItemValid и canTakeStack
Спасибо, сработало, но есть один нюанс. Могу ли я передать как-то информацию тайла на клиент при открытии контейнера в гуихандлере (сперва подумал опять же одолжить код у печки с ее прогрессбаром, но мне необходимо передать как минимум GameProfile и еще несколько строк) или только пакетом? А то есть некие баги на клиенте
 

VeniVidiVici

Санта Барбарис
327
15
198
Я не нашел как это сделать, пришлось копировать метод openGui и слать свой пакет с нужными данными клиенту и там уже открывать скрин.
Можно же при активации блока слать пакет с данными игроку инициатору, и из пакета уже вызывать скрин, вместо прямого переделывания openGui, не? Или это та же шляпа, только сбоку?)
 
В общем, пытаюсь задать данные тайла на клиенте через пакет, отсылаю данные при открытии в GuiHandler, но где-то косячу.
packet:
public class UpdateTraderInfoPacket implements IMessage, IMessageHandler<UpdateTraderInfoPacket, IMessage>  {

    public GameProfile gameProfile;
    public int x, y, z;

    public UpdateTraderInfoPacket() {

    }

    public UpdateTraderInfoPacket(GameProfile gameProfile, int x, int y, int z) {
        this.gameProfile = gameProfile;
        this.x = x;
        this.y = y;
        this.z = z;
    }

    @Override
    public void fromBytes(ByteBuf buf) {
        System.out.println("read");
        UUID uuid = new UUID(buf.readLong(), buf.readLong());
        gameProfile = new GameProfile(uuid, ByteBufUtils.readUTF8String(buf));
        x = buf.readInt();
        y = buf.readInt();
        z = buf.readInt();
    }

    @Override
    public void toBytes(ByteBuf buf) {
        System.out.println("write");
        buf.writeLong(gameProfile.getId().getMostSignificantBits());
        buf.writeLong(gameProfile.getId().getLeastSignificantBits());
        ByteBufUtils.writeUTF8String(buf, gameProfile.getName());
        buf.writeInt(x);
        buf.writeInt(y);
        buf.writeInt(z);
    }

    @Override
    public IMessage onMessage(UpdateTraderInfoPacket message, MessageContext ctx) {
        if(ctx.side.isClient()) {
            System.out.println("update");
            EntityPlayer player = Minecraft.getMinecraft().thePlayer;
            TileEntity tileEntity = player.worldObj.getTileEntity(x, y, z);
            if(tileEntity != null) {
                ((TileTrader)tileEntity).setOpeners(gameProfile, player);
            }
        }
        return null;
    }
}

Guihandler:
public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
        switch (ID) {
            case Constants.TRADER_GUI_ID:
                TileEntity tileEntity = world.getTileEntity(x, y, z);
                if(tileEntity != null) {
                    if (tileEntity instanceof TileTrader) {
                        if(((TileTrader) tileEntity).getEntityPlayer() != null) {
                            player.addChatMessage(new ChatComponentText("§4Данный аппарат уже используется!"));
                            return null;
                        }
                        NetWorkHandler.sendToPlayer(new UpdateTraderInfoPacket(player.getGameProfile(), x, y, z), (EntityPlayerMP) player);
                        ((TileTrader) tileEntity).setOpeners(player.getGameProfile(), player);
                        return new TraderContainer(player, (TileTrader) tileEntity);
                    }
                }
                break;
        }
        return null;
    }
Прошу помощи
 

tox1cozZ

aka Agravaine
8,454
598
2,890
Можно же при активации блока слать пакет с данными игроку инициатору, и из пакета уже вызывать скрин, вместо прямого переделывания openGui, не? Или это та же шляпа, только сбоку?)
Контейнер же все равно надо открывать на сервере.
Можно конечно слать пакет после openGui и записывать данные в уже открытый гуи, но мне это чет не нравится.


Ты сначала шлёшь ракет данных, а потом пакет открытия гуи метод openGui. То есть у тебя не проходит проверка на открытый гуи, ибо он ещё не открыт 😄
 
В какой момент тогда надо слать пакет? Ведь getServerGuiElement это уже самый последний метод, который отрабатывается на сервере, дальше же идет клиент часть Разве если я даже пошлю пакет рано, то тайл не обновится на клиенте, дабы клиент мог его смотреть всегда?
 

tox1cozZ

aka Agravaine
8,454
598
2,890
Открой openGui метод и посмотри.
Сначала вызывается getServerGuiElement твоего хандлера, а дальше уже отсылается фордже-пакет для открытия гуи на клиенте.
А так как ты в ханделере шлешь свой пакет (пакеты идут строго по порядку, внезапно), то сначала приходит твой пакет где ты пытаешься установить данные в гуи, а только потом уже прилетает фордже-пакет с открытием этого самого гуи.

В какой момент тогда надо слать пакет?
Ну я написал выше как делал я. Копируешь весь openGui и вместо фордже-пакет шлешь свой с нужными данными, а на клиент их принимаешь и открываешь сам гуи.
Либо, как сказал @VeniVidiVici, после вызова openGui у себя в коде.
 
Сверху