Теряются предметы в GUI

Версия Minecraft
1.7.10
90
1
Здравствуйте! Пишу небольшой аддон, в котором есть один блок с ГУИ, в верхний слот которого кладутся фильтры для воды. Через некоторое время фильтры ломаются и попадают в нижний слот. Проблема состоит в том, что периодически происходит следующая проблема. Если оставить наполовину сломанный фильтр в ГУИ( у фильтров есть прочность) и перезайти в мир или перезапустить игру, то прочность у фильтра самопроизвольно восстанавливается. Иногда до конца, иногда нет. И если один фильтр сломался и попал в нижний слот, а в верхний был помещен новый, целый, то при перезаходе сломанный фильтр с нижнего слота исчезает вообще. Предполагаю, что проблема с NBT, пытался переписать. Но не помогло. Проблема то есть, то нет. Не понимаю условия её появления, из за этого сложно отладить. Вот код всех нужных классов

Код:
public class CleanMachineTile extends TileBuildCraft implements ISidedInventory,IPipeConnection,IFluidHandler,IRedstoneEngineReceiver {

    private String inventoryTitle="CleanserInventory";

    private int slotsCount;

    private ItemStack[] inventoryContents;

    private List field_70480_d;

    private boolean hasCustomInventoryName;

    private boolean powerRedEnable;

    private Tank tankWater;

    private Tank tankCleanWater;

    private TankManager<Tank> tankManager;

    private int tempVolumeCleanWater=0;

    private int damageFilter=0;


    private double rateProduction=1;

    private double rateEnergyCost=1;


   public CleanMachineTile(String inventoryTitle, boolean hasCustomInventoryName)

    {

        this.inventoryTitle = inventoryTitle;

        this.hasCustomInventoryName = hasCustomInventoryName;

        init();

    }

    public CleanMachineTile(){

        init();

    }


    private void init(){

        if(this.inventoryContents==null) {

            this.inventoryContents = new ItemStack[Constants.SlotCount];

        }

        this.slotsCount = Constants.SlotCount;

        tankWater=new Tank("tank", Constants.CapacityDirtWater, this);

        tankCleanWater=new Tank("tank2",Constants.CapacityClearWater,this);

        tankManager=new TankManager<>();

        this.tankManager.add(tankWater);

        this.tankManager.add(tankCleanWater);

        this.setBattery(new RFBattery(Constants.CapacityEnergy, Constants.MaxReceiveEnergy, 0));

    }


    @Override

    public int[] getAccessibleSlotsFromSide(int p_94128_1_) {

        return new int[0];

    }


    @Override

    public boolean canInsertItem(int p_102007_1_, ItemStack p_102007_2_, int p_102007_3_) {

        return this.isItemValidForSlot(p_102007_1_, p_102007_2_);

    }


    @Override

    public boolean canExtractItem(int p_102008_1_, ItemStack p_102008_2_, int p_102008_3_) {

        return false;

    }


    @Override

    public int getSizeInventory()

    {

        return this.slotsCount;

    }


    @Override

    public ItemStack getStackInSlot(int index) {

        if(this.inventoryContents!=null){

            return index >= 0 && index < this.inventoryContents.length ? this.inventoryContents[index] : null;

        }else

            return null;


    }


    @Override

    public void updateEntity() {

            super.updateEntity();

            //Если соблюдены условия, раз в секунду очищать воду

            if (!worldObj.isRemote) {

                if (checkCondition()) {

                    if (new Date().getTime() % 1000 < 50) {

                        clearWater();

                        getBattery().setEnergy(getBattery().getEnergyStored()-(int)(Constants.CountEnergyForOneCleaning*rateEnergyCost));


                        calculateRate();

                    }

                }

                worldObj.markBlockForUpdate(xCoord,yCoord,zCoord);

                updateContainingBlockInfo();


            }

    }

    //Рассчитываем коэффициенты эффективност и затрат энергии в зависимости от количества запасенной энергии

    // (а косвенно от количество приходящей энергии - для поддержания запаса)

