Jump to content

[1.7.10] TileEntity issue


pickaxe_engineer

Recommended Posts

G'Day everyone

 

I do have a little strange issue: Whenever I insert a single item into my generator (like coal), the item is consumed but client-side still displayed (consumed = tested with some logging that the itemstack which represents the said slot is null).

Closing and re-open the GUI or just clicking on the item removes it from the GUI.

Since I've just started with modding I'm very confused. Especially since I thought I did understood the vanilla furnace updateEntity method and adapted it for my purposes.

 

I learned from the vanilla code and made my own furnace (in this custom furnace it works with a single fuel item too) and added a few methods, suggested by wuppy's mod dev book (I'm talking about the TileEntityGenerator.onDataPacket() and the TileEntityGenerator.getDescriptionPacket() methods)

But still I did not understood everything, so please don't blame me, TileEntityGenerator.decrStackSize(int, int)

and ContainerGenerator.transferStackInSlot(EntityPlayer, int) are mostly copied from the vanilla furnace and slightly renamed the variables.

 

Thus I do have this questions:

 

- Does anyone see why the above mentioned 'ghost item' thingy happens? The funny part is, that if I put two items into the slot, the stacksize gets immediately decreased by one (as expected).

 

Thank you in advance and feel free to criticize my coding, as mentioned before, I'm a forge newbie.

Sincerely -pick

 

Appending the code:

 

TileEntityGenerator.java:

 

package org.bitbucket.pickaxe_engineer.randmod.tileentity;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Items;
import net.minecraft.inventory.ISidedInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.Packet;
import net.minecraft.network.play.server.S35PacketUpdateTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityFurnace;
import net.minecraftforge.common.util.ForgeDirection;

import org.bitbucket.pickaxe_engineer.randmod.RandMod;
import org.bitbucket.pickaxe_engineer.randmod.block.GeneratorBlock;
import org.bitbucket.pickaxe_engineer.randmod.energy.SimpleEnergyStorage;
import org.bitbucket.pickaxe_engineer.randmod.util.Location;

import cofh.api.energy.IEnergyProvider;
import cofh.api.energy.IEnergyReceiver;

