Posted November 26, 201410 yr Hey, I'm trying to get my tile entity to save its data so it doesn't reset when the world is reloaded. I'm having a lot of trouble understand NBT, so it isn't going too well. I thought this read and write code would work, however it doesn't seem to do anything at all. If someone could explain this to me, it would be greatly appreciated. My tile entity class: public class TileEntityCoffeeMaker extends TileEntityInventory implements IFluidHandler { public TileEntityCoffeeMaker() { super(); this.size = 4; this.invName = Names.COFFEE_MAKER; } public static String getName() { return Names.TILE_COFFEE_MAKER; } @Override public void updateEntity() { if (getStackInSlot(0) != null) { if (getStackInSlot(0).isItemEqual(new ItemStack(Items.water_bucket))) { decrStackSize(0, 1); setInventorySlotContents(0, new ItemStack(Items.bucket)); fill(ForgeDirection.EAST, new FluidStack(FluidRegistry.WATER, 1000), true); } } if (getStackInSlot(3) != null) { if (getStackInSlot(3).isItemEqual(new ItemStack(ModItems.ItemCoffeeMug))) { if (getCoffeeTank().getFluidAmount() >= 1000) { decrStackSize(3, 1); setInventorySlotContents(3, new ItemStack(ModItems.BeverageBlackCoffee)); drain(ForgeDirection.EAST, new FluidStack(ModFluids.LiquidCoffee, 1000), true); } } } } public FluidTank waterTank = new FluidTank(12000); public FluidTank coffeeTank = new FluidTank(12000); public FluidTank getWaterTank() { return waterTank; } public FluidTank getCoffeeTank() { return coffeeTank; } @Override public void readFromNBT(NBTTagCompound tag) { super.readFromNBT(tag); if (tag.hasKey("WaterTank")) waterTank = waterTank.readFromNBT(tag.getCompoundTag("WaterTank")); if (tag.hasKey("CoffeeTank")) coffeeTank = coffeeTank.readFromNBT(tag.getCompoundTag("CoffeeTank")); } @Override public void writeToNBT(NBTTagCompound tag) { super.writeToNBT(tag); NBTTagCompound waterTankNBT = new NBTTagCompound(); waterTank.writeToNBT(waterTankNBT); NBTTagCompound coffeeTankNBT = new NBTTagCompound(); coffeeTank.writeToNBT(coffeeTankNBT); tag.setTag("WaterTank", waterTankNBT); tag.setTag("CoffeeTank", coffeeTankNBT); tag.setString("A_Warning", "Don't let looks fool you, that panda eating bamboo over there is crazy"); } @Override public int fill(ForgeDirection from, FluidStack resource, boolean doFill) { if (resource.isFluidEqual(new FluidStack(FluidRegistry.WATER, 1))) { return waterTank.fill(resource, doFill); } else if (resource.isFluidEqual(new FluidStack(ModFluids.LiquidCoffee, 1))) { return coffeeTank.fill(resource, doFill); } return 0; } @Override public FluidStack drain(ForgeDirection from, FluidStack resource, boolean doDrain) { if (resource == null || !resource.isFluidEqual(waterTank.getFluid()) || !resource.isFluidEqual(coffeeTank.getFluid())) { return null; } else if (resource.isFluidEqual(waterTank.getFluid())) { return waterTank.drain(resource.amount, doDrain); } else if (resource.isFluidEqual(coffeeTank.getFluid())) { return coffeeTank.drain(resource.amount, doDrain); } return null; } @Override public FluidStack drain(ForgeDirection from, int maxDrain, boolean doDrain) { if (from.equals(ForgeDirection.EAST)) { return waterTank.drain(maxDrain, doDrain); } return coffeeTank.drain(maxDrain, doDrain); } @Override public boolean canFill(ForgeDirection from, Fluid fluid) { if (fluid == FluidRegistry.WATER || fluid == ModFluids.LiquidCoffee) { return true; } return false; } @Override public boolean canDrain(ForgeDirection from, Fluid fluid) { return true; } @Override public FluidTankInfo[] getTankInfo(ForgeDirection from) { return new FluidTankInfo[]{waterTank.getInfo(), coffeeTank.getInfo()}; } } I'm pretty sure you only need the read and write from NBT methods, but I've included the whole class just in case. If you need anything else, please tell me. Thanks!
November 26, 201410 yr Author On reload of the world, the data is reset. For example, if I deposit a bucket of water into the machine, leave the world, and then reenter the world, the machine will no longer have the water in the tank.
November 26, 201410 yr Author Ah, interesting point. Yes, I am going completely off the GUI. Would you say that the problem is related to the GUI and not the actual tile entity? This is the GUI class: public class GuiCoffeeMaker extends GuiContainer { private int x, y, z; private EntityPlayer entityPlayer; private World world; private int xSize, ySize; private TileEntityCoffeeMaker instance; private ResourceLocation backgroundImage = new ResourceLocation(Reference.MOD_ID.toLowerCase() + ":textures/gui/GuiCoffeeMaker.png"); public GuiCoffeeMaker(EntityPlayer entityPlayer, World world, int x, int y, int z) { super(new ContainerCofffeeMaker(entityPlayer, (IInventory) world.getTileEntity(x, y, z), 176, 170)); this.x = x; this.y = y; this.z = z; this.entityPlayer = entityPlayer; this.world = world; this.instance = (TileEntityCoffeeMaker)world.getTileEntity(x, y, z); xSize = 176; ySize = 170; } @Override protected void drawGuiContainerBackgroundLayer(float p_146976_1_, int p_146976_2_, int p_146976_3_) { GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); this.mc.getTextureManager().bindTexture(backgroundImage); int x = (this.width - xSize) / 2; int y = (this.height - ySize) / 2; drawTexturedModalRect(x, y, 0, 0, xSize, ySize); drawFluidTank(instance.getWaterTank(), x + 35, y + 17); drawFluidTank(instance.getCoffeeTank(), x + 126, y + 17); fontRendererObj.drawString(StatCollector.translateToLocal("acoj.inv.coffeeMaker"), x + 56, y + 4, 0x313131); fontRendererObj.drawString(StatCollector.translateToLocal("acoj.inv.Inventory"), x + 9, y + 79, 0x313131); } @Override public boolean doesGuiPauseGame() { return false; } public void drawFluidTank(IFluidTank tank, int x, int y) { FluidStack fluid = tank.getFluid(); TextureManager manager = Minecraft.getMinecraft().renderEngine; if (fluid != null) { manager.bindTexture(manager.getResourceLocation(0)); float amount = fluid.amount; float capacity = tank.getCapacity(); float scale = amount / capacity; int fluidTankHeight = 60; int fluidAmount = (int) (scale * fluidTankHeight); drawFluid(x, y + fluidTankHeight - fluidAmount, fluid.getFluid().getIcon(fluid), 16, fluidAmount); } } private void drawFluid(int x, int y, IIcon icon, int width, int height) { int i = 0; int j = 0; int drawHeight = 0; int drawWidth = 0; for (i = 0; i < width; i += 16) { for (j = 0; j < height; j += 16) { drawWidth = Math.min(width - i, 16); drawHeight = Math.min(height - j, 16); drawRectangleFromIcon(x + i, y + j, icon, drawWidth, drawHeight); } } } private void drawRectangleFromIcon(int x, int y, IIcon icon, int width, int height) { if (icon == null) { LogHelper.info("Null"); return; } double minU = icon.getMinU(); double maxU = icon.getMaxU(); double minV = icon.getMinV(); double maxV = icon.getMaxV(); Tessellator tessellator = Tessellator.instance; tessellator.startDrawingQuads(); tessellator.addVertexWithUV(x, y + height, 0, minU, minV + (maxV - minV) * height / 16.0D); tessellator.addVertexWithUV(x + width, y + height, 0, minU + (maxU - minU) * width / 16.0D, minV + (maxV - minV) * height / 16.0D); tessellator.addVertexWithUV(x + width, y, 0, minU + (maxU - minU) * width / 16.0D, minV); tessellator.addVertexWithUV(x, y, 0, minU, minV); tessellator.draw(); } }
November 27, 201410 yr Author Hey, thanks for the solution. After doing a bit of research on your suggestion, I found that I could properly sync my tank data by using getDescriptionPacket() and onDataPacket(). However, I believe I still need your solution for the actual container. I have a couple questions, if you don't mind. While looking at the code for detectAndSendChanges() and addCraftingToCrafters(), I saw that the default methods in the Container class already should do what I want them to. That is, of course, if I understand the methods fully. Is there a reason why these don't seem to be working?
November 28, 201410 yr Author Oh ok, thanks didn't know that. I think I worded my last post strangely. I was saying that at the moment, with no changes, the items in the inventory are NOT synchronized.
November 28, 201410 yr Author Yeah, that's what I thought. Would you mind taking a look at my container class? public class ContainerCofffeeMaker extends Container { private EntityPlayer entityPlayer; private IInventory inv; private final int bucketSlotX = 10; private final int bucketSlotY = 59; private final int coffeeSlotX = 80; private final int coffeeSlotY = 12; private final int filterSlotX = 80; private final int filterSlotY = 52; private final int mugSlotX = 151; private final int mugSlotY = 59; public ContainerCofffeeMaker(EntityPlayer entityPlayer, IInventory inv, int xSize, int ySize) { this.entityPlayer = entityPlayer; this.inv = inv; inv.openInventory(); layout(xSize, ySize); } protected void layout(int xSize, int ySize) { addSlotToContainer(new Slot(inv, 0, bucketSlotX, bucketSlotY)); addSlotToContainer(new Slot(inv, 1, coffeeSlotX, coffeeSlotY)); addSlotToContainer(new Slot(inv, 2, filterSlotX, filterSlotY)); addSlotToContainer(new Slot(inv, 3, mugSlotX, mugSlotY)); int leftCol = (xSize - 162) / 2 + 1; for (int playerInvRow = 0; playerInvRow < 3; playerInvRow++) { for (int playerInvCol = 0; playerInvCol < 9; playerInvCol++) { addSlotToContainer(new Slot(entityPlayer.inventory, playerInvCol + playerInvRow * 9 + 9, leftCol + playerInvCol * 18, ySize - (4 - playerInvRow) * 18 - 11)); } } for (int hotbarSlot = 0; hotbarSlot < 9; hotbarSlot++) { addSlotToContainer(new Slot(entityPlayer.inventory, hotbarSlot, leftCol + hotbarSlot * 18, ySize - 25)); } } @Override public ItemStack transferStackInSlot(EntityPlayer entityPlayer, int slot) { ItemStack var2 = null; Slot var3 = (Slot) this.inventorySlots.get(slot); if (var3 != null && var3.getHasStack()) { ItemStack var4 = var3.getStack(); var2 = var4.copy(); if (slot < 4) { if (!this.mergeItemStack(var4, 1, this.inventorySlots.size(), true)) { return null; } } else if (!this.mergeItemStack(var4, 0, 1, false)) return null; if (var4.stackSize == 0) var3.putStack((ItemStack) null); else var3.onSlotChanged(); } return var2; } @Override public boolean canInteractWith(EntityPlayer entityPlayer) { return inv.isUseableByPlayer(entityPlayer); } }
November 28, 201410 yr Author Again, I'm going off the of the GUI so I assumed it was a problem with syncing again. If I understand you correctly, I open it just by right-clicking. This is the block class where I do that: public class BlockCoffeeMaker extends BlockContainer implements ITileEntityProvider { TileEntityCoffeeMaker instance; public BlockCoffeeMaker() { super(Material.rock); this.setCreativeTab(CreativeTabACOJ.ACOJ_TAB); this.setBlockName(Names.COFFEE_MAKER); } @Override public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer entityPlayer, int meta, float hitX, float hitY, float hitZ) { if (!world.isRemote && world.getTileEntity(x, y, z) instanceof TileEntityCoffeeMaker) { entityPlayer.openGui(ACOJ.instance, GUIs.COFFEE_MAKER.ordinal(), world, x, y, z); } return true; } @Override public ArrayList<ItemStack> getDrops(World world, int x, int y, int z, int metadata, int fortune) { ArrayList<ItemStack> itemStacks = Lists.newArrayList(); ItemStack itemStack = new ItemStack(ModBlocks.BlockCoffeeMaker, 1, metadata); itemStacks.add(itemStack); TileEntityCoffeeMaker coffeeMaker = getInstance(); if (coffeeMaker.getStackInSlot(0) != null) itemStacks.add(coffeeMaker.getStackInSlot(0)); if (coffeeMaker.getStackInSlot(1) != null) itemStacks.add(coffeeMaker.getStackInSlot(1)); if (coffeeMaker.getStackInSlot(2) != null) itemStacks.add(coffeeMaker.getStackInSlot(2)); if (coffeeMaker.getStackInSlot(3) != null) itemStacks.add(coffeeMaker.getStackInSlot(3)); return itemStacks; } @Override public TileEntity createNewTileEntity(World world, int meta) { instance = new TileEntityCoffeeMaker(); return instance; } @Override public boolean hasTileEntity() { return true; } public TileEntityCoffeeMaker getInstance() { return instance; } @Override public String getUnlocalizedName() { return String.format("tile.%s%s", Reference.MOD_ID.toLowerCase() + ":", getUnwrappedUnlocalizedName(super.getUnlocalizedName())); } protected String getUnwrappedUnlocalizedName(String unlocalizedName) { return unlocalizedName.substring(unlocalizedName.indexOf(".") + 1); } } It has pahimar's name code that I know you love Haven't gotten around to fixing that yet.
November 28, 201410 yr Author But I want my $200! D: I thought I could get around that problem by returning a new instance of the tile entity in createNewTileEntity(). Suppose not. So now I have two questions: 1) So without the instance, how can I get the current instance of the tile entity for getDrops()? 2) Removing the instance, did not allow for the tile entity to sync properly. The items still do not appear to save in the container. Is there something else I must do?
November 28, 201410 yr Author Ah ok, I'll take a look at that. Thanks This is the container before I relog: http://puu.sh/d8fwG/1c710cba87.png After I relog, it looks like this: http://puu.sh/d8fyT/9fb200c779.png The bucket is now gone.
November 28, 201410 yr Author Ah ok, sorry. Man do I feel dumb, saw the mistake in my writeToNBT method right away Thanks for the help with that. So now back to the original question... In order to sync my fluid amounts, I should override detectAndSendChanges() and addCraftingToCrafters() in my Container class. Would you mind explaining how to use these a bit more? I didn't really understand what you meant with the lastTick field. I've also not done anything with packets whatsoever. Do you know of a good tutorial I could read to gain an understanding of how to use them? Thanks!
November 28, 201410 yr Author Thanks so much! I'm a bit confused on how to get the old fluidstack and copy it. I tried this: private FluidStack oldWater = coffeeMaker.getWaterTank().getFluid().copy(); but it crashed with this error: Ticking memory connection java.lang.NullPointerException: Ticking memory connection at com.darichey.ACOJ.tileentity.containers.ContainerCofffeeMaker.<init>(ContainerCofffeeMaker.java:105) at com.darichey.ACOJ.handler.GuiHandler.getServerGuiElement(GuiHandler.java:18) at cpw.mods.fml.common.network.NetworkRegistry.getRemoteGuiContainer(NetworkRegistry.java:241) at cpw.mods.fml.common.network.internal.FMLNetworkHandler.openGui(FMLNetworkHandler.java:75) at net.minecraft.entity.player.EntityPlayer.openGui(EntityPlayer.java:2501) at com.darichey.ACOJ.block.BlockCoffeeMaker.onBlockActivated(BlockCoffeeMaker.java:45) at net.minecraft.server.management.ItemInWorldManager.activateBlockOrUseItem(ItemInWorldManager.java:409) at net.minecraft.network.NetHandlerPlayServer.processPlayerBlockPlacement(NetHandlerPlayServer.java:593) at net.minecraft.network.play.client.C08PacketPlayerBlockPlacement.processPacket(C08PacketPlayerBlockPlacement.java:74) at net.minecraft.network.play.client.C08PacketPlayerBlockPlacement.processPacket(C08PacketPlayerBlockPlacement.java:122) at net.minecraft.network.NetworkManager.processReceivedPackets(NetworkManager.java:241) at net.minecraft.network.NetworkSystem.networkTick(NetworkSystem.java:182) at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:726) at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:614) at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:118) at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:485) at net.minecraft.server.MinecraftServer$2.run(MinecraftServer.java:752) I'm sure I just misunderstood something.
November 28, 201410 yr Not sure if thats the correct way to do it but i believe you are crashing because coffeeMaker.getWaterTank().getFluid() == null when the tank is empty and you cant copy null. So just like you would with an itemstack make sure the fluid != null before you try to do anything with it. I am the author of Draconic Evolution
December 5, 201410 yr Author Hey again, I have another quick question. I thought I could figure it out on my own, but it apparently not. Where do I get the value from the last tick? someValueLastTick in your example.
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.