    private void calculateRate(){

        if(getBattery().getEnergyStored()<Constants.CapacityEnergy/3){

            rateProduction=1;

            rateEnergyCost=1;

        }else if(getBattery().getEnergyStored()>=Constants.CapacityEnergy/3&&getBattery().getEnergyStored()<Constants.CapacityEnergy*2/3){

            rateProduction=1.5;

            rateEnergyCost=1.5;

        }else if(getBattery().getEnergyStored()>=Constants.CapacityEnergy*2/3&&getBattery().getEnergyStored()<=Constants.CapacityEnergy){

            rateProduction=2.5;

            rateEnergyCost=5;

        }

    }


    @Override

    public ItemStack decrStackSize(int numberSlot, int p_70298_2_) {

        if (this.inventoryContents[numberSlot] != null){

            ItemStack itemstack;


            if (this.inventoryContents[numberSlot].stackSize <= p_70298_2_){

                itemstack = this.inventoryContents[numberSlot];

                this.inventoryContents[numberSlot] = null;

                this.markDirty();

                return itemstack;

            }

            else{

                itemstack = this.inventoryContents[numberSlot].splitStack(p_70298_2_);


                if(this.inventoryContents[numberSlot].stackSize == 0){

                    this.inventoryContents[numberSlot] = null;

                }


                this.markDirty();

                return itemstack;

            }

        }else{

            return null;

        }

    }


    @Override

    public ItemStack getStackInSlotOnClosing(int indexSlot) {

        if (this.inventoryContents[indexSlot] != null){

            ItemStack itemstack = this.inventoryContents[indexSlot];

            this.inventoryContents[indexSlot] = null;

            return itemstack;

        }else{

            return null;

        }


    }


    @SideOnly(Side.CLIENT)

    @Override

    public void setInventorySlotContents(int index, ItemStack stack) {

        if(this.inventoryContents!=null){

            this.inventoryContents[index] = stack;

            if(stack!=null&&stack.getItem()==ItemLoader.filter){

                damageFilter = stack.getItemDamage();

            }


            if(index==0){

                if (stack != null && stack.stackSize > this.getInventoryStackLimit()){

                    stack.stackSize = this.getInventoryStackLimit();


                }

            }else if(index==1){

                if (stack != null && stack.stackSize > 64) {

                    stack.stackSize = 64;


                }

            }

            this.markDirty();

        }

    }


    @Override

    public String getInventoryName(){

        return this.hasCustomInventoryName() ? this.inventoryTitle : "CleanserInventory";

    }


    @Override

    public boolean hasCustomInventoryName()

    {

        return this.hasCustomInventoryName;

    }


    @Override

    public int getInventoryStackLimit() {

        return 1;

    }


    @Override

    public boolean isUseableByPlayer(EntityPlayer player)

    {

        return true;

    }


    @Override

    public void openInventory() {}


    @Override

    public void closeInventory() {}


    @Override

    public boolean isItemValidForSlot(int p_94041_1_, ItemStack stack) {

        if(stack!=null){

            return (stack.getItem() == ItemLoader.filter);

        } else return false;

    }

    @Override

    public void readFromNBT(NBTTagCompound data) {

    super.readFromNBT(data);

    NBTTagList tagList = data.getTagList("Data", 10);

    inventoryContents = new ItemStack[Constants.SlotCount];

    if(data.hasKey("CleanserInventory", 8)){

        this.inventoryTitle = data.getString("CleanserInventory");

    }

    //Последний тег под дополнительные данные, а все до него - под предметы

    for(int i = 0; i < tagList.tagCount()-1; i++){

        NBTTagCompound tagCompound = tagList.getCompoundTagAt(i);

        int j = tagCompound.getByte("Slot") & 255;

        setInventorySlotContents(j,ItemStack.loadItemStackFromNBT(tagCompound));

        System.out.println("READ: "+ItemStack.loadItemStackFromNBT(tagCompound));

    }

        //Подгружаем дополнительные данные

        NBTTagCompound tagCompound = tagList.getCompoundTagAt(tagList.tagCount()-1);

        powerRedEnable = tagCompound.getBoolean("redPowerEnable");

        tankWater.fill(new FluidStack(FluidRegistry.WATER,tagCompound.getInteger("waterAmount")),true);

        tankCleanWater.fill(new FluidStack(CoreMod.cleanWaterFluid,tagCompound.getInteger("clearWaterAmount")),true);


        tankWater.readFromNBT(data);

        tankCleanWater.readFromNBT(data);

    }

