Jump to content

[1.16.4] [SOLVED] Problems ticking client side method on TileEntity


StealthyNoodle

Recommended Posts

Hello. I've been trying for some time now, to add a particle effect to my custom Dynamite TileEntity, identical to that of a TNTEntity, but can't seem to make the particle-effect appear. I reckon this have to do with the TileEntity's isActive-boolean being called on server, rather than client, resulting in the conditions never being checked on client-side.  I'm unsure what the proper way of implementing this would be though, and I can't find a similar example of conditional ticking elsewhere in vanilla. I had a look at the Block method animateTick, but that one only seems to be called periodically, thus not producing the wanted results.

DynamiteTileEntity:

	@Override
	public void tick()
	{

		if(isActive) {
			if(this.fuse <= 0) {
				this.remove();
				if (!this.world.isRemote) {
					execute();
				}	
			}
			else {
			
				this.fuse--;
				
				if (this.world.isRemote) {
					this.world.addParticle(ParticleTypes.SMOKE, this.getPos().getX() + 0.5D, this.getPos().getY() + 0.5D, this.getPos().getZ() + 0.5D, 0.0D, 0.0D, 0.0D);
				}
			}
		}
		
	}


DynamiteBlock:

	public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand handIn, BlockRayTraceResult hit)
	{
		if (world.isRemote) {
	         return ActionResultType.CONSUME;
	     }
		else {
	    	  TileEntity tileentity = world.getTileEntity(pos);
	    	  if (tileentity instanceof DynamiteTileEntity) {
	    		  DynamiteTileEntity dynamiteTileEntity = (DynamiteTileEntity)tileentity;
	    		  if(dynamiteTileEntity.isActive) {
	    			  this.isActive = false;
	    			  dynamiteTileEntity.deactivate();
	    			  CodingPanda.LOGGER.info("DISENGAGED");
	    			  return ActionResultType.SUCCESS;
	    		  }
	    		  else {
	    			  dynamiteTileEntity.activate();
	    			  this.isActive = true;
	    			  CodingPanda.LOGGER.info("ACTIVATED");
	    			  world.playSound((PlayerEntity)null, pos.getX(), pos.getY(), pos.getZ(), SoundEvents.ENTITY_TNT_PRIMED, SoundCategory.BLOCKS, 1.0F, 1.0F);
	    			  return ActionResultType.SUCCESS;
	    		  }
	    	  }
	    	  
	    	  return ActionResultType.CONSUME;
	     }
	}

 

Edited by StealthyNoodle
Link to comment
Share on other sites

Block.onBlockActivate:

if world.isRemote: do nothing

else: revert DynamiteTileEntity.active

 

DynamiteTileEntity.tick:

if world.isRemote & active: spawn particles

 

DynamiteTileEntity.active on client side is always false as it's never updated. Although it can be caused by something else but I can't access your full code so this is just my guess.

You need to find a way to sync the value, using some default syncing methods in TileEntity/IForgeTileEntity e.g.

getUpdatePacket and onDataPacket. There are also quite a lot of posts about this, you should have a look at them.

 

Edited by poopoodice
  • Thanks 1
Link to comment
Share on other sites

On 12/29/2020 at 10:58 PM, poopoodice said:

You need to find a way to sync the value, using some default syncing methods in TileEntity/IForgeTileEntity e.g.

getUpdatePacket and onDataPacket. There are also quite a lot of posts about this, you should have a look at them.


Thanks for your quick response! This should probably point me in the right direction - not sure if I grasp exactly how they're used yet, but will see if I can't figure it out. Here's the full code of both classes, should that be more insightful:

DynamiteTileEntity:

package com.snoodle.codingpanda.tileentities;

import com.snoodle.codingpanda.CodingPanda;
import com.snoodle.codingpanda.setup.ModTileEntityTypes;

import net.minecraft.block.BlockState;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.world.Explosion;

