Jump to content

[1.12.2] [SOLVED] spawnParticle not working


dalobstah

Recommended Posts

I've been trying to get this to work for a few days without much success. Every example I find doesn't work properly and I can't figure out why.

 

public void onUpdate()
{
    --this.fuse;
    if (this.fuse <= 0)
    {
        this.setDead();

        if (!this.world.isRemote)
        {
            world.setBlockState(pos, Blocks.AIR.getDefaultState());
            world.createExplosion(null, this.posX + 0.5D, this.posY + 0.5D, this.posZ + 0.5D, 4.f, true);
        }
    }
    else
    {
        Main.proxy.spawnParticle(EnumParticleTypes.SMOKE_NORMAL, this.posX + 0.5D, this.posY + 1.0D, this.posZ + 0.5D, 0.0D, 0.0D, 0.0D);
    }
        
        
        
    if(world.getBlockState(pos).getBlock() instanceof BlockAir) 
    {
        this.setDead();
    }
}
//Client proxy
@Override
public void spawnParticle(EnumParticleTypes particleType, double xCoord, double yCoord, double zCoord, double xSpeed, double ySpeed, double zSpeed)
{
	MINECRAFT.world.spawnParticle(particleType, xCoord, yCoord, zCoord, xSpeed, ySpeed, zSpeed);
}

^ This is the only code that has worked at all, however it doesn't spawn particles in LAN or multiplayer.

 

this.world.spawnParticle(EnumParticleTypes.SMOKE_NORMAL, this.posX + 0.5D, this.posY + 1.0D, this.posZ + 0.5D, 0.0D, 0.0D, 0.0D);

When I try to use this like in EntityTNTPrimed it doesn't work at all. I also experimented with using WorldServer instead of WorldClient, but that resulted in server crashes.

Also, using else if(!this.world.isRemote) instead of just else didn't effect the code.

Edited by dalobstah
Link to comment
Share on other sites

Particles are a client-side thing. Calling the spawn particle method on the server does nothing.

You also do not need a proxy to spawn a particle. The spawning method is present on both sides, but only the client actually does something, the server has a noop implementation.

Blocks spawn particle in a special method Block#randomDisplayTick that is called on the client only.

If you need to spawn particles for every player either make sure that the data for those players stays consistent so their clients are aware of when to spawn particles or use packets.

Link to comment
Share on other sites

32 minutes ago, V0idWa1k3r said:

Particles are a client-side thing. Calling the spawn particle method on the server does nothing.

You also do not need a proxy to spawn a particle. The spawning method is present on both sides, but only the client actually does something, the server has a noop implementation.

Blocks spawn particle in a special method Block#randomDisplayTick that is called on the client only.

If you need to spawn particles for every player either make sure that the data for those players stays consistent so their clients are aware of when to spawn particles or use packets.

I'm spawning the particles on an entity. Nothing I've tried outside of the client proxy has spawned particles. EntityTNTPrimed spawns particles for every player with just this.world.spawnParticle, however that doesn't work at all for me. How would I go about getting the clients aware of particles or using packets?

Link to comment
Share on other sites

4 hours ago, V0idWa1k3r said:

You either need the data to be consistent for the client with DataParameter variables or use packets.

Do you know why this works

Minecraft MINECRAFT = Minecraft.getMinecraft();
MINECRAFT.world.spawnParticle(EnumParticleTypes.SMOKE_NORMAL, this.posX + 0.5D, this.posY + 1.0D, this.posZ + 0.5D, 0.0D, 0.0D, 0.0D);

but this doesn't

this.world.spawnParticle(EnumParticleTypes.SMOKE_NORMAL, this.posX + 0.5D, this.posY + 1.0D, this.posZ + 0.5D, 0.0D, 0.0D, 0.0D);

It works in vanilla code and in other 1.12.2 mods source code I've looked at, but it doesn't work in my code. Is there a reason?

Link to comment
Share on other sites

You might be calling the second method on the server and so nothing happens. The first implementation ensures that the particles are spawned on the client. However it is also the case that you might be reaching across logical sides there. Basically I think that whenever you are calling these methods the actions actually happen on a server, not on the client. However because you are running the physical client and not the server the proxy being injected is the client proxy. And in that proxy you are explicitly using the client world for spawning your particles. This way the particles work since you are spawning them through a client world and you see them because it is the physical client but what is actually happening is you are reaching for the client from a server thread - and that is called reaching across logical sides and must never be done.

