Hi I have created a custom furnace but when i try to open the gui i get this crash here is my code.
---- Minecraft Crash Report ----
// This doesn't make any sense!
Time: 1/11/17 8:58 PM
Description: Ticking player
java.lang.NullPointerException: Ticking player
at net.minecraft.item.ItemStack.areItemStacksEqual(ItemStack.java:442)
at net.minecraft.inventory.Container.detectAndSendChanges(Container.java:89)
at com.github.tompinn23.tprocessing.gui.ContainerTFurnace.detectAndSendChanges(ContainerTFurnace.java:162)
at net.minecraft.entity.player.EntityPlayerMP.onUpdate(EntityPlayerMP.java:318)
at net.minecraft.world.World.updateEntityWithOptionalForce(World.java:2108)
at net.minecraft.world.WorldServer.updateEntityWithOptionalForce(WorldServer.java:875)
at net.minecraft.world.World.updateEntity(World.java:2075)
at net.minecraft.world.WorldServer.tickPlayers(WorldServer.java:676)
at net.minecraft.world.World.updateEntities(World.java:1864)
at net.minecraft.world.WorldServer.updateEntities(WorldServer.java:647)
at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:794)
at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:698)
at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:156)
at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:547)
at java.lang.Thread.run(Thread.java:745)
A detailed walkthrough of the error, its code path and all known details is as follows:
---------------------------------------------------------------------------------------
-- Head --
Thread: Server thread
Stacktrace:
at net.minecraft.item.ItemStack.areItemStacksEqual(ItemStack.java:442)
at net.minecraft.inventory.Container.detectAndSendChanges(Container.java:89)
at com.github.tompinn23.tprocessing.gui.ContainerTFurnace.detectAndSendChanges(ContainerTFurnace.java:162)
at net.minecraft.entity.player.EntityPlayerMP.onUpdate(EntityPlayerMP.java:318)
at net.minecraft.world.World.updateEntityWithOptionalForce(World.java:2108)
at net.minecraft.world.WorldServer.updateEntityWithOptionalForce(WorldServer.java:875)
at net.minecraft.world.World.updateEntity(World.java:2075)
-- Player being ticked --
Details:
Entity Type: null (net.minecraft.entity.player.EntityPlayerMP)
Entity ID: 71
Entity Name: Player76
Entity's Exact location: 1173.95, 4.00, -1346.13
Entity's Block location: World: (1173,4,-1347), Chunk: (at 5,0,13 in 73,-85; contains blocks 1168,0,-1360 to 1183,255,-1345), Region: (2,-3; contains chunks 64,-96 to 95,-65, blocks 1024,0,-1536 to 1535,255,-1025)
Entity's Momentum: 0.00, -0.08, 0.00
Entity's Passengers: []
Entity's Vehicle: ~~ERROR~~ NullPointerException: null
Stacktrace:
at net.minecraft.world.WorldServer.tickPlayers(WorldServer.java:676)
at net.minecraft.world.World.updateEntities(World.java:1864)
at net.minecraft.world.WorldServer.updateEntities(WorldServer.java:647)
-- Affected level --
Details:
Level name: New World
All players: 1 total; [EntityPlayerMP['Player76'/71, l='New World', x=1173.95, y=4.00, z=-1346.13]]
Chunk stats: ServerChunkCache: 657 Drop: 0
Level seed: -4789802384015756941
Level generator: ID 01 - flat, ver 0. Features enabled: true
Level generator options:
Level spawn location: World: (1179,4,-1250), Chunk: (at 11,0,14 in 73,-79; contains blocks 1168,0,-1264 to 1183,255,-1249), Region: (2,-3; contains chunks 64,-96 to 95,-65, blocks 1024,0,-1536 to 1535,255,-1025)
Level time: 31784 game time, 3208 day time
Level dimension: 0
Level storage version: 0x04ABD - Anvil
Level weather: Rain time: 54992 (now: false), thunder time: 9323 (now: true)
Level game mode: Game mode: creative (ID 1). Hardcore: false. Cheats: true
Stacktrace:
at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:794)
at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:698)
at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:156)
at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:547)
at java.lang.Thread.run(Thread.java:745)
-- System Details --
Details:
Minecraft Version: 1.11.2
Operating System: Windows 10 (amd64) version 10.0
Java Version: 1.8.0_111, Oracle Corporation
Java VM Version: Java HotSpot(TM) 64-Bit Server VM (mixed mode), Oracle Corporation
Memory: 1007328424 bytes (960 MB) / 1339031552 bytes (1277 MB) up to 2856321024 bytes (2724 MB)
JVM Flags: 0 total;
IntCache: cache: 0, tcache: 0, allocated: 0, tallocated: 0
FML: MCP 9.38 Powered by Forge 13.20.0.2206 5 mods loaded, 5 mods active
States: 'U' = Unloaded 'L' = Loaded 'C' = Constructed 'H' = Pre-initialized 'I' = Initialized 'J' = Post-initialized 'A' = Available 'D' = Disabled 'E' = Errored
UCHIJAAAA minecraft{1.11.2} [Minecraft] (minecraft.jar)
UCHIJAAAA mcp{9.19} [Minecraft Coder Pack] (minecraft.jar)
UCHIJAAAA FML{8.0.99.99} [Forge Mod Loader] (forgeSrc-1.11.2-13.20.0.2206.jar)
UCHIJAAAA forge{13.20.0.2206} [Minecraft Forge] (forgeSrc-1.11.2-13.20.0.2206.jar)
UCHIJAAAA tprocessing{1.0.0} [T's Processing] (Modding)
Loaded coremods (and transformers):
GL info: ~~ERROR~~ RuntimeException: No OpenGL context found in the current thread.
Profiler Position: N/A (disabled)
Player Count: 1 / 8; [EntityPlayerMP['Player76'/71, l='New World', x=1173.95, y=4.00, z=-1346.13]]
Type: Integrated Server (map_client.txt)
Is Modded: Definitely; Client brand changed to 'fml,forge'
ContainerTFurnace
package com.github.tompinn23.tprocessing.gui;
import com.github.tompinn23.tprocessing.block.TileEntityTFurnace;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.inventory.*;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
/**
* Created by Tom Pinnock on 11/01/2017.
*/
public class ContainerTFurnace extends Container {
// Stores the tile entity instance for later use
private TileEntityTFurnace tileEntityTFurnace;
// These store cache values, used by the server to only update the client side tile entity when values have changed
private int [] cachedFields;
// must assign a slot index to each of the slots used by the GUI.
// For this container, we can see the furnace fuel, input, and output slots as well as the player inventory slots and the hotbar.
// Each time we add a Slot to the container using addSlotToContainer(), it automatically increases the slotIndex, which means
// 0 - 8 = hotbar slots (which will map to the InventoryPlayer slot numbers 0 -
// 9 - 35 = player inventory slots (which map to the InventoryPlayer slot numbers 9 - 35)
// 36 - 37 = fuel slots (tileEntity 0 - 1)
// 37 - 38 = input slots (tileEntity 1 - 2)
// 38 - 39 = output slots (tileEntity 2 - 3)
private final int HOTBAR_SLOT_COUNT = 9;
private final int PLAYER_INVENTORY_ROW_COUNT = 3;
private final int PLAYER_INVENTORY_COLUMN_COUNT = 3;
private final int PLAYER_INVENTORY_SLOT_COUNT = PLAYER_INVENTORY_COLUMN_COUNT * PLAYER_INVENTORY_ROW_COUNT;
private final int VANILLA_SLOT_COUNT = HOTBAR_SLOT_COUNT + PLAYER_INVENTORY_SLOT_COUNT;
public final int FUEL_SLOTS_COUNT = 1;
public final int INPUT_SLOTS_COUNT = 1;
public final int OUTPUT_SLOTS_COUNT = 1;
public final int FURNACE_SLOTS_COUNT = 3;
// slot index is the unique index for all slots in this container i.e. 0 - 35 for invPlayer then 36 - 39 for tileEntityTFurnace
private final int VANILLA_FIRST_SLOT_INDEX = 0;
private final int FIRST_FUEL_SLOT_INDEX = VANILLA_FIRST_SLOT_INDEX + VANILLA_SLOT_COUNT;
private final int FIRST_INPUT_SLOT_INDEX = FIRST_FUEL_SLOT_INDEX + FUEL_SLOTS_COUNT;
private final int FIRST_OUTPUT_SLOT_INDEX = FIRST_INPUT_SLOT_INDEX + INPUT_SLOTS_COUNT;
// slot number is the slot number within each component; i.e. invPlayer slots 0 - 35, and tileEntityTFurnace slots 0 - 14
private final int FIRST_FUEL_SLOT_NUMBER = 0;
private final int FIRST_INPUT_SLOT_NUMBER =1;
private final int FIRST_OUTPUT_SLOT_NUMBER = 2;
public ContainerTFurnace(InventoryPlayer inventory, TileEntityTFurnace tileEntityTFurnace) {
this.tileEntityTFurnace = tileEntityTFurnace;
final int SLOT_X_SPACING = 18;
final int SLOT_Y_SPACING = 18;
final int HOTBAR_XPOS = 8;
final int HOTBAR_YPOS = 142;
// Add the players hotbar to the gui - the [xpos, ypos] location of each item
for (int x = 0; x < HOTBAR_SLOT_COUNT; x++) {
int slotNumber = x;
addSlotToContainer(new Slot(inventory, slotNumber, HOTBAR_XPOS + SLOT_X_SPACING * x, HOTBAR_YPOS));
}
final int PLAYER_INVENTORY_XPOS = 8;
final int PLAYER_INVENTORY_YPOS = 84;
// Add the rest of the players inventory to the gui
for (int y = 0; y < PLAYER_INVENTORY_ROW_COUNT; y++) {
for (int x = 0; x < PLAYER_INVENTORY_COLUMN_COUNT; x++) {
int slotNumber = HOTBAR_SLOT_COUNT + y * PLAYER_INVENTORY_COLUMN_COUNT + x;
int xpos = PLAYER_INVENTORY_XPOS + x * SLOT_X_SPACING;
int ypos = PLAYER_INVENTORY_YPOS + y * SLOT_Y_SPACING;
addSlotToContainer(new Slot(inventory, slotNumber, xpos, ypos));
}
}
final int FUEL_SLOTS_XPOS = 56;
final int FUEL_SLOTS_YPOS = 53;
// Add the tile fuel slots
addSlotToContainer(new SlotFuel(tileEntityTFurnace, 0, FUEL_SLOTS_XPOS, FUEL_SLOTS_YPOS));
final int INPUT_SLOTS_XPOS = 56;
final int INPUT_SLOTS_YPOS = 17;
// Add the tile input slots
addSlotToContainer(new SlotSmeltableInput(tileEntityTFurnace, 1, INPUT_SLOTS_XPOS, INPUT_SLOTS_YPOS));
final int OUTPUT_SLOTS_XPOS = 134;
final int OUTPUT_SLOTS_YPOS = 24;
// Add the tile output slots
addSlotToContainer(new SlotOutput(tileEntityTFurnace, 2, OUTPUT_SLOTS_XPOS, OUTPUT_SLOTS_YPOS));
System.out.println(super.inventorySlots.size());
System.out.println(tileEntityTFurnace.getSizeInventory());
}
@Override
public boolean canInteractWith(EntityPlayer playerIn) {
return tileEntityTFurnace.isUseableByPlayer(playerIn);
}
@Override
public ItemStack transferStackInSlot(EntityPlayer player, int sourceSlotIndex)
{
Slot sourceSlot = (Slot)inventorySlots.get(sourceSlotIndex);
if (sourceSlot == null || !sourceSlot.getHasStack()) return null;
ItemStack sourceStack = sourceSlot.getStack();
ItemStack copyOfSourceStack = sourceStack.copy();
// Check if the slot clicked is one of the vanilla container slots
if (sourceSlotIndex >= VANILLA_FIRST_SLOT_INDEX && sourceSlotIndex < VANILLA_FIRST_SLOT_INDEX + VANILLA_SLOT_COUNT) {
// This is a vanilla container slot so merge the stack into one of the furnace slots
// If the stack is smeltable try to merge merge the stack into the input slots
if(TileEntityTFurnace.getSmeltingResultForItem(sourceStack) != null){
if (!mergeItemStack(sourceStack, FIRST_INPUT_SLOT_INDEX, FIRST_INPUT_SLOT_INDEX + INPUT_SLOTS_COUNT, false)){
return null;
}
} else if(TileEntityTFurnace.getItemBurnTime(sourceStack) > 0) {
if (!mergeItemStack(sourceStack, FIRST_FUEL_SLOT_INDEX, FIRST_FUEL_SLOT_INDEX + FUEL_SLOTS_COUNT, true)) {
// Setting the boolean to true places the stack in the bottom slot first
return null;
}
} else {
return null;
}
} else if (sourceSlotIndex >= FIRST_FUEL_SLOT_INDEX && sourceSlotIndex < FIRST_FUEL_SLOT_INDEX + FURNACE_SLOTS_COUNT) {
// This is a furnace slot so merge the stack into the players inventory: try the hotbar first and then the main inventory
// because the main inventory slots are immediately after the hotbar slots, we can just merge with a single call
if (!mergeItemStack(sourceStack, VANILLA_FIRST_SLOT_INDEX, VANILLA_FIRST_SLOT_INDEX + VANILLA_SLOT_COUNT, false)) {
return null;
}
} else {
System.err.print("Invalid slotIndex:" + sourceSlotIndex);
return null;
}
// If stack size == 0 (the entire stack was moved) set slot contents to null
if (sourceStack.getCount() == 0) {
sourceSlot.putStack(null);
} else {
sourceSlot.onSlotChanged();
}
sourceSlot.onTake(player, sourceStack);
return copyOfSourceStack;
}
/* Client Synchronization */
// This is where you check if any values have changed and if so send an update to any clients accessing this container
// The container itemstacks are tested in Container.detectAndSendChanges, so we don't need to do that
// We iterate through all of the TileEntity Fields to find any which have changed, and send them.
// You don't have to use fields if you don't wish to; just manually match the ID in sendProgressBarUpdate with the value in
// updateProgressBar()
// The progress bar values are restricted to shorts. If you have a larger value (eg int), it's not a good idea to try and split it
// up into two shorts because the progress bar values are sent independently, and unless you add synchronisation logic at the
// receiving side, your int value will be wrong until the second short arrives. Use a custom packet instead.
@Override
public void detectAndSendChanges() {
super.detectAndSendChanges();
boolean allFieldsHaveChanged = false;
boolean fieldHasChanged [] = new boolean[tileEntityTFurnace.getFieldCount()];
if (cachedFields == null) {
cachedFields = new int[tileEntityTFurnace.getFieldCount()];
allFieldsHaveChanged = true;
}
for (int i = 0; i < cachedFields.length; ++i) {
if (allFieldsHaveChanged || cachedFields[i] != tileEntityTFurnace.getField(i)) {
cachedFields[i] = tileEntityTFurnace.getField(i);
fieldHasChanged[i] = true;
}
}
// go through the list of listeners (players using this container) and update them if necessary
for (IContainerListener listener : this.listeners) {
for (int fieldID = 0; fieldID < tileEntityTFurnace.getFieldCount(); ++fieldID) {
if (fieldHasChanged[fieldID]) {
// Note that although sendProgressBarUpdate takes 2 ints on a server these are truncated to shorts
listener.sendProgressBarUpdate(this, fieldID, cachedFields[fieldID]);
}
}
}
}
// Called when a progress bar update is received from the server. The two values (id and data) are the same two
// values given to sendProgressBarUpdate. In this case we are using fields so we just pass them to the tileEntity.
@SideOnly(Side.CLIENT)
@Override
public void updateProgressBar(int id, int data) {
tileEntityTFurnace.setField(id, data);
}
// SlotFuel is a slot for fuel items
public class SlotFuel extends Slot {
public SlotFuel(IInventory inventoryIn, int index, int xPosition, int yPosition) {
super(inventoryIn, index, xPosition, yPosition);
}
// if this function returns false, the player won't be able to insert the given item into this slot
@Override
public boolean isItemValid(ItemStack stack) {
return tileEntityTFurnace.isItemValidForFuelSlot(stack);
}
}
// SlotSmeltableInput is a slot for input items
public class SlotSmeltableInput extends Slot {
public SlotSmeltableInput(IInventory inventoryIn, int index, int xPosition, int yPosition) {
super(inventoryIn, index, xPosition, yPosition);
}
// if this function returns false, the player won't be able to insert the given item into this slot
@Override
public boolean isItemValid(ItemStack stack) {
return tileEntityTFurnace.isItemValidForInputSlot(stack);
}
}
// SlotOutput is a slot that will not accept any items
public class SlotOutput extends Slot {
public SlotOutput(IInventory inventoryIn, int index, int xPosition, int yPosition) {
super(inventoryIn, index, xPosition, yPosition);
}
// if this function returns false, the player won't be able to insert the given item into this slot
@Override
public boolean isItemValid(ItemStack stack) {
return tileEntityTFurnace.isItemValidForOutputSlot(stack);
}
}
}
GuiTFurnace
package com.github.tompinn23.tprocessing.gui;
import com.github.tompinn23.tprocessing.block.TileEntityTFurnace;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Tom Pinnock on 11/01/2017.
*/
@SideOnly(Side.CLIENT)
public class GuiTFurnace extends GuiContainer{
private static final ResourceLocation texture = new ResourceLocation("tprocessing", "textures/gui/tfurnace.png");
private final TileEntityTFurnace tileEntity;
public GuiTFurnace(InventoryPlayer inventory, TileEntityTFurnace tileEntityTFurnace) {
super(new ContainerTFurnace(inventory, tileEntityTFurnace));
// Set the width and height of the gui
xSize = 176;
ySize = 166;
this.tileEntity = tileEntityTFurnace;
}
// some [x,y] coordinates of graphical elements
final int COOK_BAR_XPOS = 79;
final int COOK_BAR_YPOS = 35;
final int COOK_BAR_ICON_U = 176; // texture position of white arrow icon
final int COOK_BAR_ICON_V = 14;
final int COOK_BAR_WIDTH = 24;
final int COOK_BAR_HEIGHT = 17;
final int FLAME_XPOS = 57;
final int FLAME_YPOS = 37;
final int FLAME_ICON_U = 176; // texture position of flame icon
final int FLAME_ICON_V = 0;
final int FLAME_WIDTH = 14;
final int FLAME_HEIGHT = 14;
@Override
protected void drawGuiContainerBackgroundLayer(float partialTicks, int x, int y) {
// Bind the image texture
Minecraft.getMinecraft().getTextureManager().bindTexture(texture);
// Draw the image
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize);
// get cook progress as a double between 0 and 1
double cookProgress = tileEntity.fractionOfCookTimeComplete();
// draw the cook progress bar
drawTexturedModalRect(guiLeft + COOK_BAR_XPOS, guiTop + COOK_BAR_YPOS, COOK_BAR_ICON_U, COOK_BAR_ICON_V,
(int)(cookProgress * COOK_BAR_WIDTH), COOK_BAR_HEIGHT);
// draw the burn remaining
double burnRemaining = tileEntity.fractionOfFuelRemaining();
int yOffset = (int)((1.0 - burnRemaining) * FLAME_HEIGHT);
drawTexturedModalRect(guiLeft + FLAME_XPOS , guiTop + FLAME_YPOS + yOffset,
FLAME_ICON_U, FLAME_ICON_V + yOffset, FLAME_WIDTH, FLAME_HEIGHT - yOffset);
}
@Override
protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) {
super.drawGuiContainerForegroundLayer(mouseX, mouseY);
final int LABEL_XPOS = 5;
final int LABEL_YPOS = 5;
fontRendererObj.drawString(tileEntity.getDisplayName().getUnformattedText(), LABEL_XPOS, LABEL_YPOS, Color.darkGray.getRGB());
List<String> hoveringText = new ArrayList<String>();
// If the mouse is over the progress bar add the progress bar hovering text
if (isInRect(guiLeft + COOK_BAR_XPOS, guiTop + COOK_BAR_YPOS, COOK_BAR_WIDTH, COOK_BAR_HEIGHT, mouseX, mouseY)){
hoveringText.add("Progress:");
int cookPercentage =(int)(tileEntity.fractionOfCookTimeComplete() * 100);
hoveringText.add(cookPercentage + "%");
}
// If the mouse is over one of the burn time indicator add the burn time indicator hovering text
if (isInRect(guiLeft + FLAME_XPOS , guiTop + FLAME_YPOS, FLAME_WIDTH, FLAME_HEIGHT, mouseX, mouseY)) {
hoveringText.add("Fuel Time:");
hoveringText.add(tileEntity.secondsOfFuelRemaining() + "s");
}
// If hoveringText is not empty draw the hovering text
if (!hoveringText.isEmpty()){
drawHoveringText(hoveringText, mouseX - guiLeft, mouseY - guiTop, fontRendererObj);
}
// // You must re bind the texture and reset the colour if you still need to use it after drawing a string
// Minecraft.getMinecraft().getTextureManager().bindTexture(texture);
// GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
}
// Returns true if the given x,y coordinates are within the given rectangle
public static boolean isInRect(int x, int y, int xSize, int ySize, int mouseX, int mouseY){
return ((mouseX >= x && mouseX <= x+xSize) && (mouseY >= y && mouseY <= y+ySize));
}
}
BlockTFurnace
package com.github.tompinn23.tprocessing.block;
import com.github.tompinn23.tprocessing.TProcessing;
import com.github.tompinn23.tprocessing.gui.TGuiHandler;
import com.github.tompinn23.tprocessing.item.ItemModelProvider;
import net.minecraft.block.BlockContainer;
import net.minecraft.block.BlockHorizontal;
import net.minecraft.block.material.Material;
import net.minecraft.block.properties.IProperty;
import net.minecraft.block.properties.PropertyDirection;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.stats.StatList;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityFurnace;
import net.minecraft.util.EnumBlockRenderType;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.Rotation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
/**
* Created by Tom Pinnock on 09/01/2017.
*/
public class BlockTFurnace extends BlockContainer implements ItemModelProvider {
public static final PropertyDirection FACING = BlockHorizontal.FACING;
public BlockTFurnace() {
super(Material.ROCK);
this.setDefaultState(this.blockState.getBaseState().withProperty(FACING, EnumFacing.NORTH));
setUnlocalizedName("t_furnace");
setRegistryName("t_furnace");
setCreativeTab(TProcessing.creativeTab);
}
public boolean onBlockActivated(World worldIn, BlockPos pos, IBlockState state, EntityPlayer playerIn, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ)
{
if (worldIn.isRemote)
{
return true;
}
else
{
TileEntity tileentity = worldIn.getTileEntity(pos);
if (tileentity instanceof TileEntityTFurnace)
{
playerIn.openGui(TProcessing.instance, TGuiHandler.getGuiID(), worldIn, pos.getX(), pos.getY(), pos.getZ());
playerIn.addStat(StatList.FURNACE_INTERACTION);
return true;
}
return true;
}
}
@Override
public EnumBlockRenderType getRenderType(IBlockState state)
{
return EnumBlockRenderType.MODEL;
}
private void setDefaultFacing(World worldIn, BlockPos pos, IBlockState state)
{
if (!worldIn.isRemote)
{
IBlockState iblockstate = worldIn.getBlockState(pos.north());
IBlockState iblockstate1 = worldIn.getBlockState(pos.south());
IBlockState iblockstate2 = worldIn.getBlockState(pos.west());
IBlockState iblockstate3 = worldIn.getBlockState(pos.east());
EnumFacing enumfacing = (EnumFacing)state.getValue(FACING);
if (enumfacing == EnumFacing.NORTH && iblockstate.isFullBlock() && !iblockstate1.isFullBlock())
{
enumfacing = EnumFacing.SOUTH;
}
else if (enumfacing == EnumFacing.SOUTH && iblockstate1.isFullBlock() && !iblockstate.isFullBlock())
{
enumfacing = EnumFacing.NORTH;
}
else if (enumfacing == EnumFacing.WEST && iblockstate2.isFullBlock() && !iblockstate3.isFullBlock())
{
enumfacing = EnumFacing.EAST;
}
else if (enumfacing == EnumFacing.EAST && iblockstate3.isFullBlock() && !iblockstate2.isFullBlock())
{
enumfacing = EnumFacing.WEST;
}
worldIn.setBlockState(pos, state.withProperty(FACING, enumfacing), 2);
}
}
@Override
@SuppressWarnings("deprecation")
public IBlockState getStateForPlacement(World worldIn, BlockPos pos, EnumFacing facing, float hitX, float hitY, float hitZ, int meta, EntityLivingBase placer)
{
return this.getDefaultState().withProperty(FACING, placer.getHorizontalFacing().getOpposite());
}
/**
* Convert the given metadata into a BlockState for this Block
*/
@Override
@SuppressWarnings("deprecation")
public IBlockState getStateFromMeta(int meta)
{
EnumFacing enumfacing = EnumFacing.getFront(meta);
if (enumfacing.getAxis() == EnumFacing.Axis.Y)
{
enumfacing = EnumFacing.NORTH;
}
return this.getDefaultState().withProperty(FACING, enumfacing);
}
/**
* Convert the BlockState into the correct metadata value
*/
@Override
public int getMetaFromState(IBlockState state)
{
return ((EnumFacing)state.getValue(FACING)).getIndex();
}
/**
* Returns the blockstate with the given rotation from the passed blockstate. If inapplicable, returns the passed
* blockstate.
*/
@Override
@SuppressWarnings("deprecation")
public IBlockState withRotation(IBlockState state, Rotation rot)
{
return state.withProperty(FACING, rot.rotate((EnumFacing)state.getValue(FACING)));
}
@Override
protected BlockStateContainer createBlockState()
{
return new BlockStateContainer(this, new IProperty[] {FACING});
}
@Override
public TileEntity createNewTileEntity(World worldIn, int meta) {
return new TileEntityTFurnace();
}
@Override
public void registerItemModel(Item item) {
TProcessing.proxy.registerItemRenderer(item, 0, "t_furnace");
}
}
TileEntityTFurnace
package com.github.tompinn23.tprocessing.block;
import com.github.tompinn23.tprocessing.ModItems;
import com.sun.istack.internal.Nullable;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.*;
import net.minecraft.item.*;
import net.minecraft.item.crafting.FurnaceRecipes;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagInt;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.play.server.SPacketUpdateTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityFurnace;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ITickable;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.util.text.TextComponentTranslation;
import java.util.Arrays;
/**
* Created by Tom Pinnock on 09/01/2017.
*/
public class TileEntityTFurnace extends TileEntity implements IInventory, ITickable {
// Create and initialize the itemStacks variable that will store store the itemStacks
public static final int FUEL_SLOTS_COUNT = 1;
public static final int TOTAL_SLOTS_COUNT = 3;
private ItemStack[] itemStacks = new ItemStack[TOTAL_SLOTS_COUNT];
/** The number of burn ticks remaining on the current piece of fuel */
private int burnTimeRemaining;
/** The initial fuel value of the currently burning fuel (in ticks of burn duration) */
private int burnTimeInitialValue;
/**The number of ticks the current item has been cooking*/
private short cookTime;
/**The number of ticks required to cook an item*/
private static final short COOK_TIME_FOR_COMPLETION = 200; // vanilla value is 200 = 10 seconds
private int cachedNumberOfBurningSlots = -1;
/**
* Returns the amount of fuel remaining on the currently burning item
* @return fraction remaining, between 0 - 1
*/
public double fractionOfFuelRemaining()
{
if (burnTimeInitialValue <= 0 ) return 0;
double fraction = burnTimeRemaining / (double)burnTimeInitialValue;
return MathHelper.clamp(fraction, 0.0, 1.0);
}
/**
* return the remaining burn time of the fuel slot
* @return seconds remaining
*/
public int secondsOfFuelRemaining()
{
if (burnTimeRemaining <= 0 ) return 0;
return burnTimeRemaining / 20; // 20 ticks per second
}
/**
* Returns the amount of cook time completed on the currently cooking item.
* @return fraction remaining, between 0 - 1
*/
public double fractionOfCookTimeComplete()
{
double fraction = cookTime / (double)COOK_TIME_FOR_COMPLETION;
return MathHelper.clamp(fraction, 0.0, 1.0);
}
// This method is called every tick to update the tile entity, i.e.
// - see if the fuel has run out, and if so turn the furnace "off" and slowly uncook the current item (if any)
// - see if any of the items have finished smelting
// It runs both on the server and the client.
@Override
public void update() {
// If there is nothing to smelt or there is no room in the output, reset cookTime and return
if (canSmelt()) {
int numberOfFuelBurning = burnFuel();
// If fuel is available, keep cooking the item, otherwise start "uncooking" it at double speed
if (numberOfFuelBurning > 0) {
cookTime += numberOfFuelBurning;
} else {
cookTime -= 2;
}
if (cookTime < 0) cookTime = 0;
// If cookTime has reached maxCookTime smelt the item and reset cookTime
if (cookTime >= COOK_TIME_FOR_COMPLETION) {
smeltItem();
cookTime = 0;
}
} else {
cookTime = 0;
}
}
/**
* for each fuel slot: decreases the burn time, checks if burnTimeRemaining = 0 and tries to consume a new piece of fuel if one is available
* @return the number of fuel slots which are burning
*/
private int burnFuel() {
int burningCount = 0;
boolean inventoryChanged = false;
if (burnTimeRemaining > 0) {
--burnTimeRemaining;
++burningCount;
}
if (burnTimeRemaining == 0) {
if (itemStacks[0] != null && getItemBurnTime(itemStacks[0]) > 0) {
// If the stack in this slot is not null and is fuel, set burnTimeRemaining & burnTimeInitialValue to the
// item's burn time and decrease the stack size
burnTimeRemaining = burnTimeInitialValue = getItemBurnTime(itemStacks[0]);
int size = itemStacks[0].getCount();
--size;
itemStacks[0].setCount(size);
++burningCount;
inventoryChanged = true;
// If the stack size now equals 0 set the slot contents to the items container item. This is for fuel
// items such as lava buckets so that the bucket is not consumed. If the item dose not have
// a container item getContainerItem returns null which sets the slot contents to null
if (itemStacks[0].getCount() == 0) {
itemStacks[0] = itemStacks[0].getItem().getContainerItem(itemStacks[0]);
}
}
}
if (inventoryChanged) markDirty();
return burningCount;
}
/**
* Check if any of the input items are smeltable and there is sufficient space in the output slots
* @return true if smelting is possible
*/
private boolean canSmelt() {return smeltItem(false);}
/**
* Smelt an input item into an output slot, if possible
*/
private void smeltItem() {smeltItem(true);}
/**
* checks that there is an item to be smelted in one of the input slots and that there is room for the result in the output slots
* If desired, performs the smelt
* @param performSmelt if true, perform the smelt. if false, check whether smelting is possible, but don't change the inventory
* @return false if no items can be smelted, true otherwise
*/
private boolean smeltItem(boolean performSmelt)
{
Integer firstSuitableInputSlot = null;
Integer firstSuitableOutputSlot = null;
ItemStack result = null;
// finds the first input slot which is smeltable and whose result fits into an output slot (stacking if possible)
if (itemStacks[1] != null) {
result = getSmeltingResultForItem(itemStacks[1]);
if (result != null) {
// find the first suitable output slot- either empty, or with identical item that has enough space
ItemStack outputStack = itemStacks[2];
if (outputStack == null) {
firstSuitableInputSlot = 1;
firstSuitableOutputSlot = 2;
}
if (outputStack.getItem() == result.getItem() && (!outputStack.getHasSubtypes() || outputStack.getMetadata() == outputStack.getMetadata())
&& ItemStack.areItemStackTagsEqual(outputStack, result)) {
int combinedSize = itemStacks[2].getCount() + result.getCount();
if (combinedSize <= getInventoryStackLimit() && combinedSize <= itemStacks[2].getMaxStackSize()) {
firstSuitableInputSlot = 1;
firstSuitableOutputSlot = 2;
}
}
}
}
if (firstSuitableInputSlot == null) return false;
if (!performSmelt) return true;
// alter input and output
int size = itemStacks[firstSuitableInputSlot].getCount();
--size;
itemStacks[firstSuitableInputSlot].setCount(size);
if (itemStacks[firstSuitableInputSlot].getCount() <=0) itemStacks[firstSuitableInputSlot] = null;
if (itemStacks[firstSuitableOutputSlot] == null) {
itemStacks[firstSuitableOutputSlot] = result.copy(); // Use deep .copy() to avoid altering the recipe
} else {
int osize = itemStacks[firstSuitableOutputSlot].getCount();
osize += result.getCount();
itemStacks[firstSuitableOutputSlot].setCount(osize);
}
markDirty();
return true;
}
// returns the smelting result for the given stack. Returns null if the given stack can not be smelted
public static ItemStack getSmeltingResultForItem(ItemStack stack) { return FurnaceRecipes.instance().getSmeltingResult(stack); }
// returns the number of ticks the given item will burn. Returns 0 if the given item is not a valid fuel
public static short getItemBurnTime(ItemStack stack)
{
int burntime = TileEntityFurnace.getItemBurnTime(stack); // just use the vanilla values
return (short)MathHelper.clamp(burntime, 0, Short.MAX_VALUE);
}
// Gets the number of slots in the inventory
@Override
public int getSizeInventory() {
return itemStacks.length;
}
@Override
public boolean isEmpty() {
return false;
}
// Gets the stack in the given slot
@Override
public ItemStack getStackInSlot(int i) {
return itemStacks[i];
}
/**
* Removes some of the units from itemstack in the given slot, and returns as a separate itemstack
* @param slotIndex the slot number to remove the items from
* @param count the number of units to remove
* @return a new itemstack containing the units removed from the slot
*/
@Override
public ItemStack decrStackSize(int slotIndex, int count) {
ItemStack itemStackInSlot = getStackInSlot(slotIndex);
if (itemStackInSlot == null) return null;
ItemStack itemStackRemoved;
if (itemStackInSlot.getCount() <= count) {
itemStackRemoved = itemStackInSlot;
setInventorySlotContents(slotIndex, null);
} else {
itemStackRemoved = itemStackInSlot.splitStack(count);
if (itemStackInSlot.getCount() == 0) {
setInventorySlotContents(slotIndex, null);
}
}
markDirty();
return itemStackRemoved;
}
// overwrites the stack in the given slotIndex with the given stack
@Override
public void setInventorySlotContents(int slotIndex, ItemStack itemstack) {
itemStacks[slotIndex] = itemstack;
if (itemstack != null && itemstack.getCount() > getInventoryStackLimit()) {
itemstack.setCount(getInventoryStackLimit());
}
markDirty();
}
@Override
public int getInventoryStackLimit() {
return 64;
}
@Override
public boolean isUsableByPlayer(EntityPlayer player) {
return false;
}
// Return true if the given player is able to use this block. In this case it checks that
// 1) the world tileentity hasn't been replaced in the meantime, and
// 2) the player isn't too far away from the centre of the block
public boolean isUseableByPlayer(EntityPlayer player) {
if (this.world.getTileEntity(this.pos) != this) return false;
final double X_CENTRE_OFFSET = 0.5;
final double Y_CENTRE_OFFSET = 0.5;
final double Z_CENTRE_OFFSET = 0.5;
final double MAXIMUM_DISTANCE_SQ = 8.0 * 8.0;
return player.getDistanceSq(pos.getX() + X_CENTRE_OFFSET, pos.getY() + Y_CENTRE_OFFSET, pos.getZ() + Z_CENTRE_OFFSET) < MAXIMUM_DISTANCE_SQ;
}
// Return true if the given stack is allowed to be inserted in the given slot
// Unlike the vanilla furnace, we allow anything to be placed in the fuel slots
static public boolean isItemValidForFuelSlot(ItemStack itemStack)
{
if (TileEntityFurnace.isItemFuel(itemStack) || itemStack.getItem() == ModItems.energyCoupler){
return true;
}
else
{
return false;
}
}
// Return true if the given stack is allowed to be inserted in the given slot
// Unlike the vanilla furnace, we allow anything to be placed in the fuel slots
static public boolean isItemValidForInputSlot(ItemStack itemStack)
{
return true;
}
// Return true if the given stack is allowed to be inserted in the given slot
// Unlike the vanilla furnace, we allow anything to be placed in the fuel slots
static public boolean isItemValidForOutputSlot(ItemStack itemStack)
{
return false;
}
// This is where you save any data that you don't want to lose when the tile entity unloads
// In this case, it saves the state of the furnace (burn time etc) and the itemstacks stored in the fuel, input, and output slots
@Override
public NBTTagCompound writeToNBT(NBTTagCompound parentNBTTagCompound)
{
super.writeToNBT(parentNBTTagCompound); // The super call is required to save and load the tiles location
// // Save the stored item stacks
// to use an analogy with Java, this code generates an array of hashmaps
// The itemStack in each slot is converted to an NBTTagCompound, which is effectively a hashmap of key->value pairs such
// as slot=1, id=2353, count=1, etc
// Each of these NBTTagCompound are then inserted into NBTTagList, which is similar to an array.
NBTTagList dataForAllSlots = new NBTTagList();
for (int i = 0; i < this.itemStacks.length; ++i) {
if (this.itemStacks[i] != null) {
NBTTagCompound dataForThisSlot = new NBTTagCompound();
dataForThisSlot.setByte("Slot", (byte) i);
this.itemStacks[i].writeToNBT(dataForThisSlot);
dataForAllSlots.appendTag(dataForThisSlot);
}
}
// the array of hashmaps is then inserted into the parent hashmap for the container
parentNBTTagCompound.setTag("Items", dataForAllSlots);
// Save everything else
parentNBTTagCompound.setShort("CookTime", cookTime);
parentNBTTagCompound.setTag("burnTimeRemaining", new NBTTagInt(burnTimeRemaining));
parentNBTTagCompound.setTag("burnTimeInitial", new NBTTagInt(burnTimeInitialValue));
return parentNBTTagCompound;
}
// This is where you load the data that you saved in writeToNBT
@Override
public void readFromNBT(NBTTagCompound nbtTagCompound)
{
super.readFromNBT(nbtTagCompound); // The super call is required to save and load the tiles location
final byte NBT_TYPE_COMPOUND = 10; // See NBTBase.createNewByType() for a listing
NBTTagList dataForAllSlots = nbtTagCompound.getTagList("Items", NBT_TYPE_COMPOUND);
Arrays.fill(itemStacks, null); // set all slots to empty
for (int i = 0; i < dataForAllSlots.tagCount(); ++i) {
NBTTagCompound dataForOneSlot = dataForAllSlots.getCompoundTagAt(i);
byte slotNumber = dataForOneSlot.getByte("Slot");
if (slotNumber >= 0 && slotNumber < this.itemStacks.length) {
this.itemStacks[slotNumber] = new ItemStack(dataForOneSlot);
}
}
// Load everything else. Trim the arrays (or pad with 0) to make sure they have the correct number of elements
cookTime = nbtTagCompound.getShort("CookTime");
burnTimeRemaining = nbtTagCompound.getInteger("burnTimeRemaining");
burnTimeInitialValue = nbtTagCompound.getInteger("burnTimeInitial");
cachedNumberOfBurningSlots = -1;
}
// // When the world loads from disk, the server needs to send the TileEntity information to the client
// // it uses getUpdatePacket(), getUpdateTag(), onDataPacket(), and handleUpdateTag() to do this
@Override
@Nullable
public SPacketUpdateTileEntity getUpdatePacket()
{
NBTTagCompound updateTagDescribingTileEntityState = getUpdateTag();
final int METADATA = 0;
return new SPacketUpdateTileEntity(this.pos, METADATA, updateTagDescribingTileEntityState);
}
@Override
public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt) {
NBTTagCompound updateTagDescribingTileEntityState = pkt.getNbtCompound();
handleUpdateTag(updateTagDescribingTileEntityState);
}
/* Creates a tag containing the TileEntity information, used by vanilla to transmit from server to client
Warning - although our getUpdatePacket() uses this method, vanilla also calls it directly, so don't remove it.
*/
@Override
public NBTTagCompound getUpdateTag()
{
NBTTagCompound nbtTagCompound = new NBTTagCompound();
writeToNBT(nbtTagCompound);
return nbtTagCompound;
}
/* Populates this TileEntity with information from the tag, used by vanilla to transmit from server to client
Warning - although our onDataPacket() uses this method, vanilla also calls it directly, so don't remove it.
*/
@Override
public void handleUpdateTag(NBTTagCompound tag)
{
this.readFromNBT(tag);
}
//------------------------
// set all slots to empty
@Override
public void clear() {
Arrays.fill(itemStacks, null);
}
// will add a key for this container to the lang file so we can name it in the GUI
@Override
public String getName() {
return "container.mbe31_inventory_furnace.name";
}
@Override
public boolean hasCustomName() {
return false;
}
// standard code to look up what the human-readable name is
@Nullable
@Override
public ITextComponent getDisplayName() {
return this.hasCustomName() ? new TextComponentString(this.getName()) : new TextComponentTranslation(this.getName());
}
// Fields are used to send non-inventory information from the server to interested clients
// The container code caches the fields and sends the client any fields which have changed.
// The field ID is limited to byte, and the field value is limited to short. (if you use more than this, they get cast down
// in the network packets)
// If you need more than this, or shorts are too small, use a custom packet in your container instead.
private static final byte COOK_FIELD_ID = 0;
private static final byte FIRST_BURN_TIME_REMAINING_FIELD_ID = 1;
private static final byte FIRST_BURN_TIME_INITIAL_FIELD_ID = FIRST_BURN_TIME_REMAINING_FIELD_ID + (byte)FUEL_SLOTS_COUNT;
private static final byte NUMBER_OF_FIELDS = FIRST_BURN_TIME_INITIAL_FIELD_ID + (byte)FUEL_SLOTS_COUNT;
@Override
public int getField(int id) {
if (id == COOK_FIELD_ID) return cookTime;
if (id >= FIRST_BURN_TIME_REMAINING_FIELD_ID && id < FIRST_BURN_TIME_REMAINING_FIELD_ID + FUEL_SLOTS_COUNT) {
return burnTimeRemaining;
}
if (id >= FIRST_BURN_TIME_INITIAL_FIELD_ID && id < FIRST_BURN_TIME_INITIAL_FIELD_ID + FUEL_SLOTS_COUNT) {
return burnTimeInitialValue;
}
System.err.println("Invalid field ID in TileInventorySmelting.getField:" + id);
return 0;
}
@Override
public void setField(int id, int value)
{
if (id == COOK_FIELD_ID) {
cookTime = (short)value;
} else if (id >= FIRST_BURN_TIME_REMAINING_FIELD_ID && id < FIRST_BURN_TIME_REMAINING_FIELD_ID + FUEL_SLOTS_COUNT) {
burnTimeRemaining = value;
} else if (id >= FIRST_BURN_TIME_INITIAL_FIELD_ID && id < FIRST_BURN_TIME_INITIAL_FIELD_ID + FUEL_SLOTS_COUNT) {
burnTimeInitialValue = value;
} else {
System.err.println("Invalid field ID in TileInventorySmelting.setField:" + id);
}
}
@Override
public int getFieldCount() {
return NUMBER_OF_FIELDS;
}
// -----------------------------------------------------------------------------------------------------------
// The following methods are not needed for this example but are part of IInventory so they must be implemented
// Unused unless your container specifically uses it.
// Return true if the given stack is allowed to go in the given slot
@Override
public boolean isItemValidForSlot(int slotIndex, ItemStack itemstack) {
return false;
}
/**
* This method removes the entire contents of the given slot and returns it.
* Used by containers such as crafting tables which return any items in their slots when you close the GUI
* @param slotIndex
* @return
*/
@Override
public ItemStack removeStackFromSlot(int slotIndex) {
ItemStack itemStack = getStackInSlot(slotIndex);
if (itemStack != null) setInventorySlotContents(slotIndex, null);
return itemStack;
}
@Override
public void openInventory(EntityPlayer player) {}
@Override
public void closeInventory(EntityPlayer player) {}
}