public class TileEntityGenerator extends TileEntity implements IEnergyProvider,
	ISidedInventory {

private ItemStack[] slots = new ItemStack[2];// 0: input, 1: battery slot.
private static final int FUEL_SLOT = 0;
private static final int BATTERY_SLOT = 1;
private static final int[] SLOTS = { FUEL_SLOT };// 1 unaccessible yet

// like in furnace
private int burnTime;
private int totalBurnTime;
private volatile boolean active = false;

private int powerGen = 10;// RF ?
/* === MUST HAVES === */
private final static String ITEMS_LBL = "items";
private final static String SLOT_LBL = "slot";
private final static String BURN_TIME_LBL = "burnTime";
private final static String TOTAL_BURN_TIME_LBL = "totalBurnTime";
private final static String CUSTOM_NAME_LBL = "customName";
private final static String ACTIVE_LBL = "active";

private String customName;

/* === RF API === */
private SimpleEnergyStorage storage = new SimpleEnergyStorage(100000, 512);

@Override
public boolean canConnectEnergy(ForgeDirection from) {
	return true;// until now can connect to every direction
}

@Override
public boolean canExtractItem(int slot, ItemStack item, int side) {
	//empty buckets are allowed
	if(slot == FUEL_SLOT){
		if(item != null && item.getItem() == Items.bucket){
			return true;
		}
	}
	return false;
}

@Override
public boolean canInsertItem(int slot, ItemStack item, int side) {
//		System.out.println("item inserted"+item.getUnlocalizedName());
	return isItemValidForSlot(slot, item);
}

@Override
public void closeInventory() {
}

@Override
public ItemStack decrStackSize(int slot, int amount) {
	if (slots[slot] != null) {
		ItemStack itemStack;
		if (slots[slot].stackSize <= amount) {
			itemStack = slots[slot];
			slots[slot] = null;
			return itemStack;
		} else {
			itemStack = slots[slot].splitStack(amount);
			if (slots[slot].stackSize == 0) {
				slots[slot] = null;
			}
			return itemStack;
		}
	}
	return null;
}

@Override
public int extractEnergy(ForgeDirection from, int maxExtract,
		boolean simulate) {
	return storage.extractEnergy(maxExtract, simulate);
}

@Override
public int[] getAccessibleSlotsFromSide(int side) {
	return SLOTS;
}

public int getBurnTime() {
	return burnTime;
}

public int getBurnTimeRemainingScaled(int value) {
	if(totalBurnTime == 0){
		return 0;
	}
	return burnTime * value / totalBurnTime;
}

public int getEnergyBarScaled(int value) {
	return storage.getEnergyStored() * value / storage.getMaxEnergyStored();
}

@Override
public int getEnergyStored(ForgeDirection from) {
	return storage.getEnergyStored();
}

public int getTotalBurnTime() {
	return totalBurnTime;
}

@Override
public String getInventoryName() {
	return hasCustomInventoryName() ? customName : "container.generator";
}

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

@Override
public int getMaxEnergyStored(ForgeDirection from) {
	return storage.getMaxEnergyStored();
}

/* ISidedInventory */

@Override
public int getSizeInventory() {
	return slots.length;
}

@Override
public ItemStack getStackInSlot(int slot) {
	try{
		return slots[slot];
	}catch(ArrayIndexOutOfBoundsException ex){
		return null;
	}
}

@Override
public ItemStack getStackInSlotOnClosing(int slot) {
	if(slot < 0 || slot >= slots.length){
		return null;
	}
	if (slots[slot] != null) {
		ItemStack output = slots[slot];
		slots[slot] = null;
		return output;
	}
	return null;
}

@Override
public boolean hasCustomInventoryName() {
	return customName != null && customName.length() > 0;
}

public boolean isActive() {
	return active || isBurning();
}

public boolean isBurning(){
	return burnTime > 0;
}

@Override
public boolean isItemValidForSlot(int slot, ItemStack item) {
	if(slot == FUEL_SLOT){
		return item != null && TileEntityFurnace.isItemFuel(item);
	}
	return false;
}

@Override
public boolean isUseableByPlayer(EntityPlayer player) {
	return worldObj.getTileEntity(xCoord, yCoord, zCoord) != this ? false
			: player.getDistanceSq(xCoord + 0.5d, yCoord + 0.5d,
					zCoord + 0.5d) <= 64d;
}

@Override
public void openInventory() {
}

@Override
public void readFromNBT(NBTTagCompound nbt) {
	super.readFromNBT(nbt);
	storage.readFromNBT(nbt);

	NBTTagList list = nbt.getTagList(ITEMS_LBL, 10);// why 10 ?
	this.slots = new ItemStack[getSizeInventory()];

	for (int i = 0; i < list.tagCount(); i++) {
		NBTTagCompound compound = list.getCompoundTagAt(i);
		byte b = compound.getByte(SLOT_LBL);

		if (b >= 0 && b < slots.length) {
			slots[b] = ItemStack.loadItemStackFromNBT(compound);
		}
	}

	burnTime = nbt.getShort(BURN_TIME_LBL);
	totalBurnTime = nbt.getShort(TOTAL_BURN_TIME_LBL);
	active = burnTime > 0 || nbt.getBoolean(ACTIVE_LBL);

	if (nbt.hasKey(CUSTOM_NAME_LBL)) {
		customName = nbt.getString(CUSTOM_NAME_LBL);
	}
}

public void setBurnTime(int newTime) {
	this.burnTime = newTime;
}

public void setTotalBurnTime(int newTime) {
	this.totalBurnTime = newTime;
}

public void setEnergyStored(int energy){
	storage.setEnergy(energy);
}

@Override
public void setInventorySlotContents(int slot, ItemStack stack) {
	try{
		if(canInsertItem(slot, stack, 0)){
			active = true;
			slots[slot] = stack;
			if (stack != null && stack.stackSize > getInventoryStackLimit()) {
				stack.stackSize = getInventoryStackLimit();
			}
		}

//			System.out.println("slot "+slot+"'s content set: "+stack.getUnlocalizedName()+", #"+stack.stackSize);//DEBUG
	}catch(ArrayIndexOutOfBoundsException ex){
		return;
	}catch(NullPointerException ex){
//			System.out.println("setInvSlotCnts got called with stack==null");
	}
}

private void decrStack(int slot, int value){
	//looks like doesnt work
	if(slot < 0 || slot >= slots.length){
		return;
	}
	if(slots[slot] != null){
		RandMod.logger.info(String.format("Decr Stack: %s(%d), %d - %d = %d", slots[slot].getUnlocalizedName(),slot, slots[slot].stackSize, value, slots[slot].stackSize - value));
		slots[slot].stackSize -= value;

		if(slots[slot].stackSize <= 0){
			if(slots[slot].getItem().hasContainerItem(slots[slot])){
				slots[slot] = slots[slot].getItem().getContainerItem(slots[slot]);
			}else{
				slots[slot] = null;
			}
		}
	}
}

@Override
public void updateEntity() {
	boolean burningFlag = burnTime > 0;
	if (isBurning()) {
		burnTime--;
	}
	distributeEnergy();
	markDirty();
	if(!active){
		return;
	}
	if (!worldObj.isRemote) {
		// on server
		if (burnTime == 0 && !storage.isFull()) {
			// can produce
			if (slots[FUEL_SLOT] != null) {
				// will produce
				burnTime = totalBurnTime = TileEntityFurnace
						.getItemBurnTime(slots[0]);
				if(isBurning() && slots[FUEL_SLOT] != null){
					slots[FUEL_SLOT].stackSize--;
					if(slots[FUEL_SLOT].stackSize <= 0){
						slots[FUEL_SLOT] = slots[FUEL_SLOT].getItem().getContainerItem(slots[FUEL_SLOT]);
					}
				}
				active = true;
			}else{
				active = false;
			}
			// had no item?
		}

		if (isActive()) {

			// actual production:
			int produced = storage.receiveEnergy(powerGen, false);
			// (produced <= powerGen ? //could store : //internal buffer
			// full);
		}

		if (burningFlag != isBurning()) {
			GeneratorBlock.updateBlockState(isActive(), worldObj, xCoord,
					yCoord, zCoord);
		}
	}// end on server
	markDirty();
	worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
}

private void distributeEnergy(){
	//current impl: dist even.
	Location loc = new Location(xCoord, yCoord, zCoord);
	TileEntity[] tes = loc.getAdjacentTEs(worldObj);
	int nrgyReqTot = 0;
	int receiver = 0;
	for(int i=0; i<tes.length; i++){
		if(tes[i] != null && tes[i] instanceof IEnergyReceiver){
			IEnergyReceiver er = (IEnergyReceiver)tes[i];
			ForgeDirection dir = loc.getNeighborDirection(tes[i].xCoord, tes[i].yCoord, tes[i].zCoord);
			nrgyReqTot += er.receiveEnergy(dir, storage.getIoLimit(), true);
			receiver++;
		}
	}
	if(receiver == 0){
		return;
	}
	int energyDist= nrgyReqTot / receiver ;
	for(int i=0; i<tes.length; i++){
		if(tes[i] != null && tes[i] instanceof IEnergyReceiver){
			IEnergyReceiver er = (IEnergyReceiver)tes[i];
			ForgeDirection dir = loc.getNeighborDirection(tes[i].xCoord, tes[i].yCoord, tes[i].zCoord);
			er.receiveEnergy(dir, extractEnergy(dir, energyDist, false), false);
		}
	}
}

@Override
public void writeToNBT(NBTTagCompound nbt) {
	super.writeToNBT(nbt);
	storage.writeToNBT(nbt);

	nbt.setShort(BURN_TIME_LBL, (short) burnTime);
	nbt.setShort(TOTAL_BURN_TIME_LBL, (short) totalBurnTime);
	nbt.setBoolean(ACTIVE_LBL, active);

	NBTTagList list = new NBTTagList();
	for (int i = 0; i < slots.length; i++) {
		if (slots[i] != null) {
			NBTTagCompound compound = new NBTTagCompound();
			compound.setByte(SLOT_LBL, (byte) i);
			slots[i].writeToNBT(compound);
			list.appendTag(compound);
		}
	}

	nbt.setTag(ITEMS_LBL, list);

	if (hasCustomInventoryName()) {
		nbt.setString(CUSTOM_NAME_LBL, customName);
	}
}

@Override
public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity packet){
	//from wuppy's tut book
	readFromNBT(packet.func_148857_g());
}