    @Override

    public void writeToNBT(NBTTagCompound data) {

        super.writeToNBT(data);

        NBTTagList tagList = new NBTTagList();

        NBTTagCompound tagCompound;

        for(int i = 0; i < this.inventoryContents.length; i++){

            if (getStackInSlot(i)!= null) {

                tagCompound = new NBTTagCompound();

                tagCompound.setByte("Slot", (byte) i);//Номер слота

                tagCompound=getStackInSlot(i).writeToNBT(tagCompound);//Сам предмет

                System.out.println("WRITE: "+getStackInSlot(i));


                tagList.appendTag(tagCompound);

            }

        }

        tagCompound = new NBTTagCompound();//Тег для дополнительных данных

        tagCompound.setBoolean("redPowerEnable", powerRedEnable);

        tagCompound.setInteger("waterAmount",tankWater.getFluidAmount());

        tagCompound.setInteger("clearWaterAmount",tankCleanWater.getFluidAmount());

        tagList.appendTag(tagCompound);

        data.setTag("Data", tagList);


        if(this.hasCustomInventoryName()) {

            data.setString("CleanserInventory", this.inventoryTitle);

        }

        tankWater.writeToNBT(data);

        tankCleanWater.writeToNBT(data);

    }


    @Override

    public ConnectOverride overridePipeConnection(IPipeTile.PipeType pipeType, ForgeDirection forgeDirection) {

        if(pipeType!=IPipeTile.PipeType.ITEM) {

            return ConnectOverride.CONNECT;

        }else {

            return ConnectOverride.DISCONNECT;

        }

    }

    //Нужен ли?

    @Override

    public void markDirty(){

        if (this.field_70480_d != null){

            for (int i = 0; i < this.field_70480_d.size(); ++i){

                ((IInventory)this.field_70480_d.get(i)).markDirty();

            }

        }

    }



    public boolean isPowerRedEnable() {

        return powerRedEnable;

    }


    public void setPowerRedEnable(boolean powerRedEnable) {

        this.powerRedEnable = powerRedEnable;

    }


    @Override

    public int fill(ForgeDirection from, FluidStack resource, boolean doFill) {

        worldObj.markBlockForUpdate(xCoord,yCoord,zCoord);

        if(resource!= null && resource.getFluid()!= null) {

            if(resource.getFluid()==FluidRegistry.WATER) {

                return tankWater.fill(resource, doFill);

            } else if(resource.getFluid()==CoreMod.cleanWaterFluid){

                return tankCleanWater.fill(resource, doFill);

            }else

                return 0;

        }else

            return 0;

    }


    @Override

    public FluidStack drain(ForgeDirection from, FluidStack resource, boolean doDrain) {

        if(resource==null){

            return null;

        }

        if(tankCleanWater.getFluid()!=null) {

            //Слив из аппарата в трубу?

            worldObj.markBlockForUpdate(xCoord,yCoord,zCoord);

            return tankCleanWater.drain(resource.amount,doDrain);

        }else return null;

    }


    @Override

    public FluidStack drain(ForgeDirection from, int maxDrain, boolean doDrain) {

        worldObj.markBlockForUpdate(xCoord,yCoord,zCoord);

        return tankCleanWater.drain(maxDrain,doDrain);

    }


    @Override

    public boolean canFill(ForgeDirection from, Fluid fluid) {

        return fluid==FluidRegistry.WATER;

    }


    @Override

    public boolean canDrain(ForgeDirection from, Fluid fluid) {

        return fluid==CoreMod.cleanWaterFluid;

    }


    @Override

