Jump to content

[1.8] Crash in TESR on block break


Drakmyth

Recommended Posts

Hello,

 

Long time lurker, first time poster. Before I get into my problem I just wanted to extend a long belated thanks to the members of this community. The posts on this forum have helped me immensely getting me to where I am now.

 

Anyway, to my issue, I have created a Conveyor block that uses standard block rendering for the actual block itself, but then uses a TESR to render the items on the conveyor as well as animating the belt. I am now running into an issue where I set up two conveyors pointing at each other. If I put an item on one, then break that block, the game will crash with the following error on the client side: http://pastebin.com/p5M8X48i

 

I have tracked this down to my TESR trying to look up a property from the BlockState but not being able to find it. What I'm not quite sure about though, is why it can't find it. Relevant code below:

 

BlockConveyor.java

 

public class BlockConveyor extends Block implements ITileEntityProvider {

public BlockConveyor() {

	super(Material.rock);
	this.setUnlocalizedName(Constants.BlockNames.CONVEYOR_BELT);
	this.setCreativeTab(CreativeTabs.tabBlock);
	this.setBlockBounds(0.0f, 0.0f, 0.0f, 1.0f, 0.5f, 1.0f);
}

@Override
public boolean isOpaqueCube() {

	return false;
}

@Override
public boolean isFullCube() {

	return false;
}

@Override
protected BlockState createBlockState() {

	return new BlockState(this, Constants.BlockStateProperties.FACING_HORIZONTAL);
}

@Override
public IBlockState getStateFromMeta(final int meta) {

	final EnumFacing facing = EnumFacing.getHorizontal(meta);
	return this.getDefaultState().withProperty(Constants.BlockStateProperties.FACING_HORIZONTAL, facing);
}

@Override
public int getMetaFromState(final IBlockState state) {

	final EnumFacing facing = (EnumFacing)state.getValue(Constants.BlockStateProperties.FACING_HORIZONTAL);
	return facing.getHorizontalIndex();
}

@Override
public void onBlockDestroyedByPlayer(final World worldIn, final BlockPos pos, final IBlockState state) {

	super.onBlockDestroyedByPlayer(worldIn, pos, state);
}

@Override
public IBlockState onBlockPlaced(final World world, final BlockPos pos, final EnumFacing blockSideClicked, final float hitX, final float hitY, final float hitZ, final int meta, final EntityLivingBase placer) {

	final EnumFacing enumFacing = (placer == null) ? EnumFacing.NORTH : EnumFacing.fromAngle(placer.rotationYaw);
	return this.getDefaultState().withProperty(Constants.BlockStateProperties.FACING_HORIZONTAL, enumFacing);
}

@Override
public TileEntity createNewTileEntity(final World world, final int meta) {

	return new TileEntityConveyor();
}
}

 

 

TileEntityConveyor.java

 

