Jump to content

Storing Data when block is broken


abused_master

Recommended Posts

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 by Choonster

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.

Link to comment
Share on other sites

2 minutes ago, 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.

 

Look at BlockSkull or BlockBanner.

 

You can see an example of a TileEntity's IFluidHandler being saved to an ItemStack's IFluidHandlerItem here.

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.

Link to comment
Share on other sites

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 by Daeruin
Link to comment
Share on other sites

  • 3 months later...

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 by abused_master
Link to comment
Share on other sites

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.

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.

Link to comment
Share on other sites

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);
    }
}

 

Link to comment
Share on other sites

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 by Choonster

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.

Link to comment
Share on other sites

9 minutes ago, 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

 

Post your new Block and TileEntity classes.

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.

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.



×
×
  • Create New...

Important Information

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