    public FluidTankInfo[] getTankInfo(ForgeDirection from) {

        return this.tankManager.getTankInfo(from);

    }


    public void litterFilter(int damage,ItemStack[] inventory){

        if(inventory[0].getItem()==ItemLoader.filter) {

            if (damage >= 5) {

                ItemStack dirtyFilter = new ItemStack(ItemLoader.dirtyFilter);

                if (inventory[1]!=null) {

                    inventory[0]=null;

                    ++inventory[1].stackSize;

                }else {

                    inventory[0]=null;

                    inventory[1] = dirtyFilter;

                }


            }

        }

    }

    public boolean checkCondition(){

        if(inventoryContents[0] != null &&(inventoryContents[0].getItem().equals(ItemLoader.filter)||

                inventoryContents[0].getItem().equals(ItemLoader.charcoalFilter))&&checkSlotDirtyFilters(inventoryContents[1])){

            //Если хватает энергии и бак с чистой водой не заполнен

            if(getBattery().getEnergyStored()>Constants.CountEnergyForOneCleaning&&getTankCleanWater().getFluidAmount()<getTankCleanWater().getCapacity()) {

                return tankWater.getFluid() != null && powerRedEnable;

            }else return false;

        }else return false;

    }

    //Проверяем, что слот для загрязненных фильтров не заполнен

    private boolean checkSlotDirtyFilters(ItemStack slot) {

        return slot == null || slot.stackSize < 64;

    }

    public void clearWater() {

        tempVolumeCleanWater+=Constants.VolumeCWaterAtATime;

        if (tempVolumeCleanWater >= Constants.CountClearWaterForFilter) {

            damageFilter++;

            inventoryContents[0].setItemDamage(damageFilter);

            tempVolumeCleanWater = 0;

            litterFilter(damageFilter, inventoryContents);

        }

        tankWater.drain((int)(Constants.CountWaterAbsorbing * rateProduction), true);

        tankCleanWater.fill(new FluidStack(CoreMod.cleanWaterFluid, (int)(Constants.VolumeCWaterAtATime * rateProduction)), true);

    }

    @Override

    public Packet getDescriptionPacket() {

        worldObj.markBlockForUpdate(xCoord,yCoord,zCoord);

        NBTTagCompound nbt = new NBTTagCompound();

        nbt.setInteger("countWater",tankWater.getFluidAmount());

        writeToNBT(nbt);


        return new S35PacketUpdateTileEntity(xCoord, yCoord, zCoord, 0, nbt);

    }


    @Override

    public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity packet) {

        worldObj.markBlockForUpdate(xCoord,yCoord,zCoord);

        readFromNBT(packet.func_148857_g());


    }


    @Override

    public boolean canConnectRedstoneEngine(ForgeDirection forgeDirection) {

        return true;

    }


    public Tank getTankWater() {

        return tankWater;

    }


    public Tank getTankCleanWater() {

        return tankCleanWater;

    }

    //Нужны ли эти методы??

    public void func_110134_a(IInvBasic p_110134_1_)

    {

        if (this.field_70480_d == null)

        {

            this.field_70480_d = new ArrayList();

        }


        this.field_70480_d.add(p_110134_1_);

    }

    public void func_110132_b(IInvBasic p_110132_1_)

    {

        this.field_70480_d.remove(p_110132_1_);

    }

    public void func_110133_a(String p_110133_1_)

    {

        this.hasCustomInventoryName = true;

        this.inventoryTitle = p_110133_1_;

    }

}

Код:
public class CleanMachineContainer extends BuildCraftContainer {

    public CleanMachineTile machineTile;


    public CleanMachineContainer(InventoryPlayer player, CleanMachineTile tile){

        super(Constants.SlotCount);

        machineTile=tile;


        this.addSlotToContainer(new MachineSlot(tile,0,59,24,new ItemStack[]{new ItemStack(ItemLoader.filter)},1));

        this.addSlotToContainer(new MachineSlot(tile,1,59,58,null,64));


        int i;


        for (i = 0; i < 3; ++i)

        {

            for (int j = 0; j < 9; ++j)

            {

                this.addSlotToContainer(new Slot(player, j + i * 9 + 9, 8 + j * 18, 84 + i * 18));

            }

        }


        for (i = 0; i < 9; ++i)

        {

            this.addSlotToContainer(new Slot(player, i, 8 + i * 18, 142));

        }

    }


