Jump to content

[1.10.2] Make block drop itself with all Tile Entity data?


IceMetalPunk

Recommended Posts

Solve one problem, another shows up.

 

I'm trying to simply make a block, which provides a tile entity, drop a tagged item stack when harvested. In other words, the item stack should have a BlockEntityTag with all the tile entity's data, ready to resume when placed down again.

 

Instead...I'm getting no drops at all. Whether I'm in Creative mode or Survival, using a tool or not, the block doesn't drop anything when broken.

 

Here's the relevant code:

 

	@Override
public Item getItemDropped(IBlockState state, Random rand, int fortune) {
	return null;
}

@Override
public final ArrayList<ItemStack> getDrops(IBlockAccess world, BlockPos pos, IBlockState state, int fortune) {
	int meta = state.getBlock().getMetaFromState(state);
	ItemStack ret = new ItemStack(this, 1, meta);
	NBTTagCompound nbt = new NBTTagCompound();
	TileEntity te = world.getTileEntity(pos);
	te.writeToNBT(nbt);
	ret.setTagCompound(nbt);
	return Lists.newArrayList(new ItemStack(this, 1, meta));
}

@Override
public boolean canHarvestBlock(IBlockAccess world, BlockPos pos, EntityPlayer player) {
	return true;
}

 

I'm returning an array containing the item stack from getDrops(), and I've ensured the block is harvestable at all times, so why isn't it dropping anything at all?

Whatever Minecraft needs, it is most likely not yet another tool tier.

Link to comment
Share on other sites

You're not returning the item stack with the NBT which you set up (and that part looks ok).  Instead you're returning an arraylist with a brand new ItemStack which has no NBT data at all:

return Lists.newArrayList(new ItemStack(this, 1, meta));

 

Also, why are you making getItemDropped() return null?  Looks unnecessary, especially as you've overridden getDrops(), which is what primarily calls getItemDropped() in the default Block#getDrops() implementation.

 

