Предмет и инвентарь

Версия Minecraft
1.7.10
API
Forge
24
1
1
Хочу создать предмет который будет иметь интерфейс, кароче примитивную сумку, чтобы не запариваться и придумать что то правильно решил сделать отдельный враппер под ItemStack чтобы можно переиспользовать в будущем, но проблема в том, що как только предметы попадут в слоты "предмета" при следующем открытии их не будет
Дебажил до момента записи нбт, запись идет в стак, после открытия нбт пустое
Смотрел разные реализации, в драконике, термал, таумкрафт, все используют сильно модульные класы, что приводит к непониманию
Код враппера public class ItemStackInventory implements IInventory { private final Entit - Pastebin.com

В предмете, дабы сильно не мудрить решил сделать "заглушкой" оно работает, и не крашит, мне этого достаточно
Java:
public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer player) {
        if (!world.isRemote && player.isSneaking()) {
            ItemStack currentItem = player.inventory.getCurrentItem();
            if (currentItem != null) {
                if (currentItem.getItem() == this) {
                    ItemStackInventory inventory = new ItemStackInventory(player, currentItem, 36);
                    player.displayGUIChest(inventory);
                }
            }
        }

        return stack;
    }

Пытался и хранить еще и само нбт может оно как сбрасывается, и в него записывать, сохранять при любом действии, не помогло
Есть предположение что мне передается левый стак, который не правильный, потому что логи в onUpdate пустые при любых действиях или надо писать свой контейнер, и в нем уже сохранять при закрытии, но мне кажется бред что setInventorySlotContents с обновлением нбт не сохраняет, а при контейнере будет..
Тестил только в локале, чтобы без синхронизации

При закрытии сохраняет
[Server thread/INFO] [STDOUT]: [.api.ItemStackInventory:closeInventory:121]: {Items:[0:{Slot:21b,id:225s,Count:1b,Damage:0s},1:{Slot:30b,id:225s,Count:1b,Damage:0s}]}
Но как только начинаю открывать,

[Server thread/INFO] [STDOUT]: [:eek:nUpdate:81]: {}
[Client thread/INFO] [STDOUT]: [:eek:nUpdate:81]: {}
 
