diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/MultiSpellSlot.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/MultiSpellSlot.java index 21d5c7a2..e38184b7 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/MultiSpellSlot.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/MultiSpellSlot.java @@ -1,18 +1,25 @@ package com.minelittlepony.unicopia.ability.magic; +import java.util.Optional; import java.util.UUID; import java.util.stream.Stream; import org.jetbrains.annotations.Nullable; +import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.SpellReference; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; +import com.minelittlepony.unicopia.network.track.MsgTrackedValues; import com.minelittlepony.unicopia.network.track.ObjectTracker; import com.minelittlepony.unicopia.network.track.Trackable; import com.minelittlepony.unicopia.network.track.TrackableObject; import com.minelittlepony.unicopia.util.NbtSerialisable; +import com.minelittlepony.unicopia.util.serialization.PacketCodec; + +import io.netty.buffer.Unpooled; import net.minecraft.nbt.NbtCompound; +import net.minecraft.network.PacketByteBuf; /** * Container for multiple spells @@ -21,7 +28,7 @@ import net.minecraft.nbt.NbtCompound; */ class MultiSpellSlot implements SpellSlots, NbtSerialisable { private final Caster owner; - private final ObjectTracker> tracker; + private final ObjectTracker> tracker; public MultiSpellSlot(Caster owner) { this.owner = owner; @@ -41,7 +48,7 @@ class MultiSpellSlot implements SpellSlots, NbtSerialisable { @Override public void put(@Nullable Spell effect) { if (effect != null) { - tracker.add(new Entry<>(owner, effect)); + tracker.add(effect.getUuid(), new Entry<>(owner, effect)); } } @@ -62,18 +69,32 @@ class MultiSpellSlot implements SpellSlots, NbtSerialisable { @Override public void toNBT(NbtCompound compound) { - compound.put("spells", tracker.toNBT()); + compound.put("spells", NbtSerialisable.writeMap(tracker.entries(), UUID::toString, entry -> entry.spell.toNBT())); } @Override public void fromNBT(NbtCompound compound) { - tracker.fromNBT(compound.getCompound("spells")); + tracker.load(NbtSerialisable.readMap(compound.getCompound("spells"), key -> { + try { + return UUID.fromString(key); + } catch (Throwable ignore) {} + return null; + }, (key, nbt) -> { + try { + Entry entry = new Entry<>(owner); + entry.spell.fromNBT((NbtCompound)nbt); + return entry; + } catch (Throwable t) { + Unicopia.LOGGER.warn("Exception loading tracked object: {}", t.getMessage()); + } + return null; + })); } - static final class Entry implements TrackableObject { + static final class Entry implements TrackableObject> { private final Caster owner; final SpellReference spell = new SpellReference<>(); - private Status status = Status.NEW; + private boolean hasValue; public Entry(Caster owner) { this.owner = owner; @@ -99,34 +120,63 @@ class MultiSpellSlot implements SpellSlots, NbtSerialisable { } } - @Override - public UUID getUuid() { - return spell.get().getUuid(); - } - @Override public Status getStatus() { - try { - if (spell.get() == null) { - return Status.REMOVED; - } - if (spell.hasDirtySpell()) { - return Status.UPDATED; - } - return status; - } finally { - status = Status.DEFAULT; + boolean hasValue = spell.get() != null; + if (hasValue != this.hasValue) { + this.hasValue = hasValue; + return hasValue ? Status.NEW : Status.REMOVED; } + + return spell.hasDirtySpell() ? Status.UPDATED : Status.DEFAULT; } @Override - public NbtCompound toTrackedNbt() { + public void readTrackedNbt(NbtCompound nbt) { + spell.fromNBT(nbt); + } + + @Override + public NbtCompound writeTrackedNbt() { return spell.toNBT(); } @Override - public void readTrackedNbt(NbtCompound compound) { - spell.fromNBT(compound); + public void read(PacketByteBuf buffer) { + byte contentType = buffer.readByte(); + if (contentType == 1) { + readTrackedNbt(PacketCodec.COMPRESSED_NBT.read(buffer)); + } else { + T spell = this.spell.get(); + if (spell != null) { + spell.getDataTracker().load(new MsgTrackedValues.TrackerEntries(buffer)); + } + } + } + + @Override + public Optional write(Status status) { + if (status != Status.DEFAULT) { + PacketByteBuf buffer = new PacketByteBuf(Unpooled.buffer()); + buffer.writeByte(1); + PacketCodec.COMPRESSED_NBT.write(buffer, spell.toNBT()); + return Optional.of(buffer); + } + @Nullable T spell = this.spell.get(); + if (spell == null) { + return Optional.empty(); + } + return spell.getDataTracker().getDirtyPairs().map(entries -> { + PacketByteBuf buffer = new PacketByteBuf(Unpooled.buffer()); + buffer.writeByte(0); + entries.write(buffer); + return buffer; + }); + } + + @Override + public void copyTo(Entry destination) { + destination.spell.set(spell.get()); } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/SingleSpellSlot.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/SingleSpellSlot.java index 73a1c6f2..94d6c664 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/SingleSpellSlot.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/SingleSpellSlot.java @@ -58,12 +58,12 @@ class SingleSpellSlot implements SpellSlots, NbtSerialisable { @Override public void toNBT(NbtCompound compound) { - compound.put("effect", entry.toTrackedNbt()); + compound.put("effect", entry.spell.toNBT()); } @Override public void fromNBT(NbtCompound compound) { - entry.readTrackedNbt(compound.getCompound("effect")); + entry.spell.fromNBT(compound.getCompound("effect")); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/AbstractDelegatingSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/AbstractDelegatingSpell.java index ffd032c7..78373b5a 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/AbstractDelegatingSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/AbstractDelegatingSpell.java @@ -7,15 +7,12 @@ import com.google.common.base.MoreObjects; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.SpellPredicate; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; +import com.minelittlepony.unicopia.network.track.DataTracker; import com.minelittlepony.unicopia.server.world.Ether; import net.minecraft.nbt.NbtCompound; public abstract class AbstractDelegatingSpell implements Spell { - private boolean dirty; - private boolean hidden; - private boolean destroyed; - private UUID uuid = UUID.randomUUID(); private final CustomisedSpellType type; @@ -25,10 +22,20 @@ public abstract class AbstractDelegatingSpell implements Spell { this.type = type; } + public AbstractDelegatingSpell(CustomisedSpellType type, Spell delegate) { + this.type = type; + this.delegate.set(delegate); + } + public final Spell getDelegate() { return delegate.get(); } + @Override + public final DataTracker getDataTracker() { + return getOrEmpty().getDataTracker(); + } + private Spell getOrEmpty() { return MoreObjects.firstNonNull(delegate.get(), EmptySpell.INSTANCE); } @@ -73,19 +80,21 @@ public abstract class AbstractDelegatingSpell implements Spell { return getOrEmpty().isDying(); } + @Deprecated @Override public boolean isDirty() { - return dirty || delegate.hasDirtySpell(); + return delegate.hasDirtySpell(); } + @Deprecated @Override public void setDirty() { - dirty = true; + getOrEmpty().setDirty(); } @Override public boolean isHidden() { - return hidden || getOrEmpty().isHidden(); + return getOrEmpty().isHidden(); } @Override @@ -95,15 +104,6 @@ public abstract class AbstractDelegatingSpell implements Spell { @Override public final void destroy(Caster caster) { - if (destroyed) { - return; - } - destroyed = true; - setDead(); - onDestroyed(caster); - } - - protected void onDestroyed(Caster caster) { if (!caster.isClient()) { Ether.get(caster.asWorld()).remove(this, caster); } @@ -123,14 +123,11 @@ public abstract class AbstractDelegatingSpell implements Spell { @Override public void toNBT(NbtCompound compound) { compound.putUuid("uuid", uuid); - compound.putBoolean("hidden", hidden); compound.put("spell", delegate.toNBT()); } @Override public void fromNBT(NbtCompound compound) { - dirty = false; - hidden = compound.getBoolean("hidden"); if (compound.contains("uuid")) { uuid = compound.getUuid("uuid"); } @@ -139,6 +136,6 @@ public abstract class AbstractDelegatingSpell implements Spell { @Override public final String toString() { - return "Delegate{" + getTypeAndTraits() + "}[uuid=" + uuid + ", destroyed=" + destroyed + ", hidden=" + hidden + "][spell=" + delegate.get() + "]"; + return "Delegate{" + getTypeAndTraits() + "}[uuid=" + uuid + "][spell=" + delegate.get() + "]"; } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/DispersableDisguiseSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/DispersableDisguiseSpell.java index d659cc90..7f614bc6 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/DispersableDisguiseSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/DispersableDisguiseSpell.java @@ -9,6 +9,8 @@ import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellTyp import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.entity.behaviour.EntityAppearance; import com.minelittlepony.unicopia.entity.player.Pony; +import com.minelittlepony.unicopia.network.track.DataTracker; +import com.minelittlepony.unicopia.network.track.TrackableDataType; import com.minelittlepony.unicopia.particle.MagicParticleEffect; import com.minelittlepony.unicopia.particle.UParticles; import net.minecraft.entity.Entity; @@ -21,6 +23,7 @@ import net.minecraft.nbt.NbtCompound; */ public class DispersableDisguiseSpell extends AbstractDisguiseSpell implements IllusionarySpell { + private final DataTracker.Entry suppressed = dataTracker.startTracking(TrackableDataType.BOOLEAN, false); private int suppressionCounter; public DispersableDisguiseSpell(CustomisedSpellType type) { @@ -37,7 +40,7 @@ public class DispersableDisguiseSpell extends AbstractDisguiseSpell implements I public void onSuppressed(Caster otherSource, float time) { time /= getTraits().getOrDefault(Trait.STRENGTH, 1); suppressionCounter = (int)time; - setDirty(); + suppressed.set(true); } @Override @@ -64,7 +67,9 @@ public class DispersableDisguiseSpell extends AbstractDisguiseSpell implements I Entity appearance = getDisguise().getAppearance(); if (isSuppressed()) { - suppressionCounter--; + if (--suppressionCounter <= 0) { + suppressed.set(false); + } owner.setInvisible(false); if (source instanceof Pony) { @@ -92,6 +97,9 @@ public class DispersableDisguiseSpell extends AbstractDisguiseSpell implements I public void fromNBT(NbtCompound compound) { super.fromNBT(compound); suppressionCounter = compound.getInt("suppressionCounter"); + if (suppressionCounter > 0) { + suppressed.set(true); + } } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/EmptySpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/EmptySpell.java index 202c3830..89b3f986 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/EmptySpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/EmptySpell.java @@ -5,6 +5,7 @@ import java.util.UUID; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; +import com.minelittlepony.unicopia.network.track.DataTracker; import net.minecraft.nbt.NbtCompound; import net.minecraft.util.Util; @@ -74,4 +75,9 @@ public final class EmptySpell implements Spell { public String toString() { return "EmptySpell{}"; } + + @Override + public DataTracker getDataTracker() { + return null; + } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlacementControlSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlacementControlSpell.java index f486c0d4..9eee830f 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlacementControlSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlacementControlSpell.java @@ -7,6 +7,7 @@ import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.effect.AbstractSpell; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; +import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.entity.mob.CastSpellEntity; import com.minelittlepony.unicopia.server.world.Ether; import com.minelittlepony.unicopia.util.NbtSerialisable; @@ -34,8 +35,8 @@ public class PlacementControlSpell extends AbstractSpell implements OrientedSpel super(type); } - PlacementControlSpell(CustomisedSpellType type, Spell delegate) { - this(type); + PlacementControlSpell(Spell delegate) { + this(SpellType.PLACE_CONTROL_SPELL.withTraits(delegate.getTypeAndTraits().traits())); this.delegate = delegate; } @@ -50,10 +51,12 @@ public class PlacementControlSpell extends AbstractSpell implements OrientedSpel public void setDimension(RegistryKey dimension) { this.dimension = Optional.of(dimension); + setDirty(); } public void setPosition(Vec3d position) { this.position = Optional.of(position); + setDirty(); } @Override @@ -98,7 +101,6 @@ public class PlacementControlSpell extends AbstractSpell implements OrientedSpel entity.getWorld().spawnEntity(entity); placedEntityId = entity.getUuid(); - setDirty(); } return result; } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/RageAbilitySpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/RageAbilitySpell.java index 62ef6a43..dbf98cf2 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/RageAbilitySpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/RageAbilitySpell.java @@ -54,7 +54,6 @@ public class RageAbilitySpell extends AbstractSpell { ticksExtenguishing++; source.playSound(USounds.Vanilla.ENTITY_GENERIC_EXTINGUISH_FIRE, 1); source.spawnParticles(ParticleTypes.CLOUD, 12); - setDirty(); } else { ticksExtenguishing = 0; } @@ -127,9 +126,6 @@ public class RageAbilitySpell extends AbstractSpell { age++; source.asEntity().setInvulnerable(age < 25); - - - setDirty(); return true; } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/Spell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/Spell.java index 92f44adf..aab10703 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/Spell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/Spell.java @@ -13,6 +13,7 @@ import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.SpellPredicate; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; +import com.minelittlepony.unicopia.network.track.DataTracker; import com.minelittlepony.unicopia.server.world.Ether; import com.minelittlepony.unicopia.util.NbtSerialisable; @@ -30,6 +31,8 @@ public interface Spell extends NbtSerialisable, Affine { */ CustomisedSpellType getTypeAndTraits(); + DataTracker getDataTracker(); + default boolean isOf(SpellType type) { return getTypeAndTraits().type() == type; } @@ -74,8 +77,15 @@ public interface Spell extends NbtSerialisable, Affine { /** * Returns true if this effect has changes that need to be sent to the client. */ + @Deprecated boolean isDirty(); + /** + * Marks this effect as dirty. + */ + @Deprecated + void setDirty(); + /** * Applies this spell to the supplied caster. * @param caster The caster to apply the spell to @@ -110,11 +120,6 @@ public interface Spell extends NbtSerialisable, Affine { */ void tickDying(Caster caster); - /** - * Marks this effect as dirty. - */ - void setDirty(); - boolean isHidden(); void setHidden(boolean hidden); @@ -128,7 +133,7 @@ public interface Spell extends NbtSerialisable, Affine { * Converts this spell into a placeable spell. */ default PlacementControlSpell toPlaceable() { - return new PlacementControlSpell(SpellType.PLACE_CONTROL_SPELL.withTraits(), this); + return new PlacementControlSpell(this); } /** @@ -136,7 +141,7 @@ public interface Spell extends NbtSerialisable, Affine { * @return */ default ThrowableSpell toThrowable() { - return SpellType.THROWN_SPELL.withTraits().create().setSpell(this); + return new ThrowableSpell(this); } @Nullable diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/SpellReference.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/SpellReference.java index 6c3d22e9..4bab564c 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/SpellReference.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/SpellReference.java @@ -15,7 +15,6 @@ import net.minecraft.nbt.NbtCompound; public final class SpellReference implements NbtSerialisable { @Nullable private transient T spell; - private int nbtHash; @Nullable public T get() { @@ -26,6 +25,7 @@ public final class SpellReference implements NbtSerialisable { set(spell, null); } + @Deprecated public boolean hasDirtySpell() { return spell != null && spell.isDirty(); } @@ -37,7 +37,6 @@ public final class SpellReference implements NbtSerialisable { } T oldValue = this.spell; this.spell = spell; - nbtHash = 0; if (owner != null && oldValue != null && (spell == null || !oldValue.getUuid().equals(spell.getUuid()))) { oldValue.destroy(owner); } @@ -65,19 +64,9 @@ public final class SpellReference implements NbtSerialisable { @Override public void fromNBT(NbtCompound compound) { - fromNBT(compound, true); - } - - public void fromNBT(NbtCompound compound, boolean force) { - 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 if (force || !spell.isDirty()) { + } else { spell.fromNBT(compound); } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/ThrowableSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/ThrowableSpell.java index b823cf6f..d722147d 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/ThrowableSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/ThrowableSpell.java @@ -5,6 +5,7 @@ import java.util.Optional; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; +import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.projectile.MagicBeamEntity; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.ProjectileDelegate; @@ -20,9 +21,8 @@ public final class ThrowableSpell extends AbstractDelegatingSpell implements super(type); } - public ThrowableSpell setSpell(Spell spell) { - delegate.set(spell); - return this; + public ThrowableSpell(Spell delegate) { + super(SpellType.THROWN_SPELL.withTraits(delegate.getTypeAndTraits().traits()), delegate); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AbstractSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AbstractSpell.java index f7cef06d..6d08d6a9 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AbstractSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AbstractSpell.java @@ -5,26 +5,34 @@ import java.util.UUID; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; +import com.minelittlepony.unicopia.network.track.DataTracker; +import com.minelittlepony.unicopia.network.track.TrackableDataType; import com.minelittlepony.unicopia.server.world.Ether; import net.minecraft.nbt.NbtCompound; public abstract class AbstractSpell implements Spell { - private boolean dead; - private boolean dying; - private boolean dirty; - private boolean hidden; - private boolean destroyed; - + private UUID uuid = UUID.randomUUID(); private final CustomisedSpellType type; - private UUID uuid = UUID.randomUUID(); + protected final DataTracker dataTracker = new DataTracker(0); + + private final DataTracker.Entry dead = dataTracker.startTracking(TrackableDataType.BOOLEAN, false); + private final DataTracker.Entry dying = dataTracker.startTracking(TrackableDataType.BOOLEAN, false); + private boolean dirty; + private final DataTracker.Entry hidden = dataTracker.startTracking(TrackableDataType.BOOLEAN, false); + private boolean destroyed; protected AbstractSpell(CustomisedSpellType type) { this.type = type; } + @Override + public final DataTracker getDataTracker() { + return dataTracker; + } + @Override public final UUID getUuid() { return uuid; @@ -45,25 +53,26 @@ public abstract class AbstractSpell implements Spell { @Override public final void setDead() { - dying = true; - setDirty(); + dying.set(true); } @Override public final boolean isDead() { - return dead; + return dead.get(); } @Override public final boolean isDying() { - return dying; + return dying.get(); } + @Deprecated @Override public final boolean isDirty() { return dirty; } + @Deprecated @Override public final void setDirty() { dirty = true; @@ -71,17 +80,17 @@ public abstract class AbstractSpell implements Spell { @Override public final boolean isHidden() { - return hidden; + return hidden.get(); } @Override public final void setHidden(boolean hidden) { - this.hidden = hidden; + this.hidden.set(hidden); } @Override public void tickDying(Caster caster) { - dead = true; + dead.set(true); } @Override @@ -102,9 +111,9 @@ public abstract class AbstractSpell implements Spell { @Override public void toNBT(NbtCompound compound) { - compound.putBoolean("dying", dying); - compound.putBoolean("dead", dead); - compound.putBoolean("hidden", hidden); + compound.putBoolean("dying", dying.get()); + compound.putBoolean("dead", dead.get()); + compound.putBoolean("hidden", hidden.get()); compound.putUuid("uuid", uuid); compound.put("traits", getTraits().toNbt()); } @@ -115,9 +124,9 @@ public abstract class AbstractSpell implements Spell { if (compound.containsUuid("uuid")) { uuid = compound.getUuid("uuid"); } - dying = compound.getBoolean("dying"); - dead = compound.getBoolean("dead"); - hidden = compound.getBoolean("hidden"); + dying.set(compound.getBoolean("dying")); + dead.set(compound.getBoolean("dead")); + hidden.set(compound.getBoolean("hidden")); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AttractiveSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AttractiveSpell.java index c951cb9f..04a0185d 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AttractiveSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AttractiveSpell.java @@ -42,6 +42,7 @@ public class AttractiveSpell extends ShieldSpell implements HomingSpell, TimedSp protected AttractiveSpell(CustomisedSpellType type) { super(type); timer = new Timer(BASE_DURATION + TimedSpell.getExtraDuration(getTraits())); + dataTracker.startTracking(target); } @Override @@ -57,8 +58,6 @@ public class AttractiveSpell extends ShieldSpell implements HomingSpell, TimedSp if (timer.getTicksRemaining() <= 0) { return false; } - - setDirty(); } target.getOrEmpty(caster.asWorld()) diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AwkwardSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AwkwardSpell.java index 964fe1d4..dab51b1b 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AwkwardSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AwkwardSpell.java @@ -40,8 +40,6 @@ public class AwkwardSpell extends AbstractSpell implements TimedSpell { if (timer.getTicksRemaining() <= 0) { return false; } - - setDirty(); } if (source.isClient()) { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/BubbleSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/BubbleSpell.java index 925a8c5a..bd24dfae 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/BubbleSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/BubbleSpell.java @@ -12,6 +12,8 @@ import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.entity.*; import com.minelittlepony.unicopia.entity.mob.UEntityAttributes; import com.minelittlepony.unicopia.entity.player.Pony; +import com.minelittlepony.unicopia.network.track.DataTracker; +import com.minelittlepony.unicopia.network.track.TrackableDataType; import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.ProjectileDelegate; @@ -55,15 +57,15 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell, private final Timer timer; - private int struggles; - private float prevRadius; - private float radius; + private DataTracker.Entry radius; + private DataTracker.Entry struggles; protected BubbleSpell(CustomisedSpellType type) { super(type); timer = new Timer(BASE_DURATION + TimedSpell.getExtraDuration(getTraits())); - struggles = (int)(getTraits().get(Trait.POWER) * 2); + radius = dataTracker.startTracking(TrackableDataType.FLOAT, 0F); + struggles = dataTracker.startTracking(TrackableDataType.INT, (int)(getTraits().get(Trait.POWER) * 2)); } @Override @@ -72,7 +74,7 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell, } public float getRadius(float tickDelta) { - return MathHelper.lerp(tickDelta, prevRadius, radius); + return MathHelper.lerp(tickDelta, prevRadius, radius.get()); } @Override @@ -91,7 +93,7 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell, } }); } - radius = Math.max(entity.getHeight(), entity.getWidth()) * 1.2F; + radius.set(Math.max(entity.getHeight(), entity.getWidth()) * 1.2F); source.playSound(USounds.ENTITY_PLAYER_UNICORN_TELEPORT, 1); entity.addVelocity(0, 0.2F * source.getPhysics().getGravitySignum(), 0); Living.updateVelocity(entity); @@ -111,7 +113,7 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell, boolean done = timer.getTicksRemaining() <= 0; - source.spawnParticles(source.getOriginVector().add(0, 1, 0), new Sphere(true, radius * (done ? 0.25F : 0.5F)), done ? 13 : 1, pos -> { + source.spawnParticles(source.getOriginVector().add(0, 1, 0), new Sphere(true, radius.get() * (done ? 0.25F : 0.5F)), done ? 13 : 1, pos -> { source.addParticle(done ? ParticleTypes.BUBBLE_POP : UParticles.BUBBLE, pos, Vec3d.ZERO); }); @@ -119,8 +121,6 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell, return false; } - setDirty(); - source.asEntity().addVelocity( MathHelper.sin(source.asEntity().age / 6F) / 50F, MathHelper.sin(source.asEntity().age / 6F) / 50F, @@ -129,13 +129,14 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell, source.asEntity().fallDistance = 0; - prevRadius = radius; + prevRadius = radius.get(); if (source instanceof Pony pony && pony.sneakingChanged() && pony.asEntity().isSneaking()) { - setDirty(); - radius += 0.5F; + radius.set(radius.get() + 0.5F); source.playSound(USounds.SPELL_BUBBLE_DISTURB, 1); - if (struggles-- <= 0) { + int s = struggles.get() - 1; + struggles.set(s); + if (s <= 0) { setDead(); return false; } @@ -168,16 +169,16 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell, @Override public void toNBT(NbtCompound compound) { super.toNBT(compound); - compound.putInt("struggles", struggles); - compound.putFloat("radius", radius); + compound.putInt("struggles", struggles.get()); + compound.putFloat("radius", radius.get()); timer.toNBT(compound); } @Override public void fromNBT(NbtCompound compound) { super.fromNBT(compound); - struggles = compound.getInt("struggles"); - radius = compound.getFloat("radius"); + struggles.set(compound.getInt("struggles")); + radius.set(compound.getFloat("radius")); timer.fromNBT(compound); } } 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 e46941c0..14eaa665 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 @@ -17,6 +17,8 @@ import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.entity.Living; import com.minelittlepony.unicopia.entity.damage.UDamageTypes; import com.minelittlepony.unicopia.entity.mob.CastSpellEntity; +import com.minelittlepony.unicopia.network.track.DataTracker; +import com.minelittlepony.unicopia.network.track.TrackableDataType; import com.minelittlepony.unicopia.particle.FollowingParticleEffect; import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect; import com.minelittlepony.unicopia.particle.ParticleUtils; @@ -56,7 +58,7 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate .with(Trait.DARKNESS, 100) .build(); - private float accumulatedMass = 0; + private final DataTracker.Entry accumulatedMass = this.dataTracker.startTracking(TrackableDataType.FLOAT, 0F); private final TargetSelecter targetSelecter = new TargetSelecter(this).setFilter(this::isValidTarget).setTargetowner(true).setTargetAllies(true); @@ -70,7 +72,7 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate // 3. force reaches 0 at distance of drawDropOffRange private double getMass() { - return 0.1F + accumulatedMass / 10F; + return 0.1F + accumulatedMass.get() / 10F; } public double getEventHorizonRadius() { @@ -174,11 +176,12 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate @Override public void tickDying(Caster source) { - accumulatedMass -= 0.8F; + float m = accumulatedMass.get() - 0.8F; + accumulatedMass.set(m); double mass = getMass() * 0.1; double logarithm = 1 - (1D / (1 + (mass * mass))); radius.update((float)Math.max(0.1, logarithm * source.asWorld().getGameRules().getInt(UGameRules.MAX_DARK_VORTEX_SIZE)), 200L); - if (accumulatedMass < 1) { + if (m < 1) { super.tickDying(source); } @@ -202,7 +205,7 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate @Override public boolean isFriendlyTogether(Affine other) { - return accumulatedMass < 4; + return accumulatedMass.get() < 4; } private boolean isValidTarget(Caster source, Entity entity) { @@ -274,8 +277,7 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate double massOfTarget = AttractionUtils.getMass(target); if (!source.isClient() && massOfTarget != 0) { - accumulatedMass += massOfTarget; - setDirty(); + accumulatedMass.set((float)(accumulatedMass.get() + massOfTarget)); } target.damage(source.damageOf(UDamageTypes.GAVITY_WELL_RECOIL, source), Integer.MAX_VALUE); @@ -303,12 +305,12 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate @Override public void toNBT(NbtCompound compound) { super.toNBT(compound); - compound.putFloat("accumulatedMass", accumulatedMass); + compound.putFloat("accumulatedMass", accumulatedMass.get()); } @Override public void fromNBT(NbtCompound compound) { super.fromNBT(compound); - accumulatedMass = compound.getFloat("accumulatedMass"); + accumulatedMass.set(compound.getFloat("accumulatedMass")); } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/FeatherFallSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/FeatherFallSpell.java index eb2bc744..59491f8f 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/FeatherFallSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/FeatherFallSpell.java @@ -69,8 +69,6 @@ public class FeatherFallSpell extends AbstractSpell implements TimedSpell { return false; } - setDirty(); - List targets = getTargets(caster).toList(); if (targets.isEmpty()) { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/LightSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/LightSpell.java index 48c99c0c..6825df24 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/LightSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/LightSpell.java @@ -63,8 +63,6 @@ public class LightSpell extends AbstractSpell implements TimedSpell, ProjectileD return false; } - setDirty(); - if (!caster.isClient()) { if (lights.isEmpty()) { int size = 2 + caster.asWorld().random.nextInt(2) + (int)(getTraits().get(Trait.LIFE, 10, 20) - 10)/10; @@ -83,7 +81,6 @@ public class LightSpell extends AbstractSpell implements TimedSpell, ProjectileD entity.getWorld().spawnEntity(entity); ref.set(entity); - setDirty(); } }); } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/MimicSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/MimicSpell.java index 12032913..0e599e8d 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/MimicSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/MimicSpell.java @@ -35,8 +35,6 @@ public class MimicSpell extends AbstractDisguiseSpell implements HomingSpell, Ti return false; } - setDirty(); - return super.tick(caster, situation); } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/MindSwapSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/MindSwapSpell.java index ed55f91b..eefb6ebb 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/MindSwapSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/MindSwapSpell.java @@ -135,7 +135,6 @@ public class MindSwapSpell extends MimicSpell implements ProjectileDelegate.Enti caster.playSound(USounds.SPELL_MINDSWAP_SWAP, 1); }); initialized = true; - setDirty(); } if (counterpart.isSet()) { @@ -143,13 +142,12 @@ public class MindSwapSpell extends MimicSpell implements ProjectileDelegate.Enti if (other == null) { caster.getOriginatingCaster().asEntity().damage(caster.asWorld().getDamageSources().magic(), Float.MAX_VALUE); - setDead(); + destroy(caster); return false; } if (!Caster.of(other).get().getSpellSlot().contains(SpellType.MIMIC)) { - onDestroyed(caster); - setDead(); + destroy(caster); return false; } } @@ -158,8 +156,7 @@ public class MindSwapSpell extends MimicSpell implements ProjectileDelegate.Enti counterpart.ifPresent(caster.asWorld(), e -> { e.damage(e.getDamageSources().magic(), Float.MAX_VALUE); }); - onDestroyed(caster); - setDead(); + destroy(caster); return false; } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/NecromancySpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/NecromancySpell.java index b1209943..c2063211 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/NecromancySpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/NecromancySpell.java @@ -131,7 +131,6 @@ public class NecromancySpell extends AbstractAreaEffectSpell implements Projecti float additional = source.asWorld().getLocalDifficulty(source.getOrigin()).getLocalDifficulty() + getTraits().get(Trait.CHAOS, 0, 10); - setDirty(); if (--spawnCountdown > 0 && !summonedEntities.isEmpty()) { return true; } @@ -213,7 +212,6 @@ public class NecromancySpell extends AbstractAreaEffectSpell implements Projecti source.asWorld().spawnEntity(minion); summonedEntities.add(new EntityReference<>(minion)); - setDirty(); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java index 8a012993..4277ff0c 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java @@ -14,6 +14,8 @@ import com.minelittlepony.unicopia.entity.Living; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.network.Channel; import com.minelittlepony.unicopia.network.MsgCasterLookRequest; +import com.minelittlepony.unicopia.network.track.DataTracker; +import com.minelittlepony.unicopia.network.track.TrackableDataType; import com.minelittlepony.unicopia.particle.*; import com.minelittlepony.unicopia.server.world.Ether; import com.minelittlepony.unicopia.util.shape.*; @@ -26,6 +28,7 @@ import net.minecraft.network.packet.s2c.play.PositionFlag; import net.minecraft.particle.ParticleTypes; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.Util; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; @@ -40,15 +43,13 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell. private static final Shape PARTICLE_AREA = new Sphere(true, 2, 1, 1, 0); @Nullable - private UUID targetPortalId; - private float targetPortalPitch; - private float targetPortalYaw; + private final DataTracker.Entry targetPortalId = dataTracker.startTracking(TrackableDataType.UUID, Util.NIL_UUID); + private final DataTracker.Entry targetPortalPitch = dataTracker.startTracking(TrackableDataType.FLOAT, 0F); + private final DataTracker.Entry targetPortalYaw = dataTracker.startTracking(TrackableDataType.FLOAT, 0F); private final EntityReference teleportationTarget = new EntityReference<>(); - private boolean publishedPosition; - - private float pitch; - private float yaw; + private final DataTracker.Entry pitch = dataTracker.startTracking(TrackableDataType.FLOAT, 0F); + private final DataTracker.Entry yaw = dataTracker.startTracking(TrackableDataType.FLOAT, 0F); private Shape particleArea = PARTICLE_AREA; @@ -61,30 +62,30 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell. } public float getPitch() { - return pitch; + return pitch.get(); } public float getYaw() { - return yaw; + return yaw.get(); } public float getTargetPitch() { - return targetPortalPitch; + return targetPortalPitch.get(); } public float getTargetYaw() { - return targetPortalYaw; + return targetPortalYaw.get(); } public float getYawDifference() { - return MathHelper.wrapDegrees(180 + targetPortalYaw - yaw); + return MathHelper.wrapDegrees(180 + getTargetYaw() - getYaw()); } @SuppressWarnings("unchecked") private Ether.Entry getDestination(Caster source) { - return targetPortalId == null ? null : getDestinationReference() + return Util.NIL_UUID.equals(targetPortalId.get()) ? null : getDestinationReference() .getTarget() - .map(target -> Ether.get(source.asWorld()).get((SpellType)getType(), target.uuid(), targetPortalId)) + .map(target -> Ether.get(source.asWorld()).get((SpellType)getType(), target.uuid(), targetPortalId.get())) .filter(destination -> destination.isClaimedBy(getUuid())) .orElse(null); } @@ -94,6 +95,18 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell. return toPlaceable().apply(caster); } + protected void setDestination(@Nullable Ether.Entry destination) { + if (destination == null) { + teleportationTarget.set(null); + targetPortalId.set(Util.NIL_UUID); + } else { + teleportationTarget.copyFrom(destination.entity); + targetPortalId.set(destination.getSpellId()); + targetPortalPitch.set(destination.getPitch()); + targetPortalYaw.set(destination.getYaw()); + } + } + @Override public boolean tick(Caster source, Situation situation) { if (situation == Situation.GROUND) { @@ -108,9 +121,7 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell. if (targetEntry == null) { if (teleportationTarget.isSet()) { - teleportationTarget.set(null); - targetPortalId = null; - setDirty(); + setDestination(null); source.asWorld().syncWorldEvent(WorldEvents.BLOCK_BROKEN, source.getOrigin(), Block.getRawIdFromState(Blocks.GLASS.getDefaultState())); } else { Ether.get(source.asWorld()).anyMatch(getType(), entry -> { @@ -119,18 +130,10 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell. ownEntry.claim(entry.getSpellId()); synchronized (entry) { if (entry.getSpell() instanceof PortalSpell portal) { - portal.teleportationTarget.copyFrom(ownEntry.entity); - portal.targetPortalId = getUuid(); - portal.targetPortalPitch = pitch; - portal.targetPortalYaw = yaw; - portal.setDirty(); + portal.setDestination(ownEntry); } } - teleportationTarget.copyFrom(entry.entity); - targetPortalId = entry.getSpellId(); - targetPortalPitch = entry.getPitch(); - targetPortalYaw = entry.getYaw(); - setDirty(); + setDestination(entry); } return false; }); @@ -142,8 +145,8 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell. } var entry = Ether.get(source.asWorld()).getOrCreate(this, source); - entry.setPitch(pitch); - entry.setYaw(yaw); + entry.setPitch(pitch.get()); + entry.setYaw(yaw.get()); } return !isDead(); @@ -154,7 +157,7 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell. source.findAllEntitiesInRange(1).forEach(entity -> { if (!entity.hasPortalCooldown()) { - float approachYaw = Math.abs(MathHelper.wrapDegrees(entity.getYaw() - this.yaw)); + float approachYaw = Math.abs(MathHelper.wrapDegrees(entity.getYaw() - this.yaw.get())); if (approachYaw > 80) { return; } @@ -177,7 +180,6 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell. entity.getWorld().playSoundFromEntity(null, entity, USounds.ENTITY_PLAYER_UNICORN_TELEPORT, entity.getSoundCategory(), 1, 1); entity.teleport((ServerWorld)entity.getWorld(), dest.x, dest.y, dest.z, PositionFlag.VALUES, yaw, entity.getPitch()); entity.getWorld().playSoundFromEntity(null, entity, USounds.ENTITY_PLAYER_UNICORN_TELEPORT, entity.getSoundCategory(), 1, 1); - setDirty(); Living.updateVelocity(entity); @@ -193,13 +195,12 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell. @Override public void setOrientation(Caster caster, float pitch, float yaw) { - this.pitch = 90 - pitch; - this.yaw = -yaw; + this.pitch.set(90 - pitch); + this.yaw.set(-yaw); particleArea = PARTICLE_AREA.rotate( - this.pitch * MathHelper.RADIANS_PER_DEGREE, - this.yaw * MathHelper.RADIANS_PER_DEGREE + this.pitch.get() * MathHelper.RADIANS_PER_DEGREE, + (180 - this.yaw.get()) * MathHelper.RADIANS_PER_DEGREE ); - setDirty(); } @Override @@ -227,30 +228,26 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell. @Override public void toNBT(NbtCompound compound) { super.toNBT(compound); - if (targetPortalId != null) { - compound.putUuid("targetPortalId", targetPortalId); - } - compound.putBoolean("publishedPosition", publishedPosition); + compound.putUuid("targetPortalId", targetPortalId.get()); compound.put("teleportationTarget", teleportationTarget.toNBT()); - compound.putFloat("pitch", pitch); - compound.putFloat("yaw", yaw); - compound.putFloat("targetPortalPitch", targetPortalPitch); - compound.putFloat("targetPortalYaw", targetPortalYaw); + compound.putFloat("pitch", getPitch()); + compound.putFloat("yaw", getYaw()); + compound.putFloat("targetPortalPitch", getTargetPitch()); + compound.putFloat("targetPortalYaw", getTargetYaw()); } @Override public void fromNBT(NbtCompound compound) { super.fromNBT(compound); - targetPortalId = compound.containsUuid("targetPortalId") ? compound.getUuid("targetPortalId") : null; - publishedPosition = compound.getBoolean("publishedPosition"); + targetPortalId.set(compound.containsUuid("targetPortalId") ? compound.getUuid("targetPortalId") : Util.NIL_UUID); teleportationTarget.fromNBT(compound.getCompound("teleportationTarget")); - pitch = compound.getFloat("pitch"); - yaw = compound.getFloat("yaw"); - targetPortalPitch = compound.getFloat("targetPortalPitch"); - targetPortalYaw = compound.getFloat("targetPortalYaw"); + pitch.set(compound.getFloat("pitch")); + yaw.set(compound.getFloat("yaw")); + targetPortalPitch.set(compound.getFloat("targetPortalPitch")); + targetPortalYaw.set(compound.getFloat("targetPortalYaw")); particleArea = PARTICLE_AREA.rotate( - pitch * MathHelper.RADIANS_PER_DEGREE, - (180 - yaw) * MathHelper.RADIANS_PER_DEGREE + pitch.get() * MathHelper.RADIANS_PER_DEGREE, + (180 - yaw.get()) * MathHelper.RADIANS_PER_DEGREE ); } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/SiphoningSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/SiphoningSpell.java index 42e784d1..a7e1cbfd 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/SiphoningSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/SiphoningSpell.java @@ -16,6 +16,8 @@ import com.minelittlepony.unicopia.ability.magic.spell.SpellAttributes; import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.entity.damage.UDamageTypes; import com.minelittlepony.unicopia.entity.player.Pony; +import com.minelittlepony.unicopia.network.track.DataTracker; +import com.minelittlepony.unicopia.network.track.TrackableDataType; import com.minelittlepony.unicopia.particle.FollowingParticleEffect; import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.particle.UParticles; @@ -43,6 +45,7 @@ public class SiphoningSpell extends AbstractAreaEffectSpell { tooltip.add(SpellAttributes.of(SpellAttributes.RANGE, 4)); } + private final DataTracker.Entry upset = dataTracker.startTracking(TrackableDataType.BOOLEAN, false); private int ticksUpset; protected SiphoningSpell(CustomisedSpellType type) { @@ -57,8 +60,8 @@ public class SiphoningSpell extends AbstractAreaEffectSpell { @Override public boolean tick(Caster source, Situation situation) { - if (ticksUpset > 0) { - ticksUpset--; + if (ticksUpset > 0 && --ticksUpset <= 0) { + upset.set(false); } if (source.isClient()) { @@ -108,7 +111,7 @@ public class SiphoningSpell extends AbstractAreaEffectSpell { } else { e.damage(damage, e.getHealth() / 4); ticksUpset = 100; - setDirty(); + upset.set(true); } } else { e.heal((float)Math.min(source.getLevel().getScaled(e.getHealth()) / 2F, maxHealthGain * 0.6)); @@ -174,5 +177,8 @@ public class SiphoningSpell extends AbstractAreaEffectSpell { public void fromNBT(NbtCompound compound) { super.fromNBT(compound); ticksUpset = compound.getInt("upset"); + if (ticksUpset > 0) { + upset.set(true); + } } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/SpellType.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/SpellType.java index 0490c0e0..c2255509 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/SpellType.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/SpellType.java @@ -45,7 +45,7 @@ public final class SpellType implements Affine, SpellPredicate< private static final DynamicCommandExceptionType UNKNOWN_SPELL_TYPE_EXCEPTION = new DynamicCommandExceptionType(id -> Text.translatable("spell_type.unknown", id)); public static final SpellType PLACE_CONTROL_SPELL = register("place_controller", SpellType.builder(PlacementControlSpell::new).affinity(Affinity.NEUTRAL).unobtainable().stackable().shape(GemstoneItem.Shape.DONUT)); - public static final SpellType THROWN_SPELL = register("thrown", builder(ThrowableSpell::new).affinity(Affinity.NEUTRAL).unobtainable().shape(GemstoneItem.Shape.DONUT)); + public static final SpellType THROWN_SPELL = register("thrown", SpellType.builder(ThrowableSpell::new).affinity(Affinity.NEUTRAL).unobtainable().shape(GemstoneItem.Shape.DONUT)); public static final SpellType CHANGELING_DISGUISE = register("disguise", builder(DispersableDisguiseSpell::new).affinity(Affinity.BAD).color(0x19E48E).unobtainable().shape(GemstoneItem.Shape.ARROW)); public static final SpellType FEED = register("feed", SpellType.builder(ChangelingFeedingSpell::new).affinity(Affinity.BAD).color(0xBDBDF9).unobtainable().shape(GemstoneItem.Shape.ARROW)); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/EntityReference.java b/src/main/java/com/minelittlepony/unicopia/entity/EntityReference.java index d4070f44..3f6e9ca0 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/EntityReference.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/EntityReference.java @@ -30,7 +30,7 @@ import net.minecraft.world.World; * * @param The type of the entity this reference points to. */ -public class EntityReference implements NbtSerialisable, TrackableObject { +public class EntityReference implements NbtSerialisable, TrackableObject> { private static final Serializer SERIALIZER = Serializer.of(EntityReference::new); @SuppressWarnings("unchecked") @@ -122,12 +122,7 @@ public class EntityReference implements NbtSerialisable, Track @Override public int hashCode() { - return getUuid().hashCode(); - } - - @Override - public UUID getUuid() { - return getTarget().map(EntityValues::uuid).orElse(Util.NIL_UUID); + return getTarget().map(EntityValues::uuid).orElse(Util.NIL_UUID).hashCode(); } @Override @@ -140,7 +135,7 @@ public class EntityReference implements NbtSerialisable, Track } @Override - public NbtCompound toTrackedNbt() { + public NbtCompound writeTrackedNbt() { return toNBT(); } @@ -149,6 +144,12 @@ public class EntityReference implements NbtSerialisable, Track fromNBT(compound); } + @Override + public void copyTo(EntityReference destination) { + destination.reference = reference; + destination.directReference = directReference; + } + @Override public void discard(boolean immediate) { set(null); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/CorruptionHandler.java b/src/main/java/com/minelittlepony/unicopia/entity/player/CorruptionHandler.java index 0ea3e639..489bf5b7 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/CorruptionHandler.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/CorruptionHandler.java @@ -38,12 +38,10 @@ public class CorruptionHandler implements Tickable { if (entity.age % (10 * ItemTracker.SECONDS) == 0) { if (random.nextInt(100) == 0) { pony.getCorruption().add(-1); - pony.setDirty(); } if (entity.getHealth() >= entity.getMaxHealth() - 1 && !entity.getHungerManager().isNotFull()) { pony.getCorruption().add(-random.nextInt(4)); - pony.setDirty(); } } } @@ -79,6 +77,5 @@ public class CorruptionHandler implements Tickable { MagicReserves reserves = pony.getMagicalReserves(); reserves.getExertion().addPercent(10); reserves.getEnergy().add(10); - pony.setDirty(); } } diff --git a/src/main/java/com/minelittlepony/unicopia/network/track/DataTracker.java b/src/main/java/com/minelittlepony/unicopia/network/track/DataTracker.java index bad61f3f..193246d3 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/track/DataTracker.java +++ b/src/main/java/com/minelittlepony/unicopia/network/track/DataTracker.java @@ -1,37 +1,33 @@ package com.minelittlepony.unicopia.network.track; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import net.minecraft.nbt.NbtCompound; import net.minecraft.network.PacketByteBuf; public class DataTracker { private final List> codecs = new ObjectArrayList<>(); private IntSet dirtyIndices = new IntOpenHashSet(); - private Int2ObjectMap persistentObjects = new Int2ObjectOpenHashMap<>(); + private List> persistentObjects = new ObjectArrayList<>(); - private final DataTrackerManager manager; private boolean initial = true; final int id; - public DataTracker(DataTrackerManager manager, int id) { - this.manager = manager; + public DataTracker(int id) { this.id = id; } - public Entry startTracking(T value) { - Entry entry = startTracking(TrackableDataType.COMPRESSED_NBT, value.toTrackedNbt()); - persistentObjects.put(entry.id(), value); - return entry; + public > T startTracking(T value) { + persistentObjects.add(value); + return value; } public Entry startTracking(TrackableDataType type, T initialValue) { @@ -50,10 +46,6 @@ public class DataTracker { } private void set(Entry entry, T value) { - if (manager.isClient) { - return; - } - Pair pair = getPair(entry); if (!Objects.equals(pair.value, value)) { synchronized (this) { @@ -63,25 +55,14 @@ public class DataTracker { } } - @SuppressWarnings("unchecked") - private void updateTrackables() { - for (var entry : persistentObjects.int2ObjectEntrySet()) { - int key = entry.getIntKey(); - TrackableObject.Status status = entry.getValue().getStatus(); - if (status == TrackableObject.Status.NEW || status == TrackableObject.Status.UPDATED) { - ((Pair)codecs.get(key)).value = entry.getValue().toTrackedNbt(); - dirtyIndices.add(key); - } - } - } - - @SuppressWarnings("unchecked") + @SuppressWarnings({ "unchecked", "rawtypes" }) synchronized void copyTo(DataTracker destination) { for (int i = 0; i < codecs.size(); i++) { ((Pair)destination.codecs.get(i)).value = codecs.get(i).value; - TrackableObject o = destination.persistentObjects.get(i); - if (o != null) { - o.readTrackedNbt((NbtCompound)codecs.get(i).value); + TrackableObject a = persistentObjects.get(i); + TrackableObject b = destination.persistentObjects.get(i); + if (a != null && b != null) { + ((TrackableObject)a).copyTo(b); } } } @@ -89,18 +70,17 @@ public class DataTracker { synchronized Optional getInitialPairs() { initial = false; dirtyIndices = new IntOpenHashSet(); - updateTrackables(); - return Optional.of(new MsgTrackedValues.TrackerEntries(id, true, codecs)); + return Optional.of(new MsgTrackedValues.TrackerEntries(id, true, codecs, writePersistentObjects(true))); } - synchronized Optional getDirtyPairs() { + public synchronized Optional getDirtyPairs() { if (initial) { return getInitialPairs(); } - updateTrackables(); + Map updates = writePersistentObjects(false); - if (dirtyIndices.isEmpty()) { + if (dirtyIndices.isEmpty() && updates.isEmpty()) { return Optional.empty(); } @@ -110,32 +90,40 @@ public class DataTracker { for (int i : toSend) { pairs.add(codecs.get(i)); } - return Optional.of(new MsgTrackedValues.TrackerEntries(id, false, pairs)); + return Optional.of(new MsgTrackedValues.TrackerEntries(id, false, pairs, updates)); } - synchronized void load(MsgTrackedValues.TrackerEntries values) { + private Map writePersistentObjects(boolean initial) { + Map updates = new HashMap<>(); + for (int i = 0; i < persistentObjects.size(); i++) { + TrackableObject o = persistentObjects.get(i); + TrackableObject.Status status = initial ? TrackableObject.Status.NEW : o.getStatus(); + int id = i; + o.write(status).ifPresent(data -> updates.put(id, data)); + } + return updates; + } + + public synchronized void load(MsgTrackedValues.TrackerEntries values) { if (values.wipe()) { codecs.clear(); codecs.addAll(values.values()); - for (var entry : persistentObjects.int2ObjectEntrySet()) { - Pair pair = codecs.get(entry.getIntKey()); - if (pair != null) { - entry.getValue().readTrackedNbt((NbtCompound)pair.value); - } - } } else { for (var value : values.values()) { if (value.id >= 0 && value.id < codecs.size()) { if (codecs.get(value.id).type == value.type) { codecs.set(value.id, value); - TrackableObject o = persistentObjects.get(value.id); - if (o != null) { - o.readTrackedNbt((NbtCompound)value.value); - } } } } } + + for (var entry : values.objects().entrySet()) { + TrackableObject o = persistentObjects.get(entry.getKey()); + if (o != null) { + o.read(entry.getValue()); + } + } } public record Entry(DataTracker tracker, int id) { diff --git a/src/main/java/com/minelittlepony/unicopia/network/track/DataTrackerManager.java b/src/main/java/com/minelittlepony/unicopia/network/track/DataTrackerManager.java index b13e9eaa..5b3cc76b 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/track/DataTrackerManager.java +++ b/src/main/java/com/minelittlepony/unicopia/network/track/DataTrackerManager.java @@ -41,7 +41,7 @@ public class DataTrackerManager { } public synchronized DataTracker checkoutTracker() { - DataTracker tracker = new DataTracker(this, trackers.size()); + DataTracker tracker = new DataTracker(trackers.size()); trackers.add(tracker); packetEmitters.add((sender, initial) -> { var update = initial ? tracker.getInitialPairs() : tracker.getDirtyPairs(); @@ -56,7 +56,7 @@ public class DataTrackerManager { return tracker; } - public synchronized ObjectTracker checkoutTracker(Supplier objFunction) { + public synchronized > ObjectTracker checkoutTracker(Supplier objFunction) { ObjectTracker tracker = new ObjectTracker<>(objectTrackers.size(), objFunction); objectTrackers.add(tracker); packetEmitters.add((sender, initial) -> { diff --git a/src/main/java/com/minelittlepony/unicopia/network/track/MsgTrackedValues.java b/src/main/java/com/minelittlepony/unicopia/network/track/MsgTrackedValues.java index 0baa2a9b..d9c405cc 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/track/MsgTrackedValues.java +++ b/src/main/java/com/minelittlepony/unicopia/network/track/MsgTrackedValues.java @@ -14,7 +14,6 @@ import com.sollace.fabwork.api.packets.HandledPacket; import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.nbt.NbtCompound; import net.minecraft.network.PacketByteBuf; public record MsgTrackedValues( @@ -45,12 +44,12 @@ public record MsgTrackedValues( } } - public record TrackerObjects(int id, Set removedValues, Map values) { + public record TrackerObjects(int id, Set removedValues, Map values) { public TrackerObjects(PacketByteBuf buffer) { this( buffer.readInt(), buffer.readCollection(HashSet::new, PacketByteBuf::readUuid), - buffer.readMap(HashMap::new, PacketByteBuf::readUuid, PacketCodec.COMPRESSED_NBT::read) + buffer.readMap(HashMap::new, PacketByteBuf::readUuid, PacketCodec.RAW_BYTES::read) ); } @@ -58,19 +57,25 @@ public record MsgTrackedValues( public void write(PacketByteBuf buffer) { buffer.writeInt(id); buffer.writeCollection(removedValues, PacketByteBuf::writeUuid); - buffer.writeMap(values, PacketByteBuf::writeUuid, PacketCodec.COMPRESSED_NBT::write); + buffer.writeMap(values, PacketByteBuf::writeUuid, PacketCodec.RAW_BYTES::write); } } - public record TrackerEntries(int id, boolean wipe, List> values) { + public record TrackerEntries(int id, boolean wipe, List> values, Map objects) { public TrackerEntries(PacketByteBuf buffer) { - this(buffer.readInt(), buffer.readBoolean(), buffer.readCollection(ArrayList::new, DataTracker.Pair::new)); + this( + buffer.readInt(), + buffer.readBoolean(), + buffer.readCollection(ArrayList::new, DataTracker.Pair::new), + buffer.readMap(PacketByteBuf::readInt, PacketCodec.RAW_BYTES::read) + ); } public void write(PacketByteBuf buffer) { buffer.writeInt(id); buffer.writeBoolean(wipe); buffer.writeCollection(values, (buf, pair) -> pair.write(buf)); + buffer.writeMap(objects, PacketByteBuf::writeInt, PacketCodec.RAW_BYTES::write); } } } diff --git a/src/main/java/com/minelittlepony/unicopia/network/track/ObjectTracker.java b/src/main/java/com/minelittlepony/unicopia/network/track/ObjectTracker.java index 85554c4a..8e4ef2e2 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/track/ObjectTracker.java +++ b/src/main/java/com/minelittlepony/unicopia/network/track/ObjectTracker.java @@ -11,14 +11,11 @@ import java.util.function.Supplier; import org.jetbrains.annotations.Nullable; -import com.minelittlepony.unicopia.Unicopia; -import com.minelittlepony.unicopia.util.NbtSerialisable; - +import com.minelittlepony.unicopia.network.track.TrackableObject.Status; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.util.Util; +import net.minecraft.network.PacketByteBuf; -public class ObjectTracker implements NbtSerialisable { +public class ObjectTracker> { private final Map trackedObjects = new Object2ObjectOpenHashMap<>(); private volatile Map quickAccess = Map.of(); @@ -30,6 +27,10 @@ public class ObjectTracker implements NbtSerialisable this.constructor = constructor; } + public Map entries() { + return quickAccess; + } + public Set keySet() { return quickAccess.keySet(); } @@ -68,15 +69,15 @@ public class ObjectTracker implements NbtSerialisable return true; } - public synchronized void add(T obj) { - trackedObjects.put(obj.getUuid(), obj); + public synchronized void add(UUID id, T obj) { + trackedObjects.put(id, obj); quickAccess = Map.copyOf(trackedObjects); } synchronized void copyTo(ObjectTracker destination) { for (var entry : trackedObjects.entrySet()) { T copy = destination.constructor.get(); - copy.readTrackedNbt(entry.getValue().toTrackedNbt()); + entry.getValue().copyTo(copy); destination.trackedObjects.put(entry.getKey(), copy); } destination.quickAccess = Map.copyOf(destination.trackedObjects); @@ -87,31 +88,35 @@ public class ObjectTracker implements NbtSerialisable return Optional.empty(); } - Map trackableCompounds = new HashMap<>(); + Map updates = new HashMap<>(); quickAccess.entrySet().forEach(object -> { - trackableCompounds.put(object.getKey(), object.getValue().toTrackedNbt()); + object.getValue().write(Status.NEW).ifPresent(data -> { + updates.put(object.getKey(), data); + }); }); - return Optional.of(new MsgTrackedValues.TrackerObjects(id, Set.of(), trackableCompounds)); + return Optional.of(new MsgTrackedValues.TrackerObjects(id, Set.of(), updates)); } synchronized Optional getDirtyPairs() { if (!trackedObjects.isEmpty()) { - Map trackableCompounds = new HashMap<>(); + Map updates = new HashMap<>(); Set removedTrackableObjects = new HashSet<>(); trackedObjects.entrySet().removeIf(object -> { TrackableObject.Status status = object.getValue().getStatus(); if (status == TrackableObject.Status.REMOVED) { removedTrackableObjects.add(object.getKey()); - } else if (status != TrackableObject.Status.DEFAULT) { - trackableCompounds.put(object.getKey(), object.getValue().toTrackedNbt()); + return true; } - return status == TrackableObject.Status.REMOVED; + object.getValue().write(status).ifPresent(data -> { + updates.put(object.getKey(), data); + }); + return false; }); quickAccess = Map.copyOf(trackedObjects); - if (!trackableCompounds.isEmpty() || !removedTrackableObjects.isEmpty()) { - return Optional.of(new MsgTrackedValues.TrackerObjects(id, removedTrackableObjects, trackableCompounds)); + if (!updates.isEmpty() || !removedTrackableObjects.isEmpty()) { + return Optional.of(new MsgTrackedValues.TrackerObjects(id, removedTrackableObjects, updates)); } } @@ -125,44 +130,18 @@ public class ObjectTracker implements NbtSerialisable o.discard(true); } }); - objects.values().forEach((id, nbt) -> { + objects.values().forEach((id, data) -> { T o = trackedObjects.get(id); if (o == null) { o = constructor.get(); trackedObjects.put(id, o); } - o.readTrackedNbt(nbt); + o.read(data); }); quickAccess = Map.copyOf(trackedObjects); } - @Override - public synchronized void toNBT(NbtCompound compound) { - quickAccess.forEach((id, value) -> { - compound.put(id.toString(), value.toTrackedNbt()); - }); - } - - @Override - public void fromNBT(NbtCompound compound) { - Map values = new Object2ObjectOpenHashMap<>(); - compound.getKeys().forEach(key -> { - try { - UUID id = Util.NIL_UUID; - try { - id = UUID.fromString(key); - } catch (Throwable ignore) {} - - if (id != null && !Util.NIL_UUID.equals(id)) { - NbtCompound nbt = compound.getCompound(key); - T entry = constructor.get(); - entry.readTrackedNbt(nbt); - values.put(id, entry); - } - } catch (Throwable t) { - Unicopia.LOGGER.warn("Exception loading tracked object: {}", t.getMessage()); - } - }); + public void load(Map values) { synchronized (this) { trackedObjects.clear(); trackedObjects.putAll(values); diff --git a/src/main/java/com/minelittlepony/unicopia/network/track/TrackableDataType.java b/src/main/java/com/minelittlepony/unicopia/network/track/TrackableDataType.java index e106081f..9c5c6b74 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/track/TrackableDataType.java +++ b/src/main/java/com/minelittlepony/unicopia/network/track/TrackableDataType.java @@ -23,6 +23,7 @@ public record TrackableDataType(int id, PacketCodec codec) { public static final TrackableDataType UUID = of(new Identifier("uuid"), PacketCodec.UUID); public static final TrackableDataType NBT = of(new Identifier("nbt"), PacketCodec.NBT); public static final TrackableDataType COMPRESSED_NBT = of(new Identifier("compressed_nbt"), PacketCodec.COMPRESSED_NBT); + public static final TrackableDataType> RAW_BYTES = of(new Identifier("raw_bytes"), PacketCodec.RAW_BYTES.asOptional()); public static final TrackableDataType> OPTIONAL_POS = of(new Identifier("optional_pos"), PacketCodec.OPTIONAL_POS); public static final TrackableDataType RACE = TrackableDataType.of(Unicopia.id("race"), PacketCodec.ofRegistry(Race.REGISTRY)); diff --git a/src/main/java/com/minelittlepony/unicopia/network/track/TrackableObject.java b/src/main/java/com/minelittlepony/unicopia/network/track/TrackableObject.java index 40439f93..8065aa81 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/track/TrackableObject.java +++ b/src/main/java/com/minelittlepony/unicopia/network/track/TrackableObject.java @@ -1,20 +1,37 @@ package com.minelittlepony.unicopia.network.track; -import java.util.UUID; +import java.util.Optional; +import com.minelittlepony.unicopia.util.serialization.PacketCodec; + +import io.netty.buffer.Unpooled; import net.minecraft.nbt.NbtCompound; +import net.minecraft.network.PacketByteBuf; -public interface TrackableObject { - UUID getUuid(); - +public interface TrackableObject> { Status getStatus(); - NbtCompound toTrackedNbt(); + default void read(PacketByteBuf buffer) { + readTrackedNbt(PacketCodec.COMPRESSED_NBT.read(buffer)); + } - void readTrackedNbt(NbtCompound compound); + default Optional write(Status status) { + if (status == Status.NEW || status == Status.UPDATED) { + PacketByteBuf buffer = new PacketByteBuf(Unpooled.buffer()); + PacketCodec.COMPRESSED_NBT.write(buffer, writeTrackedNbt()); + return Optional.of(buffer); + } + return Optional.empty(); + } + + void readTrackedNbt(NbtCompound nbt); + + NbtCompound writeTrackedNbt(); void discard(boolean immediate); + void copyTo(T destination); + public enum Status { DEFAULT, NEW, diff --git a/src/main/java/com/minelittlepony/unicopia/server/world/Ether.java b/src/main/java/com/minelittlepony/unicopia/server/world/Ether.java index 21980091..840e66fa 100644 --- a/src/main/java/com/minelittlepony/unicopia/server/world/Ether.java +++ b/src/main/java/com/minelittlepony/unicopia/server/world/Ether.java @@ -37,7 +37,7 @@ public class Ether extends PersistentState { this.world = world; this.endpoints = NbtSerialisable.readMap(compound.getCompound("endpoints"), Identifier::tryParse, typeNbt -> { return NbtSerialisable.readMap((NbtCompound)typeNbt, UUID::fromString, entityNbt -> { - return NbtSerialisable.readMap((NbtCompound)entityNbt, UUID::fromString, Entry::new); + return NbtSerialisable.readMap((NbtCompound)entityNbt, UUID::fromString, nbt -> new Entry<>(nbt)); }); }); } diff --git a/src/main/java/com/minelittlepony/unicopia/util/NbtSerialisable.java b/src/main/java/com/minelittlepony/unicopia/util/NbtSerialisable.java index 1dcf0bba..32f9ea21 100644 --- a/src/main/java/com/minelittlepony/unicopia/util/NbtSerialisable.java +++ b/src/main/java/com/minelittlepony/unicopia/util/NbtSerialisable.java @@ -69,14 +69,31 @@ public interface NbtSerialisable { } static Map readMap(NbtCompound nbt, Function keyFunction, Function valueFunction) { - return nbt.getKeys().stream().collect(Collectors.toMap(keyFunction, k -> valueFunction.apply(nbt.get(k)))); + return readMap(nbt, keyFunction, (k, v) -> valueFunction.apply(v)); + } + + static Map readMap(NbtCompound nbt, Function keyFunction, BiFunction valueFunction) { + return nbt.getKeys().stream().map(k -> { + K key = keyFunction.apply(k); + if (key == null) { + return null; + } + V value = valueFunction.apply(key, nbt.get(k)); + if (value == null) { + return null; + } + return Map.entry(key, value); + }) + .filter(Objects::nonNull) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } static NbtCompound writeMap(Map map, Function keyFunction, Function valueFunction) { - NbtCompound nbt = new NbtCompound(); - map.forEach((k, v) -> { - nbt.put(keyFunction.apply(k), valueFunction.apply(v)); - }); + return writeMap(new NbtCompound(), map, keyFunction, valueFunction); + } + + static NbtCompound writeMap(NbtCompound nbt, Map map, Function keyFunction, Function valueFunction) { + map.forEach((k, v) -> nbt.put(keyFunction.apply(k), valueFunction.apply(v))); return nbt; }