And the reason the second method isn't called on the client is likely because some kind of condition isn't satisfied on the client but is satisfied on the server. For example vanilla uses data parameters to sync the fuse timer of the TNT to the client so it is always aware of the fuse and is able to do client-related stuff according to it, like the flashing animation. You need to satisfy that condition on the client by using a DataParameter or packets, as I've told you twice already.

  • Thanks 1
Link to comment
Share on other sites

Thank you for the help. I got it to work with packets. I'll put the code below for any future googlers

@Override
public void onUpdate()
{
	setFuse(--this.fuse);
		
    if (getFuse() <= 0)
    {
        this.setDead();

        if (!this.world.isRemote)
        {
            	
            world.setBlockState(pos, Blocks.AIR.getDefaultState());
            world.createExplosion(null, this.posX + 0.5D, this.posY + 0.5D, this.posZ + 0.5D, 4.f, true);
        }
    }
    else
    {
        ParticlePacket particlePacket = new ParticlePacket(this.posX, this.posY, this.posZ);
        	
        NetworkRegistry.TargetPoint target = new TargetPoint(world.provider.getDimension(), this.posX + 0.5D, this.posY + 1.0D, this.posZ + 0.5D, 20.d);
        CommonProxy.simpleNetworkWrapper.sendToAllAround(particlePacket, target);
    }
        
        
        
    if(world.getBlockState(pos).getBlock() instanceof BlockAir) 
    {
        this.setDead();
    }
}

 

//Message and client handler
public class ParticlePacket implements IMessage
{

	private boolean messageValid;
	
	private double x, y, z;
	
	public ParticlePacket()
	{
		this.messageValid = false;
	}
	
	public ParticlePacket(double x, double y, double z)
	{
		this.x = x;
		this.y = y;
		this.z = z;
		
		this.messageValid = true;
	}
	
	@Override
	public void fromBytes(ByteBuf buf)
	{
		try
		{
			this.x = buf.readDouble();
			this.y = buf.readDouble();
			this.z = buf.readDouble();
		}
		catch(IndexOutOfBoundsException ioe)
		{
			return;
		}
	}

	@Override
	public void toBytes(ByteBuf buf)
	{
		if(!this.messageValid)
			return;
		
		buf.writeDouble(x);
		buf.writeDouble(y);
		buf.writeDouble(z);
	}
	
	
	public static class Handler implements IMessageHandler<ParticlePacket, IMessage>
	{

		@Override
		public IMessage onMessage(ParticlePacket message, MessageContext ctx)
		{
			if(!message.messageValid && ctx.side != Side.CLIENT)
			{
				return null;
			}
			
			
			Minecraft minecraft = Minecraft.getMinecraft();
		    final WorldClient worldClient = minecraft.world;
			
		    minecraft.addScheduledTask(() -> processMessage(message, worldClient));
		    
			return null;
		}
		
		void processMessage(ParticlePacket message, WorldClient worldClient)
		{
			worldClient.spawnParticle(EnumParticleTypes.SMOKE_NORMAL, message.x + 0.5D, message.y + 1.0D, message.z + 0.5D, 0.0D, 0.0D, 0.0D);
		}
		
	}
}

 

//ClientProxy
public static void preInitClientOnly()
{
	CommonProxy.simpleNetworkWrapper.registerMessage(ParticlePacket.Handler.class, ParticlePacket.class, CommonProxy.FUSE_SMOKE, Side.CLIENT);
}

//CommonProxy
public static final byte FUSE_SMOKE = 88;
public static SimpleNetworkWrapper simpleNetworkWrapper;
	
public static void preInitCommon()
{
	simpleNetworkWrapper = NetworkRegistry.INSTANCE.newSimpleChannel("TestChannel");
	simpleNetworkWrapper.registerMessage(ServerHandlerDummy.class, ParticlePacket.class, FUSE_SMOKE, Side.SERVER);
}

 

//Server handler
public class ServerHandlerDummy implements IMessageHandler<ParticlePacket, IMessage>
{

	@Override
	public IMessage onMessage(ParticlePacket message, MessageContext ctx)
	{
		return null;
	}

}

 

  • Like 1
  • 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.