public class TileEntityConveyor extends TileEntity implements IUpdatePlayerListBox, IConveyorReceiver {

private static final String NBT_ITEM_ON_CONVEYOR = "itemOnConveyor";
private static final String NBT_HOLD_TIME = "holdTime";
private static final int MAX_HOLD_TIME = 16;

private ItemStack itemOnConveyor;
private int holdTime;

public TileEntityConveyor() {

	itemOnConveyor = null;
	holdTime = 0;
}

@Override
public Packet getDescriptionPacket() {

	final NBTTagCompound compound = new NBTTagCompound();
	writeToNBT(compound);
	return new S35PacketUpdateTileEntity(pos, getBlockMetadata(), compound);
}

@Override
public void onDataPacket(final NetworkManager networkManager, final S35PacketUpdateTileEntity packet) {

	readFromNBT(packet.getNbtCompound());
}

@Override
public void writeToNBT(final NBTTagCompound compound) {

	super.writeToNBT(compound);

	if (itemOnConveyor != null) {
		final NBTTagCompound itemOnConveyorNBT = new NBTTagCompound();
		itemOnConveyor.writeToNBT(itemOnConveyorNBT);
		compound.setTag(NBT_ITEM_ON_CONVEYOR, itemOnConveyorNBT);
	}

	compound.setInteger(NBT_HOLD_TIME, holdTime);
}

@Override
public void readFromNBT(final NBTTagCompound compound) {

	super.readFromNBT(compound);

	itemOnConveyor = null;
	if (compound.hasKey(NBT_ITEM_ON_CONVEYOR)) {
		final NBTTagCompound itemOnConveyorNBT = compound.getCompoundTag(NBT_ITEM_ON_CONVEYOR);
		itemOnConveyor = ItemStack.loadItemStackFromNBT(itemOnConveyorNBT);
	}

	holdTime = 0;
	if (compound.hasKey(NBT_HOLD_TIME)) {
		holdTime = compound.getInteger(NBT_HOLD_TIME);
	}
}

@Override
public void update() {

	if (!hasWorldObj() || getWorld().isRemote) {
		return;
	}

	if (holdTime > 0) {
		holdTime--;
		markDirty();
		getWorld().markBlockForUpdate(pos);
		return;
	}

	final IConveyorReceiver destination = getItemDestination();
	if (itemOnConveyor == null) {
		// TODO: Grab item sitting on top of conveyor
	} else if (destination == null) {
		ejectItem();
	} else if (destination.readyToReceiveItem(pos)) {
		giveItemToReceiver(destination);
	}

	if (itemOnConveyor == null) { // TODO: This whole 'if' block is temporary and for testing until loaders are implemented
		final World world = getWorld();
		final EnumFacing sourceDir = getFacing().getOpposite();
		final Block potentialSource = world.getBlockState(pos.offset(sourceDir)).getBlock();

		if (potentialSource == Blocks.bedrock) {
			itemOnConveyor = new ItemStack(Blocks.coal_ore);
			holdTime = MAX_HOLD_TIME;
			markDirty();
			getWorld().markBlockForUpdate(pos);
		}
	}
}

public int getMaxHoldTime() {

	return MAX_HOLD_TIME;
}

public int getHoldTime() {

	return holdTime;
}

private void ejectItem() {

	final Vec3i facing = getFacing().getDirectionVec();
	final double spawnX = pos.getX() + 0.5 + (facing.getX() * 0.7);
	final double spawnY = pos.getY() + (5.0 / 16);
	final double spawnZ = pos.getZ() + 0.5 + (facing.getZ() * 0.7);
	final EntityItem item = new EntityItem(getWorld(), spawnX, spawnY, spawnZ, itemOnConveyor);
	final double velocity = 0.1;
	item.motionX = facing.getX() * velocity;
	item.motionY = facing.getY() * velocity;
	item.motionZ = facing.getZ() * velocity;
	getWorld().spawnEntityInWorld(item);
	clearItem();
}

private void giveItemToReceiver(final IConveyorReceiver receiver) {

	receiver.giveItem(itemOnConveyor);
	clearItem();
}

private void clearItem() {

	itemOnConveyor = null;
	holdTime = 0;
	markDirty();
	getWorld().markBlockForUpdate(pos);
}

public EnumFacing getFacing() {

	return (EnumFacing)getWorld().getBlockState(pos).getValue(Constants.BlockStateProperties.FACING_HORIZONTAL);
}

public ItemStack getItemOnConveyor() {

	return itemOnConveyor;
}

private IConveyorReceiver getItemDestination() {

	final World world = getWorld();
	final TileEntity potentialDestination = world.getTileEntity(pos.offset(getFacing()));
	return potentialDestination instanceof IConveyorReceiver ? (IConveyorReceiver)potentialDestination : null; // TODO: Can we do this logic without returning null?
}

@Override
public boolean readyToReceiveItem(final BlockPos sourceDir) {

	return itemOnConveyor == null && !pos.offset(getFacing()).equals(sourceDir);
}

@Override
public void giveItem(final ItemStack itemStack) {

	itemOnConveyor = itemStack;
	holdTime = MAX_HOLD_TIME;
	markDirty();
	getWorld().markBlockForUpdate(pos);
}
}

 

 

TileEntityConveyorRenderer.java

 

