From 063d4e6dd2ea07b5fc5850204ed38b8d6c3ba04c Mon Sep 17 00:00:00 2001 From: Sollace Date: Fri, 24 Dec 2021 00:20:47 +0200 Subject: [PATCH] Change particle handles and linking to be a little more robust. Should help with some cases where duplicate particles happen --- .../ability/magic/SpellContainer.java | 16 ++++ .../ability/magic/spell/JoustingSpell.java | 4 +- .../ability/magic/spell/PlaceableSpell.java | 3 +- .../magic/spell/effect/DarkVortexSpell.java | 3 +- .../magic/spell/effect/ShieldSpell.java | 3 +- .../client/particle/RainbowTrailParticle.java | 13 +-- .../client/particle/RunesParticle.java | 28 +++---- .../client/particle/SphereParticle.java | 15 ++-- .../unicopia/network/datasync/EffectSync.java | 6 ++ .../datasync/NetworkedReferenceSet.java | 4 + .../unicopia/particle/ParticleHandle.java | 84 ++++++++----------- 11 files changed, 97 insertions(+), 82 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/SpellContainer.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/SpellContainer.java index 42d649ff..5bc337c2 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/SpellContainer.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/SpellContainer.java @@ -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 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 Optional get(@Nullable SpellPredicate type, boolean update) { return delegate().get(type, update); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/JoustingSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/JoustingSpell.java index 3cf8c9b0..977dd19f 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/JoustingSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/JoustingSpell.java @@ -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(); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java index 425fbd7e..85a70bab 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java @@ -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()); }); } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java index acc248fa..67758a09 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java @@ -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); }); } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java index 971ed3a4..7b5508bb 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java @@ -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()); }); diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/RainbowTrailParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/RainbowTrailParticle.java index 939e8eab..ce48ce99 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/RainbowTrailParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/RainbowTrailParticle.java @@ -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 segments = new ArrayList<>(); - private final Link link = new Link(); + private Optional 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)); } diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/RunesParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/RunesParticle.java index 6d16cd9c..cb090235 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/RunesParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/RunesParticle.java @@ -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 = 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) { diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/SphereParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/SphereParticle.java index ff92997e..cabe1ba3 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/SphereParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/SphereParticle.java @@ -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 = 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; diff --git a/src/main/java/com/minelittlepony/unicopia/network/datasync/EffectSync.java b/src/main/java/com/minelittlepony/unicopia/network/datasync/EffectSync.java index 31e9e79b..1f8d1138 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/datasync/EffectSync.java +++ b/src/main/java/com/minelittlepony/unicopia/network/datasync/EffectSync.java @@ -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 Optional get(@Nullable SpellPredicate type, boolean update) { diff --git a/src/main/java/com/minelittlepony/unicopia/network/datasync/NetworkedReferenceSet.java b/src/main/java/com/minelittlepony/unicopia/network/datasync/NetworkedReferenceSet.java index 43dfca27..4da024cf 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/datasync/NetworkedReferenceSet.java +++ b/src/main/java/com/minelittlepony/unicopia/network/datasync/NetworkedReferenceSet.java @@ -34,6 +34,10 @@ public class NetworkedReferenceSet { this.factory = factory; } + public boolean containsReference(UUID id) { + return ids.contains(id); + } + public Stream getReferences() { return ids.stream().map(id -> values.get(id)) .map(a -> a.getReference()) diff --git a/src/main/java/com/minelittlepony/unicopia/particle/ParticleHandle.java b/src/main/java/com/minelittlepony/unicopia/particle/ParticleHandle.java index d5d16871..45bf37fd 100644 --- a/src/main/java/com/minelittlepony/unicopia/particle/ParticleHandle.java +++ b/src/main/java/com/minelittlepony/unicopia/particle/ParticleHandle.java @@ -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> particleEffect = Optional.empty(); - private Optional particleEffect = Optional.empty(); - - public Optional ifAbsent(ParticleSource source, Consumer constructor) { - particleEffect.filter(Attachment::isStillAlive).orElseGet(() -> { + public Optional ifAbsent(UUID id, ParticleSource source, Consumer 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 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 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 = Optional.empty(); + private Optional>> 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> 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> get() { + caster = caster.filter(r -> r.get() != null && r.get().getSpellSlot().contains(effect)); + return caster.map(WeakReference::get); } } }