@Override
public Packet getDescriptionPacket(){
	NBTTagCompound nbt = new NBTTagCompound();
	writeToNBT(nbt);
	return new S35PacketUpdateTileEntity(xCoord, yCoord, zCoord, 1, nbt);//from wuppy's tut book -> why 1?
}

}

 

 

GUIGenerator.java:

 

package org.bitbucket.pickaxe_engineer.randmod.gui;

import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.resources.I18n;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;

import org.bitbucket.pickaxe_engineer.randmod.RandMod;
import org.bitbucket.pickaxe_engineer.randmod.container.ContainerGenerator;
import org.bitbucket.pickaxe_engineer.randmod.tileentity.TileEntityGenerator;
import org.lwjgl.opengl.GL11;

public class GUIGenerator extends GuiContainer{

private static final ResourceLocation BG = new ResourceLocation(RandMod.getModIdPrefix()+"textures/gui/container/generator.png");

private TileEntityGenerator generator;

public GUIGenerator(InventoryPlayer inventoryPlayer, TileEntityGenerator entity) {
	super(new ContainerGenerator(inventoryPlayer, entity));
	generator = entity;

	this.xSize = 176;
	this.ySize = 166;

}

@Override
public void drawGuiContainerForegroundLayer(int i, int j){//args ?
	String name = generator.hasCustomInventoryName() ? generator.getInventoryName() : I18n.format(generator.getInventoryName(),  new Object[0]);

	fontRendererObj.drawString(name, xSize / 2 - fontRendererObj.getStringWidth(name) / 2, 6, 4210752);
	String inventoryLabel = I18n.format("container.inventory", new Object[0]);
	fontRendererObj.drawString(inventoryLabel, xSize - fontRendererObj.getStringWidth(inventoryLabel) - 8, ySize - 96 + 2, 4210752);
	String energyInfo = I18n.format(generator.getEnergyStored(ForgeDirection.DOWN) + " RF", new Object[0]);
	String max = I18n.format(generator.getMaxEnergyStored(ForgeDirection.DOWN) + " RF", new Object[0]);
	fontRendererObj.drawString(energyInfo, 27+fontRendererObj.getStringWidth(max)-fontRendererObj.getStringWidth(energyInfo), 70-9, 4210752);
}

@Override
public void drawGuiContainerBackgroundLayer(float f, int f1, int f2) {//really don't know what these mean
	GL11.glColor4f(1f, 1f, 1f, 1f);

	Minecraft.getMinecraft().getTextureManager().bindTexture(BG);

	drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize);

