abused_master Posted February 9, 2017 Posted February 9, 2017 Hey guys, so i was wondering how would i go about storing data to a block after its broken? in that sense i have an EnergyBank, when broken id like the block to retain its energy, how would i go about doing that? Quote
Choonster Posted February 9, 2017 Posted February 9, 2017 (edited) You need to save the TileEntity's data to the ItemStack's NBT (or capabilities). You can do this by overriding Block#getDrops to create an ItemStack with the appropriate data and then return a List<ItemStack> containing it. By default, the TileEntity is removed from the world before Block#getDrops is called. You need to delay this like Forge's patch to BlockFlowerPot does. Edited February 9, 2017 by Choonster Quote Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.
abused_master Posted February 9, 2017 Author Posted February 9, 2017 Do you by any chance have a simple example of some data being saved to athe ItemStack's NBT with getDrops? if not thats all right, but thanks for the info will see what happens. Quote
Choonster Posted February 9, 2017 Posted February 9, 2017 On 2/9/2017 at 2:10 AM, abused_master said: Do you by any chance have a simple example of some data being saved to athe ItemStack's NBT with getDrops? if not thats all right, but thanks for the info will see what happens. Expand Look at BlockSkull or BlockBanner. You can see an example of a TileEntity's IFluidHandler being saved to an ItemStack's IFluidHandlerItem here. Quote Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.
Daeruin Posted February 10, 2017 Posted February 10, 2017 (edited) I did this for my containers, because I wanted them to retain their inventory when broken. I created a method in my Tile Entity that looked like this: public void dropContainerWithInventory(World world, BlockPos pos, IBlockState state, EntityPlayer player, TileEntity tileEntity) { if (tileEntity != null && Item.getItemFromBlock(state.getBlock()) != null) { ItemStack stack = new ItemStack(this); NBTTagCompound inventoryTag = new NBTTagCompound(); tileEntity.writeToNBT(inventoryTag); NBTTagCompound masterTag = new NBTTagCompound(); masterTag.setTag("BlockEntityTag", inventoryTag); stack.setTagCompound(masterTag); spawnAsEntity(world, pos, stack); } world.setBlockToAir(pos); } If I remember right, you can delay the deletion of the tile entity by overriding removedByPlayer to return true if the willHarvest parameter is true. Edited February 10, 2017 by Daeruin Quote
abused_master Posted May 20, 2017 Author Posted May 20, 2017 (edited) All right so when trying this on a fluid tank it would crash when breaking with java.lang.NullPointerException: Unexpected error at abused_master.techexpansion.blocks.tank.BlockTank.saveFluidToStack(BlockTank.java:151) at abused_master.techexpansion.blocks.tank.BlockTank.removedByPlayer(BlockTank.java:165) at net.minecraft.client.multiplayer.PlayerControllerMP.onPlayerDestroyBlock(PlayerControllerMP.java:192) at net.minecraft.client.multiplayer.PlayerControllerMP.onPlayerDamageBlock(PlayerControllerMP.java:339) at net.minecraft.client.Minecraft.sendClickBlockToController(Minecraft.java:1512) at net.minecraft.client.Minecraft.processKeyBinds(Minecraft.java:2298) at net.minecraft.client.Minecraft.runTickKeyboard(Minecraft.java:2061) at net.minecraft.client.Minecraft.runTick(Minecraft.java:1849) at net.minecraft.client.Minecraft.runGameLoop(Minecraft.java:1127) at net.minecraft.client.Minecraft.run(Minecraft.java:407) at net.minecraft.client.main.Main.main(Main.java:118) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at net.minecraft.launchwrapper.Launch.launch(Launch.java:135) at net.minecraft.launchwrapper.Launch.main(Launch.java:28) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at net.minecraftforge.gradle.GradleStartCommon.launch(GradleStartCommon.java:97) at GradleStart.main(GradleStart.java:26) this is how i'm saving my data: public ItemStack saveFluidToStack(World world, BlockPos pos) { ItemStack stack = new ItemStack(Item.getItemFromBlock(this)); TileEntityTank te = (TileEntityTank) world.getTileEntity(pos); if (te.tank.getFluid() != null && te.tank.getFluidAmount() > 0) { final NBTTagCompound tileTag = te.writeToNBT(new NBTTagCompound()); stack.getTagCompound().setTag("TileData", tileTag); } return stack; } @Override public boolean removedByPlayer (IBlockState state, World world, BlockPos pos, EntityPlayer player, boolean willHarvest) { ItemStack dropStack = saveFluidToStack(world, pos); EntityItem item = new EntityItem(world, pos.getX(), pos.getY(), pos.getZ(), dropStack); world.spawnEntity(item); return world.setBlockToAir(pos); } @Override public int quantityDropped(Random random) { return 0; } @Override public void onBlockPlacedBy(World world, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack) { world.setBlockState(pos, state.withProperty(TYPE, TankTier.get(stack.getMetadata()))); final TileEntityTank tank = (TileEntityTank) world.getTileEntity(pos); if (stack.hasTagCompound()) { if (tank.tank != null) { tank.readFromNBT(stack.getTagCompound().getCompoundTag("TileData")); } } } Line 151 is stack.getTagCompound().setTag("TileData", tileTag); Edited May 20, 2017 by abused_master Quote
Choonster Posted May 20, 2017 Posted May 20, 2017 ItemStacks don't have a stack compound tag by default, you need to create one and set it as the stack compound tag by calling ItemStack#setTagCompound. Alternatively, use a method like ItemStack#setTagInfo that automatically creates the stack compound tag if it doesn't exist and then calls NBTTagCompound#setTag on it with the specified key and value. Quote Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.
abused_master Posted May 20, 2017 Author Posted May 20, 2017 All right i've used NBTTagCompound#setTag and it works fine, but when the block is broken it can be picked up but a ghost block remains on the floor, any idea what could be causing this? also when the block is broken then placed again with fluid inside it gives me this: [09:29:15] [Client thread/ERROR]: Received invalid update packet for null tile entity at BlockPos{x=-137, y=64, z=70} with data: {FluidName:"water",Amount:2000,FluidData:{FluidName:"water",Amount:2000},x:-137,y:64,z:70,id:"minecraft:tile_tank"} the fluid is also rendered at the old position instead of the new one my TileEntity Code: public class TileEntityTank extends TileEntityBase { public FluidTank tank; public TileEntityTank() { tank = new FluidTank(this.tier()); } @Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); if (nbt.hasKey("FluidData")) { tank.setFluid(FluidStack.loadFluidStackFromNBT(nbt.getCompoundTag("FluidData"))); } if(tank != null && tank.getFluid() != null) { tank.readFromNBT(nbt); } if (this.tank != null) { tank.setTileEntity(this); } } @Override public NBTTagCompound writeToNBT(NBTTagCompound nbt) { if (tank != null && tank.getFluid() != null) { final NBTTagCompound tankTag = new NBTTagCompound(); tank.getFluid().writeToNBT(tankTag); nbt.setTag("FluidData", tankTag); } tank.writeToNBT(nbt); return super.writeToNBT(nbt); } public int tier() { return 60000; } @Override public void update() { if(!world.isRemote) { IBlockState state = world.getBlockState(pos); world.notifyBlockUpdate(pos, state, state, 8); } } @Override public boolean hasCapability(Capability<?> capability, @Nullable EnumFacing facing) { return this.getCapability(capability, facing) != null; } @Nullable @Override public <T> T getCapability(Capability<T> capability, @Nullable EnumFacing facing) { if(capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) { return (T) this.tank; } return super.getCapability(capability, facing); } } Quote
Choonster Posted May 20, 2017 Posted May 20, 2017 (edited) Block#removedByPlayer is called on both sides, so you're spawning a real EntityItem on the server (which also spawns it on all nearby clients) and a "ghost" EntityItem on the client (that can't be interacted with because the server doesn't know about it). Entities should only be spawned on the server. Don't manually spawn the EntityItem, override Block#getDrops like I told you to in my first reply. If you store a compound tag in the "BlockEntityTag" key of an ItemStack's stack compound tag, Minecraft will automatically call TileEntity#readFromNBT with this tag when a player places the block. You don't need to do this yourself. Is the update method being called every tick (e.g. because it implements ITickable#update)? If so, you shouldn't be calling World#notifyBlockUpdate every tick, only call it to send the TileEntity's update packet or re-render the chunk (it always does both). It's possible that this is causing the TileEntity's update packet to be sent before the client knows that there's now a TileEntity at that position. The flags argument of World#notifyBlockUpdate is completely unused by the server and isn't sent to the client. Why are you reading/writing the tank and its contents from/to NBT separately? Just read/write the tank (FluidTank#readFromNBT/FluidTank#writeToNBT) and it will read/write its contents. Edited May 20, 2017 by Choonster Quote Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.
abused_master Posted May 20, 2017 Author Posted May 20, 2017 All right i've fixed those, but the problem still persists, also when the block is broken then replaced, and the world is reloaded the contents disappear, this however does not occur if the block is not moved on initial placement Quote
Choonster Posted May 20, 2017 Posted May 20, 2017 On 5/20/2017 at 3:53 PM, abused_master said: All right i've fixed those, but the problem still persists, also when the block is broken then replaced, and the world is reloaded the contents disappear, this however does not occur if the block is not moved on initial placement Expand Post your new Block and TileEntity classes. Quote Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.
Recommended Posts
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.