Posted September 11, 20169 yr I'm creating a TileEntity which functions like a furnace. It now works in the game, and the inventory and cook time stay and keep working when the GUI is opened and closed. But when I save and exit the game while something is in the inventory, it's lost when the world is reloaded. From using printlns it looks like the inventory is being written to NBT correctly, but when the game is loaded a new TileEntity is created instead of loading it from NBT like it should. How can I make sure the game loads the existing TileEntity instead of creating a new one each time the world is loaded? I can post my code if needed but there's a lot of it so I thought I'd ask for general suggestions first and see if there's anything obvious I might have missed. Edit: Solved. I made a really silly error, I had just mixed up the names of my NBT tags so they weren't the same in the read and write methods.
September 11, 20169 yr I'm creating a TileEntity which functions like a furnace. It now works in the game, and the inventory and cook time stay and keep working when the GUI is opened and closed. But when I save and exit the game while something is in the inventory, it's lost when the world is reloaded. From using printlns it looks like the inventory is being written to NBT correctly, but when the game is loaded a new TileEntity is created instead of loading it from NBT like it should. How can I make sure the game loads the existing TileEntity instead of creating a new one each time the world is loaded? I can post my code if needed but there's a lot of it so I thought I'd ask for general suggestions first and see if there's anything obvious I might have missed. Hmm maybe you missed the thingymabober in the whoseitwhatsit that caused the dohicky to become flabbergasted. How are we supposed to know if you don't show what you have? *Edit The more code there is the more possibilities it might be... VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect. Forge and vanilla BlockState generator.
September 11, 20169 yr Author I'm creating a TileEntity which functions like a furnace. It now works in the game, and the inventory and cook time stay and keep working when the GUI is opened and closed. But when I save and exit the game while something is in the inventory, it's lost when the world is reloaded. From using printlns it looks like the inventory is being written to NBT correctly, but when the game is loaded a new TileEntity is created instead of loading it from NBT like it should. How can I make sure the game loads the existing TileEntity instead of creating a new one each time the world is loaded? I can post my code if needed but there's a lot of it so I thought I'd ask for general suggestions first and see if there's anything obvious I might have missed. Hmm maybe you missed the thingymabober in the whoseitwhatsit that caused the dohicky to become flabbergasted. How are we supposed to know if you don't show what you have? Like I said, I am happy to post my code but there are lots of files and I wondered if there might be a common or obvious solution without the hassle of reading through everything. At this point I'm going to have to post 5+ classes worth of code because I have no ideas about what is causing the problem. My custom furnace is one of several types subclassed from an abstract class so that's where a lot of the action happens. The abstract class: package com.jj.jjmod.tileentities.abstracts ; import javax.annotation.Nullable ; import com.jj.jjmod.crafting.ModCookingManager ; import net.minecraft.block.state.IBlockState ; import net.minecraft.entity.player.EntityPlayer ; import net.minecraft.entity.player.InventoryPlayer ; import net.minecraft.inventory.Container ; import net.minecraft.inventory.IInventory ; import net.minecraft.inventory.ItemStackHelper ; import net.minecraft.item.ItemStack ; import net.minecraft.nbt.NBTTagCompound ; import net.minecraft.nbt.NBTTagList ; import net.minecraft.tileentity.TileEntityLockable ; import net.minecraft.util.ITickable ; import net.minecraft.util.math.BlockPos ; import net.minecraft.util.math.MathHelper ; import net.minecraft.world.World ; public abstract class TEFurnaceAbstract extends TileEntityLockable implements ITickable , IInventory { public ItemStack[] stacks = new ItemStack[3] ; public int fuelLeft ; public int fuelEach ; public int cookSpent ; public int cookEach ; public ModCookingManager cookingManager ; public TEFurnaceAbstract( ModCookingManager cookingManager , int cookEach ) { this.cookingManager = cookingManager ; this.cookEach = cookEach ; } // TODO // Inventory is not saved on world save/load // *-Move cookEach to constructor // cleanup @Override @Nullable public ItemStack getStackInSlot( int index ) { return this.stacks[index] ; } @Override @Nullable public ItemStack decrStackSize( int index , int count ) { return ItemStackHelper.getAndSplit( this.stacks , index , count ) ; } @Override @Nullable public ItemStack removeStackFromSlot( int index ) { return ItemStackHelper.getAndRemove( this.stacks , index ) ; } @Override public void setInventorySlotContents( int index , @Nullable ItemStack stack ) { // Check if the same item boolean same = stack != null && stack .isItemEqual( this.stacks[index] ) && ItemStack .areItemStackTagsEqual( stack , this.stacks[index] ) ; System.out.println( "setting stacks " + index + "to " + stack ) ; this.stacks[index] = stack ; System.out.println( "stacks " + index + " now " + this.stacks[index] ) ; // Make sure the stack is not illegal size if ( stack != null && stack.stackSize > this .getInventoryStackLimit() ) { stack.stackSize = this.getInventoryStackLimit() ; } // If a new item and in the cooking slot then update cook info if ( index == 0 && !same ) { System.out.println( "is new item and in cooking slot" ) ; this.cookSpent = 0 ; this.markDirty() ; } } public abstract int getFuelTime( ItemStack stack ) ; @Override public void readFromNBT( NBTTagCompound compound ) { System.out.println( "reading from nbt " + compound ) ; super.readFromNBT( compound ) ; // Get inventory stacks NBTTagList taglist = compound.getTagList( "stacks" , 10 ) ; this.stacks = new ItemStack[this.getSizeInventory()] ; System.out.println( "loading items list " + taglist ) ; for ( int i = 0 ; i < taglist.tagCount() ; ++i ) { NBTTagCompound tagcompound = taglist.getCompoundTagAt( i ) ; int j = tagcompound.getByte( "slot" ) ; if ( j >= 0 && j < this.stacks.length ) { this.stacks[j] = ItemStack.loadItemStackFromNBT( tagcompound ) ; } } // Get other info this.fuelLeft = compound.getInteger( "fuelLeft" ) ; this.cookSpent = compound.getInteger( "cookSpent" ) ; this.cookEach = compound.getInteger( "cookEach" ) ; this.fuelEach = getFuelTime( this.stacks[1] ) ; } @Override public NBTTagCompound writeToNBT( NBTTagCompound compound ) { System.out.println( "writing to nbt" ) ; super.writeToNBT( compound ) ; // Send other info compound.setInteger( "fuelLeft" , this.fuelLeft ) ; compound.setInteger( "cookSpent" , this.cookSpent ) ; compound.setInteger( "cookEach" , this.cookEach ) ; // Send inventory stacks NBTTagList taglist = new NBTTagList() ; for ( int i = 0 ; i < this.stacks.length ; ++i ) { if ( this.stacks[i] != null ) { NBTTagCompound tagcompound = new NBTTagCompound() ; tagcompound.setByte( "slot" , (byte) i ) ; this.stacks[i].writeToNBT( tagcompound ) ; taglist.appendTag( tagcompound ) ; } } System.out.println( "setting items list " + taglist ) ; compound.setTag( "items" , taglist ) ; return compound ; } public boolean isBurning() { return this.fuelLeft > 0 ; } @Override public NBTTagCompound getUpdateTag() { System.out.println( "getting update tag" ) ; return this.writeToNBT( new NBTTagCompound() ) ; } @Override public void update() { // System.out.println( "slot 0 " + this.stacks[0] ) ; // System.out.println( "slot 1 " + this.stacks[1] ) ; // System.out.println( "slot 2 " + this.stacks[2] ) ; boolean isDirty = false ; // Reduce cook time if ( this.isBurning() ) { this.fuelLeft-- ; } // Do nothing if wrong side if ( this.worldObj.isRemote ) { return ; } // If ready to cook if ( this .canSmelt() && this.stacks[0] != null && this.stacks[1] != null ) { System.out.println( "ready to cook can smelt and stacks ready" ) ; System.out.println( "input stack " + this.stacks[0] ) ; System.out.println( "fuel stack " + this.stacks[1] ) ; // If already cooking if ( this.isBurning() ) { System.out.println( "is already cooking" ) ; this.cookSpent++ ; // If finished cooking if ( this.cookSpent == this.cookEach ) { System.out.println( "finished cooking" ) ; this.cookSpent = 0 ; this.smeltItem() ; isDirty = true ; } // If not already cooking } else { System.out.println( "not already cooking" ) ; // Start new cook this.fuelEach = getFuelTime( this.stacks[1] ) ; this.fuelLeft = this.fuelEach ; this.stacks[1].stackSize-- ; // If used last item if ( this.stacks[1].stackSize == 0 ) { System.out.println( "used last item" ) ; this.stacks[1] = stacks[1].getItem().getContainerItem( stacks[1] ) ; } isDirty = true ; } } // If cooking from last fuel? if ( !this.isBurning() && this.cookSpent > 0 ) { this.cookSpent = MathHelper.clamp_int( this.cookSpent - 2 , 0 , this.cookEach ) ; } // If dirty if ( isDirty ) { this.markDirty() ; } } private boolean canSmelt() { // If no input if ( this.stacks[0] == null ) { return false ; } ItemStack result = cookingManager.getSmeltingResult( this.stacks[0] ) ; // Check recipe and space boolean hasRecipe = result != null ; boolean hasRoom = ( this.stacks[2] == null ) || ( hasRecipe && this.stacks[2] .isItemEqual( result ) && ( this.stacks[2].stackSize + result.stackSize < getInventoryStackLimit() ) ) ; return hasRecipe && hasRoom ; } public void smeltItem() { System.out.println( "smelt item" ) ; // If can't cook if ( !this.canSmelt() ) { System.out.println( "can't smelt" ) ; return ; } ItemStack result = cookingManager.getSmeltingResult( this.stacks[0] ) ; // If output empty if ( this.stacks[2] == null ) { System.out.println( "output empty " + this.stacks[2] ) ; this.stacks[2] = result.copy() ; // If output contains same as result } else if ( this.stacks[2].getItem() == result.getItem() ) { System.out.println( "output same as result " + this.stacks[2] ) ; this.stacks[2].stackSize += result.stackSize ; } this.stacks[0].stackSize-- ; if ( this.stacks[0].stackSize <= 0 ) { this.stacks[0] = null ; } } public boolean isItemFuel( ItemStack stack ) { return getFuelTime( stack ) > 0 ; } @Override public boolean isUseableByPlayer( EntityPlayer player ) { return true ; } @Override public abstract Container createContainer( InventoryPlayer playerInv , EntityPlayer player ) ; public int getField( int id ) { switch ( id ) { case 0 : return this.fuelLeft ; case 1 : return this.fuelEach ; case 2 : return this.cookSpent ; case 3 : return this.cookEach ; default : return 0 ; } } public void setField( int id , int value ) { switch ( id ) { case 0 : this.fuelLeft = value ; break ; case 1 : this.fuelEach = value ; break ; case 2 : this.cookSpent = value ; break ; case 3 : this.cookEach = value ; } } } The subclass: package com.jj.jjmod.tileentities ; import com.jj.jjmod.crafting.CraftingManagers ; import com.jj.jjmod.inventory.container.ContainerFurnaceCampfire ; import com.jj.jjmod.tileentities.abstracts.TEFurnaceAbstract ; import net.minecraft.entity.player.EntityPlayer ; import net.minecraft.entity.player.InventoryPlayer ; import net.minecraft.init.Items ; import net.minecraft.inventory.Container ; import net.minecraft.item.Item ; import net.minecraft.item.ItemStack ; public class TEFurnaceCampfire extends TEFurnaceAbstract { public TEFurnaceCampfire() { super( CraftingManagers.CAMPFIRE , 200 ) ; } @Override public Container createContainer( InventoryPlayer playerInv , EntityPlayer player ) { return new ContainerFurnaceCampfire( playerInv , this ) ; } @Override public int getSizeInventory() { return this.stacks.length ; } @Override public int getInventoryStackLimit() { return 64 ; } // Required by interfaces @Override public void openInventory( EntityPlayer player ) { } @Override public void closeInventory( EntityPlayer player ) { } @Override public boolean isItemValidForSlot( int index , ItemStack stack ) { return true ; } @Override public int getFieldCount() { return 4 ; } @Override public void clear() { for ( int i = 0 ; i < this.stacks.length ; ++i ) { this.stacks[i] = null ; } } @Override public String getName() { return null ; } @Override public boolean hasCustomName() { return false ; } @Override public String getGuiID() { return null ; } @Override public int getFuelTime( ItemStack stack ) { System.out.println( "getting fuel time for " + stack ) ; if ( stack == null ) { return 0 ; } Item item = stack.getItem() ; if ( item == Items.COAL ) { System.out.println( "item is coal" ) ; return 100 ; } return 0 ; } } The same for the blocks, abstract class: package com.jj.jjmod.blocks.abstracts ; import javax.annotation.Nullable ; import com.jj.jjmod.main.GuiHandler.GuiList ; import com.jj.jjmod.main.Main ; import com.jj.jjmod.tileentities.abstracts.TEFurnaceAbstract ; import net.minecraft.block.BlockContainer ; import net.minecraft.block.material.Material ; import net.minecraft.block.state.IBlockState ; import net.minecraft.creativetab.CreativeTabs ; import net.minecraft.entity.player.EntityPlayer ; import net.minecraft.init.Blocks ; import net.minecraft.inventory.InventoryHelper ; import net.minecraft.item.ItemStack ; import net.minecraft.tileentity.TileEntity ; import net.minecraft.util.EnumFacing ; import net.minecraft.util.EnumHand ; import net.minecraft.util.math.BlockPos ; import net.minecraft.world.World ; public abstract class BlockFurnaceAbstract extends BlockContainer { public GuiList GUI ; public BlockFurnaceAbstract( String name , Material material , GuiList GUI ) { super( material ) ; this.setRegistryName( name ) ; this.setUnlocalizedName( name ) ; this.setCreativeTab( CreativeTabs.DECORATIONS ) ; this.GUI = GUI ; } @Override public boolean onBlockActivated( World world , BlockPos pos , IBlockState state , EntityPlayer player , EnumHand hand , @Nullable ItemStack item , EnumFacing side , float x , float y , float z ) { if ( world.isRemote ) { return true ; } System.out.println( "on block activated pos " + pos ) ; player.openGui( Main.instance , this.GUI.ordinal() , world , pos.getX() , pos.getY() , pos.getZ() ) ; return true ; } public void breakBlock( World worldIn , BlockPos pos , IBlockState state ) { TileEntity tileentity = worldIn.getTileEntity( pos ) ; if ( tileentity instanceof TEFurnaceAbstract ) { InventoryHelper.dropInventoryItems( worldIn , pos , (TEFurnaceAbstract) tileentity ) ; } super.breakBlock( worldIn , pos , state ) ; } // copy public static void setState( boolean active , World worldIn , BlockPos pos ) { System.out.println( "set state" ) ; IBlockState iblockstate = worldIn.getBlockState( pos ) ; TileEntity tileentity = worldIn.getTileEntity( pos ) ; if ( tileentity != null ) { tileentity.validate() ; worldIn.setTileEntity( pos , tileentity ) ; } } @Override public abstract ItemStack getItem( World world , BlockPos pos , IBlockState state ) ; /* * @Override public TileEntity createNewTileEntity( World world , int meta ) * { * * System.out.println( "creating new tileentity" ) ; return this.tileEntity * ; * * } */ } Subclass: package com.jj.jjmod.blocks ; import com.jj.jjmod.blocks.abstracts.BlockFurnaceAbstract ; import com.jj.jjmod.main.GuiHandler.GuiList ; import com.jj.jjmod.tileentities.TEFurnaceCampfire ; import net.minecraft.block.material.Material ; import net.minecraft.block.state.IBlockState ; import net.minecraft.item.Item ; import net.minecraft.item.ItemStack ; import net.minecraft.tileentity.TileEntity ; import net.minecraft.util.math.BlockPos ; import net.minecraft.world.World ; public class BlockFurnaceCampfire extends BlockFurnaceAbstract { public BlockFurnaceCampfire() { super( "furnaceCampfire" , Material.PLANTS , GuiList.CAMPFIRE ) ; } @Override public ItemStack getItem( World world , BlockPos pos , IBlockState state ) { return new ItemStack( Item.getItemFromBlock( ModBlocks.furnaceCampfire ) ) ; } @Override public TileEntity createNewTileEntity( World world , int meta ) { System.out.println( "creating new tileentity" ) ; return new TEFurnaceCampfire() ; } } The Containers if you need to see those - abstract class: package com.jj.jjmod.inventory.container.abstracts ; import javax.annotation.Nullable ; import com.jj.jjmod.blocks.abstracts.BlockFurnaceAbstract ; import com.jj.jjmod.tileentities.abstracts.TEFurnaceAbstract ; import net.minecraft.entity.player.EntityPlayer ; import net.minecraft.entity.player.InventoryPlayer ; import net.minecraft.inventory.Container ; import net.minecraft.inventory.IContainerListener ; import net.minecraft.inventory.IInventory ; import net.minecraft.inventory.Slot ; import net.minecraft.inventory.SlotFurnaceFuel ; import net.minecraft.inventory.SlotFurnaceOutput ; import net.minecraft.item.ItemStack ; import net.minecraftforge.fml.relauncher.Side ; import net.minecraftforge.fml.relauncher.SideOnly ; public class ContainerFurnaceAbstract extends Container { public final IInventory tileFurnace ; public int fuelLeft ; public int fuelEach ; public int cookSpent ; public int cookEach ; public ContainerFurnaceAbstract( InventoryPlayer playerInv , IInventory tileFurnace ) { this.tileFurnace = tileFurnace ; // Add furnace slots this.addSlotToContainer( new Slot( this.tileFurnace , 0 , 56 , 17 ) ) ; this.addSlotToContainer( new SlotFurnaceFuel( this.tileFurnace , 1 , 56 , 53 ) ) ; this.addSlotToContainer( new SlotFurnaceOutput( playerInv.player , this.tileFurnace , 2 , 116 , 35 ) ) ; // Add inventory grid slots for ( int i = 0 ; i < 3 ; ++i ) { for ( int j = 0 ; j < 9 ; ++j ) { this.addSlotToContainer( new Slot( playerInv , j + i * 9 + 9 , 8 + j * 18 , 84 + i * 18 ) ) ; } } // Add hotbar slots for ( int k = 0 ; k < 9 ; ++k ) { this.addSlotToContainer( new Slot( playerInv , k , 8 + k * 18 , 142 ) ) ; } } @Override public void addListener( IContainerListener listener ) { super.addListener( listener ) ; listener.sendAllWindowProperties( this , this.tileFurnace ) ; } @Override public void detectAndSendChanges() { super.detectAndSendChanges() ; for ( int i = 0 ; i < this.listeners.size() ; ++i ) { IContainerListener icontainerlistener = (IContainerListener) this.listeners.get( i ) ; if ( this.fuelLeft != this.tileFurnace.getField( 0 ) ) { icontainerlistener.sendProgressBarUpdate( this , 0 , this.tileFurnace.getField( 0 ) ) ; } if ( this.fuelEach != this.tileFurnace.getField( 1 ) ) { icontainerlistener.sendProgressBarUpdate( this , 1 , this.tileFurnace.getField( 1 ) ) ; } if ( this.cookSpent != this.tileFurnace.getField( 2 ) ) { icontainerlistener.sendProgressBarUpdate( this , 2 , this.tileFurnace.getField( 2 ) ) ; } if ( this.cookEach != this.tileFurnace.getField( 3 ) ) { icontainerlistener.sendProgressBarUpdate( this , 3 , this.tileFurnace.getField( 3 ) ) ; } } this.fuelLeft = this.tileFurnace.getField( 0 ) ; this.fuelEach = this.tileFurnace.getField( 1 ) ; this.cookSpent = this.tileFurnace.getField( 2 ) ; this.cookEach = this.tileFurnace.getField( 3 ) ; } @Override @SideOnly( Side.CLIENT ) public void updateProgressBar( int id , int data ) { this.tileFurnace.setField( id , data ) ; } @Override public boolean canInteractWith( EntityPlayer player ) { return this.tileFurnace.isUseableByPlayer( player ) ; } //TODO direct copy from vanilla furnace with minimal edits @Nullable public ItemStack transferStackInSlot( EntityPlayer playerIn , int index ) { System.out.println( "transfer stack in slot" ) ; ItemStack itemstack = null ; Slot slot = (Slot) this.inventorySlots.get( index ) ; if ( slot != null && slot.getHasStack() ) { System.out.println( "slot already has stack " ) ; ItemStack itemstack1 = slot.getStack() ; itemstack = itemstack1.copy() ; if ( index == 2 ) { System.out.println( "putting into index 2" ) ; if ( !this.mergeItemStack( itemstack1 , 3 , 39 , true ) ) { return null ; } slot.onSlotChange( itemstack1 , itemstack ) ; } else if ( index != 1 && index != 0 ) { System.out.println( "putting into index not 1 or 0" ) ; if ( ( (TEFurnaceAbstract) tileFurnace ).cookingManager .getSmeltingResult( itemstack1 ) != null ) { if ( !this.mergeItemStack( itemstack1 , 0 , 1 , false ) ) { return null ; } } else if ( ( (TEFurnaceAbstract) tileFurnace ).isItemFuel( itemstack1 ) ) { if ( !this.mergeItemStack( itemstack1 , 1 , 2 , false ) ) { return null ; } } else if ( index >= 3 && index < 30 ) { if ( !this.mergeItemStack( itemstack1 , 30 , 39 , false ) ) { return null ; } } else if ( index >= 30 && index < 39 && !this .mergeItemStack( itemstack1 , 3 , 30 , false ) ) { return null ; } } else if ( !this.mergeItemStack( itemstack1 , 3 , 39 , false ) ) { return null ; } if ( itemstack1.stackSize == 0 ) { slot.putStack( (ItemStack) null ) ; } else { slot.onSlotChanged() ; } if ( itemstack1.stackSize == itemstack.stackSize ) { return null ; } slot.onPickupFromSlot( playerIn , itemstack1 ) ; } tileFurnace.markDirty() ; return itemstack ; } } Subclass: package com.jj.jjmod.inventory.container ; import com.jj.jjmod.inventory.container.abstracts.ContainerFurnaceAbstract ; import com.jj.jjmod.tileentities.TEFurnaceCampfire ; import net.minecraft.entity.player.InventoryPlayer ; import net.minecraft.inventory.IInventory ; public class ContainerFurnaceCampfire extends ContainerFurnaceAbstract { public ContainerFurnaceCampfire( InventoryPlayer playerInv , IInventory te ) { super( playerInv , te ) ; } } (I know this isn't a necessary subclass yet but I have plans to add more customisations so that's why) I also have the TileEntities registered in init in my common proxy, the blocks registered in preinit, and a GUI handler to set up the containers. I can post all of that if you really want but I'll stop here for now.
September 11, 20169 yr I'm creating a TileEntity which functions like a furnace. It now works in the game, and the inventory and cook time stay and keep working when the GUI is opened and closed. But when I save and exit the game while something is in the inventory, it's lost when the world is reloaded. From using printlns it looks like the inventory is being written to NBT correctly, but when the game is loaded a new TileEntity is created instead of loading it from NBT like it should. How can I make sure the game loads the existing TileEntity instead of creating a new one each time the world is loaded? I can post my code if needed but there's a lot of it so I thought I'd ask for general suggestions first and see if there's anything obvious I might have missed. Hmm maybe you missed the thingymabober in the whoseitwhatsit that caused the dohicky to become flabbergasted. How are we supposed to know if you don't show what you have? Like I said, I am happy to post my code but there are lots of files and I wondered if there might be a common or obvious solution without the hassle of reading through everything. At this point I'm going to have to post 5+ classes worth of code because I have no ideas about what is causing the problem. My custom furnace is one of several types subclassed from an abstract class so that's where a lot of the action happens. The abstract class: package com.jj.jjmod.tileentities.abstracts ; import javax.annotation.Nullable ; import com.jj.jjmod.crafting.ModCookingManager ; import net.minecraft.block.state.IBlockState ; import net.minecraft.entity.player.EntityPlayer ; import net.minecraft.entity.player.InventoryPlayer ; import net.minecraft.inventory.Container ; import net.minecraft.inventory.IInventory ; import net.minecraft.inventory.ItemStackHelper ; import net.minecraft.item.ItemStack ; import net.minecraft.nbt.NBTTagCompound ; import net.minecraft.nbt.NBTTagList ; import net.minecraft.tileentity.TileEntityLockable ; import net.minecraft.util.ITickable ; import net.minecraft.util.math.BlockPos ; import net.minecraft.util.math.MathHelper ; import net.minecraft.world.World ; public abstract class TEFurnaceAbstract extends TileEntityLockable implements ITickable , IInventory { public ItemStack[] stacks = new ItemStack[3] ; public int fuelLeft ; public int fuelEach ; public int cookSpent ; public int cookEach ; public ModCookingManager cookingManager ; public TEFurnaceAbstract( ModCookingManager cookingManager , int cookEach ) { this.cookingManager = cookingManager ; this.cookEach = cookEach ; } // TODO // Inventory is not saved on world save/load // *-Move cookEach to constructor // cleanup @Override @Nullable public ItemStack getStackInSlot( int index ) { return this.stacks[index] ; } @Override @Nullable public ItemStack decrStackSize( int index , int count ) { return ItemStackHelper.getAndSplit( this.stacks , index , count ) ; } @Override @Nullable public ItemStack removeStackFromSlot( int index ) { return ItemStackHelper.getAndRemove( this.stacks , index ) ; } @Override public void setInventorySlotContents( int index , @Nullable ItemStack stack ) { // Check if the same item boolean same = stack != null && stack .isItemEqual( this.stacks[index] ) && ItemStack .areItemStackTagsEqual( stack , this.stacks[index] ) ; System.out.println( "setting stacks " + index + "to " + stack ) ; this.stacks[index] = stack ; System.out.println( "stacks " + index + " now " + this.stacks[index] ) ; // Make sure the stack is not illegal size if ( stack != null && stack.stackSize > this .getInventoryStackLimit() ) { stack.stackSize = this.getInventoryStackLimit() ; } // If a new item and in the cooking slot then update cook info if ( index == 0 && !same ) { System.out.println( "is new item and in cooking slot" ) ; this.cookSpent = 0 ; this.markDirty() ; } } public abstract int getFuelTime( ItemStack stack ) ; @Override public void readFromNBT( NBTTagCompound compound ) { System.out.println( "reading from nbt " + compound ) ; super.readFromNBT( compound ) ; // Get inventory stacks NBTTagList taglist = compound.getTagList( "stacks" , 10 ) ; this.stacks = new ItemStack[this.getSizeInventory()] ; System.out.println( "loading items list " + taglist ) ; for ( int i = 0 ; i < taglist.tagCount() ; ++i ) { NBTTagCompound tagcompound = taglist.getCompoundTagAt( i ) ; int j = tagcompound.getByte( "slot" ) ; if ( j >= 0 && j < this.stacks.length ) { this.stacks[j] = ItemStack.loadItemStackFromNBT( tagcompound ) ; } } // Get other info this.fuelLeft = compound.getInteger( "fuelLeft" ) ; this.cookSpent = compound.getInteger( "cookSpent" ) ; this.cookEach = compound.getInteger( "cookEach" ) ; this.fuelEach = getFuelTime( this.stacks[1] ) ; } @Override public NBTTagCompound writeToNBT( NBTTagCompound compound ) { System.out.println( "writing to nbt" ) ; super.writeToNBT( compound ) ; // Send other info compound.setInteger( "fuelLeft" , this.fuelLeft ) ; compound.setInteger( "cookSpent" , this.cookSpent ) ; compound.setInteger( "cookEach" , this.cookEach ) ; // Send inventory stacks NBTTagList taglist = new NBTTagList() ; for ( int i = 0 ; i < this.stacks.length ; ++i ) { if ( this.stacks[i] != null ) { NBTTagCompound tagcompound = new NBTTagCompound() ; tagcompound.setByte( "slot" , (byte) i ) ; this.stacks[i].writeToNBT( tagcompound ) ; taglist.appendTag( tagcompound ) ; } } System.out.println( "setting items list " + taglist ) ; compound.setTag( "items" , taglist ) ; return compound ; } public boolean isBurning() { return this.fuelLeft > 0 ; } @Override public NBTTagCompound getUpdateTag() { System.out.println( "getting update tag" ) ; return this.writeToNBT( new NBTTagCompound() ) ; } @Override public void update() { // System.out.println( "slot 0 " + this.stacks[0] ) ; // System.out.println( "slot 1 " + this.stacks[1] ) ; // System.out.println( "slot 2 " + this.stacks[2] ) ; boolean isDirty = false ; // Reduce cook time if ( this.isBurning() ) { this.fuelLeft-- ; } // Do nothing if wrong side if ( this.worldObj.isRemote ) { return ; } // If ready to cook if ( this .canSmelt() && this.stacks[0] != null && this.stacks[1] != null ) { System.out.println( "ready to cook can smelt and stacks ready" ) ; System.out.println( "input stack " + this.stacks[0] ) ; System.out.println( "fuel stack " + this.stacks[1] ) ; // If already cooking if ( this.isBurning() ) { System.out.println( "is already cooking" ) ; this.cookSpent++ ; // If finished cooking if ( this.cookSpent == this.cookEach ) { System.out.println( "finished cooking" ) ; this.cookSpent = 0 ; this.smeltItem() ; isDirty = true ; } // If not already cooking } else { System.out.println( "not already cooking" ) ; // Start new cook this.fuelEach = getFuelTime( this.stacks[1] ) ; this.fuelLeft = this.fuelEach ; this.stacks[1].stackSize-- ; // If used last item if ( this.stacks[1].stackSize == 0 ) { System.out.println( "used last item" ) ; this.stacks[1] = stacks[1].getItem().getContainerItem( stacks[1] ) ; } isDirty = true ; } } // If cooking from last fuel? if ( !this.isBurning() && this.cookSpent > 0 ) { this.cookSpent = MathHelper.clamp_int( this.cookSpent - 2 , 0 , this.cookEach ) ; } // If dirty if ( isDirty ) { this.markDirty() ; } } private boolean canSmelt() { // If no input if ( this.stacks[0] == null ) { return false ; } ItemStack result = cookingManager.getSmeltingResult( this.stacks[0] ) ; // Check recipe and space boolean hasRecipe = result != null ; boolean hasRoom = ( this.stacks[2] == null ) || ( hasRecipe && this.stacks[2] .isItemEqual( result ) && ( this.stacks[2].stackSize + result.stackSize < getInventoryStackLimit() ) ) ; return hasRecipe && hasRoom ; } public void smeltItem() { System.out.println( "smelt item" ) ; // If can't cook if ( !this.canSmelt() ) { System.out.println( "can't smelt" ) ; return ; } ItemStack result = cookingManager.getSmeltingResult( this.stacks[0] ) ; // If output empty if ( this.stacks[2] == null ) { System.out.println( "output empty " + this.stacks[2] ) ; this.stacks[2] = result.copy() ; // If output contains same as result } else if ( this.stacks[2].getItem() == result.getItem() ) { System.out.println( "output same as result " + this.stacks[2] ) ; this.stacks[2].stackSize += result.stackSize ; } this.stacks[0].stackSize-- ; if ( this.stacks[0].stackSize <= 0 ) { this.stacks[0] = null ; } } public boolean isItemFuel( ItemStack stack ) { return getFuelTime( stack ) > 0 ; } @Override public boolean isUseableByPlayer( EntityPlayer player ) { return true ; } @Override public abstract Container createContainer( InventoryPlayer playerInv , EntityPlayer player ) ; public int getField( int id ) { switch ( id ) { case 0 : return this.fuelLeft ; case 1 : return this.fuelEach ; case 2 : return this.cookSpent ; case 3 : return this.cookEach ; default : return 0 ; } } public void setField( int id , int value ) { switch ( id ) { case 0 : this.fuelLeft = value ; break ; case 1 : this.fuelEach = value ; break ; case 2 : this.cookSpent = value ; break ; case 3 : this.cookEach = value ; } } } The subclass: package com.jj.jjmod.tileentities ; import com.jj.jjmod.crafting.CraftingManagers ; import com.jj.jjmod.inventory.container.ContainerFurnaceCampfire ; import com.jj.jjmod.tileentities.abstracts.TEFurnaceAbstract ; import net.minecraft.entity.player.EntityPlayer ; import net.minecraft.entity.player.InventoryPlayer ; import net.minecraft.init.Items ; import net.minecraft.inventory.Container ; import net.minecraft.item.Item ; import net.minecraft.item.ItemStack ; public class TEFurnaceCampfire extends TEFurnaceAbstract { public TEFurnaceCampfire() { super( CraftingManagers.CAMPFIRE , 200 ) ; } @Override public Container createContainer( InventoryPlayer playerInv , EntityPlayer player ) { return new ContainerFurnaceCampfire( playerInv , this ) ; } @Override public int getSizeInventory() { return this.stacks.length ; } @Override public int getInventoryStackLimit() { return 64 ; } // Required by interfaces @Override public void openInventory( EntityPlayer player ) { } @Override public void closeInventory( EntityPlayer player ) { } @Override public boolean isItemValidForSlot( int index , ItemStack stack ) { return true ; } @Override public int getFieldCount() { return 4 ; } @Override public void clear() { for ( int i = 0 ; i < this.stacks.length ; ++i ) { this.stacks[i] = null ; } } @Override public String getName() { return null ; } @Override public boolean hasCustomName() { return false ; } @Override public String getGuiID() { return null ; } @Override public int getFuelTime( ItemStack stack ) { System.out.println( "getting fuel time for " + stack ) ; if ( stack == null ) { return 0 ; } Item item = stack.getItem() ; if ( item == Items.COAL ) { System.out.println( "item is coal" ) ; return 100 ; } return 0 ; } } The same for the blocks, abstract class: package com.jj.jjmod.blocks.abstracts ; import javax.annotation.Nullable ; import com.jj.jjmod.main.GuiHandler.GuiList ; import com.jj.jjmod.main.Main ; import com.jj.jjmod.tileentities.abstracts.TEFurnaceAbstract ; import net.minecraft.block.BlockContainer ; import net.minecraft.block.material.Material ; import net.minecraft.block.state.IBlockState ; import net.minecraft.creativetab.CreativeTabs ; import net.minecraft.entity.player.EntityPlayer ; import net.minecraft.init.Blocks ; import net.minecraft.inventory.InventoryHelper ; import net.minecraft.item.ItemStack ; import net.minecraft.tileentity.TileEntity ; import net.minecraft.util.EnumFacing ; import net.minecraft.util.EnumHand ; import net.minecraft.util.math.BlockPos ; import net.minecraft.world.World ; public abstract class BlockFurnaceAbstract extends BlockContainer { public GuiList GUI ; public BlockFurnaceAbstract( String name , Material material , GuiList GUI ) { super( material ) ; this.setRegistryName( name ) ; this.setUnlocalizedName( name ) ; this.setCreativeTab( CreativeTabs.DECORATIONS ) ; this.GUI = GUI ; } @Override public boolean onBlockActivated( World world , BlockPos pos , IBlockState state , EntityPlayer player , EnumHand hand , @Nullable ItemStack item , EnumFacing side , float x , float y , float z ) { if ( world.isRemote ) { return true ; } System.out.println( "on block activated pos " + pos ) ; player.openGui( Main.instance , this.GUI.ordinal() , world , pos.getX() , pos.getY() , pos.getZ() ) ; return true ; } public void breakBlock( World worldIn , BlockPos pos , IBlockState state ) { TileEntity tileentity = worldIn.getTileEntity( pos ) ; if ( tileentity instanceof TEFurnaceAbstract ) { InventoryHelper.dropInventoryItems( worldIn , pos , (TEFurnaceAbstract) tileentity ) ; } super.breakBlock( worldIn , pos , state ) ; } // copy public static void setState( boolean active , World worldIn , BlockPos pos ) { System.out.println( "set state" ) ; IBlockState iblockstate = worldIn.getBlockState( pos ) ; TileEntity tileentity = worldIn.getTileEntity( pos ) ; if ( tileentity != null ) { tileentity.validate() ; worldIn.setTileEntity( pos , tileentity ) ; } } @Override public abstract ItemStack getItem( World world , BlockPos pos , IBlockState state ) ; /* * @Override public TileEntity createNewTileEntity( World world , int meta ) * { * * System.out.println( "creating new tileentity" ) ; return this.tileEntity * ; * * } */ } Subclass: package com.jj.jjmod.blocks ; import com.jj.jjmod.blocks.abstracts.BlockFurnaceAbstract ; import com.jj.jjmod.main.GuiHandler.GuiList ; import com.jj.jjmod.tileentities.TEFurnaceCampfire ; import net.minecraft.block.material.Material ; import net.minecraft.block.state.IBlockState ; import net.minecraft.item.Item ; import net.minecraft.item.ItemStack ; import net.minecraft.tileentity.TileEntity ; import net.minecraft.util.math.BlockPos ; import net.minecraft.world.World ; public class BlockFurnaceCampfire extends BlockFurnaceAbstract { public BlockFurnaceCampfire() { super( "furnaceCampfire" , Material.PLANTS , GuiList.CAMPFIRE ) ; } @Override public ItemStack getItem( World world , BlockPos pos , IBlockState state ) { return new ItemStack( Item.getItemFromBlock( ModBlocks.furnaceCampfire ) ) ; } @Override public TileEntity createNewTileEntity( World world , int meta ) { System.out.println( "creating new tileentity" ) ; return new TEFurnaceCampfire() ; } } The Containers if you need to see those - abstract class: package com.jj.jjmod.inventory.container.abstracts ; import javax.annotation.Nullable ; import com.jj.jjmod.blocks.abstracts.BlockFurnaceAbstract ; import com.jj.jjmod.tileentities.abstracts.TEFurnaceAbstract ; import net.minecraft.entity.player.EntityPlayer ; import net.minecraft.entity.player.InventoryPlayer ; import net.minecraft.inventory.Container ; import net.minecraft.inventory.IContainerListener ; import net.minecraft.inventory.IInventory ; import net.minecraft.inventory.Slot ; import net.minecraft.inventory.SlotFurnaceFuel ; import net.minecraft.inventory.SlotFurnaceOutput ; import net.minecraft.item.ItemStack ; import net.minecraftforge.fml.relauncher.Side ; import net.minecraftforge.fml.relauncher.SideOnly ; public class ContainerFurnaceAbstract extends Container { public final IInventory tileFurnace ; public int fuelLeft ; public int fuelEach ; public int cookSpent ; public int cookEach ; public ContainerFurnaceAbstract( InventoryPlayer playerInv , IInventory tileFurnace ) { this.tileFurnace = tileFurnace ; // Add furnace slots this.addSlotToContainer( new Slot( this.tileFurnace , 0 , 56 , 17 ) ) ; this.addSlotToContainer( new SlotFurnaceFuel( this.tileFurnace , 1 , 56 , 53 ) ) ; this.addSlotToContainer( new SlotFurnaceOutput( playerInv.player , this.tileFurnace , 2 , 116 , 35 ) ) ; // Add inventory grid slots for ( int i = 0 ; i < 3 ; ++i ) { for ( int j = 0 ; j < 9 ; ++j ) { this.addSlotToContainer( new Slot( playerInv , j + i * 9 + 9 , 8 + j * 18 , 84 + i * 18 ) ) ; } } // Add hotbar slots for ( int k = 0 ; k < 9 ; ++k ) { this.addSlotToContainer( new Slot( playerInv , k , 8 + k * 18 , 142 ) ) ; } } @Override public void addListener( IContainerListener listener ) { super.addListener( listener ) ; listener.sendAllWindowProperties( this , this.tileFurnace ) ; } @Override public void detectAndSendChanges() { super.detectAndSendChanges() ; for ( int i = 0 ; i < this.listeners.size() ; ++i ) { IContainerListener icontainerlistener = (IContainerListener) this.listeners.get( i ) ; if ( this.fuelLeft != this.tileFurnace.getField( 0 ) ) { icontainerlistener.sendProgressBarUpdate( this , 0 , this.tileFurnace.getField( 0 ) ) ; } if ( this.fuelEach != this.tileFurnace.getField( 1 ) ) { icontainerlistener.sendProgressBarUpdate( this , 1 , this.tileFurnace.getField( 1 ) ) ; } if ( this.cookSpent != this.tileFurnace.getField( 2 ) ) { icontainerlistener.sendProgressBarUpdate( this , 2 , this.tileFurnace.getField( 2 ) ) ; } if ( this.cookEach != this.tileFurnace.getField( 3 ) ) { icontainerlistener.sendProgressBarUpdate( this , 3 , this.tileFurnace.getField( 3 ) ) ; } } this.fuelLeft = this.tileFurnace.getField( 0 ) ; this.fuelEach = this.tileFurnace.getField( 1 ) ; this.cookSpent = this.tileFurnace.getField( 2 ) ; this.cookEach = this.tileFurnace.getField( 3 ) ; } @Override @SideOnly( Side.CLIENT ) public void updateProgressBar( int id , int data ) { this.tileFurnace.setField( id , data ) ; } @Override public boolean canInteractWith( EntityPlayer player ) { return this.tileFurnace.isUseableByPlayer( player ) ; } //TODO direct copy from vanilla furnace with minimal edits @Nullable public ItemStack transferStackInSlot( EntityPlayer playerIn , int index ) { System.out.println( "transfer stack in slot" ) ; ItemStack itemstack = null ; Slot slot = (Slot) this.inventorySlots.get( index ) ; if ( slot != null && slot.getHasStack() ) { System.out.println( "slot already has stack " ) ; ItemStack itemstack1 = slot.getStack() ; itemstack = itemstack1.copy() ; if ( index == 2 ) { System.out.println( "putting into index 2" ) ; if ( !this.mergeItemStack( itemstack1 , 3 , 39 , true ) ) { return null ; } slot.onSlotChange( itemstack1 , itemstack ) ; } else if ( index != 1 && index != 0 ) { System.out.println( "putting into index not 1 or 0" ) ; if ( ( (TEFurnaceAbstract) tileFurnace ).cookingManager .getSmeltingResult( itemstack1 ) != null ) { if ( !this.mergeItemStack( itemstack1 , 0 , 1 , false ) ) { return null ; } } else if ( ( (TEFurnaceAbstract) tileFurnace ).isItemFuel( itemstack1 ) ) { if ( !this.mergeItemStack( itemstack1 , 1 , 2 , false ) ) { return null ; } } else if ( index >= 3 && index < 30 ) { if ( !this.mergeItemStack( itemstack1 , 30 , 39 , false ) ) { return null ; } } else if ( index >= 30 && index < 39 && !this .mergeItemStack( itemstack1 , 3 , 30 , false ) ) { return null ; } } else if ( !this.mergeItemStack( itemstack1 , 3 , 39 , false ) ) { return null ; } if ( itemstack1.stackSize == 0 ) { slot.putStack( (ItemStack) null ) ; } else { slot.onSlotChanged() ; } if ( itemstack1.stackSize == itemstack.stackSize ) { return null ; } slot.onPickupFromSlot( playerIn , itemstack1 ) ; } tileFurnace.markDirty() ; return itemstack ; } } Subclass: package com.jj.jjmod.inventory.container ; import com.jj.jjmod.inventory.container.abstracts.ContainerFurnaceAbstract ; import com.jj.jjmod.tileentities.TEFurnaceCampfire ; import net.minecraft.entity.player.InventoryPlayer ; import net.minecraft.inventory.IInventory ; public class ContainerFurnaceCampfire extends ContainerFurnaceAbstract { public ContainerFurnaceCampfire( InventoryPlayer playerInv , IInventory te ) { super( playerInv , te ) ; } } (I know this isn't a necessary subclass yet but I have plans to add more customisations so that's why) I also have the TileEntities registered in init in my common proxy, the blocks registered in preinit, and a GUI handler to set up the containers. I can post all of that if you really want but I'll stop here for now. You write your ItemStacks to the tag items and then try to read them from stacks. That was not obvious, also I see you override getUpdateTag(...) but not its partner. VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect. Forge and vanilla BlockState generator.
September 11, 20169 yr Author You write your ItemStacks to the tag items and then try to read them from stacks. That was not obvious, also I see you override getUpdateTag(...) but not its partner. Oh my goodness, that is such an embarrassing mistake to make and not notice. Thank you for pointing it out. The inventory is now correctly saving and loading with the world. (The getUpdateTag(...) was just an experiment from another post I found about a similar problem so I added it without fully understanding, but luckily it turns out it's not needed at all anyway).
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.