Change particle handles and linking to be a little more robust. Should help with some cases where duplicate particles happen

This commit is contained in:
Sollace 2021-12-24 00:20:47 +02:00
parent 3806799cf2
commit 063d4e6dd2
11 changed files with 97 additions and 82 deletions

View file

@ -1,6 +1,7 @@
package com.minelittlepony.unicopia.ability.magic;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Function;
import java.util.function.Predicate;
@ -30,8 +31,18 @@ public interface SpellContainer {
public boolean forEach(Function<Spell, Operation> action, boolean update) {
return false;
}
@Override
public boolean contains(UUID id) {
return false;
}
};
/**
* Checks if a spell with the given uuid is present.
*/
boolean contains(UUID id);
/**
* Gets the active effect for this caster updating it if needed.
*/
@ -79,6 +90,11 @@ public interface SpellContainer {
SpellContainer delegate();
@Override
default boolean contains(UUID id) {
return delegate().contains(id);
}
@Override
default <T extends Spell> Optional<T> get(@Nullable SpellPredicate<T> type, boolean update) {
return delegate().get(type, update);

View file

@ -54,10 +54,10 @@ public class JoustingSpell extends AbstractSpell {
}
if (source.isClient()) {
particlEffect.ifAbsent(source, spawner -> {
particlEffect.ifAbsent(getUuid(), source, spawner -> {
spawner.addParticle(UParticles.RAINBOOM_TRAIL, source.getOriginVector(), Vec3d.ZERO);
spawner.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, source.getPhysics().getMotionAngle()), source.getOriginVector(), Vec3d.ZERO);
}).ifPresent(p -> p.attach(source));
});
}
LivingEntity owner = source.getMaster();

View file