public class DynamiteTileEntity extends TileEntity implements ITickableTileEntity{

	private final int FUSE = 160;
	private int fuse;
	public boolean isActive;
	
	public DynamiteTileEntity(final TileEntityType<?> tileEntityTypeIn) {
		super(tileEntityTypeIn);
	}
	
	public DynamiteTileEntity() {
		this(ModTileEntityTypes.DYNAMITE.get());
	}

	@Override
	public void tick()
	{

		if(isActive) {
			if(this.fuse <= 0) {
				this.remove();
				if (!this.world.isRemote) {
					execute();
				}	
			}
			else {
			
				this.fuse--;
				
				if (this.world.isRemote) {
					this.world.addParticle(ParticleTypes.SMOKE, this.getPos().getX() + 0.5D, this.getPos().getY() + 0.5D, this.getPos().getZ() + 0.5D, 0.0D, 0.0D, 0.0D);
				}
			}
		}
		
	}

	private void execute() {
		CodingPanda.LOGGER.info("TICK!");
	    this.world.createExplosion(null, this.getPos().getX() + 0.5D, this.getPos().getY() + 0.5D, this.getPos().getZ() + 0.5D, 6.0F, Explosion.Mode.BREAK);
	}
	
	public void activate() {
		isActive = true;
		fuse = FUSE;
	}
	
	public void deactivate() {
		isActive = false;
	}
	
	@Override
	public void read(BlockState state, CompoundNBT nbt) {
		super.read(state, nbt);
		this.fuse = nbt.getInt("fuse");
		this.isActive = nbt.getBoolean("isActive");
		
	}

	@Override
	public CompoundNBT write(CompoundNBT compound) {
		compound.putInt("fuse", this.fuse);
		compound.putBoolean("isActive", this.isActive);
		return super.write(compound);
	}
}



DynamiteBlock:

package com.snoodle.codingpanda.blocks;


import java.util.Random;

import com.snoodle.codingpanda.CodingPanda;
import com.snoodle.codingpanda.setup.ModTileEntityTypes;
import com.snoodle.codingpanda.tileentities.DynamiteTileEntity;

import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Hand;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvents;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;

public class DynamiteBlock extends Block{

	private boolean isActive;
	
	public DynamiteBlock(Properties properties) {
		super(properties);
	}
	
	public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand handIn, BlockRayTraceResult hit)
	{
		if (world.isRemote) {
	         return ActionResultType.CONSUME;
	     }
		else {
	    	  TileEntity tileentity = world.getTileEntity(pos);
	    	  if (tileentity instanceof DynamiteTileEntity) {
	    		  DynamiteTileEntity dynamiteTileEntity = (DynamiteTileEntity)tileentity;
	    		  if(dynamiteTileEntity.isActive) {
	    			  this.isActive = false;
	    			  dynamiteTileEntity.deactivate();
	    			  CodingPanda.LOGGER.info("DISENGAGED");
	    			  return ActionResultType.SUCCESS;
	    		  }
	    		  else {
	    			  dynamiteTileEntity.activate();
	    			  this.isActive = true;
	    			  CodingPanda.LOGGER.info("ACTIVATED");
	    			  world.playSound((PlayerEntity)null, pos.getX(), pos.getY(), pos.getZ(), SoundEvents.ENTITY_TNT_PRIMED, SoundCategory.BLOCKS, 1.0F, 1.0F);
	    			  return ActionResultType.SUCCESS;
	    		  }
	    	  }
	    	  
	    	  return ActionResultType.CONSUME;
	     }
	}
	
	@Override
	public boolean hasTileEntity(BlockState state) {
		return true;
	}
	
	@Override
	public TileEntity createTileEntity(BlockState state, IBlockReader world)
    {
		return ModTileEntityTypes.DYNAMITE.get().create();
    }
}

Edited by StealthyNoodle
Put code into spoilers
Link to comment
Share on other sites