	if(generator.isBurning() ){
		int bW = 15;
		int bH = 24;
		int i = generator.getBurnTimeRemainingScaled(bH);
		drawTexturedModalRect(guiLeft+81, guiTop + 43, xSize, 58, 15, i);
	}
	if(generator.getEnergyStored(ForgeDirection.DOWN)> 0){
		int bW = 14;
		int bH = 58;
		int i = generator.getEnergyBarScaled(bH);//58: barHeight
		int x = bH - i;
		drawTexturedModalRect(guiLeft+9, guiTop+12+bH-i, xSize, x, 14, i);
	}
}

private void drawTexturedBar(int xOff, int yOff, int xSrc, int ySrc, int width, int height){
	drawTexturedModalRect(xOff, yOff, xSrc, ySrc, width, height);
}
}

 

 

ContainerGenerator.java

 

package org.bitbucket.pickaxe_engineer.randmod.container;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.inventory.Container;
import net.minecraft.inventory.ICrafting;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.FurnaceRecipes;
import net.minecraft.tileentity.TileEntityFurnace;
import net.minecraftforge.common.util.ForgeDirection;

import org.bitbucket.pickaxe_engineer.randmod.tileentity.TileEntityGenerator;

public class ContainerGenerator extends Container {

public static final int BURN_TIME_ID = 0;
public static final int TOTAL_BURN_ID = 1;
public static final int ENERGY_ID = 2;

private TileEntityGenerator generator;

//	private int lastBurnTime;
//	private int lastFreshTime;
//	private int lastEnergy;

public ContainerGenerator(InventoryPlayer inventoryPlayer,
		TileEntityGenerator entity) {
	generator = entity;
	//generator slots
	//make slot IDS!
	addSlotToContainer(new Slot(entity, 0, 80, 21));
//		addSlotToContainer(new Slot(entity, 1, 148, 53));//battery container

	//playerInv
	for(int y = 0; y<3; y++){
		for(int x = 0; x<9; x++){
			addSlotToContainer(new Slot(inventoryPlayer, x + y*9 + 9, 8 + x*18, 84 + y*18));
		}
	}
	//Hotbar
	for(int x=0; x<9; x++){
		addSlotToContainer(new Slot(inventoryPlayer,x, 8 + x*18, 142 ));
	}
}

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

@Override
public void detectAndSendChanges(){
	super.detectAndSendChanges();

	for(int i=0; i<crafters.size(); i++){
		ICrafting crafter = (ICrafting)crafters.get(i);
		if(crafter != null){
			crafter.sendProgressBarUpdate(this,  BURN_TIME_ID, generator.getBurnTime());
			crafter.sendProgressBarUpdate(this, TOTAL_BURN_ID, generator.getTotalBurnTime());
			crafter.sendProgressBarUpdate(this, ENERGY_ID, generator.getEnergyStored(ForgeDirection.DOWN));
		}
	}
}

@Override
public void updateProgressBar(int id, int value){
	if(id == BURN_TIME_ID){
		generator.setBurnTime(value);
	}else if(id == TOTAL_BURN_ID){
		generator.setTotalBurnTime(value);
	}else if(id == ENERGY_ID){
		generator.setEnergyStored(value);
	}
}

@Override
public ItemStack transferStackInSlot(EntityPlayer player, int slotIndex)
    {
	//absolutely no plan what this does
        ItemStack incomingStackBU = null;
        Slot slot = (Slot)this.inventorySlots.get(slotIndex);

        if (slot != null && slot.getHasStack()) {
            ItemStack incomingStack = slot.getStack();
            incomingStackBU = incomingStack.copy();

            if (slotIndex == 2) {
                if (!this.mergeItemStack(incomingStack, 3, 39, true)) {
                    return null;
                }

                slot.onSlotChange(incomingStack, incomingStackBU);
            }
            else if (slotIndex != 1 && slotIndex != 0) {
                if (FurnaceRecipes.smelting().getSmeltingResult(incomingStack) != null) {
                    if (!this.mergeItemStack(incomingStack, 0, 1, false))
                    {
                        return null;
                    }
                }else if (TileEntityFurnace.isItemFuel(incomingStack)) {
                    if (!this.mergeItemStack(incomingStack, 1, 2, false))
                    {
                        return null;
                    }
                }else if (slotIndex >= 3 && slotIndex < 30){
                    if (!this.mergeItemStack(incomingStack, 30, 39, false)){
                        return null;
                    }
                }else if (slotIndex >= 30 && slotIndex < 39 && !this.mergeItemStack(incomingStack, 3, 30, false)) {
                    return null;
                }
            } else if (!this.mergeItemStack(incomingStack, 3, 39, false)) {
                return null;
            }

            if (incomingStack.stackSize == 0) {
                slot.putStack((ItemStack)null);
            } else {
                slot.onSlotChanged();
            }

            if (incomingStack.stackSize == incomingStackBU.stackSize) {
                return null;
            }

            slot.onPickupFromSlot(player, incomingStack);
        }

        return incomingStackBU;
    }

}

 

 