Announcements



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • My name is SARAH GOLDFISH, and I'm the CEO of a once-thriving tech startup. Today, I want to share a deeply personal story about how Tropical Delight Recovery saved my business and, quite possibly, my life. In the summer of 2023, my company was at its peak. We had just secured a major partnership, our products were flying off the shelves, and our future looked brighter than ever. However, in the midst of this success, I made a decision that nearly led to our downfall.  Like many others, I was drawn to the world of cryptocurrency. The promise of quick and significant returns was enticing, and I saw it as an opportunity to further bolster our financial standing. After some research, I invested a substantial amount of the company’s funds into a new and seemingly promising cryptocurrency. At first, it seemed like a great move. The value of our investment soared, and I was hailed as a visionary. But as quickly as our fortunes rose, they plummeted. The cryptocurrency market is notoriously volatile, and within weeks, the value of our investment tanked. What I had failed to see was that the currency was part of a sophisticated scam. The entire platform vanished overnight, and with it, our hard-earned money. The impact was immediate and devastating. Our financial cushion was obliterated, and we found ourselves unable to meet payroll or fulfill orders. Our partners began to lose faith, and the media caught wind of our predicament, amplifying our troubles. I was overwhelmed with guilt and fear. I had not only jeopardized the company but also the livelihoods of my employees. Despair set in, and I couldn't see a way out. In my darkest hour, a friend recommended Tropical Delight Recovery. Skeptical and with little hope, I reached out to them. From the first contact, their team was professional, understanding, and, most importantly, optimistic about our chances of recovery. They assured me that they had dealt with similar cases and had a strong track record of retrieving lost funds from fraudulent schemes. Tropical Delight Recovery took swift action. They meticulously traced the fraudulent transactions and employed their extensive network and legal expertise to go after the perpetrators. Their process was transparent, and they kept me informed at every step. It was a complex and arduous journey, but their determination never wavered. Weeks turned into months, and just when I was starting to lose hope, Tropical Delight Recovery delivered a miracle. They managed to recover a significant portion of our lost funds. The relief I felt was indescribable. This wasn't just about the money; it was about the restoration of hope and the possibility of a future for my company. With the recovered funds, we were able to stabilize our finances, reassure our partners, and regain the trust of our employees. It wasn't an instant fix, but it gave us the lifeline we needed to rebuild. Slowly but surely, we started to turn things around. Today, our company is back on track, stronger and more resilient than ever. I owe a debt of gratitude to Tropical Delight Recovery. They didn't just save my company; they saved me from a path of despair and hopelessness. Their professionalism, expertise, and unwavering commitment to their clients make them a beacon of hope for anyone caught in the web of financial fraud. If you find yourself in a situation like mine, don't hesitate to reach out to Tropical Delight Recovery. They are more than a recovery company; they are life-savers. You can reach out to them via: Email: tropicaldelightrecoveryhacker @ out look . Com.
Signal: + 16 14568 3873
Telegram: @ tropical delightrecovery hacker Website: h. t. t ps : // dev-tropicaldelightrecovery pantheonsite   .io
    • You have to set the java path in your start script
    • i tried that and i got rid of java to install the new one but it still says i have the old one and i cant get the new one because of the old one  
    • Я создал босса для Майнкрафт и когда он вызывается «Произошла непредвиденная ошибка при попытке выполнить эту команду» и в описании «Не удается вызвать ошибку "net.minecraft.world.entity.ai.attributes.attribute instance.m_22100_ (double)", потому что возвращаемое значение "net.minecraft.world.entity.monster.zombie.m_21051_ (net.minecraft.world.entity.ai.attributes.attribute)" имеет значение null. Я не до конца понимаю, в чем ошибка. Но, похоже, это связано с атрибутами. Помогите пожалуйста разобраться   Вот сам класс босса: package org.mymod.afraid; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.goal.*; import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal; import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal; import net.minecraft.world.entity.monster.Zombie; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.phys.Vec3; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.entity.projectile.SmallFireball; import org.jetbrains.annotations.NotNull; public class AfraidBoss extends Zombie { private static final int MAX_HEALTH = 1000; private static final double ATTACK_DAMAGE = 10.0D; private static final double FOLLOW_RANGE = 50.0D; private static final double ATTACK_KNOCKBACK = 1.0D; private static final double MOVEMENT_SPEED = 0.25D; private static final int TELEPORT_RADIUS = 20; private static final int FIREBALL_COOLDOWN = 100; // 5 seconds (20 ticks per second) private static final int FIREBALL_COUNT = 3; private int fireballCooldown = 0; private int fireDashCooldown = 0; public AfraidBoss(EntityType<? extends Zombie> type, Level level) { super(type, level); this.setHealth(MAX_HEALTH); this.setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(Items.DIAMOND_SWORD)); this.setItemSlot(EquipmentSlot.OFFHAND, new ItemStack(Items.DIAMOND_SWORD)); } @Override protected void registerGoals() { this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.0, true)); this.goalSelector.addGoal(2, new MoveTowardsTargetGoal(this, 1.0, (float) TELEPORT_RADIUS)); this.goalSelector.addGoal(3, new LookAtPlayerGoal(this, Player.class, 8.0F)); this.goalSelector.addGoal(4, new WaterAvoidingRandomStrollGoal(this, 1.0)); this.goalSelector.addGoal(5, new HurtByTargetGoal(this)); this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Player.class, true)); } @Override public void aiStep() { super.aiStep(); if (this.getTarget() instanceof Player) { Player player = (Player) this.getTarget(); double distanceToPlayer = this.distanceToSqr(player); // Fire Dash ability if (distanceToPlayer <= TELEPORT_RADIUS * TELEPORT_RADIUS && fireDashCooldown == 0) { this.fireDash(player); fireDashCooldown = 200; // Cooldown for fire dash (10 seconds) } // Fireball attack if (fireballCooldown == 0) { this.shootFireballs(player); fireballCooldown = FIREBALL_COOLDOWN; // Cooldown for fireball attack (5 seconds) } // Decrement cooldowns if (fireDashCooldown > 0) { fireDashCooldown--; } if (fireballCooldown > 0) { fireballCooldown--; } } } private void fireDash(Player player) { Vec3 direction = player.position().subtract(this.position()).normalize(); Vec3 newPos = this.position().add(direction.scale(10)); this.teleportTo(newPos.x, newPos.y, newPos.z); this.createFireTrail(newPos); player.hurt(DamageSource.mobAttack(this), 20.0F); // Damage the player } private void createFireTrail(Vec3 position) { for (int x = -2; x <= 2; x++) { for (int z = -2; z <= 2; z++) { BlockPos firePos = new BlockPos(position.x + x, position.y, position.z + z); this.level.setBlock(firePos, Blocks.FIRE.defaultBlockState(), 11); } } } private void shootFireballs(Player player) { Vec3 direction = player.position().subtract(this.position()).normalize(); for (int i = 0; i < FIREBALL_COUNT; i++) { SmallFireball fireball = new SmallFireball(this.level, this, direction.x, direction.y, direction.z); fireball.setPos(this.getX() + direction.x, this.getY() + direction.y, this.getZ() + direction.z); this.level.addFreshEntity(fireball); } } public static AttributeSupplier.Builder createAttributes() { return Zombie.createMobAttributes() .add(Attributes.MAX_HEALTH, MAX_HEALTH) .add(Attributes.ATTACK_DAMAGE, ATTACK_DAMAGE) .add(Attributes.FOLLOW_RANGE, FOLLOW_RANGE) .add(Attributes.ATTACK_KNOCKBACK, ATTACK_KNOCKBACK) .add(Attributes.MOVEMENT_SPEED, MOVEMENT_SPEED); } @Override public boolean hurt(@NotNull DamageSource source, float amount) { boolean flag = super.hurt(source, amount); if (flag && source.getEntity() instanceof Player) { Player player = (Player) source.getEntity(); if (this.random.nextInt(10) == 0) { this.teleportAndAttack(player); } } return flag; } private void teleportAndAttack(Player player) { Vec3 randomPos = player.position().add((this.random.nextDouble() - 0.5) * 4, 0, (this.random.nextDouble() - 0.5) * 4); if (this.randomTeleport(randomPos.x, randomPos.y, randomPos.z, true)) { player.hurt(DamageSource.mobAttack(this), 10.0F); // Damage the player } } }  
  • Topics

×
×
  • Create New...

Important Information

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