If I understand it correctly getUpdateTag provides the information that needs to be synced (check its doc), and getUpdatePacket provides the packet that going to be send to the client, I think it's fine to put any number for the tileEntityTypeIn (the second parameter of SUpdateTileEntityPacket), since it's hardcoded to check if the tileentity and the number matches.

And then onDataPacket is called when the client has received the packet, it provides the data send from the server which you gather the information you need for client from it.

  • Like 1
Link to comment
Share on other sites

Thanks. I did some reading up on the docs in regards to syncing, but I'm worrying that my formatting here is still off (even though it seems to pick up the changes I do). The logger triggers whenever I call notifyBlockUpdate on the DynamiteBlock class, yet the isActive-bool still doesn't appear to change on client side.

I've been looking at several cases, but few examples handling custom data through getUpdatePacket and onDataPacket. Here's what I've tried out so far (even though it still isn't functioning correctly)

DynamiteBlock:

 

package com.snoodle.codingpanda.blocks;

import com.snoodle.codingpanda.CodingPanda;
import com.snoodle.codingpanda.setup.ModTileEntityTypes;
import com.snoodle.codingpanda.tileentities.DynamiteTileEntity;

import net.minecraft.block.AbstractBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Hand;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvents;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;

public class DynamiteBlock extends Block{
	
	public DynamiteBlock(AbstractBlock.Properties properties) {
		super(properties);
	}
	
	public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand handIn, BlockRayTraceResult hit)
	{
		if (world.isRemote) {
	         return ActionResultType.SUCCESS;
	     }
		else {
	    	  TileEntity tileentity = world.getTileEntity(pos);
	    	  if (tileentity instanceof DynamiteTileEntity) {
	    		  DynamiteTileEntity dynamiteTileEntity = (DynamiteTileEntity)tileentity;
	    		  if(dynamiteTileEntity.isActive) {
	    			  dynamiteTileEntity.deactivate();
	    			  CodingPanda.LOGGER.info("DEACTIVATED");
	    		  }
	    		  else {
	    			  dynamiteTileEntity.activate();
	    			  CodingPanda.LOGGER.info("ACTIVATED");
	    			  world.playSound((PlayerEntity)null, pos.getX(), pos.getY(), pos.getZ(), SoundEvents.ENTITY_TNT_PRIMED, SoundCategory.BLOCKS, 1.0F, 1.0F);
	    		  }
	    		  
	    		  world.notifyBlockUpdate(pos, state, state, 2);
	    		  return ActionResultType.CONSUME;
	    	  }
	    	  
	    	  return ActionResultType.CONSUME;
	     }
	}
	
	@Override
	public boolean hasTileEntity(BlockState state) {
		return true;
	}
	
	@Override
	public TileEntity createTileEntity(BlockState state, IBlockReader world)
    {
		return ModTileEntityTypes.DYNAMITE.get().create();
    }
	
}



DynamiteTileEntity:

 

package com.snoodle.codingpanda.tileentities;

import com.snoodle.codingpanda.CodingPanda;
import com.snoodle.codingpanda.setup.ModTileEntityTypes;

import net.minecraft.block.BlockState;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.play.server.SUpdateTileEntityPacket;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.world.Explosion;

public class DynamiteTileEntity extends TileEntity implements ITickableTileEntity{

	private final int FUSE = 160;
	private int fuse;
	public boolean isActive;
	
	public DynamiteTileEntity(final TileEntityType<?> tileEntityTypeIn) {
		super(tileEntityTypeIn);
	}
	
	public DynamiteTileEntity() {
		this(ModTileEntityTypes.DYNAMITE.get());
	}