public class TileEntityConveyorRenderer extends TileEntitySpecialRenderer {

private static final ResourceLocation beltTexture = new ResourceLocation("manufactory:textures/blocks/conveyor_belt.png");

private final RenderEntityItem itemRender;

public TileEntityConveyorRenderer() {

	itemRender = new RenderEntityItem(Minecraft.getMinecraft().getRenderManager(), Minecraft.getMinecraft().getRenderItem()) {

		@Override
		public boolean shouldBob() {

			return false;
		}
	};
}

@Override
public void renderTileEntityAt(final TileEntity entity, final double x, final double y, final double z, final float partialTicks, final int destroyStage) {

	if (!(entity instanceof TileEntityConveyor)) {
		return;
	}

	final TileEntityConveyor tileEntity = (TileEntityConveyor)entity;
	final ItemStack itemOnConveyor = tileEntity.getItemOnConveyor();

	GlStateManager.pushMatrix();
	GlStateManager.translate(x, y, z);
	drawConveyorBelt(tileEntity);
	if (itemOnConveyor != null) {
		drawItemOnConveyor(tileEntity, itemOnConveyor);
	}
	GlStateManager.popMatrix();
}

private void drawItemOnConveyor(final TileEntityConveyor tileEntity, final ItemStack itemOnConveyor) {

	GlStateManager.pushMatrix();
	final EntityItem customItem = new EntityItem(tileEntity.getWorld());
	customItem.hoverStart = 0.0f;
	customItem.setEntityItemStack(itemOnConveyor);
	final Vec3i facing = tileEntity.getFacing().getDirectionVec();
	final float progressOffset = getProgressOffset(tileEntity.getHoldTime(), tileEntity.getMaxHoldTime());

	GlStateManager.translate(0.5f, 0.2f, 0.5f);
	GlStateManager.translate(facing.getX() * progressOffset, 0, facing.getZ() * progressOffset);
	itemRender.doRender(customItem, 0, 0, 0, 0, 0);
	GlStateManager.scale(0.7f, 0.7f, 0.7f);
	GlStateManager.popMatrix();
}

private void drawConveyorBelt(final TileEntityConveyor tileEntity) {


	final Tessellator tessellator = Tessellator.getInstance();
	final WorldRenderer worldrenderer = tessellator.getWorldRenderer();

	final float beltOffset = (tileEntity.getMaxHoldTime() - tileEntity.getHoldTime()) / (float)tileEntity.getMaxHoldTime();

	this.bindTexture(beltTexture);

	GlStateManager.pushMatrix();
	GlStateManager.disableLighting();
	GlStateManager.disableBlend();
	GlStateManager.enableDepth();

	GlStateManager.translate(0.5f, 0.0f, 0.5f);
	worldrenderer.startDrawingQuads();
	addBeltVertices(worldrenderer, beltOffset);

	final float angle;
	switch(tileEntity.getFacing()) {
		case WEST:
			angle = 90;
			break;
		case SOUTH:
			angle = 180;
			break;
		case EAST:
			angle = 270;
			break;
		default:
			angle = 0;
			break;
	}

	GlStateManager.rotate(angle, 0, 1, 0);


	tessellator.draw();
	GlStateManager.popMatrix();
}

private void addBeltVertices(final WorldRenderer worldrenderer, final float beltOffset) {

	final double[][] vertexTable = { { -7.0 / 16, 5.001 / 16, -8.0 / 16, 0.0, 0.0 + beltOffset },          //1
	                                 { -7.0 / 16, 5.001 / 16, 8.0 / 16, 0.0, 1.0 + beltOffset },
	                                 { 7.0 / 16, 5.001 / 16, 8.0 / 16, 1.0, 1.0 + beltOffset },
	                                 { 7.0 / 16, 5.001 / 16, -8.0 / 16, 1.0, 0.0 + beltOffset },
	                                 { -7.0 / 16, 5.0 / 16, 8.001 / 16, 0.0, 0.0 + beltOffset },          //2
	                                 { -7.0 / 16, 0.0 / 16, 8.001 / 16, 0.0, 0.5 + beltOffset },
	                                 { 7.0 / 16, 0.0 / 16, 8.001 / 16, 1.0, 0.5 + beltOffset },
	                                 { 7.0 / 16, 5.0 / 16, 8.001 / 16, 1.0, 0.0 + beltOffset },
	                                 { -7.0 / 16, 5.0 / 16, -8.001 / 16, 0.0, 0.0 - beltOffset },          //3
	                                 { 7.0 / 16, 5.0 / 16, -8.001 / 16, 1.0, 0.0 - beltOffset },
	                                 { 7.0 / 16, 0.0 / 16, -8.001 / 16, 1.0, 0.5 - beltOffset },
	                                 { -7.0 / 16, 0.0 / 16, -8.001 / 16, 0.0, 0.5 - beltOffset } };

	for (final double[] vertex : vertexTable) {
		worldrenderer.addVertexWithUV(vertex[0], vertex[1], vertex[2], vertex[3], vertex[4]);
	}
}

private float getProgressOffset(final int currentHoldTime, final int maxHoldTime) {

	final float progress = (maxHoldTime - currentHoldTime) / (float)maxHoldTime;
	return progress - 0.5f;
}
}

 

 

The crash occurs in TileEntityConveyorRenderer, in both the drawConveyorBelt and drawItemOnConveyor methods. When tileEntity.getFacing() is called, the following line in TileEntityConveyor.getFacing fails:

 

return (EnumFacing)getWorld().getBlockState(pos).getValue(Constants.BlockStateProperties.FACING_HORIZONTAL);

 

because the requested property is not on the BlockState at that position. I can only assume this occurs because the block has already been destroyed but the tile entity (and the accompanying renderer) have not yet. What is the best way to deal with this? Should I put a check at the beginning of the render method to ensure the block is still there and is the right type? Or is there a way to ensure the tile entity (and renderer) gets destroyed before the block does?

 

Thanks in advance!

Link to comment
Share on other sites

Why don't you just store the facing value in the tile entity itself? By default Minecraft worlds delete tile entities the tick AFTER they were flagged to be removed. That means you can safely get data from the tileEntity for rendering before it gets removed. Just make sure to add a check in your rendering code like if(tileEntity!=null&&!tileEntity.tileEntityInvalid){} to make sure you dont render it after the tick afterwards.

"you seem to be THE best modder I've seen imo."

~spynathan

 

ლ(́◉◞౪◟◉‵ლ

Link to comment
Share on other sites

Why don't you just store the facing value in the tile entity itself? By default Minecraft worlds delete tile entities the tick AFTER they were flagged to be removed. That means you can safely get data from the tileEntity for rendering before it gets removed. Just make sure to add a check in your rendering code like if(tileEntity!=null&&!tileEntity.tileEntityInvalid){} to make sure you dont render it after the tick afterwards.

 

That... is a good idea that didn't occur to me. :)

It seems unfortunate that I need to store that value in the NBT data since while the block is there I can just look it up and when the block is gone it's not useful anymore, but it's a much nicer solution than having the renderer check the block state directly.

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.