Решение
пробовать в saveToNBT() сохранять данные в стак, а потом делать player.inventory.setCurrentItem()
private void saveToNBT():
private void saveToNBT() {
System.out.printf("Item %s == %s: %s%n", player.getCurrentEquippedItem(), itemStack, player.getCurrentEquippedItem() == itemStack);
if (!itemStack.hasTagCompound()) {
itemStack.setTagCompound(new NBTTagCompound());
    }
NBTTagList tagList = new NBTTagList();
for (int i = 0; i < inventory.length; i++) {
if (inventory[i] != null) {
NBTTagCompound tag = new NBTTagCompound();
tag.setByte("Slot", (byte) i);
 inventory[i].writeToNBT(tag);
            tagList.appendTag(tag);
        }
    }
itemStack.getTagCompound().setTag("Items", tagList);

// Замена предмета
int currentItem =...
В целом всё правильно. Ошибка думаю в том что логика closeInventory() отрабатывает на стороне клиента, а не на сервере.
Стандартный GUI сундука не подходит для сумок: надо ещё реализовать блокировку слота с сумкой, проверку её наличия в руке и там же реализовать логику финального сохранения при закрытии.

Код onItemRightClick() содержит много ненужного. Параметр stack уже содержит нужный предмет. Проверяйте только чтобы кол-во было равно единице (защита от дюпа через стакнутые предметы).
 
closeInventory() отрабатывает на стороне клиента, а не на сервер
По факту не сможет сработать на клиенте потому что враппер создается только на сервере, в предмете логика предусмотрена на открытие только на сервере
А клиенту приходят слоты, или какой нибдуь IInvetoryImpl, или я не прав?

Переписал также на контейнер, с "финальным" сохранением, и проверкой на сервер при сохранении
Контейнер:
@Override
public void onContainerClosed(EntityPlayer player) {
 inventory.closeInventory();
 super.onContainerClosed(player);
}


Враппер:
 @Override
    public void closeInventory() {
        if (!player.worldObj.isRemote) {
            this.saveToNBT();
        }
    }

При открытии нбт как и не было...
Думал что какая то логика не правильно написана при первом открытии и пересоздает нбт, но даже до создания враппера нбт уже пустое
 
Не открывается пастбин, есть вариант куда то еще залить?
Так навскидку бы посмотреть куда сейвишь нбт
if (currentItem.getItem() == this)
Лучше уж на instanceof YourItemClass проверить, чем так
 
Последнее редактирование:
Паста и хаста не работают, заливаем сюда..:
public class ItemStackInventory implements IInventory {
    private final EntityPlayer player;
    private final ItemStack itemStack;
    private final ItemStack[] inventory;
    private final int size;

    public ItemStackInventory(EntityPlayer player, ItemStack itemStack, int size) {
        this.player = player;
        this.itemStack = itemStack;
        this.size = size;
        this.inventory = new ItemStack[size];
        loadFromNBT();
    }

    private void loadFromNBT() {
        if (!itemStack.hasTagCompound()) {
            itemStack.setTagCompound(new NBTTagCompound());
        }
        NBTTagList tagList = itemStack.getTagCompound().getTagList("Items", 10);
        for (int i = 0; i < tagList.tagCount(); i++) {
            NBTTagCompound tag = tagList.getCompoundTagAt(i);
            int slot = tag.getByte("Slot") & 255;
            if (slot < size) {
                inventory[slot] = ItemStack.loadItemStackFromNBT(tag);
            }
        }
    }

    private void saveToNBT() {
        if (!itemStack.hasTagCompound()) {
            itemStack.setTagCompound(new NBTTagCompound());
        }
        NBTTagList tagList = new NBTTagList();
        for (int i = 0; i < inventory.length; i++) {
            if (inventory[i] != null) {
                NBTTagCompound tag = new NBTTagCompound();
                tag.setByte("Slot", (byte) i);
                inventory[i].writeToNBT(tag);
                tagList.appendTag(tag);
            }
        }
        itemStack.getTagCompound().setTag("Items", tagList);
    }

    @Override
    public int getSizeInventory() {
        return size;
    }

    @Override
    public ItemStack getStackInSlot(int index) {
        return inventory[index];
    }

    @Override
    public void setInventorySlotContents(int index, ItemStack itemStack) {
        inventory[index] = itemStack;
        markDirty();
    }

    @Override
    public String getInventoryName() {
        return itemStack.getDisplayName();
    }

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

    @Override
    public ItemStack decrStackSize(int index, int count) {
        if (inventory[index] != null) {
            ItemStack itemstack;
            if (inventory[index].stackSize <= count) {
                itemstack = inventory[index];
                inventory[index] = null;
            } else {
                itemstack = inventory[index].splitStack(count);
                if (inventory[index].stackSize == 0) {
                    inventory[index] = null;
                }
            }
            markDirty();
            return itemstack;
        }
        return null;
    }

    @Override
    public ItemStack getStackInSlotOnClosing(int index) {
        if (inventory[index] != null) {
            ItemStack itemstack = inventory[index];
            inventory[index] = null;
            markDirty();
            return itemstack;
        }
        return null;
    }

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

    @Override
    public void openInventory() {
    }

    @Override
    public void closeInventory() {
        if (!player.worldObj.isRemote) {
            this.saveToNBT();
        }
    }

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

    @Override
    public void markDirty() {
        this.saveToNBT();
    }

    @Override
    public boolean isItemValidForSlot(int index, ItemStack stack) {
        return true;
    }
}
 
Где ты этот IInventory вообще юзаешь?
Переписал под свой контейнер, на основе сундука, там при добавлении слотов надо IInventory
И в контейнере хранится экземпляр враппера

Java:
public class ContainerPortableBag extends Container {
    public final ItemStackInventory inventory;
    private final int numRows;

    public ContainerPortableBag(ItemStackInventory inventory, InventoryPlayer inventoryPlayer) {
        this.inventory = inventory;
        this.numRows = inventory.getSizeInventory() / 9;
        this.inventory.openInventory();
        int i = (this.numRows - 4) * 18;

        for (int y = 0; y < 4; ++y) {
            for (int x = 0; x < 9; ++x) {
                this.addSlotToContainer(new Slot(inventory, x + y * 9, 8 + x * 18, 18 + y * 18));
            }
        }

        for (int j = 0; j < 3; ++j) {
            for (int k = 0; k < 9; ++k) {
                this.addSlotToContainer(new Slot(inventoryPlayer, k + j * 9 + 9, 8 + k * 18, 103 + j * 18 + i));
            }
        }

        for (int j = 0; j < 9; ++j) {
            this.addSlotToContainer(new Slot(inventoryPlayer, j, 8 + j * 18, 161 + i));
        }
    }

Проверял стаки сразу же, это один и тот же стак && предмет
 
Очень вряд ли, но я бы попробовал убрать final у поля itemStack (но это дичь, вряд ли)
Либо пробовать в saveToNBT() сохранять данные в стак, а потом делать player.inventory.setCurrentItem() и смотреть будет ли разница
Так то странно, ItemStack вроде мутабельный
 
пробовать в saveToNBT() сохранять данные в стак, а потом делать player.inventory.setCurrentItem()
private void saveToNBT():
private void saveToNBT() {
System.out.printf("Item %s == %s: %s%n", player.getCurrentEquippedItem(), itemStack, player.getCurrentEquippedItem() == itemStack);
if (!itemStack.hasTagCompound()) {
itemStack.setTagCompound(new NBTTagCompound());
    }
NBTTagList tagList = new NBTTagList();
for (int i = 0; i < inventory.length; i++) {
if (inventory[i] != null) {
NBTTagCompound tag = new NBTTagCompound();
tag.setByte("Slot", (byte) i);
 inventory[i].writeToNBT(tag);
            tagList.appendTag(tag);
        }
    }
itemStack.getTagCompound().setTag("Items", tagList);

// Замена предмета
int currentItem = player.inventory.currentItem;
player.inventory.mainInventory[currentItem] = itemStack;
}

Ну это прикол, вот этот блок кода решил проблему с сохранением, но по хешу стаки идентичны, почему так происхоит?

Заметил также интересный момент,
[Server thread/INFO] [STDOUT]: [.api.ItemStackInventory:saveToNBT:39]: Item 1xitem.PortableBag@0 == 1xitem.PortableBag@0: true
... Спам еще 34 слотами по TRUE
и в слот который я положил предмет
[Server thread/INFO] [STDOUT]: [.api.ItemStackInventory:saveToNBT:39]: Item 1xitem.PortableBag@0 == 1xitem.PortableBag@0: false
Выходит что в каком то моменте клонируется стак?
 
Вряд ли JVM умеет неопределенно вызвать метод ItemStack#copy().
К сожалению доступа на гитхаб у меня нет в данный момент, хотя как раз так же редактирую в гуишке нбт предмета, который находится в руках. Если вечером дойду, посмотрю и сравню, как сделано у меня
 
В общем, дополню свой ответ. Я бы на твоем месте, раз ты открываешь сумку исходя из того, что она в руках, то в момент сохранения данных сумки в НБТ, я бы не обращался к final полю itemStack, а брал бы текущий предмет через игрока - player#getHeldItem()
Так ты получишь гарантированно тот стак, и вполне можешь менять его кишки. В твоем случае - запись в его НБТ списка итемстаков. Тогда возможно и не придется принудительно сетать предмет в руки
 
а брал бы текущий предмет через игрока - player#getHeldItem()
Окей, для сумок это подойдет я так и оставлю, а если это будет вариация которая будет в инвентаре а не в руке, и что то через ивенты ловить я уже не смогу так просто засетать, потому что даже ссылки нет одинаковой
 
я про то что "сумка" будет в инвентаре, и через ивент подбирать предметы, но стандартная версия "сохранения" же нбт вообще не сохраняет, а сетать предмет я не найду его, потому что нет уникальности, или одинаковой ссылки, да если была бы одинаковая ссылка не потребовалось извращаться с сетом в руке
 
Тут ничего сложного. Тебе даже куда то слать итемстак не надо. Достаточно в цикле в момент подбирания пройтись по инвентарю и запихать предмет в первую попавшуюся сумку, аля:

Java:
for(int slotID = 0; slotID < this.player.inventory.getInventorySize(); i++) {
    Slot slot = this.player.inventory.getSlot(slotID);
    ItemStack stackInSlot = slot.getStack();
    if(stackInSlot != null && stackInSlot.getItem() instanceof ItemBag) { //Вместо ItemBag - класс твоего предмета-сумки
        NBTTagCompound tag = stackInSlot.getTagCompound();
         //модификация нбт стака
    }
}
Код накидал примерно в уме, и это сработает 100%
Ну и естественно не забыть прервать цикл в нужный момент, и если сумка переполнилась, то искать следующую и добавлять в нее. Не нашлась - добавлять просто в инвентарь

Я вообще более чем уверен, что у тебя не сохранялось нбт из-за того, что ты итемстак просто вынес в отдельное поле в IInventory
 
Назад
Сверху