    @Override

    public boolean canInteractWith(EntityPlayer player) {

        return machineTile.isUseableByPlayer(player);

    }
Код:
@Mod(modid = MODID)

public class CoreMod {

    public static final String MODID = "cleanwatermod";

    public static Block cleanMachine;

    public static Fluid cleanWaterFluid;

    public static Block cleanWaterBlock;


    @Mod.Instance(MODID)

    public static CoreMod INSTANCE;


    @Mod.EventHandler

    public void preInit(FMLPreInitializationEvent event)

    {

        cleanMachine = (new CleanMachine(Material.iron,this));

        GameRegistry.registerBlock(cleanMachine, "cleanMachine");

        LanguageRegistry.addName(cleanMachine,"Очистное сооружение");


        cleanWaterFluid = new Fluid("CleanWaterFluid");

        FluidRegistry.registerFluid(cleanWaterFluid);

        FluidContainerRegistry.registerFluidContainer(cleanWaterFluid,new ItemStack(ItemLoader.freshWaterBucket),

                new ItemStack(Items.bucket));


        cleanWaterBlock = new FreshWaterFluid(cleanWaterFluid, Material.water);

        GameRegistry.registerBlock(cleanWaterBlock, "cleanWaterBlock");

        LanguageRegistry.addName(cleanWaterBlock, "Чистая вода");


        GameRegistry.registerTileEntity(CleanMachineTile.class,MODID+":CleanMachineTile");

        FreshBucketHandler.INSTANCE.buckets.put(cleanWaterBlock, ItemLoader.freshWaterBucket);


        ItemLoader.freshWaterBucket.setTextureName(CoreMod.MODID+":cleanwaterbucket");

    }

    @Mod.EventHandler

    public void Init(FMLInitializationEvent event){

        NetworkRegistry.INSTANCE.registerGuiHandler(this,new CleanMachineGuiHandler());


    }

    @Mod.EventHandler

    public void postInit(FMLPostInitializationEvent event){

        MinecraftForge.EVENT_BUS.register(FreshBucketHandler.INSTANCE);

    }

}
Код:
public class CleanMachine extends BlockContainer implements ITileEntityProvider {

    private final Random random = new Random();

    private IIcon[] iconSide = new IIcon[6];


    private static CoreMod instance;

    public CleanMachine(Material material, CoreMod instanceMod)

    {

        super(material);

        setCreativeTab(ThirstMod.thirstCreativeTab);

        setBlockName(CoreMod.MODID + "." + "cleanMachine");

        setHardness(2.0F);

        setResistance(10.0F);

        setHarvestLevel("pickaxe",3);

        setStepSound(Block.soundTypeMetal);

        setBlockTextureName(CoreMod.MODID+":cleanser");


        instance = instanceMod;

    }


    @Override

    public IIcon getIcon(int side, int meta) {

        return this.iconSide[side];

    }


    @Override

    public void registerBlockIcons(IIconRegister reg) {

        for (int a=0;a<6;a++){

            iconSide[a]=reg.registerIcon(this.textureName+"/cleanser"+"_"+a);

        }

    }


    @Override

    public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player,

                                    int par1, float par2, float par3, float par4) {

        if (!player.isSneaking()) {

            player.openGui(instance, Constants.GuiIDCleanMachine, world, x, y, z);

            return true;

        } else {

            return false;

        }


    }


    @Override

    public Item getItemDropped(int p_149650_1_, Random p_149650_2_, int p_149650_3_) {

        return Item.getItemFromBlock(CoreMod.cleanMachine);

    }

    @Override

    public TileEntity createNewTileEntity(World p_149915_1_, int p_149915_2_) {

        return new CleanMachineTile("CleanserInventory", true);

    }

