Calling "particle builder" frequently makes the client not see any particles
AntiThesis1250 opened this issue · 9 comments
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
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();
}
}
}
}
Such as frequent use of this effect
There is a probability that the client will not see any particles
Usually, triggering this bug requires multiple entities to load particles at the same time and reduce the number of client frames
Please tell me some solutions. Thank you very much @Electroblob77
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).
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?