EDIT: Corrected the closing spoiler tags

Since English is not my mother tongue, my sentences may are confusing.

 

I'm coding java for a long time now - just MC and forge stop me sometimes.

Link to comment
Share on other sites

Add this line anywhere the inventory changes:

 

if (worldObj != null) worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);

 

That tells the server it needs to send the changes to the client.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

Hi Draco18s

 

Thank you for your reply.

 

Unfortunately this does not help solving the mentioned issue.

The item is still displayed, after consumption and it still displays the decreased stackSize correctly when putting more than one fuel item into it.

 

I guess the problem is somewhere in my updateEntity method, which I changed slightly.

 

Sincerely -pick

 

New TileEntityGenerator.updateEntity() method:

 

@Override
public void updateEntity() {
	boolean burningFlag = burnTime > 0;
	boolean notify = false;
	if (isBurning()) {
		burnTime--;
		notify = true;
	}
	distributeEnergy();
	notifyClient(notify);
	if (!active) {
		return;
	}
	if (!worldObj.isRemote) {
		// on server
		if (burnTime == 0 && !storage.isFull()) {
			// can produce
			if (slots[FUEL_SLOT] != null) {
				// will produce
				burnTime = totalBurnTime = TileEntityFurnace
						.getItemBurnTime(slots[0]);
				if (isBurning() && slots[FUEL_SLOT] != null) {
					slots[FUEL_SLOT].stackSize--;
					if (slots[FUEL_SLOT].stackSize <= 0) {
						slots[FUEL_SLOT] = slots[FUEL_SLOT].getItem()
								.getContainerItem(slots[FUEL_SLOT]);
					}

				}
				active = true;
			} else {
				active = false;
			}
			// had no item?
			notify = true;
		}

		if (isActive()) {
			// actual production:
			int produced = storage.receiveEnergy(powerGen, false);
			// (produced <= powerGen ? //could store : //internal buffer
			// full);
			notify = true;
		}

		if (burningFlag != isBurning()) {
			GeneratorBlock.updateBlockState(isActive(), worldObj, xCoord,
					yCoord, zCoord);
		}
	}// end on server
	notifyClient(notify);
}

private void notifyClient(boolean really){
	if(!really){
		return;
	}
	markDirty();
	if (worldObj != null) {
		worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
	}
}

 

Since English is not my mother tongue, my sentences may are confusing.

 

I'm coding java for a long time now - just MC and forge stop me sometimes.

Link to comment
Share on other sites

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.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.