Better handle exceptions in ticking spells and try to prevent spells from being destroyed more than once

This commit is contained in:
Sollace 2023-09-02 19:59:42 +01:00
parent 820856fdc4
commit 77d6e6d1b0
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
21 changed files with 87 additions and 76 deletions

View file

@ -20,8 +20,9 @@ import net.minecraft.util.hit.EntityHitResult;
public abstract class AbstractDelegatingSpell implements Spell, public abstract class AbstractDelegatingSpell implements Spell,
ProjectileDelegate.ConfigurationListener, ProjectileDelegate.BlockHitListener, ProjectileDelegate.EntityHitListener { ProjectileDelegate.ConfigurationListener, ProjectileDelegate.BlockHitListener, ProjectileDelegate.EntityHitListener {
private boolean isDirty; private boolean dirty;
private boolean hidden; private boolean hidden;
private boolean destroyed;
private UUID uuid = UUID.randomUUID(); private UUID uuid = UUID.randomUUID();
@ -59,7 +60,7 @@ public abstract class AbstractDelegatingSpell implements Spell,
} }
@Override @Override
public UUID getUuid() { public final UUID getUuid() {
return uuid; return uuid;
} }
@ -75,12 +76,12 @@ public abstract class AbstractDelegatingSpell implements Spell,
@Override @Override
public boolean isDirty() { public boolean isDirty() {
return isDirty || getDelegates().stream().anyMatch(Spell::isDirty); return dirty || getDelegates().stream().anyMatch(Spell::isDirty);
} }
@Override @Override
public void setDirty() { public void setDirty() {
isDirty = true; dirty = true;
} }
@Override @Override
@ -94,8 +95,17 @@ public abstract class AbstractDelegatingSpell implements Spell,
} }
@Override @Override
public void onDestroyed(Caster<?> caster) { public final void destroy(Caster<?> caster) {
getDelegates().forEach(a -> a.onDestroyed(caster)); if (destroyed) {
return;
}
destroyed = true;
setDead();
onDestroyed(caster);
}
protected void onDestroyed(Caster<?> caster) {
getDelegates().forEach(a -> a.destroy(caster));
} }
@Override @Override
@ -127,7 +137,7 @@ public abstract class AbstractDelegatingSpell implements Spell,
@Override @Override
public void fromNBT(NbtCompound compound) { public void fromNBT(NbtCompound compound) {
isDirty = false; dirty = false;
hidden = compound.getBoolean("hidden"); hidden = compound.getBoolean("hidden");
if (compound.contains("uuid")) { if (compound.contains("uuid")) {
uuid = compound.getUuid("uuid"); uuid = compound.getUuid("uuid");

View file

@ -27,7 +27,7 @@ public abstract class AbstractDisguiseSpell extends AbstractSpell implements Dis
} }
@Override @Override
public void onDestroyed(Caster<?> caster) { protected void onDestroyed(Caster<?> caster) {
caster.asEntity().calculateDimensions(); caster.asEntity().calculateDimensions();
caster.asEntity().setInvisible(false); caster.asEntity().setInvisible(false);
if (caster instanceof Pony) { if (caster instanceof Pony) {
@ -51,12 +51,6 @@ public abstract class AbstractDisguiseSpell extends AbstractSpell implements Dis
return situation == Situation.BODY && update(source, true); return situation == Situation.BODY && update(source, true);
} }
@Override
public void setDead() {
super.setDead();
disguise.remove();
}
@Override @Override
public void toNBT(NbtCompound compound) { public void toNBT(NbtCompound compound) {
super.toNBT(compound); super.toNBT(compound);

View file

@ -24,6 +24,7 @@ public class DispersableDisguiseSpell extends AbstractDisguiseSpell implements I
public DispersableDisguiseSpell(CustomisedSpellType<?> type) { public DispersableDisguiseSpell(CustomisedSpellType<?> type) {
super(type); super(type);
setHidden(true);
} }
@Override @Override
@ -92,13 +93,4 @@ public class DispersableDisguiseSpell extends AbstractDisguiseSpell implements I
public Optional<EntityAppearance> getAppearance() { public Optional<EntityAppearance> getAppearance() {
return isSuppressed() ? Optional.empty() : super.getAppearance(); return isSuppressed() ? Optional.empty() : super.getAppearance();
} }
@Override
public boolean isHidden() {
return true;
}
@Override
public void setHidden(boolean hidden) {
}
} }

View file

@ -164,7 +164,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
} }
@Override @Override
public void onDestroyed(Caster<?> source) { protected void onDestroyed(Caster<?> source) {
if (!source.isClient()) { if (!source.isClient()) {
castEntity.getTarget().ifPresent(target -> { castEntity.getTarget().ifPresent(target -> {
getWorld(source).map(Ether::get) getWorld(source).map(Ether::get)

View file

@ -32,11 +32,11 @@ public class RainboomAbilitySpell extends AbstractSpell {
public RainboomAbilitySpell(CustomisedSpellType<?> type) { public RainboomAbilitySpell(CustomisedSpellType<?> type) {
super(type); super(type);
setHidden(true);
} }
@Override @Override
public void setDead() { protected void onDestroyed(Caster<?> source) {
super.setDead();
particlEffect.destroy(); particlEffect.destroy();
} }
@ -93,13 +93,4 @@ public class RainboomAbilitySpell extends AbstractSpell {
super.fromNBT(compound); super.fromNBT(compound);
age = compound.getInt("age"); age = compound.getInt("age");
} }
@Override
public boolean isHidden() {
return false;
}
@Override
public void setHidden(boolean hidden) {
}
} }

View file

@ -101,7 +101,7 @@ public interface Spell extends NbtSerialisable, Affine {
/** /**
* Called when a gem is destroyed. * Called when a gem is destroyed.
*/ */
void onDestroyed(Caster<?> caster); void destroy(Caster<?> caster);
/** /**
* Converts this spell into a placeable spell. * Converts this spell into a placeable spell.

View file

@ -14,6 +14,7 @@ public abstract class AbstractSpell implements Spell {
private boolean dead; private boolean dead;
private boolean dirty; private boolean dirty;
private boolean hidden; private boolean hidden;
private boolean destroyed;
private CustomisedSpellType<?> type; private CustomisedSpellType<?> type;
@ -43,33 +44,33 @@ public abstract class AbstractSpell implements Spell {
} }
@Override @Override
public void setDead() { public final void setDead() {
dead = true; dead = true;
setDirty(); setDirty();
} }
@Override @Override
public boolean isDead() { public final boolean isDead() {
return dead; return dead;
} }
@Override @Override
public boolean isDirty() { public final boolean isDirty() {
return dirty; return dirty;
} }
@Override @Override
public void setDirty() { public final void setDirty() {
dirty = true; dirty = true;
} }
@Override @Override
public boolean isHidden() { public final boolean isHidden() {
return hidden; return hidden;
} }
@Override @Override
public void setHidden(boolean hidden) { public final void setHidden(boolean hidden) {
this.hidden = hidden; this.hidden = hidden;
} }
@ -78,8 +79,17 @@ public abstract class AbstractSpell implements Spell {
return getType().getAffinity(); return getType().getAffinity();
} }
protected void onDestroyed(Caster<?> caster) {
}
@Override @Override
public void onDestroyed(Caster<?> caster) { public final void destroy(Caster<?> caster) {
if (destroyed) {
return;
}
destroyed = true;
setDead();
onDestroyed(caster);
} }
@Override @Override

View file

@ -138,7 +138,8 @@ public class AttractiveSpell extends ShieldSpell implements HomingSpell, TimedSp
} }
@Override @Override
public void onDestroyed(Caster<?> caster) { protected void onDestroyed(Caster<?> caster) {
super.onDestroyed(caster);
target.getOrEmpty(caster.asWorld()).ifPresent(target -> target.setGlowing(false)); target.getOrEmpty(caster.asWorld()).ifPresent(target -> target.setGlowing(false));
} }

View file

@ -138,7 +138,7 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell,
} }
@Override @Override
public void onDestroyed(Caster<?> source) { protected void onDestroyed(Caster<?> source) {
particlEffect.destroy(); particlEffect.destroy();
if (source.asEntity() instanceof LivingEntity l) { if (source.asEntity() instanceof LivingEntity l) {
MODIFIERS.forEach((attribute, modifier) -> { MODIFIERS.forEach((attribute, modifier) -> {

View file

@ -104,7 +104,7 @@ public class DisplacementSpell extends AbstractSpell implements HomingSpell, Pla
} }
@Override @Override
public void onDestroyed(Caster<?> caster) { protected void onDestroyed(Caster<?> caster) {
caster.getOriginatingCaster().asEntity().setGlowing(false); caster.getOriginatingCaster().asEntity().setGlowing(false);
target.ifPresent(caster.asWorld(), e -> e.setGlowing(false)); target.ifPresent(caster.asWorld(), e -> e.setGlowing(false));
} }

View file

@ -106,7 +106,7 @@ public class HydrophobicSpell extends AbstractSpell {
} }
@Override @Override
public void onDestroyed(Caster<?> caster) { protected void onDestroyed(Caster<?> caster) {
storedFluidPositions.removeIf(entry -> { storedFluidPositions.removeIf(entry -> {
entry.restore(caster.asWorld()); entry.restore(caster.asWorld());
return true; return true;

View file

@ -90,7 +90,7 @@ public class LightSpell extends AbstractSpell implements TimedSpell, ProjectileD
} }
@Override @Override
public void onDestroyed(Caster<?> caster) { protected void onDestroyed(Caster<?> caster) {
if (caster.isClient()) { if (caster.isClient()) {
return; return;
} }

View file

@ -44,7 +44,7 @@ public class MindSwapSpell extends MimicSpell implements ProjectileDelegate.Enti
} }
@Override @Override
public void onDestroyed(Caster<?> caster) { protected void onDestroyed(Caster<?> caster) {
super.onDestroyed(caster); super.onDestroyed(caster);
if (initialized && !caster.isClient()) { if (initialized && !caster.isClient()) {
counterpart.ifPresent(caster.asWorld(), e -> { counterpart.ifPresent(caster.asWorld(), e -> {

View file

@ -151,7 +151,7 @@ public class NecromancySpell extends AbstractAreaEffectSpell implements Projecti
} }
@Override @Override
public void onDestroyed(Caster<?> caster) { protected void onDestroyed(Caster<?> caster) {
if (caster.isClient()) { if (caster.isClient()) {
return; return;
} }

View file

@ -186,7 +186,8 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
} }
@Override @Override
public void onDestroyed(Caster<?> caster) { protected void onDestroyed(Caster<?> caster) {
particleEffect.destroy();
Ether ether = Ether.get(caster.asWorld()); Ether ether = Ether.get(caster.asWorld());
ether.remove(getType(), caster.asEntity().getUuid()); ether.remove(getType(), caster.asEntity().getUuid());
getTarget(caster).ifPresent(e -> e.setTaken(false)); getTarget(caster).ifPresent(e -> e.setTaken(false));
@ -213,10 +214,4 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
(180 - yaw) * MathHelper.RADIANS_PER_DEGREE (180 - yaw) * MathHelper.RADIANS_PER_DEGREE
); );
} }
@Override
public void setDead() {
super.setDead();
particleEffect.destroy();
}
} }

View file

@ -54,8 +54,7 @@ public class ShieldSpell extends AbstractSpell {
} }
@Override @Override
public void setDead() { protected void onDestroyed(Caster<?> caster) {
super.setDead();
particlEffect.destroy(); particlEffect.destroy();
} }

View file

@ -14,7 +14,6 @@ import com.minelittlepony.unicopia.ability.Abilities;
import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.SpellContainer; import com.minelittlepony.unicopia.ability.magic.SpellContainer;
import com.minelittlepony.unicopia.ability.magic.SpellPredicate; import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.SpellContainer.Operation;
import com.minelittlepony.unicopia.ability.magic.spell.AbstractDisguiseSpell; import com.minelittlepony.unicopia.ability.magic.spell.AbstractDisguiseSpell;
import com.minelittlepony.unicopia.ability.magic.spell.Situation; import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.advancement.UCriteria; import com.minelittlepony.unicopia.advancement.UCriteria;
@ -291,12 +290,7 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
@Override @Override
public void tick() { public void tick() {
tickers.forEach(Tickable::tick); tickers.forEach(Tickable::tick);
effectDelegate.tick(Situation.BODY);
try {
getSpellSlot().forEach(spell -> Operation.ofBoolean(spell.tick(this, Situation.BODY)), entity.getWorld().isClient);
} catch (Exception e) {
Unicopia.LOGGER.error("Error whilst ticking spell on entity {}", entity, e);
}
if (!(entity instanceof PlayerEntity)) { if (!(entity instanceof PlayerEntity)) {
if (!entity.hasVehicle() && getCarrierId().isPresent() && !asWorld().isClient && entity.age % 10 == 0) { if (!entity.hasVehicle() && getCarrierId().isPresent() && !asWorld().isClient && entity.age % 10 == 0) {

View file

@ -4,7 +4,6 @@ import com.minelittlepony.unicopia.*;
import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Levelled; import com.minelittlepony.unicopia.ability.magic.Levelled;
import com.minelittlepony.unicopia.ability.magic.SpellContainer; import com.minelittlepony.unicopia.ability.magic.SpellContainer;
import com.minelittlepony.unicopia.ability.magic.SpellContainer.Operation;
import com.minelittlepony.unicopia.ability.magic.spell.Situation; import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.entity.EntityPhysics; import com.minelittlepony.unicopia.entity.EntityPhysics;
@ -67,7 +66,7 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster<CastS
return; return;
} }
if (!getSpellSlot().forEach(spell -> Operation.ofBoolean(spell.tick(this, Situation.GROUND_ENTITY)), getWorld().isClient)) { if (!effectDelegate.tick(Situation.GROUND_ENTITY)) {
discard(); discard();
} }
} }

View file

@ -8,9 +8,11 @@ import java.util.stream.Stream;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.SpellContainer; import com.minelittlepony.unicopia.ability.magic.SpellContainer;
import com.minelittlepony.unicopia.ability.magic.SpellPredicate; import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.util.NbtSerialisable; import com.minelittlepony.unicopia.util.NbtSerialisable;
@ -41,6 +43,26 @@ public class EffectSync implements SpellContainer, NbtSerialisable {
this.param = param; this.param = param;
} }
public boolean tick(Situation situation) {
return tick(spell -> Operation.ofBoolean(spell.tick(owner, situation)));
}
public boolean tick(Function<Spell, Operation> tickAction) {
try {
return forEach(spell -> {
try {
return tickAction.apply(spell);
} catch (Throwable t) {
Unicopia.LOGGER.error("Error whilst ticking spell on entity {}", owner, t);
}
return Operation.REMOVE;
}, owner.isClient());
} catch (Exception e) {
Unicopia.LOGGER.error("Error whilst ticking spell on entity {}", owner.asEntity(), e);
}
return false;
}
@Override @Override
public boolean contains(UUID id) { public boolean contains(UUID id) {
return spells.containsReference(id) || spells.getReferences().anyMatch(s -> s.equalsOrContains(id)); return spells.containsReference(id) || spells.getReferences().anyMatch(s -> s.equalsOrContains(id));
@ -60,8 +82,8 @@ public class EffectSync implements SpellContainer, NbtSerialisable {
public void put(@Nullable Spell effect) { public void put(@Nullable Spell effect) {
spells.addReference(effect); spells.addReference(effect);
write(); write();
if (owner instanceof UpdateCallback) { if (owner instanceof UpdateCallback callback) {
((UpdateCallback)owner).onSpellSet(effect); callback.onSpellSet(effect);
} }
} }

View file

@ -60,8 +60,7 @@ public class SpellNetworkedReference<T extends Spell> implements NetworkedRefere
currentValue = Optional.ofNullable(newValue); currentValue = Optional.ofNullable(newValue);
if (oldValue != null && (newValue == null || !oldValue.getUuid().equals(newValue.getUuid()))) { if (oldValue != null && (newValue == null || !oldValue.getUuid().equals(newValue.getUuid()))) {
oldValue.setDead(); oldValue.destroy(owner);
oldValue.onDestroyed(owner);
} }
} }
} }

View file

@ -8,6 +8,7 @@ import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Affinity; import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.WeaklyOwned; import com.minelittlepony.unicopia.WeaklyOwned;
import com.minelittlepony.unicopia.ability.magic.Affine; import com.minelittlepony.unicopia.ability.magic.Affine;
import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.Caster;
@ -198,7 +199,7 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Caster<Ma
return; return;
} }
getSpellSlot().get(true).filter(spell -> spell.tick(this, Situation.PROJECTILE)); effectDelegate.tick(Situation.PROJECTILE);
if (getHydrophobic()) { if (getHydrophobic()) {
if (StatePredicate.isFluid(getWorld().getBlockState(getBlockPos()))) { if (StatePredicate.isFluid(getWorld().getBlockState(getBlockPos()))) {
@ -317,11 +318,15 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Caster<Ma
} }
protected <T extends ProjectileDelegate> void forEachDelegates(Consumer<T> consumer, Function<Object, T> predicate) { protected <T extends ProjectileDelegate> void forEachDelegates(Consumer<T> consumer, Function<Object, T> predicate) {
getSpellSlot().forEach(spell -> { effectDelegate.tick(spell -> {
Optional.ofNullable(predicate.apply(spell)).ifPresent(consumer); Optional.ofNullable(predicate.apply(spell)).ifPresent(consumer);
return Operation.SKIP; return Operation.SKIP;
}, getWorld().isClient); });
Optional.ofNullable(predicate.apply(getItem().getItem())).ifPresent(consumer); try {
Optional.ofNullable(predicate.apply(getItem().getItem())).ifPresent(consumer);
} catch (Throwable t) {
Unicopia.LOGGER.error("Error whilst ticking spell on entity {}", owner, t);
}
} }
@Override @Override