    @Override

    public void breakBlock(World world, int x, int y, int z, Block block, int p_149749_6_) {

        CleanMachineTile entity = (CleanMachineTile) world.getTileEntity(x, y, z);


        if (entity != null) {

            ItemStack itemStack = entity.getStackInSlot(0);


            if (itemStack != null) {

                float f = this.random.nextFloat() * 0.8F + 0.1F;

                float f1 = this.random.nextFloat() * 0.8F + 0.1F;

                float f2 = this.random.nextFloat() * 0.8F + 0.1F;


                while (itemStack.stackSize > 0) {

                    int j1 = this.random.nextInt(21) + 10;


                    if (j1 > itemStack.stackSize) {

                        j1 = itemStack.stackSize;

                    }


                    itemStack.stackSize -= j1;

                    EntityItem entityItem = new EntityItem(world, (double) ((float) x + f), (double) ((float) y + f1),

                            (double) ((float) z + f2), new ItemStack(itemStack.getItem(), j1, itemStack.getItemDamage()));


                    if (itemStack.hasTagCompound()) {

                        entityItem.getEntityItem().setTagCompound((NBTTagCompound) itemStack.getTagCompound().copy());

                    }



                    float f3 = 0.05F;

                    entityItem.motionX = (double) ((float) this.random.nextGaussian() * f3);

                    entityItem.motionY = (double) ((float) this.random.nextGaussian() * f3 + 0.2F);

                    entityItem.motionZ = (double) ((float) this.random.nextGaussian() * f3);

                    world.spawnEntityInWorld(entityItem);

                }

            }



            world.func_147453_f(x, y, z, block);

        }


    }

    @SideOnly(Side.CLIENT)

    @Override

    public void onNeighborBlockChange(World world, int x, int y, int z, Block block){

        if(!world.isRemote) {

            CleanMachineTile tile=(CleanMachineTile)world.getTileEntity(x,y,z);

            if (tile.isPowerRedEnable() && !world.isBlockIndirectlyGettingPowered(x, y, z))

            {

                tile.setPowerRedEnable(false);

            }

            else if (!tile.isPowerRedEnable() && world.isBlockIndirectlyGettingPowered(x, y, z))

            {

                tile.setPowerRedEnable(true);

            }


        }

    }

}
 
7,099
324
1,510
А как на счет кода фильтра?
 
7,099
324
1,510
Попробуй в litterFilter вставить markDirty(), у тебя там изменяется содержимое инвентаря. Тайл сохраняется при сохранении мира только, если помечен, по идее
 
90
1
Попробуй в litterFilter вставить markDirty(), у тебя там изменяется содержимое инвентаря. Тайл сохраняется при сохранении мира только, если помечен, по идее
Попробовал. Вторая часть проблемы про исчезновение фильтра из нижнего слота вроде не проявляется. А вот первая о сталась. Но я вроде могу её конкретизировать. Фильтр иногда после перезахода имеет прочность на 1 единицу больше чем до выхода. То есть как будто не сохраняется последнее изменение связанное со слотами. Вот пример: Фильтр имел прочность 3 из 5 до перезахода. После перезахода восстанавливается до 4 из 5 сам. Или если он был загрязнен и попал в нижний слот, то после перезахода он вновь появляется в верхнем, но с прочностью 1 из 5. То есть грубо говоря последнее повреждение не всегда сохраняется.
 
3,005
192
592
7,099
324
1,510
@Putnik у тебя есть методы для изменения инвентаря, почему ты работаешь напрямую с массивом, в котором хранятся стаки?

Вообще стоит разделить абстракции хранилища и обработчика:
Пусть тайл содержит объект инвентаря, который инкапсулирует работу со слотами, чтобы доступ к инвентарю был доступен только через его методы(таким образом markDirty будет всегда вызываться при изменении инвентаря).
Обработчиком может выступать сам тайл
 
7,099
324
1,510
Сделать инвентарь и обработчик процессинга(фильтрация или че там) разными сущностями
 
3,005
192
592
Сверху