	@Override
	public void tick()
	{

		if(isActive) {
			if(this.fuse <= 0) {
				this.remove();
				if (!this.world.isRemote) {
					execute();
				}	
			}
			else {
			
				this.fuse--;
				
				if (this.world.isRemote) {
					this.world.addParticle(ParticleTypes.SMOKE, this.getPos().getX() + 0.5D, this.getPos().getY() + 0.5D, this.getPos().getZ() + 0.5D, 0.0D, 0.0D, 0.0D);
					CodingPanda.LOGGER.info("I'm getting called");
				}
			}
		}
		
	}

	private void execute() {
		CodingPanda.LOGGER.info("TICK!");
	    this.world.createExplosion(null, this.getPos().getX() + 0.5D, this.getPos().getY() + 0.5D, this.getPos().getZ() + 0.5D, 6.0F, Explosion.Mode.BREAK);
	}
	
	public void activate() {
		isActive = true;
		fuse = FUSE;
	}
	
	public void deactivate() {
		isActive = false;
	}
	
	@Override
	public SUpdateTileEntityPacket getUpdatePacket(){
	    CompoundNBT compound = new CompoundNBT();
	    compound.putBoolean("isActive", this.isActive);
	    CodingPanda.LOGGER.info("wrote isActive to " + this.isActive);
	    
	    return new SUpdateTileEntityPacket(getPos(), -1, compound);
	}

	@Override
	public void onDataPacket(NetworkManager net, SUpdateTileEntityPacket pkt){
	    CompoundNBT tag = pkt.getNbtCompound();
    	if(this.world.isRemote) {
    		this.isActive = tag.getBoolean("isActive");
    		CodingPanda.LOGGER.info("loaded isActive is " + this.isActive);
    	}
	}
	
	@Override
	public void read(BlockState state, CompoundNBT nbt) {
		super.read(state, nbt);
		this.fuse = nbt.getInt("fuse");
		this.isActive = nbt.getBoolean("isActive");
		
	}

	@Override
	public CompoundNBT write(CompoundNBT compound) {
		compound.putInt("fuse", this.fuse);
		compound.putBoolean("isActive", this.isActive);
		return super.write(compound);
	}
}
Edited by StealthyNoodle
Link to comment
Share on other sites

Wooh, got it!

smokey_dynamite.gif.75e7c79fc1b1ee018739886744df928b.gif

I continued reading about getUpdatePacket and onDataPacket, and stumbled over @diesieben07's post on the matter:

I was certain I had to handle all the data through getUpdateTag and onDataPacket, but seems like the data first has to be sent through getUpdateTag (which is then processed by handleUpdateTag). I still still struggle with wrapping my head around exactly how it works, but I think I'm starting to grasp the concept.

Here's the latest, working version. Some of the code has been optimized and moved around since last time.

DynamiteBlock:

 

package com.snoodle.codingpanda.blocks;

import com.snoodle.codingpanda.CodingPanda;
import com.snoodle.codingpanda.setup.ModTileEntityTypes;
import com.snoodle.codingpanda.tileentities.DynamiteTileEntity;

import net.minecraft.block.AbstractBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;

public class DynamiteBlock extends Block{
	
	public DynamiteBlock(AbstractBlock.Properties properties) {
		super(properties);
	}
	
	public ActionResultType onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand handIn, BlockRayTraceResult hit)
	{
		if (world.isRemote) {
			world.notifyBlockUpdate(pos, state, state, 2);
			return ActionResultType.SUCCESS;
	     }
		else {
	    	  TileEntity tileentity = world.getTileEntity(pos);
	    	  if (tileentity instanceof DynamiteTileEntity) {
	    		  DynamiteTileEntity dynamiteTileEntity = (DynamiteTileEntity)tileentity;
	    		 
    			  dynamiteTileEntity.toggle();
    			  CodingPanda.LOGGER.info("ACTIVATED");
    			  world.notifyBlockUpdate(pos, state, state, 2);
	    		  return ActionResultType.CONSUME;
	    	  }
	    	  
	    	  return ActionResultType.CONSUME;
	     }
	}
	
	@Override
	public boolean hasTileEntity(BlockState state) {
		return true;
	}
	
	@Override
	public TileEntity createTileEntity(BlockState state, IBlockReader world)
    {
		return ModTileEntityTypes.DYNAMITE.get().create();
    }
	
}



