Improve network syncing of spells and fix some shield radius resets

This commit is contained in:
Sollace 2024-02-07 12:24:53 +00:00
parent 5fb54aa2fd
commit 36e22f1ead
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
8 changed files with 101 additions and 87 deletions

View file

@ -51,7 +51,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
/**
* The spell being cast
*/
private Spell spell;
private final SpellReference<Spell> spell = new SpellReference<>();
public float pitch;
public float yaw;
@ -70,7 +70,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
}
public PlaceableSpell setSpell(Spell spell) {
this.spell = spell;
this.spell.set(spell);
return this;
}
@ -103,7 +103,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
@Override
public Collection<Spell> getDelegates() {
return List.of(spell);
return List.of(spell.get());
}
@Override
@ -162,8 +162,8 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
CastSpellEntity entity = UEntities.CAST_SPELL.create(source.asWorld());
Vec3d pos = getPosition().orElse(position.orElse(source.asEntity().getPos()));
entity.updatePositionAndAngles(pos.x, pos.y, pos.z, source.asEntity().getYaw(), source.asEntity().getPitch());
PlaceableSpell copy = spell.toPlaceable();
if (spell instanceof PlacementDelegate delegate) {
PlaceableSpell copy = spell.get().toPlaceable();
if (spell.get() instanceof PlacementDelegate delegate) {
delegate.onPlaced(source, copy, entity);
}
entity.getSpellSlot().put(copy);
@ -272,13 +272,12 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
@Override
protected void loadDelegates(NbtCompound compound) {
spell = Spell.SERIALIZER.read(compound.getCompound("spell"));
spell.fromNBT(compound.getCompound("spell"));
}
@Override
protected void saveDelegates(NbtCompound compound) {
compound.put("spell", Spell.SERIALIZER.write(spell));
compound.put("spell", spell.toNBT());
}
@Override

View file

@ -15,6 +15,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.util.NbtSerialisable;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.util.Util;
/**
* Interface for a magic spells
@ -129,10 +130,11 @@ public interface Spell extends NbtSerialisable, Affine {
}
@Nullable
static Spell readNbt(@Nullable NbtCompound compound) {
static <T extends Spell> T readNbt(@Nullable NbtCompound compound) {
try {
if (compound != null && compound.contains("effect_id")) {
Spell effect = SpellType.getKey(compound).withTraits().create();
@SuppressWarnings("unchecked")
T effect = (T)SpellType.getKey(compound).withTraits().create();
if (effect != null) {
effect.fromNBT(compound);
@ -147,6 +149,10 @@ public interface Spell extends NbtSerialisable, Affine {
return null;
}
static UUID getUuid(@Nullable NbtCompound compound) {
return compound == null || !compound.containsUuid("uuid") ? Util.NIL_UUID : compound.getUuid("uuid");
}
static NbtCompound writeNbt(Spell effect) {
NbtCompound compound = effect.toNBT();
effect.getType().toNbt(compound);

View file

@ -0,0 +1,65 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import java.util.Objects;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.util.NbtSerialisable;
import net.minecraft.nbt.NbtCompound;
public final class SpellReference<T extends Spell> implements NbtSerialisable {
@Nullable
private transient T spell;
private int nbtHash;
@Nullable
public T get() {
return spell == null || spell.isDead() ? null : spell;
}
public void set(T spell) {
set(spell, null);
}
public boolean hasDirtySpell() {
return spell != null && spell.isDirty();
}
public boolean set(T spell, @Nullable Caster<?> owner) {
spell = spell == null || spell.isDead() ? null : spell;
if (spell == this.spell) {
return false;
}
T oldValue = this.spell;
this.spell = spell;
nbtHash = 0;
if (owner != null && oldValue != null && (spell == null || !oldValue.getUuid().equals(spell.getUuid()))) {
oldValue.destroy(owner);
}
return true;
}
@Override
public void toNBT(NbtCompound compound) {
if (spell != null && !spell.isDead()) {
spell.toNBT(compound);
spell.getType().toNbt(compound);
}
}
@Override
public void fromNBT(NbtCompound compound) {
final int hash = compound.hashCode();
if (nbtHash == hash) {
return;
}
nbtHash = hash;
if (spell == null || !Objects.equals(Spell.getUuid(compound), spell.getUuid())) {
spell = Spell.readNbt(compound);
} else {
spell.fromNBT(compound);
}
}
}

View file

@ -17,20 +17,20 @@ import net.minecraft.world.World;
public final class ThrowableSpell extends AbstractDelegatingSpell {
private Spell spell;
private final SpellReference<Spell> spell = new SpellReference<>();
public ThrowableSpell(CustomisedSpellType<?> type) {
super(type);
}
public ThrowableSpell setSpell(Spell spell) {
this.spell = spell;
this.spell.set(spell);
return this;
}
@Override
public Collection<Spell> getDelegates() {
return List.of(spell);
return List.of(spell.get());
}
@Override
@ -63,7 +63,7 @@ public final class ThrowableSpell extends AbstractDelegatingSpell {
return Optional.empty();
}
Spell s = spell.prepareForCast(caster, CastingMethod.STORED);
Spell s = spell.get().prepareForCast(caster, CastingMethod.STORED);
if (s == null) {
return Optional.empty();
}
@ -71,7 +71,7 @@ public final class ThrowableSpell extends AbstractDelegatingSpell {
MagicProjectileEntity projectile = UEntities.MAGIC_BEAM.create(world);
projectile.setPosition(entity.getX(), entity.getEyeY() - 0.1F, entity.getZ());
projectile.setOwner(entity);
projectile.setItem(UItems.GEMSTONE.getDefaultStack(spell.getType()));
projectile.setItem(UItems.GEMSTONE.getDefaultStack(spell.get().getType()));
s.apply(projectile);
projectile.setVelocity(entity, entity.getPitch(), entity.getYaw(), 0, 1.5F, divergance);
projectile.setNoGravity(true);
@ -83,12 +83,12 @@ public final class ThrowableSpell extends AbstractDelegatingSpell {
@Override
protected void loadDelegates(NbtCompound compound) {
spell = Spell.SERIALIZER.read(compound.getCompound("spell"));
spell.fromNBT(compound.getCompound("spell"));
}
@Override
protected void saveDelegates(NbtCompound compound) {
compound.put("spell", Spell.SERIALIZER.write(spell));
compound.put("spell", spell.toNBT());
}
@Override

View file

@ -115,7 +115,7 @@ public abstract class AbstractSpell implements Spell {
@Override
public void fromNBT(NbtCompound compound) {
dirty = false;
if (compound.contains("uuid")) {
if (compound.containsUuid("uuid")) {
uuid = compound.getUuid("uuid");
}
dying = compound.getBoolean("dying");

View file

@ -78,6 +78,7 @@ public class ShieldSpell extends AbstractSpell {
@Override
public boolean tick(Caster<?> source, Situation situation) {
rangeMultiplier.update(source instanceof Pony pony && pony.asEntity().isSneaking() ? 1 : 2, 500L);
radius.update((float)getDrawDropOffRange(source), 200L);
if (source.isClient()) {
@ -104,6 +105,8 @@ public class ShieldSpell extends AbstractSpell {
@Override
public void tickDying(Caster<?> caster) {
rangeMultiplier.update(caster instanceof Pony pony && pony.asEntity().isSneaking() ? 1 : 2, 10L);
radius.update((float)getDrawDropOffRange(caster), 10L);
prevTicksDying = ticksDying;
if (ticksDying++ > 25) {
super.tickDying(caster);
@ -124,7 +127,7 @@ public class ShieldSpell extends AbstractSpell {
public float getRadius(float tickDelta) {
float base = radius.getValue();
float scale = 1 - MathHelper.clamp(MathHelper.lerp(tickDelta, (float)prevTicksDying, ticksDying), 0, 1);
float scale = 1 - MathHelper.clamp(MathHelper.lerp(tickDelta, (float)prevTicksDying, ticksDying) / 25F, 0, 1);
return base * scale;
}
@ -132,8 +135,6 @@ public class ShieldSpell extends AbstractSpell {
* Calculates the maximum radius of the shield. aka The area of effect.
*/
public double getDrawDropOffRange(Caster<?> source) {
rangeMultiplier.update(source instanceof Pony pony && pony.asEntity().isSneaking() ? 1 : 2, 500L);
float min = (source instanceof Pony ? 4 : 6) + getTraits().get(Trait.POWER);
double range = (min + (source.getLevel().getScaled(source instanceof Pony ? 4 : 40) * (source instanceof Pony ? 2 : 10))) / rangeMultiplier.getValue();

View file

@ -36,6 +36,7 @@ public class ShieldSpellRenderer extends SpellRenderer<ShieldSpell> {
float thickness = 0.02F * MathHelper.sin(animationProgress / 30F);
float alpha = 1 - Math.abs(MathHelper.sin(animationProgress / 20F)) * 0.2F;
alpha *= MathHelper.clamp(radius - 1, 0, 1);
if (firstPerson) {
matrices.translate(0, -1.75F, 0);

View file

@ -1,23 +1,17 @@
package com.minelittlepony.unicopia.network.datasync;
import java.util.Objects;
import java.util.Optional;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellReference;
import net.minecraft.nbt.NbtCompound;
public class SpellNetworkedReference<T extends Spell> implements NetworkedReference<T> {
private Optional<T> currentValue = Optional.empty();
@Nullable
private NbtCompound lastValue;
private final SpellReference<T> currentValue = new SpellReference<>();
private final Caster<?> owner;
private boolean dirty;
public SpellNetworkedReference(Caster<?> owner) {
@ -26,81 +20,29 @@ public class SpellNetworkedReference<T extends Spell> implements NetworkedRefere
@Override
public Optional<T> getReference() {
return currentValue.filter(s -> !s.isDead());
}
private boolean mustDelete(@Nullable NbtCompound comp) {
return comp == null || !comp.contains("effect_id") || !comp.contains("uuid");
}
private boolean mustReplace(NbtCompound comp) {
return currentValue.isEmpty() || !currentValue.get().getUuid().equals(comp.getUuid("uuid"));
}
private boolean mustUpdate(NbtCompound comp) {
if (owner.isClient() && !Objects.equals(lastValue, comp)) {
lastValue = comp;
return true;
}
return false;
}
private boolean mustSend() {
return currentValue.filter(Spell::isDirty).isPresent();
return Optional.ofNullable(currentValue.get());
}
@Override
public void updateReference(@Nullable T newValue) {
newValue = newValue == null || newValue.isDead() ? null : newValue;
@Nullable
T oldValue = currentValue.orElse(null);
if (oldValue != newValue) {
dirty = true;
currentValue = Optional.ofNullable(newValue);
if (oldValue != null && (newValue == null || !oldValue.getUuid().equals(newValue.getUuid()))) {
oldValue.destroy(owner);
}
}
dirty |= currentValue.set(newValue, owner);
}
@Override
@SuppressWarnings("unchecked")
public boolean fromNbt(NbtCompound comp) {
dirty = false;
if (mustDelete(comp)) {
updateReference(null);
return false;
}
if (mustReplace(comp)) {
updateReference((T)Spell.readNbt(comp));
return false;
}
if (mustUpdate(comp)) {
currentValue.ifPresent(s -> s.fromNBT(comp));
return false;
}
if (mustSend()) {
updateReference(getReference().orElse(null));
return true;
}
return false;
currentValue.fromNBT(comp);
return isDirty();
}
@Override
public NbtCompound toNbt() {
dirty = false;
return getReference().map(Spell::writeNbt).orElseGet(NbtCompound::new);
return currentValue.toNBT();
}
@Override
public boolean isDirty() {
return dirty || mustSend();
return !owner.isClient() && (dirty || currentValue.hasDirtySpell());
}
}