@ -82,10 +82,9 @@ public class PlaceableSpell extends AbstractDelegatingSpell {
return super.tick(source, Situation.GROUND);
} else if (situation == Situation.GROUND_ENTITY) {
particlEffect.ifAbsent(source, spawner -> {
particlEffect.ifAbsent(getUuid(), source, spawner -> {
spawner.addParticle(new OrientedBillboardParticleEffect(UParticles.MAGIC_RUNES, 90, 0), source.getOriginVector(), Vec3d.ZERO);
}).ifPresent(p -> {
p.attach(source);
p.setAttribute(1, spell.getType().getColor());
});
}

View file

@ -55,10 +55,9 @@ public class DarkVortexSpell extends AttractiveSpell {
float radius = (float)getDrawDropOffRange(source) / 2;
particlEffect.ifAbsent(source, spawner -> {
particlEffect.ifAbsent(getUuid(), source, spawner -> {
spawner.addParticle(new SphereParticleEffect(getType().getColor(), 0.99F, radius), source.getOriginVector(), Vec3d.ZERO);
}).ifPresent(p -> {
p.attach(source);
p.setAttribute(0, radius);
});
}

View file

@ -70,10 +70,9 @@ public class ShieldSpell extends AbstractSpell {
source.addParticle(new MagicParticleEffect(getType().getColor()), pos, Vec3d.ZERO);
});
particlEffect.ifAbsent(source, spawner -> {
particlEffect.ifAbsent(getUuid(), source, spawner -> {
spawner.addParticle(new SphereParticleEffect(getType().getColor(), 0.3F, radius), source.getOriginVector(), Vec3d.ZERO);
}).ifPresent(p -> {
p.attach(source);
p.setAttribute(0, radius);
p.setAttribute(1, getType().getColor());
});

View file

@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.client.particle;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.entity.player.Pony;
@ -22,7 +23,7 @@ public class RainbowTrailParticle extends AbstractBillboardParticle implements A
private final List<Segment> segments = new ArrayList<>();
private final Link link = new Link();
private Optional<Link> link = Optional.empty();
public RainbowTrailParticle(DefaultParticleType effect, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) {
super(world, x, y, z, velocityX, velocityY, velocityZ);
@ -41,13 +42,13 @@ public class RainbowTrailParticle extends AbstractBillboardParticle implements A
}
@Override
public void attach(Caster<?> caster) {
link.attach(caster);
public void attach(Link link) {
this.link = Optional.of(link);
}
@Override
public void detach() {
link.detach();
link = Optional.empty();
}
@Override
@ -90,9 +91,9 @@ public class RainbowTrailParticle extends AbstractBillboardParticle implements A
public void tick() {
super.tick();
if (link.linked()) {
if (link.isPresent()) {
age = 0;
link.ifAbsent(() -> {}).ifPresent(this::follow);
link.flatMap(Link::get).ifPresent(this::follow);
} else if (!dead) {
follow(Pony.of(MinecraftClient.getInstance().player));
}

View file

@ -1,5 +1,7 @@
package com.minelittlepony.unicopia.client.particle;
import java.util.Optional;
import com.minelittlepony.common.util.Color;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect;
@ -33,7 +35,7 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme
private float prevRotationAngle;
private float rotationAngle;
private final Link link = new Link();
private Optional<Link> link = Optional.empty();
private int stasisAge = -1;
@ -52,18 +54,18 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme
}
@Override
public void attach(Caster<?> caster) {
link.attach(caster);
public void attach(Link link) {
this.link = Optional.of(link);
velocityX = 0;
velocityY = 0;
velocityZ = 0;
Vec3d pos = caster.getOriginVector();
Vec3d pos = link.get().map(Caster::getOriginVector).orElse(Vec3d.ZERO);
setPos(pos.x, pos.y, pos.z);
}
@Override
public void detach() {
link.detach();
link = Optional.empty();
}
@Override
@ -135,16 +137,14 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme
public void tick() {
super.tick();
if (link.linked()) {
link.ifAbsent(this::detach).map(Caster::getEntity).ifPresent(e -> {
if (getAlphaScale() >= 0.9F) {
if (stasisAge < 0) {
stasisAge = age;
}
age = stasisAge;
link.flatMap(Link::get).map(Caster::getEntity).ifPresentOrElse(e -> {
if (getAlphaScale() >= 0.9F) {
if (stasisAge < 0) {
stasisAge = age;
}
});
}
age = stasisAge;
}
}, this::detach);
prevBaseSize = baseSize;
if (baseSize < 3) {

View file

@ -17,6 +17,9 @@ import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
import com.minelittlepony.unicopia.particle.ParticleHandle.Link;
import com.minelittlepony.unicopia.util.ColorHelper;
import com.mojang.blaze3d.systems.RenderSystem;
import java.util.Optional;
import com.minelittlepony.common.util.Color;
public class SphereParticle extends Particle implements Attachment {
@ -28,7 +31,7 @@ public class SphereParticle extends Particle implements Attachment {
protected float lerpIncrement;
protected float toRadius;
private final Link link = new Link();
private Optional<Link> link = Optional.empty();
private static final SphereModel MODEL = new SphereModel();
@ -58,9 +61,9 @@ public class SphereParticle extends Particle implements Attachment {
}
@Override
public void attach(Caster<?> caster) {
public void attach(Link link) {
setMaxAge(50000);
link.attach(caster);
this.link = Optional.of(link);
}
@Override
@ -92,14 +95,14 @@ public class SphereParticle extends Particle implements Attachment {
public void tick() {
super.tick();
if (link.linked()) {
link.ifAbsent(this::markDead).map(Caster::getEntity).ifPresent(e -> {
if (link.isPresent()) {
link.flatMap(Link::get).map(Caster::getEntity).ifPresentOrElse(e -> {
setPos(e.getX(), e.getY() + 0.5, e.getZ());
prevPosX = e.lastRenderX;
prevPosY = e.lastRenderY + 0.5;
prevPosZ = e.lastRenderZ;
});
}, this::detach);
if (steps-- > 0) {
radius += lerpIncrement;

View file

@ -1,6 +1,7 @@
package com.minelittlepony.unicopia.network.datasync;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
@ -39,6 +40,11 @@ public class EffectSync implements SpellContainer {
this.param = param;
}
@Override
public boolean contains(UUID id) {
return spells.containsReference(id);
}
@Override
@SuppressWarnings("unchecked")
public <T extends Spell> Optional<T> get(@Nullable SpellPredicate<T> type, boolean update) {

View file

@ -34,6 +34,10 @@ public class NetworkedReferenceSet<T> {
this.factory = factory;
}
public boolean containsReference(UUID id) {
return ids.contains(id);
}
public Stream<T> getReferences() {
return ids.stream().map(id -> values.get(id))
.map(a -> a.getReference())

View file

@ -1,17 +1,18 @@
package com.minelittlepony.unicopia.particle;
import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.WeakHashMap;
import java.util.function.Consumer;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.particle.Particle;
import net.minecraft.entity.Entity;
import net.minecraft.particle.ParticleEffect;
import net.minecraft.util.math.Vec3d;
@ -19,30 +20,41 @@ import net.minecraft.util.math.Vec3d;
* A connection class for updating and persisting an attached particle effect.
*/
public class ParticleHandle {
private Optional<WeakReference<Attachment>> particleEffect = Optional.empty();
private Optional<Attachment> particleEffect = Optional.empty();
public Optional<Attachment> ifAbsent(ParticleSource source, Consumer<ParticleSpawner> constructor) {
particleEffect.filter(Attachment::isStillAlive).orElseGet(() -> {
public Optional<Attachment> ifAbsent(UUID id, ParticleSource source, Consumer<ParticleSpawner> constructor) {
return get().or(() -> {
if (source.getWorld().isClient) {
constructor.accept(this::addParticle);
constructor.accept((effect, pos, vel) -> new ClientHandle().addParticle(id, source, effect, pos, vel));
}
return null;
return get();
});
return particleEffect;
}
public void destroy() {
particleEffect.ifPresent(Attachment::detach);
get().ifPresent(Attachment::detach);
}
@Environment(EnvType.CLIENT)
private void addParticle(ParticleEffect effect, Vec3d pos, Vec3d vel) {
Particle p = MinecraftClient.getInstance().particleManager.addParticle(effect, pos.x, pos.y, pos.z, vel.x, vel.y, vel.z);
private Optional<Attachment> get() {
return particleEffect.map(WeakReference::get).filter(Attachment::isStillAlive);
}
if (p instanceof Attachment) {
particleEffect = Optional.of((Attachment)p);
private final class ClientHandle {
private static final Map<UUID, Particle> SPAWNED_PARTICLES = new WeakHashMap<>();
@Environment(EnvType.CLIENT)
private void addParticle(UUID id, ParticleSource source, ParticleEffect effect, Vec3d pos, Vec3d vel) {
Particle p = SPAWNED_PARTICLES.computeIfAbsent(id, i -> {
Particle pp = MinecraftClient.getInstance().particleManager.addParticle(effect, pos.x, pos.y, pos.z, vel.x, vel.y, vel.z);
if (pp instanceof Attachment && source instanceof Caster<?>) {
((Attachment) pp).attach(new Link(id, (Caster<?>)source));
}
return pp;
});
if (p instanceof Attachment) {
particleEffect = Optional.of(new WeakReference<>((Attachment)p));
}
}
}
@ -50,7 +62,7 @@ public class ParticleHandle {
boolean isStillAlive();
void attach(Caster<?> caster);
void attach(Link link);
void detach();
@ -58,41 +70,17 @@ public class ParticleHandle {
}
public static final class Link {
private Optional<Caster<?>> caster = Optional.empty();
private Optional<WeakReference<Caster<?>>> caster = Optional.empty();
private UUID effect;
private boolean linked;
public void attach(Caster<?> caster) {
this.linked = true;
this.caster = Optional.of(caster);
this.effect = caster.getSpellSlot().get(false).map(Spell::getUuid).orElse(null);
private Link(UUID effect, Caster<?> caster) {
this.caster = Optional.of(new WeakReference<>(caster));
this.effect = effect;
}
public void detach() {
caster = Optional.empty();
}
public boolean linked() {
return linked;
}
public Optional<Caster<?>> ifAbsent(Runnable action) {
caster = caster.filter(c -> {
Entity e = c.getEntity();
return Caster.of(e).orElse(null) == c
&& c.getSpellSlot().get(false)
.filter(s -> s.getUuid().equals(effect))
.isPresent()
&& e != null
&& c.getWorld().getEntityById(e.getId()) != null;
});
if (!caster.isPresent()) {
action.run();
}
return caster;
public Optional<Caster<?>> get() {
caster = caster.filter(r -> r.get() != null && r.get().getSpellSlot().contains(effect));
return caster.map(WeakReference::get);
}
}
}