Jump to content

Different particles depending on hit type & EntityData behaviour (1.16.5)


DeValdi

Recommended Posts

Hi there!

 

I am currently developing my first mod and I've run into a bit of a problem regarding an item that I am trying to implement. The item is a flintlock pistol and it shoots out a projectile which inherits from ProjectileItemEntity, the same class that SnowballEntity inherits from. I was able to overwrite several things like the damage, default item, etc. on that projectile, however, I am a little bit stuck when it comes to the particle effect that should be played whenever the projectile hits something.

As far as I know, each entity exists both on the client and the server and it is mostly up to the modder to make sure that both "copies" and their relevant data are synchronized properly. Furthermore, it seems that the vanilla snowball shows it's particles on a hit by broadcasting the event from the server-side onHit method, which will then get handled by the client-side handleEntityEvent method.

Since I want my particles to either be the block particles of the block that was hit OR a blood splatter (currently just a redstone dust particle) if it hit an entity, I thought I could use the EntityData (formerly known as EntityDataManager) to pass my particle from the server to the client just before broadcasting the event and therefore before the client handles the event. However, it seems as if the new value I'm trying to set doesn't even reach the client. It just utilizes the standard value I set in defineSynchedData when adding the particles into the world.

Is it possible that the "bond" between client and server is already disrupted by the time I'm trying to transmit my new particle data, since the projectile already impacted and is therefore considered non-existent? Or is the value just simply not transmitted fast enough for the client to pick it up before handling the event? Or is there something I got completely wrong about the mechanism of projectile hitting?

Here is my code of the projectile entity so far. The default particle data is the block particle of a diamond block, which is weirdly used whenever the projectile hits something:

package com.gabvaland.tutorialmod.common.entities;

import com.gabvaland.tutorialmod.core.init.EntityTypeInit;
import com.gabvaland.tutorialmod.core.init.ItemInit;

import net.minecraft.block.Blocks;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.entity.EntityRenderer;
import net.minecraft.client.renderer.entity.EntityRendererManager;
import net.minecraft.client.renderer.entity.SpriteRenderer;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.EntityType.IFactory;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.projectile.ProjectileItemEntity;
import net.minecraft.item.Item;
import net.minecraft.network.IPacket;
import net.minecraft.network.datasync.DataParameter;
import net.minecraft.network.datasync.DataSerializers;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.particles.BlockParticleData;
import net.minecraft.particles.IParticleData;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.particles.RedstoneParticleData;
import net.minecraft.util.DamageSource;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.EntityRayTraceResult;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.client.registry.IRenderFactory;
import net.minecraftforge.fml.network.NetworkHooks;

public class IronBulletEntity extends ProjectileItemEntity {
	
	public static final DataParameter<IParticleData> PARTICLE = EntityDataManager.defineId(IronBulletEntity.class, DataSerializers.PARTICLE);
	
	public IronBulletEntity(EntityType<? extends IronBulletEntity> p_i50159_1_, World p_i50159_2_) {
		super(p_i50159_1_, p_i50159_2_);
		setNoGravity(true);
	}

	public IronBulletEntity(World p_i1774_1_, LivingEntity p_i1774_2_) {
		super(EntityTypeInit.IRON_BULLET.get(), p_i1774_2_, p_i1774_1_);
		setNoGravity(true);
	}

	public IronBulletEntity(World p_i1775_1_, double p_i1775_2_, double p_i1775_4_, double p_i1775_6_) {
		super(EntityTypeInit.IRON_BULLET.get(), p_i1775_2_, p_i1775_4_, p_i1775_6_, p_i1775_1_);
		setNoGravity(true);
	}

	protected Item getDefaultItem() {
		return ItemInit.IRON_BULLET.get();
	}

	@OnlyIn(Dist.CLIENT)
	public void handleEntityEvent(byte eventType) {
		if (eventType == 3) {
			for(int i = 0; i < 8; i++) {
				this.level.addParticle(entityData.get(PARTICLE), this.getX(), this.getY(), this.getZ(), 0.0D, 0.0D, 0.0D);
			}
		}
	}

	protected void onHitEntity(EntityRayTraceResult result) {
		super.onHitEntity(result);
		result.getEntity().hurt(DamageSource.thrown(this, this.getOwner()), 20);
	}

	protected void onHit(RayTraceResult result) {
		super.onHit(result);
		if (!this.level.isClientSide) {
			this.entityData.set(PARTICLE, result.getType().equals(RayTraceResult.Type.BLOCK)
					? new BlockParticleData(ParticleTypes.BLOCK, this.level.getBlockState(new BlockPos(result.getLocation())))
					: new RedstoneParticleData(1, 0, 0, 1)
			);
			this.level.broadcastEntityEvent(this, (byte)3);
			this.remove();
		}
	}
	
	public static class ItemFactory implements IFactory<IronBulletEntity> {
		@Override
		public IronBulletEntity create(EntityType<IronBulletEntity> entityType, World world) {
			return new IronBulletEntity(entityType, world);
		}
	}
	
	public static class RenderFactory implements IRenderFactory<IronBulletEntity> {
		@Override
		public EntityRenderer<? super IronBulletEntity> createRenderFor(EntityRendererManager manager) {
			return new SpriteRenderer<>(manager, Minecraft.getInstance().getItemRenderer());
		}
	}
	
	@Override
	public IPacket<?> getAddEntityPacket() {
		return NetworkHooks.getEntitySpawningPacket(this);
	}
	
	@Override
	public void defineSynchedData() {
		super.defineSynchedData();
		this.entityData.define(PARTICLE, new BlockParticleData(ParticleTypes.BLOCK, Blocks.DIAMOND_BLOCK.defaultBlockState()));
	}
}

 

Thank you for your time and help in advance!

 

Best regards,

DeValdi

Link to comment
Share on other sites

On 6/7/2021 at 6:10 PM, diesieben07 said:
  • Use @Override when overriding methods.
  • Do not use @OnlyIn.
  • Instead of using EntityDataManager and then broadcastEntityEvent it would probably be simpler to just send a custom packet that contains the necessary data.
  • Regardless of this, this should work fine, I would advice playing around with the debugger to see what exactly is going on (are the packets send correctly, etc.).

First of all, thank you very much for the quick response and your help!

I've played around a bit more and tried setting a random particle color each tick on the server side and then rendering that particle client-side by overriding the Projectile's tick method and differentiating via the isClientSide method. As expected, it works flawlessly and my bullet now has a nice rainbow trail 😅.

	@Override
	public void tick() {
		super.tick();
		if (this.level.isClientSide) {
			this.level.addParticle(this.entityData.get(PARTICLE), this.getX(), this.getY(), this.getZ(), 0.0D, 0.0D, 0.0D);
		} else {
			this.entityData.set(PARTICLE, new RedstoneParticleData(random.nextFloat(), random.nextFloat(), random.nextFloat(), 1));
		}
	}

This leads me to believe that Minecraft somehow disrupts or invalidates the connection between the two projectile objects once they hit something. Therefore, I can see that there's probably no way around a custom packet.

Do you know any up-to-date resources regarding custom packets? The one's I found are mostly for older versions of Minecraft and as far I could tell, a lot of method names etc. changed in the recent updates.

Appreciate the support!

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.