(and any particular reason you've made getDrops() final?)

Link to comment
Share on other sites

You're not returning the item stack with the NBT which you set up (and that part looks ok).  Instead you're returning an arraylist with a brand new ItemStack which has no NBT data at all:

return Lists.newArrayList(new ItemStack(this, 1, meta));

D'oh! This is what I get for trying to code at 5AM; how did I not see that? >_<

 

Unfortunately, fixing it to return Lists.newArrayList(ret) doesn't seem to fix the problem. It still drops nothing at all no matter how I harvest it.

 

Also, why are you making getItemDropped() return null?  Looks unnecessary, especially as you've overridden getDrops(), which is what primarily calls getItemDropped() in the default Block#getDrops() implementation.

 

I wasn't sure if there were certain cases where getItemDropped() was called instead of getDrops(), and if so, I didn't want it to drop anything different. Since I can't return an ItemStack with getItemDropped(), I just had it return null for safe measure.

 

But removing that override doesn't help; it still drops nothing.

 

(and any particular reason you've made getDrops() final?)

 

Just because it's final in the Block class, so I kept it that way in my override. Probably not necessary, but it also doesn't hurt, right?

Whatever Minecraft needs, it is most likely not yet another tool tier.

Link to comment
Share on other sites

The TileEntity cannot be accessed in

getDrops

, it is called after the Block has already been removed from the World. See the patches forge makes to

BlockFlowerPot

(between FORGE START and FORGE END).

 

Thank you! It's a bit silly to check what the block should drop after its associated data is already removed, isn't it? But anyway, overriding the removedByPlayer and harvestBlock methods appropriately did the trick, and now it's dropping! It's almost working, except... when I place the block back down, it correctly retains the tile entity data, but it seems as though the tile entity stops ticking/updating. No errors, it just never runs the update code (and yet, the constructor is being called, so the TE is created, just not ticking).

 

Here's the entire TE class, block base class, and specific block class I'm testing with:

 

Tile Entity:

package com.icemetalpunk.chaotica.tileentities;

import java.util.Iterator;
import java.util.Map;

import javax.annotation.Nullable;

import com.icemetalpunk.chaotica.Chaotica;
import com.icemetalpunk.chaotica.ChaoticaUtils;
import com.icemetalpunk.chaotica.fluids.FluidTankChaos;
import com.icemetalpunk.chaotica.handlers.ChaoticaMessage;
import com.icemetalpunk.chaotica.handlers.ChaoticaPacketHandler;
import com.icemetalpunk.chaotica.sounds.ChaoticaSoundRegistry;

import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ITickable;
import net.minecraft.util.SoundCategory;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;

public class TileEntityChaoticCondenser extends ChaoticaTEBase implements ICapabilityProvider, ITickable {

protected Fluid fluid = Chaotica.fluids.CORROSIVE_CHAOS;
protected int capacity = 5 * Fluid.BUCKET_VOLUME;
protected FluidTankChaos tank = new FluidTankChaos(this.capacity);
protected int countdown = 40;
protected int maxCountdown = 60; // Max amount it resets to

public TileEntityChaoticCondenser() {
	this.tank.setTileEntity(this);
	System.out.println("Created TE");
}

@Override
public boolean hasCapability(Capability<?> capability, @Nullable EnumFacing facing) {
	if (capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) {
		return true;
	}
	return super.hasCapability(capability, facing);
}

@Override
public <T> T getCapability(Capability<T> capability, @Nullable EnumFacing facing) {
	if (capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) {
		return CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY.cast(this.tank);
	}
	return super.getCapability(capability, facing);
}

// Ticks until the next check for blocks to convert to chaos
public int getCountdown() {
	return this.countdown;
}

public int getMaxCountdown() {
	return this.maxCountdown;
}

public Fluid getFluid() {
	if (tank.getFluid() == null) {
		return null;
	}
	return this.tank.getFluid().getFluid();
}

public void setFluidAmount(int amount) {
	if (tank.getFluid() == null) {
		tank.setFluid(new FluidStack(this.fluid, 0));
	}
	tank.getFluid().amount = amount;
}

@Override
public void readFromNBT(NBTTagCompound tag) {
	super.readFromNBT(tag);
	NBTTagCompound tankTag = tag.getCompoundTag("Tank");
	this.tank.readFromNBT(tankTag);
	this.countdown = tag.getInteger("Countdown");
}

@Override
public NBTTagCompound writeToNBT(NBTTagCompound tag) {
	super.writeToNBT(tag);
	NBTTagCompound tankTag = new NBTTagCompound();
	tank.writeToNBT(tankTag);
	tag.setTag("Tank", tankTag);
	tag.setInteger("Countdown", this.countdown);
	return tag;
}

public int fill(int amount, boolean doFill) {
	return this.tank.fill(new FluidStack(this.fluid, amount), doFill);
}

@Override
public void update() {
	System.out.println("Updating TE");
	if (--this.countdown == 0) {
		this.countdown = this.maxCountdown;
		if (!this.worldObj.isRemote) {
			IBlockState east = this.getWorld().getBlockState(this.pos.east());
			IBlockState west = this.getWorld().getBlockState(this.pos.west());
			IBlockState north = this.getWorld().getBlockState(this.pos.north());
			IBlockState south = this.getWorld().getBlockState(this.pos.south());
			IBlockState up = this.getWorld().getBlockState(this.pos.up());
			IBlockState down = this.getWorld().getBlockState(this.pos.down());

			// Iterate over the amounts map and add the appropriate amount
			// of fluid if applicable
			boolean playSound = false;
			Iterator<Map.Entry<ChaoticaUtils.BlockPair, Integer>> it = ChaoticaUtils.pairAmounts.entrySet().iterator();
			while (it.hasNext()) {
				Map.Entry<ChaoticaUtils.BlockPair, Integer> entry = it.next();
				ChaoticaUtils.BlockPair pair = entry.getKey();
				int amount = entry.getValue().intValue();

				if (pair.isPair(east.getBlock(), west.getBlock())) {
					if (this.fill(amount, true) > 0) {
						playSound = true;
						this.getWorld().destroyBlock(this.pos.east(), false);
						this.getWorld().destroyBlock(this.pos.west(), false);
					}
				}
				if (pair.isPair(north.getBlock(), south.getBlock())) {
					if (this.fill(amount, true) > 0) {
						playSound = true;
						this.getWorld().destroyBlock(this.pos.north(), false);
						this.getWorld().destroyBlock(this.pos.south(), false);
					}
				}
				if (pair.isPair(up.getBlock(), down.getBlock())) {
					if (this.fill(amount, true) > 0) {
						playSound = true;
						this.getWorld().destroyBlock(this.pos.up(), false);
						this.getWorld().destroyBlock(this.pos.down(), false);
					}
				}
			}
			if (playSound) {
				this.worldObj.playSound(null, this.getPos(), ChaoticaSoundRegistry.CONDENSE_CHAOS, SoundCategory.BLOCKS, 1.0f, 1.0f);
				ChaoticaPacketHandler.INSTANCE.sendToAll(new ChaoticaMessage(ChaoticaMessage.MessageTypes.CONDENSER_LEVEL, this.pos.getX(), this.pos.getY(), this.pos.getZ(), this.tank.getFluidAmount()));
			}
		}
	}
}

// If shouldHarvest() returns true, this will drop a fully tagged item
// instead of an empty one.
@Override
public boolean shouldHarvest() {
	return true;
}

@Override
public ItemStack getHarvest() {
	Block block = this.worldObj.getBlockState(this.pos).getBlock();
	Item item = Item.getItemFromBlock(block);
	ItemStack stack = new ItemStack(item, 1);
	NBTTagCompound nbt = new NBTTagCompound();
	this.writeToNBT(nbt);
	stack.setTagCompound(nbt);
	return stack;
}

}

 

TE Provider Block Base Class:

package com.icemetalpunk.chaotica.blocks;

import java.util.ArrayList;

import com.google.common.collect.Lists;

import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;

public abstract class ChaoticaTEBlock extends ChaoticaBlockBase {

public ChaoticaTEBlock(String name, Material materialIn) {
	super(name, materialIn);
}

// Make sure when the block is dropped, the item retains the TE's data tags

@Override
public final ArrayList<ItemStack> getDrops(IBlockAccess world, BlockPos pos, IBlockState state, int fortune) {
	int meta = state.getBlock().getMetaFromState(state);
	ItemStack ret = new ItemStack(this, 1, meta);
	NBTTagCompound nbt = new NBTTagCompound();
	TileEntity te = world.getTileEntity(pos);
	NBTTagCompound teTag = new NBTTagCompound();
	te.writeToNBT(teTag);
	nbt.setTag("BlockEntityTag", teTag);
	ret.setTagCompound(nbt);
	return Lists.newArrayList(ret);
}

@Override
public boolean removedByPlayer(IBlockState state, World world, BlockPos pos, EntityPlayer player,
		boolean willHarvest) {
	if (willHarvest) return true; // If it will harvest, delay deletion of
									// the block until after getDrops
	return super.removedByPlayer(state, world, pos, player, willHarvest);
}

@Override
public void harvestBlock(World world, EntityPlayer player, BlockPos pos, IBlockState state, TileEntity te,
		ItemStack tool) {
	super.harvestBlock(world, player, pos, state, te, tool);
	world.setBlockToAir(pos);
}

@Override
public boolean canHarvestBlock(IBlockAccess world, BlockPos pos, EntityPlayer player) {
	return true;
}

// When placed, if the item has a tag, set the tile entity's tag
/*
 * FIXME: When placed like this, tags are properly set, but tile entity
 * fails to update ever after.
 */
@Override
public void onBlockPlacedBy(World worldIn, BlockPos pos, IBlockState state, EntityLivingBase placer,
		ItemStack stack) {
	super.onBlockPlacedBy(worldIn, pos, state, placer, stack);
	if (stack.hasTagCompound() && stack.getTagCompound().hasKey("BlockEntityTag")) {
		NBTTagCompound tag = stack.getTagCompound().getCompoundTag("BlockEntityTag");
		worldIn.getTileEntity(pos).readFromNBT(tag);
	}
}

// Tile entity providers should provide the class and name of their tile
// entity here for registration.
public abstract Class<? extends TileEntity> getTileEntityClass();

public abstract String getTileEntityName();

@Override
public boolean hasTileEntity(IBlockState state) {
	return true;
}

// Generic createNewTileEntity so only the getTileEntityClass needs to be
// specified.
@Override
public TileEntity createTileEntity(World world, IBlockState state) {
	try {
		return this.getTileEntityClass().newInstance();
	}
	catch (InstantiationException e) {
		e.printStackTrace();
		return null;
	}
	catch (IllegalAccessException e) {
		e.printStackTrace();
		return null;
	}
}
}

 

Block Base Class:

package com.icemetalpunk.chaotica.blocks;

import com.icemetalpunk.chaotica.Chaotica;

import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.common.registry.GameRegistry;
import net.minecraftforge.oredict.OreDictionary;

public class ChaoticaBlockBase extends Block {

public ChaoticaBlockBase(String name, Material materialIn) {
	super(materialIn);
	this.setUnlocalizedName(name).setRegistryName(new ResourceLocation(Chaotica.MODID, name)).setCreativeTab(Chaotica.tab);
}

protected void register() {
	GameRegistry.register(this);
	if (this instanceof ChaoticaTEBlock) {
		GameRegistry.registerTileEntity(((ChaoticaTEBlock) this).getTileEntityClass(), ((ChaoticaTEBlock) this).getTileEntityName());
	}

	String[] oreDict = this.getOreDict();
	if (oreDict != null) {
		for (String entry : oreDict) {
			OreDictionary.registerOre(entry, this);
		}
	}
}

// Override this if this block has an oredict entry.
public String[] getOreDict() {
	return null;
}

}

 

Specific Block Class:

package com.icemetalpunk.chaotica.blocks;

import javax.annotation.Nullable;

import com.icemetalpunk.chaotica.Chaotica;
import com.icemetalpunk.chaotica.gui.ChaoticaGuiHandler;
import com.icemetalpunk.chaotica.tileentities.TileEntityChaoticCondenser;

import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;

public class BlockChaoticCondenser extends ChaoticaTEBlock {

public BlockChaoticCondenser() {
	super("chaotic_condenser", Material.ROCK);
	this.setHardness(3.5F);
	this.setSoundType(SoundType.STONE);
	this.register();
}

@Override
public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand,
		@Nullable ItemStack item, EnumFacing side, float hitX, float hitY, float hitZ) {
	if (!world.isRemote) {
		player.openGui(Chaotica.instance, ChaoticaGuiHandler.Guis.CONDENSER.ordinal(), world, pos.getX(), pos.getY(), pos.getZ());
	}
	return true;
}

@Override
public Class<? extends TileEntity> getTileEntityClass() {
	return TileEntityChaoticCondenser.class;
}

@Override
public String getTileEntityName() {
	return "ChaoticCondenser";
}

}

 

How could the tile entity be created (it is!), its data be accessible (it is! It's displaying properly!), but the update method fail to be called, even though it works fine if I place down a fresh, "clean" block, just not when I place one down which already has tags?

 

Just because it's final in the Block class, so I kept it that way in my override. Probably not necessary, but it also doesn't hurt, right?
How could it be final in the Block class? Then you would not be able to override it. You do know what final means, right? But yes, it does not hurt. Finality is good actually.

 

Yes, I did think it was strange that I was able to override it, but being relatively new to Java, I thought there was some quirk of the language that allowed me to do it...turns out I'm just an idiot and confused the example code I was learning from (which declared it as final) with the base Block class itself xD Sorry for the confusion!

Whatever Minecraft needs, it is most likely not yet another tool tier.

Link to comment
Share on other sites

When you save the

NBTTagCompound

inside the

ItemStack

, you also save the coordinates of the

Block

at the location it was broken at. Then, when you place it down, you read from the

ItemStack

's

NBTTagCompound

, thus also setting the position of the

TileEntity

to the coordinates of the previous location. This may be why it's not getting ticked.

 

Setting the correct position after reading from NBT should fix this issue, if this is what caused it.

Don't PM me with questions. They will be ignored! Make a thread on the appropriate board for support.

 

1.12 -> 1.13 primer by williewillus.

 

1.7.10 and older versions of Minecraft are no longer supported due to it's age! Update to the latest version for support.

 

http://www.howoldisminecraft1710.today/

Link to comment
Share on other sites

When you save the

NBTTagCompound

inside the

ItemStack

, you also save the coordinates of the

Block

at the location it was broken at. Then, when you place it down, you read from the

ItemStack

's

NBTTagCompound

, thus also setting the position of the

TileEntity

to the coordinates of the previous location. This may be why it's not getting ticked.

 

Setting the correct position after reading from NBT should fix this issue, if this is what caused it.

 

That worked! And it makes sense, too! Thank you! :D (Also, an interesting note: if the position tags are wrong, and I run the /blockdata command on the block's position, it seems to automatically fix the position in the tile entity tags [showing a successful change to the tags even if I supply an empty tag to the command] and the updates start ticking again. Weird, but interesting.)

 

You don't need to read the data into the TE manually. Minecraft already does that for the BlockEntityTag tag on ItemBlocks.

 

And yet if I don't load it myself in that onBlockPlacedBy override, when I re-place a broken block, all the tile entity data is lost completely (I just tested it)...

Whatever Minecraft needs, it is most likely not yet another tool tier.

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.