DynamiteTileEntity:

 

package com.snoodle.codingpanda.tileentities;

import com.snoodle.codingpanda.CodingPanda;
import com.snoodle.codingpanda.setup.ModTileEntityTypes;

import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.play.server.SUpdateTileEntityPacket;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvents;
import net.minecraft.world.Explosion;

public class DynamiteTileEntity extends TileEntity implements ITickableTileEntity{

	private final int FUSE = 160;
	private int fuse;
	private boolean isActive;
	
	public DynamiteTileEntity() {
		super(ModTileEntityTypes.DYNAMITE.get());
	}

	@Override
	public void tick()
	{
		
		if(isActive) {
			if(this.fuse <= 0) {
				
				this.remove();
				
				if (!this.world.isRemote) {
					execute();
				}	
			}
			else
			{				
				this.fuse--;
				
				if (this.world.isRemote) {
					this.world.addParticle(ParticleTypes.SMOKE, this.getPos().getX() + 0.5D, this.getPos().getY() + 0.5D, this.getPos().getZ() + 0.5D, 0.0D, 0.0D, 0.0D);
					CodingPanda.LOGGER.info("I'm getting called");
				}
			}
		}
		
		if (this.world.isRemote) {
			CodingPanda.LOGGER.info("isActive is " + isActive);
		}
	}

	private void execute() {
		CodingPanda.LOGGER.info("TICK!");
	    this.world.createExplosion(null, this.getPos().getX() + 0.5D, this.getPos().getY() + 0.5D, this.getPos().getZ() + 0.5D, 6.0F, Explosion.Mode.BREAK);
	}
	
	public void toggle() {

		if(!isActive) {
			world.playSound((PlayerEntity)null, pos.getX(), pos.getY(), pos.getZ(), SoundEvents.ENTITY_TNT_PRIMED, SoundCategory.BLOCKS, 1.0F, 1.0F);
			isActive = true;
			fuse = FUSE;
		}
		else {
			isActive = false;
		}
		
		this.markDirty();
	}
	
	@Override
	public CompoundNBT getUpdateTag() {
		CompoundNBT nbtTag = new CompoundNBT();
		nbtTag.putBoolean("isActive", this.isActive);
		write(nbtTag);
		return nbtTag;
	}
	
	@Override
	public void handleUpdateTag(BlockState blockState, CompoundNBT parentNBTTagCompound)
	{
		this.read(blockState,  parentNBTTagCompound);
	}
	
	@Override
	public SUpdateTileEntityPacket getUpdatePacket(){
	    CodingPanda.LOGGER.info("wrote isActive to " + this.isActive);
	    
	    return new SUpdateTileEntityPacket(getPos(), -1, getUpdateTag());
	}

	@Override
	public void onDataPacket(NetworkManager net, SUpdateTileEntityPacket pkt){
	    CompoundNBT tag = pkt.getNbtCompound();
		this.isActive = tag.getBoolean("isActive");
		this.read(this.getBlockState(), pkt.getNbtCompound());
		CodingPanda.LOGGER.info("loaded isActive is " + this.isActive);
	}
	
	@Override
	public void read(BlockState state, CompoundNBT nbt) {
		super.read(state, nbt);
		this.fuse = nbt.getInt("fuse");
		this.isActive = nbt.getBoolean("isActive");
		
	}

	@Override
	public CompoundNBT write(CompoundNBT compound) {
		compound.putInt("fuse", this.fuse);
		compound.putBoolean("isActive", this.isActive);
		return super.write(compound);
	}
}

 

 

Thanks a lot for helping out, poopoodice!

Edited by StealthyNoodle
  • Thanks 1
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.