EDIT: I have managed to solve MOST of my problems. If you wish to see what they were or what they were related to, look inside:
Okay, so I've been doing some work on a mod that adds a few tiers of tools, as well as a custom furnace which smelts twice as fast as a regular furnace. The furnace, itself, works fine. Every class, such as TileEntityCustomFurnace, or CustomFurnaceRecipes are all based on the vanilla furnace classes. It was working fine, with all the custom recipes I added and there were no problems. However, I wished to try something further.
I plan on allowing the furnace to double the output. This isn't too difficult...it's just a matter of changing the furnace recipes. These aren't the problem. If I'm going to let the output double, I wish to make a second output slot (this is, of course, optional for the furnace to be functional, but it would be convenient for people who just throw more than half a stack of ore into a non-automated furnace).
I set out through the code I almost barely understand and located a place I could add a new slot. Well, it wasn't too difficult to do. I created a simple 4th furnace slot, but it was simply a storage slot. I could put things in it, and take things out of it, but the furnace didn't actually drop its extra output into it like it should. I had to implement further.
After a lot of trial and error, I finally got it working. One problem...whenever I took what was in the original output slot out of the furnace, the game would instantly crash...whether the second output slot had anything in it or not.
So there goes another few hours of my life I'll never get back. I finally got it working again. The slots were filling up as they should, however, this time, when I took items out of the original output slot, the game wouldn't crash...until a new item was smelted. -sigh-
A few more hours later, I'm stumped. I've done as much as I could and I've attempted to pinpoint the problem. I've done so much changing around of the code, that now it just won't smelt at all (well, it will, but it crashes as soon as a new item is smelted, whether any items were taken out of it or not...essentially, I can place the furnace down, put fuel into it, put an ore into it...it will begin smelting...then crash as soon as the item should go to the output slot).
So, without further ado, I give you my TileEntityFurnace class. You shouldn't need any of my other classes, as none of them have been changed in any way, except for the names of a few methods and whatnot. The only exception is I added "this.addSlotToContainer(new SlotEnderFurnace(par1InventoryPlayer.player, par2TileEntityEnderFurnace, 3, 148, 35));" to my ContainerFurnace class
package dragontools_common;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.ISidedInventory;
import net.minecraft.item.Item;
import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemHoe;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemSword;
import net.minecraft.item.ItemTool;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.tileentity.TileEntity;
import net.minecraftforge.common.ForgeDirection;
import net.minecraftforge.common.ForgeDummyContainer;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class TileEntityEnderFurnace extends TileEntity implements ISidedInventory, net.minecraftforge.common.ISidedInventory
{
private static final int[] field_102010_d = new int[] {0};
private static final int[] field_102011_e = new int[] {2, 1};
private static final int[] field_102009_f = new int[] {1};
/**
* The ItemStacks that hold the items currently being used in the furnace
*/
private ItemStack[] furnaceItemStacks = new ItemStack[4];
/** The number of ticks that the furnace will keep burning */
public int furnaceBurnTime = 0;
/**
* The number of ticks that a fresh copy of the currently-burning item would keep the furnace burning for
*/
public int currentItemBurnTime = 0;
/** The number of ticks that the current item has been cooking for */
public int furnaceCookTime = 0;
private String field_94130_e;
/**
* Returns the number of slots in the inventory.
*/
public int getSizeInventory()
{
return this.furnaceItemStacks.length;
}
/**
* Returns the stack in slot i
*/
public ItemStack getStackInSlot(int par1)
{
return this.furnaceItemStacks[par1];
}
/**
* Removes from an inventory slot (first arg) up to a specified number (second arg) of items and returns them in a
* new stack.
*/
public ItemStack decrStackSize(int par1, int par2)
{
if (this.furnaceItemStacks[par1] != null)
{
ItemStack itemstack;
if (this.furnaceItemStacks[par1].stackSize <= par2)
{
itemstack = this.furnaceItemStacks[par1];
this.furnaceItemStacks[par1] = null;
return itemstack;
}
else
{
itemstack = this.furnaceItemStacks[par1].splitStack(par2);
if (this.furnaceItemStacks[par1].stackSize == 0)
{
this.furnaceItemStacks[par1] = null;
}
return itemstack;
}
}
else
{
return null;
}
}
/**
* When some containers are closed they call this on each slot, then drop whatever it returns as an EntityItem -
* like when you close a workbench GUI.
*/
public ItemStack getStackInSlotOnClosing(int par1)
{
if (this.furnaceItemStacks[par1] != null)
{
ItemStack itemstack = this.furnaceItemStacks[par1];
this.furnaceItemStacks[par1] = null;
return itemstack;
}
else
{
return null;
}
}
/**
* Sets the given item stack to the specified slot in the inventory (can be crafting or armor sections).
*/
public void setInventorySlotContents(int par1, ItemStack par2ItemStack)
{
this.furnaceItemStacks[par1] = par2ItemStack;
if (par2ItemStack != null && par2ItemStack.stackSize > this.getInventoryStackLimit())
{
par2ItemStack.stackSize = this.getInventoryStackLimit();
}
}
/**
* Returns the name of the inventory.
*/
public String getInvName()
{
return this.isInvNameLocalized() ? this.field_94130_e : "Ender Furnace";
}
/**
* If this returns false, the inventory name will be used as an unlocalized name, and translated into the player's
* language. Otherwise it will be used directly.
*/
public boolean isInvNameLocalized()
{
return this.field_94130_e != null && this.field_94130_e.length() > 0;
}
public void func_94129_a(String par1Str)
{
this.field_94130_e = par1Str;
}
/**
* Reads a tile entity from NBT.
*/
public void readFromNBT(NBTTagCompound par1NBTTagCompound)
{
super.readFromNBT(par1NBTTagCompound);
NBTTagList nbttaglist = par1NBTTagCompound.getTagList("Items");
this.furnaceItemStacks = new ItemStack[this.getSizeInventory()];
for (int i = 0; i < nbttaglist.tagCount(); ++i)
{
NBTTagCompound nbttagcompound1 = (NBTTagCompound)nbttaglist.tagAt(i);
byte b0 = nbttagcompound1.getByte("Slot");
if (b0 >= 0 && b0 < this.furnaceItemStacks.length)
{
this.furnaceItemStacks[b0] = ItemStack.loadItemStackFromNBT(nbttagcompound1);
}
}
this.furnaceBurnTime = par1NBTTagCompound.getShort("BurnTime");
this.furnaceCookTime = par1NBTTagCompound.getShort("CookTime");
this.currentItemBurnTime = getItemBurnTime(this.furnaceItemStacks[1]);
if (par1NBTTagCompound.hasKey("Ender Furnace"))
{
this.field_94130_e = par1NBTTagCompound.getString("Ender Furnace");
}
}
/**
* Writes a tile entity to NBT.
*/
public void writeToNBT(NBTTagCompound par1NBTTagCompound)
{
super.writeToNBT(par1NBTTagCompound);
par1NBTTagCompound.setShort("BurnTime", (short)this.furnaceBurnTime);
par1NBTTagCompound.setShort("CookTime", (short)this.furnaceCookTime);
NBTTagList nbttaglist = new NBTTagList();
for (int i = 0; i < this.furnaceItemStacks.length; ++i)
{
if (this.furnaceItemStacks[i] != null)
{
NBTTagCompound nbttagcompound1 = new NBTTagCompound();
nbttagcompound1.setByte("Slot", (byte)i);
this.furnaceItemStacks[i].writeToNBT(nbttagcompound1);
nbttaglist.appendTag(nbttagcompound1);
}
}
par1NBTTagCompound.setTag("Items", nbttaglist);
if (this.isInvNameLocalized())
{
par1NBTTagCompound.setString("Ender Furnace", this.field_94130_e);
}
}
/**
* Returns the maximum stack size for a inventory slot. Seems to always be 64, possibly will be extended. *Isn't
* this more of a set than a get?*
*/
public int getInventoryStackLimit()
{
return 64;
}
@SideOnly(Side.CLIENT)
/**
* Returns an integer between 0 and the passed value representing how close the current item is to being completely
* cooked
*/
public int getCookProgressScaled(int par1)
{
return this.furnaceCookTime * par1 / 100;
}
@SideOnly(Side.CLIENT)
/**
* Returns an integer between 0 and the passed value representing how much burn time is left on the current fuel
* item, where 0 means that the item is exhausted and the passed value means that the item is fresh
*/
public int getBurnTimeRemainingScaled(int par1)
{
if (this.currentItemBurnTime == 0)
{
this.currentItemBurnTime = 100;
}
return this.furnaceBurnTime * par1 / this.currentItemBurnTime;
}
/**
* Returns true if the furnace is currently burning
*/
public boolean isBurning()
{
return this.furnaceBurnTime > 0;
}
/**
* Allows the entity to update its state. Overridden in most subclasses, e.g. the mob spawner uses this to count
* ticks and creates a new spawn inside its implementation.
*/
public void updateEntity()
{
boolean flag = this.furnaceBurnTime > 0;
boolean flag1 = false;
if (this.furnaceBurnTime > 0)
{
--this.furnaceBurnTime;
}
if (!this.worldObj.isRemote)
{
if (this.furnaceBurnTime == 0 && this.canSmelt())
{
this.currentItemBurnTime = this.furnaceBurnTime = getItemBurnTime(this.furnaceItemStacks[1]);
if (this.furnaceBurnTime > 0)
{
flag1 = true;
if (this.furnaceItemStacks[1] != null)
{
--this.furnaceItemStacks[1].stackSize;
if (this.furnaceItemStacks[1].stackSize == 0)
{
this.furnaceItemStacks[1] = this.furnaceItemStacks[1].getItem().getContainerItemStack(furnaceItemStacks[1]);
}
}
}
}
if (this.isBurning() && this.canSmelt())
{
++this.furnaceCookTime;
if (this.furnaceCookTime == 100)
{
this.furnaceCookTime = 0;
this.smeltItem();
flag1 = true;
}
}
else
{
this.furnaceCookTime = 0;
}
if (flag != this.furnaceBurnTime > 0)
{
flag1 = true;
BlockEnderFurnace.updateFurnaceBlockState(this.furnaceBurnTime > 0, this.worldObj, this.xCoord, this.yCoord, this.zCoord);
}
}
if (flag1)
{
this.onInventoryChanged();
}
}
/**
* Returns true if the furnace can smelt an item, i.e. has a source item, destination stack isn't full, etc.
*/
private boolean canSmelt()
{
if (this.furnaceItemStacks[0] == null)
{
return false;
}
else
{
ItemStack itemstack = EnderFurnaceRecipes.smelting().getSmeltingResult(this.furnaceItemStacks[0]);
if (itemstack == null) return false;
if (this.furnaceItemStacks[2] == null || this.furnaceItemStacks[3] == null) return true;
if (!this.furnaceItemStacks[2].isItemEqual(itemstack) || !this.furnaceItemStacks[3].isItemEqual(itemstack)) return false;
int result = furnaceItemStacks[2].stackSize + furnaceItemStacks[3].stackSize + itemstack.stackSize;
return (result <= getInventoryStackLimit() && result <= itemstack.getMaxStackSize());
}
}
/**
* Turn one item from the furnace source stack into the appropriate smelted item in the furnace result stack
*/
public void smeltItem()
{
if (this.canSmelt())
{
ItemStack itemstack = EnderFurnaceRecipes.smelting().getSmeltingResult(this.furnaceItemStacks[0]);
if(furnaceItemStacks[2].stackSize == itemstack.getMaxStackSize())
{
if (this.furnaceItemStacks[3] == null)
{
this.furnaceItemStacks[3] = itemstack.copy();
}
else if (this.furnaceItemStacks[3].isItemEqual(itemstack))
{
furnaceItemStacks[3].stackSize += itemstack.stackSize;
}
}
else
{
if (this.furnaceItemStacks[2] == null)
{
this.furnaceItemStacks[2] = itemstack.copy();
}
else if (this.furnaceItemStacks[2].isItemEqual(itemstack))
{
furnaceItemStacks[2].stackSize += itemstack.stackSize;
}
}
--this.furnaceItemStacks[0].stackSize;
if (this.furnaceItemStacks[0].stackSize <= 0)
{
this.furnaceItemStacks[0] = null;
}
}
}
/**
* Returns the number of ticks that the supplied fuel item will keep the furnace burning, or 0 if the item isn't
* fuel
*/
public static int getItemBurnTime(ItemStack par0ItemStack)
{
if (par0ItemStack == null)
{
return 0;
}
else
{
int i = par0ItemStack.getItem().itemID;
Item item = par0ItemStack.getItem();
if (par0ItemStack.getItem() instanceof ItemBlock && Block.blocksList[i] != null)
{
Block block = Block.blocksList[i];
if (block == Block.woodSingleSlab)
{
return 195;
}
if (block.blockMaterial == Material.wood)
{
return 390;
}
}
if (item instanceof ItemTool && ((ItemTool) item).getToolMaterialName().equals("blazed")) return 100;
if (item instanceof ItemSword && ((ItemSword) item).getToolMaterialName().equals("blazed")) return 100;
if (item instanceof ItemHoe && ((ItemHoe) item).getMaterialName().equals("blazed")) return 100;
if (i == DragonTools.blazedDust.itemID) return 800;
if (i == DragonTools.blazedIngot.itemID) return 1600;
if (i == DragonTools.blazedBlock.blockID) return 8000;
return GameRegistry.getFuelValue(par0ItemStack);
}
}
/**
* Return true if item is a fuel source (getItemBurnTime() > 0).
*/
public static boolean isItemFuel(ItemStack par0ItemStack)
{
return getItemBurnTime(par0ItemStack) > 0;
}
/**
* Do not make give this method the name canInteractWith because it clashes with Container
*/
public boolean isUseableByPlayer(EntityPlayer par1EntityPlayer)
{
return this.worldObj.getBlockTileEntity(this.xCoord, this.yCoord, this.zCoord) != this ? false : par1EntityPlayer.getDistanceSq((double)this.xCoord + 0.5D, (double)this.yCoord + 0.5D, (double)this.zCoord + 0.5D) <= 64.0D;
}
public void openChest() {}
public void closeChest() {}
/**
* Returns true if automation is allowed to insert the given stack (ignoring stack size) into the given slot.
*/
public boolean isStackValidForSlot(int par1, ItemStack par2ItemStack)
{
return par1 == 2 ? false : (par1 == 1 ? isItemFuel(par2ItemStack) : true);
}
/**
* Returns an array containing the indices of the slots that can be accessed by automation on the given side of this
* block.
*/
public int[] getAccessibleSlotsFromSide(int par1)
{
return par1 == 0 ? field_102011_e : (par1 == 1 ? field_102010_d : field_102009_f);
}
/**
* Returns true if automation can insert the given item in the given slot from the given side. Args: Slot, item,
* side
*/
public boolean canInsertItem(int par1, ItemStack par2ItemStack, int par3)
{
return this.isStackValidForSlot(par1, par2ItemStack);
}
/**
* Returns true if automation can extract the given item in the given slot from the given side. Args: Slot, item,
* side
*/
public boolean canExtractItem(int par1, ItemStack par2ItemStack, int par3)
{
return par3 != 0 || par1 != 1 || par2ItemStack.itemID == Item.bucketEmpty.itemID;
}
/***********************************************************************************
* This function is here for compatibilities sake, Modders should Check for
* Sided before ContainerWorldly, Vanilla Minecraft does not follow the sided standard
* that Modding has for a while.
*
* In vanilla:
*
* Top: Ores
* Sides: Fuel
* Bottom: Output
*
* Standard Modding:
* Top: Ores
* Sides: Output
* Bottom: Fuel
*
* The Modding one is designed after the GUI, the vanilla one is designed because its
* intended use is for the hopper, which logically would take things in from the top.
*
* This will possibly be removed in future updates, and make vanilla the definitive
* standard.
*/
@Override
public int getStartInventorySide(ForgeDirection side)
{
if (ForgeDummyContainer.legacyFurnaceSides)
{
if (side == ForgeDirection.DOWN) return 1;
if (side == ForgeDirection.UP) return 0;
return 2;
}
else
{
if (side == ForgeDirection.DOWN) return 2;
if (side == ForgeDirection.UP) return 0;
return 1;
}
}
@Override
public int getSizeInventorySide(ForgeDirection side)
{
return 1;
}
}
Only a part of the code has been changed, and I have reason to believe the main problem is either within:
private boolean canSmelt()
{
if (this.furnaceItemStacks[0] == null)
{
return false;
}
else
{
ItemStack itemstack = EnderFurnaceRecipes.smelting().getSmeltingResult(this.furnaceItemStacks[0]);
if (itemstack == null) return false;
if (this.furnaceItemStacks[2] == null || this.furnaceItemStacks[3] == null) return true;
if (!this.furnaceItemStacks[2].isItemEqual(itemstack) || !this.furnaceItemStacks[3].isItemEqual(itemstack)) return false;
int result = furnaceItemStacks[2].stackSize + itemstack.stackSize + furnaceItemStacks[3].stackSize + itemstack.stackSize;
return (result <= getInventoryStackLimit() && result <= itemstack.getMaxStackSize());
}
}
or
public void smeltItem()
{
if (this.canSmelt())
{
ItemStack itemstack = EnderFurnaceRecipes.smelting().getSmeltingResult(this.furnaceItemStacks[0]);
//if first output slot is full, start outputting to second output slot
if(furnaceItemStacks[2].stackSize == itemstack.getMaxStackSize())
{
if (this.furnaceItemStacks[3] == null)
{
this.furnaceItemStacks[3] = itemstack.copy();
}
else if (this.furnaceItemStacks[3].isItemEqual(itemstack))
{
furnaceItemStacks[3].stackSize += itemstack.stackSize;
}
}
else
{
if (this.furnaceItemStacks[2] == null)
{
this.furnaceItemStacks[2] = itemstack.copy();
}
else if (this.furnaceItemStacks[2].isItemEqual(itemstack))
{
furnaceItemStacks[2].stackSize += itemstack.stackSize;
}
}
--this.furnaceItemStacks[0].stackSize;
if (this.furnaceItemStacks[0].stackSize <= 0)
{
this.furnaceItemStacks[0] = null;
}
}
}
If you haven't figured out what I want the furnace to do by now...it's simple:
1-Basic furnace-ly duties, such as check if the items inside can be smelted up together to create something else, then output the result
2-If the first output slot is full, move on to fill the second output slot
3-If both are full...do the obvious thing and stop smelting, all together
The main error I've been getting when the game crashes in the ways I've described is
Minecraft has crashed!
----------------------
Minecraft has stopped running because it encountered a problem; Ticking tile entity
----------------------------------------------------------------------------------
-- Head --
Stacktrace:
at dragontools_common.TileEntityEnderFurnace.smeltItem(TileEntityEnderFurnace.java:345)
at dragontools_common.TileEntityEnderFurnace.updateEntity(TileEntityEnderFurnace.java:294)
-- Tile entity being ticked --
Details:
Name: tileEntityEnderFurnace // dragontools_common.TileEntityEnderFurnace
Block type: ID #509 (tile.enderFurnaceBurning // dragontools_common.BlockEnderFurnace)
Block data value: 2 / 0x2 / 0b0010
Block location: World: (188,4,232), Chunk: (at 12,0,8 in 11,14; contains blocks 176,0,224 to 191,255,239), Region: (0,0; contains chunks 0,0 to 31,31, blocks 0,0,0 to 511,255,511)
Actual block type: ID #509 (tile.enderFurnaceBurning // dragontools_common.BlockEnderFurnace)
Actual block data value: 2 / 0x2 / 0b0010
Stacktrace:
at net.minecraft.world.World.updateEntities(World.java:2202)
at net.minecraft.world.WorldServer.updateEntities(WorldServer.java:546)
It points out lines 345 ("if(furnaceItemStacks[2].stackSize == itemstack.getMaxStackSize())" and 294 ("this.smeltItem();") If anyone could help me out here, that would be great.
As I said. I have managed to solve MOST of my problem. There is just ONE MORE THING that is keeping me from calling my furnace a complete machine.
First of all, here is the updated TileEntityFurnace class of mine:
package dragontools_common;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.ISidedInventory;
import net.minecraft.item.Item;
import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemHoe;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemSword;
import net.minecraft.item.ItemTool;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.tileentity.TileEntity;
import net.minecraftforge.common.ForgeDirection;
import net.minecraftforge.common.ForgeDummyContainer;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class TileEntityEnderFurnace extends TileEntity implements ISidedInventory, net.minecraftforge.common.ISidedInventory
{
private static final int[] field_102010_d = new int[] {0};
private static final int[] field_102011_e = new int[] {2, 1};
private static final int[] field_102009_f = new int[] {1};
/**
* The ItemStacks that hold the items currently being used in the furnace
*/
private ItemStack[] furnaceItemStacks = new ItemStack[4];
/** The number of ticks that the furnace will keep burning */
public int furnaceBurnTime = 0;
/**
* The number of ticks that a fresh copy of the currently-burning item would keep the furnace burning for
*/
public int currentItemBurnTime = 0;
/** The number of ticks that the current item has been cooking for */
public int furnaceCookTime = 0;
private String field_94130_e;
/**
* Returns the number of slots in the inventory.
*/
public int getSizeInventory()
{
return this.furnaceItemStacks.length;
}
/**
* Returns the stack in slot i
*/
public ItemStack getStackInSlot(int par1)
{
return this.furnaceItemStacks[par1];
}
/**
* Removes from an inventory slot (first arg) up to a specified number (second arg) of items and returns them in a
* new stack.
*/
public ItemStack decrStackSize(int par1, int par2)
{
if (this.furnaceItemStacks[par1] != null)
{
ItemStack itemstack;
if (this.furnaceItemStacks[par1].stackSize <= par2)
{
itemstack = this.furnaceItemStacks[par1];
this.furnaceItemStacks[par1] = null;
return itemstack;
}
else
{
itemstack = this.furnaceItemStacks[par1].splitStack(par2);
if (this.furnaceItemStacks[par1].stackSize == 0)
{
this.furnaceItemStacks[par1] = null;
}
return itemstack;
}
}
else
{
return null;
}
}
/**
* When some containers are closed they call this on each slot, then drop whatever it returns as an EntityItem -
* like when you close a workbench GUI.
*/
public ItemStack getStackInSlotOnClosing(int par1)
{
if (this.furnaceItemStacks[par1] != null)
{
ItemStack itemstack = this.furnaceItemStacks[par1];
this.furnaceItemStacks[par1] = null;
return itemstack;
}
else
{
return null;
}
}
/**
* Sets the given item stack to the specified slot in the inventory (can be crafting or armor sections).
*/
public void setInventorySlotContents(int par1, ItemStack par2ItemStack)
{
this.furnaceItemStacks[par1] = par2ItemStack;
if (par2ItemStack != null && par2ItemStack.stackSize > this.getInventoryStackLimit())
{
par2ItemStack.stackSize = this.getInventoryStackLimit();
}
}
/**
* Returns the name of the inventory.
*/
public String getInvName()
{
return this.isInvNameLocalized() ? this.field_94130_e : "Ender Furnace";
}
/**
* If this returns false, the inventory name will be used as an unlocalized name, and translated into the player's
* language. Otherwise it will be used directly.
*/
public boolean isInvNameLocalized()
{
return this.field_94130_e != null && this.field_94130_e.length() > 0;
}
public void func_94129_a(String par1Str)
{
this.field_94130_e = par1Str;
}
/**
* Reads a tile entity from NBT.
*/
public void readFromNBT(NBTTagCompound par1NBTTagCompound)
{
super.readFromNBT(par1NBTTagCompound);
NBTTagList nbttaglist = par1NBTTagCompound.getTagList("Items");
this.furnaceItemStacks = new ItemStack[this.getSizeInventory()];
for (int i = 0; i < nbttaglist.tagCount(); ++i)
{
NBTTagCompound nbttagcompound1 = (NBTTagCompound)nbttaglist.tagAt(i);
byte b0 = nbttagcompound1.getByte("Slot");
if (b0 >= 0 && b0 < this.furnaceItemStacks.length)
{
this.furnaceItemStacks[b0] = ItemStack.loadItemStackFromNBT(nbttagcompound1);
}
}
this.furnaceBurnTime = par1NBTTagCompound.getShort("BurnTime");
this.furnaceCookTime = par1NBTTagCompound.getShort("CookTime");
this.currentItemBurnTime = getItemBurnTime(this.furnaceItemStacks[1]);
if (par1NBTTagCompound.hasKey("Ender Furnace"))
{
this.field_94130_e = par1NBTTagCompound.getString("Ender Furnace");
}
}
/**
* Writes a tile entity to NBT.
*/
public void writeToNBT(NBTTagCompound par1NBTTagCompound)
{
super.writeToNBT(par1NBTTagCompound);
par1NBTTagCompound.setShort("BurnTime", (short)this.furnaceBurnTime);
par1NBTTagCompound.setShort("CookTime", (short)this.furnaceCookTime);
NBTTagList nbttaglist = new NBTTagList();
for (int i = 0; i < this.furnaceItemStacks.length; ++i)
{
if (this.furnaceItemStacks[i] != null)
{
NBTTagCompound nbttagcompound1 = new NBTTagCompound();
nbttagcompound1.setByte("Slot", (byte)i);
this.furnaceItemStacks[i].writeToNBT(nbttagcompound1);
nbttaglist.appendTag(nbttagcompound1);
}
}
par1NBTTagCompound.setTag("Items", nbttaglist);
if (this.isInvNameLocalized())
{
par1NBTTagCompound.setString("Ender Furnace", this.field_94130_e);
}
}
/**
* Returns the maximum stack size for a inventory slot. Seems to always be 64, possibly will be extended. *Isn't
* this more of a set than a get?*
*/
public int getInventoryStackLimit()
{
return 64;
}
@SideOnly(Side.CLIENT)
/**
* Returns an integer between 0 and the passed value representing how close the current item is to being completely
* cooked
*/
public int getCookProgressScaled(int par1)
{
return this.furnaceCookTime * par1 / 100;
}
@SideOnly(Side.CLIENT)
/**
* Returns an integer between 0 and the passed value representing how much burn time is left on the current fuel
* item, where 0 means that the item is exhausted and the passed value means that the item is fresh
*/
public int getBurnTimeRemainingScaled(int par1)
{
if (this.currentItemBurnTime == 0)
{
this.currentItemBurnTime = 100;
}
return this.furnaceBurnTime * par1 / this.currentItemBurnTime;
}
/**
* Returns true if the furnace is currently burning
*/
public boolean isBurning()
{
return this.furnaceBurnTime > 0;
}
/**
* Allows the entity to update its state. Overridden in most subclasses, e.g. the mob spawner uses this to count
* ticks and creates a new spawn inside its implementation.
*/
public void updateEntity()
{
boolean flag = this.furnaceBurnTime > 0;
boolean flag1 = false;
if (this.furnaceBurnTime > 0)
{
--this.furnaceBurnTime;
}
if (!this.worldObj.isRemote)
{
if (this.furnaceBurnTime == 0 && this.canSmelt())
{
this.currentItemBurnTime = this.furnaceBurnTime = getItemBurnTime(this.furnaceItemStacks[1]);
if (this.furnaceBurnTime > 0)
{
flag1 = true;
if (this.furnaceItemStacks[1] != null)
{
--this.furnaceItemStacks[1].stackSize;
if (this.furnaceItemStacks[1].stackSize == 0)
{
this.furnaceItemStacks[1] = this.furnaceItemStacks[1].getItem().getContainerItemStack(furnaceItemStacks[1]);
}
}
}
}
if (this.isBurning() && this.canSmelt())
{
++this.furnaceCookTime;
if (this.furnaceCookTime == 10)
{
this.furnaceCookTime = 0;
this.smeltItem();
flag1 = true;
}
}
else
{
this.furnaceCookTime = 0;
}
if (flag != this.furnaceBurnTime > 0)
{
flag1 = true;
BlockEnderFurnace.updateFurnaceBlockState(this.furnaceBurnTime > 0, this.worldObj, this.xCoord, this.yCoord, this.zCoord);
}
}
if (flag1)
{
this.onInventoryChanged();
}
}
/**
* Returns true if the furnace can smelt an item, i.e. has a source item, destination stack isn't full, etc.
*/
private boolean canSmelt()
{
if (this.furnaceItemStacks[0] == null)
{
return false;
}
else
{
ItemStack itemstack = EnderFurnaceRecipes.smelting().getSmeltingResult(this.furnaceItemStacks[0]);
if (itemstack == null) return false;
if (this.furnaceItemStacks[2] == null) return true;
if (this.furnaceItemStacks[3] == null) return true;
if (!this.furnaceItemStacks[2].isItemEqual(itemstack)) return false;
if (!this.furnaceItemStacks[3].isItemEqual(itemstack)) return false;
int result = (this.furnaceItemStacks[2].stackSize + this.furnaceItemStacks[3].stackSize) / 2 + itemstack.stackSize;
return (result <= getInventoryStackLimit() && result <= itemstack.getMaxStackSize());
}
}
/**
* Turn one item from the furnace source stack into the appropriate smelted item in the furnace result stack
*/
public void smeltItem()
{
if (this.canSmelt())
{
ItemStack itemstack = EnderFurnaceRecipes.smelting().getSmeltingResult(this.furnaceItemStacks[0]);
if (this.furnaceItemStacks[2] == null)
{
this.furnaceItemStacks[2] = itemstack.copy();
}
else if (this.furnaceItemStacks[2].isItemEqual(itemstack))
{
this.furnaceItemStacks[2].stackSize += itemstack.stackSize;
if(this.furnaceItemStacks[2].stackSize > itemstack.getMaxStackSize())
{
this.furnaceItemStacks[3].stackSize += this.furnaceItemStacks[2].stackSize - itemstack.getMaxStackSize();
this.furnaceItemStacks[2].stackSize = itemstack.getMaxStackSize();
}
}
ItemStack itemstack1 = EnderFurnaceRecipes.smelting().getSmeltingResult(this.furnaceItemStacks[0]);
if (this.furnaceItemStacks[3] == null)
{
this.furnaceItemStacks[3] = itemstack1.copy();
}
else if (this.furnaceItemStacks[3].isItemEqual(itemstack1))
{
this.furnaceItemStacks[3].stackSize += itemstack1.stackSize;
if(this.furnaceItemStacks[3].stackSize > itemstack1.getMaxStackSize())
{
this.furnaceItemStacks[2].stackSize += this.furnaceItemStacks[3].stackSize - itemstack1.getMaxStackSize();
this.furnaceItemStacks[3].stackSize = itemstack1.getMaxStackSize();
}
}
--this.furnaceItemStacks[0].stackSize;
if (this.furnaceItemStacks[0].stackSize <= 0)
{
this.furnaceItemStacks[0] = null;
}
}
}
/**
* Returns the number of ticks that the supplied fuel item will keep the furnace burning, or 0 if the item isn't
* fuel
*/
public static int getItemBurnTime(ItemStack par0ItemStack)
{
if (par0ItemStack == null)
{
return 0;
}
else
{
int i = par0ItemStack.getItem().itemID;
Item item = par0ItemStack.getItem();
if (par0ItemStack.getItem() instanceof ItemBlock && Block.blocksList[i] != null)
{
Block block = Block.blocksList[i];
if (block == Block.woodSingleSlab)
{
return 195;
}
if (block.blockMaterial == Material.wood)
{
return 390;
}
}
if (item instanceof ItemTool && ((ItemTool) item).getToolMaterialName().equals("blazed")) return 100;
if (item instanceof ItemSword && ((ItemSword) item).getToolMaterialName().equals("blazed")) return 100;
if (item instanceof ItemHoe && ((ItemHoe) item).getMaterialName().equals("blazed")) return 100;
if (i == DragonTools.blazedDust.itemID) return 800;
if (i == DragonTools.blazedIngot.itemID) return 1600;
if (i == DragonTools.blazedBlock.blockID) return 8000;
return GameRegistry.getFuelValue(par0ItemStack);
}
}
/**
* Return true if item is a fuel source (getItemBurnTime() > 0).
*/
public static boolean isItemFuel(ItemStack par0ItemStack)
{
return getItemBurnTime(par0ItemStack) > 0;
}
/**
* Do not make give this method the name canInteractWith because it clashes with Container
*/
public boolean isUseableByPlayer(EntityPlayer par1EntityPlayer)
{
return this.worldObj.getBlockTileEntity(this.xCoord, this.yCoord, this.zCoord) != this ? false : par1EntityPlayer.getDistanceSq((double)this.xCoord + 0.5D, (double)this.yCoord + 0.5D, (double)this.zCoord + 0.5D) <= 64.0D;
}
public void openChest() {}
public void closeChest() {}
/**
* Returns true if automation is allowed to insert the given stack (ignoring stack size) into the given slot.
*/
public boolean isStackValidForSlot(int par1, ItemStack par2ItemStack)
{
return par1 == 2 ? false : (par1 == 1 ? isItemFuel(par2ItemStack) : true);
}
/**
* Returns an array containing the indices of the slots that can be accessed by automation on the given side of this
* block.
*/
public int[] getAccessibleSlotsFromSide(int par1)
{
return par1 == 0 ? field_102011_e : (par1 == 1 ? field_102010_d : field_102009_f);
}
/**
* Returns true if automation can insert the given item in the given slot from the given side. Args: Slot, item,
* side
*/
public boolean canInsertItem(int par1, ItemStack par2ItemStack, int par3)
{
return this.isStackValidForSlot(par1, par2ItemStack);
}
/**
* Returns true if automation can extract the given item in the given slot from the given side. Args: Slot, item,
* side
*/
public boolean canExtractItem(int par1, ItemStack par2ItemStack, int par3)
{
return par3 != 0 || par1 != 1 || par2ItemStack.itemID == Item.bucketEmpty.itemID;
}
/***********************************************************************************
* This function is here for compatibilities sake, Modders should Check for
* Sided before ContainerWorldly, Vanilla Minecraft does not follow the sided standard
* that Modding has for a while.
*
* In vanilla:
*
* Top: Ores
* Sides: Fuel
* Bottom: Output
*
* Standard Modding:
* Top: Ores
* Sides: Output
* Bottom: Fuel
*
* The Modding one is designed after the GUI, the vanilla one is designed because its
* intended use is for the hopper, which logically would take things in from the top.
*
* This will possibly be removed in future updates, and make vanilla the definitive
* standard.
*/
@Override
public int getStartInventorySide(ForgeDirection side)
{
if (ForgeDummyContainer.legacyFurnaceSides)
{
if (side == ForgeDirection.DOWN) return 1;
if (side == ForgeDirection.UP) return 0;
return 2;
}
else
{
if (side == ForgeDirection.DOWN) return 2;
if (side == ForgeDirection.UP) return 0;
return 1;
}
}
@Override
public int getSizeInventorySide(ForgeDirection side)
{
return 1;
}
}
As I thought, the main problem was in this part of the code:
public void smeltItem()
{
if (this.canSmelt())
{
ItemStack itemstack = EnderFurnaceRecipes.smelting().getSmeltingResult(this.furnaceItemStacks[0]);
//if first output slot is full, start outputting to second output slot
if(furnaceItemStacks[2].stackSize == itemstack.getMaxStackSize())
{
if (this.furnaceItemStacks[3] == null)
{
this.furnaceItemStacks[3] = itemstack.copy();
}
else if (this.furnaceItemStacks[3].isItemEqual(itemstack))
{
furnaceItemStacks[3].stackSize += itemstack.stackSize;
}
}
else
{
if (this.furnaceItemStacks[2] == null)
{
this.furnaceItemStacks[2] = itemstack.copy();
}
else if (this.furnaceItemStacks[2].isItemEqual(itemstack))
{
furnaceItemStacks[2].stackSize += itemstack.stackSize;
}
}
--this.furnaceItemStacks[0].stackSize;
if (this.furnaceItemStacks[0].stackSize <= 0)
{
this.furnaceItemStacks[0] = null;
}
}
}
For those who don't see exactly what it does, it will smelt one item and output one of that item's smelted variant in BOTH of the output slots. This way, I am effectively doubling any output, without needing to even touch the furnace recipes (handy if people install my mod alongside other mods that add new ores).
The furnace will also check to see if one of the slots is more full than the other. If it is, then it will put two items into the other, instead of one in each.
That last kinks I need help with:
- When one of the slots has at least one kind of smelted material in it and a different kind of material is being smelted, one ingot will be put into the other slot, and then the furnace will stop cooking until either that ingot is removed, or the other slot is cleared of the incompatible material. How can I fix this, or get the furnace to at least not smelt ANYTHING except for the ore that matches what's currently in the slots, so that ore isn't being wasted.
- When one of the slots is full and the other is at 63 (or at a number where the next output will bring it above 64...in this case 2+63), an item will still smelt and one of the stacks will become a number higher than 64 (in this case, 65). I would like it so that the furnace will not smelt unless there is enough room for the two outputs.
if (this.furnaceItemStacks[2] == null) return true;
if (this.furnaceItemStacks[3] == null) return true;
if (!this.furnaceItemStacks[2].isItemEqual(itemstack)) return false;
if (!this.furnaceItemStacks[3].isItemEqual(itemstack)) return false;
You are saying that you can smelt if slot 2 OR slot 3 is empty and you are saying that if the result is the same as what is in slot 2 OR 3, then check inventory.
What you need, from what it sounds, you need to combine these lines like so:
if (this.furnaceItemStacks[2] == null && this.furnaceItemStacks[3] == null) return true;
if (!this.furnaceItemStacks[2].isItemEqual(itemstack)&& !this.furnaceItemStacks[3].isItemEqual(itemstack)) return false;
This ensures that both requirements are met before smelting.
I plan on allowing the furnace to double the output. This isn't too difficult...it's just a matter of changing the furnace recipes. These aren't the problem. If I'm going to let the output double, I wish to make a second output slot (this is, of course, optional for the furnace to be functional, but it would be convenient for people who just throw more than half a stack of ore into a non-automated furnace).
I set out through the code I almost barely understand and located a place I could add a new slot. Well, it wasn't too difficult to do. I created a simple 4th furnace slot, but it was simply a storage slot. I could put things in it, and take things out of it, but the furnace didn't actually drop its extra output into it like it should. I had to implement further.
After a lot of trial and error, I finally got it working. One problem...whenever I took what was in the original output slot out of the furnace, the game would instantly crash...whether the second output slot had anything in it or not.
So there goes another few hours of my life I'll never get back. I finally got it working again. The slots were filling up as they should, however, this time, when I took items out of the original output slot, the game wouldn't crash...until a new item was smelted. -sigh-
A few more hours later, I'm stumped. I've done as much as I could and I've attempted to pinpoint the problem. I've done so much changing around of the code, that now it just won't smelt at all (well, it will, but it crashes as soon as a new item is smelted, whether any items were taken out of it or not...essentially, I can place the furnace down, put fuel into it, put an ore into it...it will begin smelting...then crash as soon as the item should go to the output slot).
So, without further ado, I give you my TileEntityFurnace class. You shouldn't need any of my other classes, as none of them have been changed in any way, except for the names of a few methods and whatnot. The only exception is I added "this.addSlotToContainer(new SlotEnderFurnace(par1InventoryPlayer.player, par2TileEntityEnderFurnace, 3, 148, 35));" to my ContainerFurnace class
Only a part of the code has been changed, and I have reason to believe the main problem is either within:
If you haven't figured out what I want the furnace to do by now...it's simple:
1-Basic furnace-ly duties, such as check if the items inside can be smelted up together to create something else, then output the result
2-If the first output slot is full, move on to fill the second output slot
3-If both are full...do the obvious thing and stop smelting, all together
The main error I've been getting when the game crashes in the ways I've described is
It points out lines 345 ("if(furnaceItemStacks[2].stackSize == itemstack.getMaxStackSize())" and 294 ("this.smeltItem();") If anyone could help me out here, that would be great.
As I said. I have managed to solve MOST of my problem. There is just ONE MORE THING that is keeping me from calling my furnace a complete machine.
First of all, here is the updated TileEntityFurnace class of mine:
As I thought, the main problem was in this part of the code:
I cleaned up that code. It's now:
For those who don't see exactly what it does, it will smelt one item and output one of that item's smelted variant in BOTH of the output slots. This way, I am effectively doubling any output, without needing to even touch the furnace recipes (handy if people install my mod alongside other mods that add new ores).
The furnace will also check to see if one of the slots is more full than the other. If it is, then it will put two items into the other, instead of one in each.
That last kinks I need help with:
- When one of the slots has at least one kind of smelted material in it and a different kind of material is being smelted, one ingot will be put into the other slot, and then the furnace will stop cooking until either that ingot is removed, or the other slot is cleared of the incompatible material. How can I fix this, or get the furnace to at least not smelt ANYTHING except for the ore that matches what's currently in the slots, so that ore isn't being wasted.
- When one of the slots is full and the other is at 63 (or at a number where the next output will bring it above 64...in this case 2+63), an item will still smelt and one of the stacks will become a number higher than 64 (in this case, 65). I would like it so that the furnace will not smelt unless there is enough room for the two outputs.
in your canSmelt() function, you are using
What you need, from what it sounds, you need to combine these lines like so: