Electroblob's Wizardry

Electroblob's Wizardry

18M Downloads

Calling "particle builder" frequently makes the client not see any particles

AntiThesis1250 opened this issue · 9 comments

commented

Please read the guide for contributing before posting.

Minecraft version: 1.12.2 [change as necessary]
Wizardry version: 4.3.3,4.3.4 [change as necessary]

Question details:

Hi Buddy,I just learned module development some time ago.
At present, I am developing a subproject of "Wizardry"
Because I feel in love with your particle effects
I use“ electroblob.wizardry.util.ParticleBuilder "to call your methods to combine new particle effects
But when it is used frequently, it has a chance to make the client stop loading any particles

I used multithreading to call your method

Once, I used multithreading to call your method and loaded a lot of particles in the server.
Initially, the number of client frames was reduced. After a period of time, the client will no longer load any particles. These particles will not be visible until the client is restarted

I don't know why

commented

If you don't understand, I'll take a video

commented
package electroblob.wizardry.spell;

import electroblob.wizardry.item.SpellActions;
import electroblob.wizardry.util.EntityUtils;
import electroblob.wizardry.util.MagicDamage;
import electroblob.wizardry.util.MagicDamage.DamageType;
import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.ParticleBuilder.Type;
import electroblob.wizardry.util.SpellModifiers;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.World;

public class Arc extends SpellRay implements Runnable{
    public Entity caster;
    public Entity target;
    World world;
    Vec3d origin;


    public Arc(){
        super("arc", SpellActions.POINT, false);
        this.aimAssist(0.6f);
        this.soundValues(1, 1.7f, 0.2f);
        this.addProperties(DAMAGE);
    }

    @Override
    protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){

        if(EntityUtils.isLiving(target)){

            if(world.isRemote){

                this.caster=caster;
                this.target=target;
                this.world=world;
                this.origin=origin;

                // Rather neatly, the entity can be set here and if it's null nothing will happen.
                    ParticleBuilder.create(Type.LIGHTNING).entity(caster)
                            .pos(caster != null ? origin.subtract(caster.getPositionVector()) : origin).target(target).spawn(world);
                    ParticleBuilder.spawnShockParticles(world, target.posX, target.posY + target.height/2, target.posZ);
                Thread a = new Thread(this);
                a.start();

            }

            // This is a lot neater than it was, thanks to the damage type system.
            if(MagicDamage.isEntityImmune(DamageType.SHOCK, target)){
                if(!world.isRemote && caster instanceof EntityPlayer) ((EntityPlayer)caster).sendStatusMessage(
                        new TextComponentTranslation("spell.resist",
                                target.getName(), this.getNameForTranslationFormatted()), true);
            }else{
                target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK),
                        getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY));
            }

            return true;
        }

        return false;
    }

    @Override
    protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
        return false;
    }

    @Override
    protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
        return false;
    }
//----------------↓↓↓-------------this--
    @Override
    public void run() {
        for (int i = 0; i <10000 ; i++) {
            ParticleBuilder.create(Type.LIGHTNING).entity(caster)
                    .pos(caster != null ? origin.subtract(caster.getPositionVector()) : origin).target(target).spawn(world);
            ParticleBuilder.spawnShockParticles(world, target.posX, target.posY + target.height/2, target.posZ);
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
commented

Such as frequent use of this effect
There is a probability that the client will not see any particles

commented

No other modules installed
forge14.23.5.2847 -1.12.2

commented

Usually, triggering this bug requires multiple entities to load particles at the same time and reduce the number of client frames

commented

Please tell me some solutions. Thank you very much @Electroblob77

commented

I used multithreading to call your method

Do not do this. ParticleBuilder is intentionally not threadsafe because particles should only ever be spawned on the client thread. You should also not be creating threads randomly during gameplay. And don't store things like caster in the spell, remember that each spell only has one instance that is loaded at the start of the game.

It looks like you want to make the particles keep spawning for a while after the spell is cast. There are several correct ways of doing this, but you could achieve it quite easily using the WizardData system built into wizardry. Have a look at IVariable for an explanation of how it works, there are also some good examples of how to use it in some of wizardry's spell classes.

The main thing to understand is that if you want a certain thing to happen after a delay, you cannot just call Thread.sleep(...), you have to count game ticks and store any variables you need somewhere appropriate, normally in an entity or sometimes statically (but be careful doing that).

commented

Is it only when I use "particle builder" in the main thread that there is no problem?

You should only use it in the client thread, yes. This normally means checking that world.isRemote is true before using ParticleBuilder.

I don't make spells, I just use "particle builder" to make effects

Surely you must have made a spell if you've written a class that extends Spell? Unless you're trying to edit the Arc spell?

commented

I don't make spells, I just use "particle builder" to make effects
Is it only when I use "particle builder" in the main thread that there is no problem?
Thank you for teaching me