Jump to content
View in the app

A better way to browse. Learn more.

Forge Forums

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

Featured Replies

Posted

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

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

  • Author
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

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.

  • Author

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

  • Author

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

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...

Important Information

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

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.