mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-11-27 15:17:59 +01:00
Implement data trackers for spells
This commit is contained in:
parent
d716d2e00b
commit
2edf2c6989
33 changed files with 383 additions and 325 deletions
|
@ -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<Entry<?>> tracker;
|
||||
private final ObjectTracker<Entry<Spell>> 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<Spell> 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<T extends Spell> implements TrackableObject {
|
||||
static final class Entry<T extends Spell> implements TrackableObject<Entry<T>> {
|
||||
private final Caster<?> owner;
|
||||
final SpellReference<T> 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<PacketByteBuf> 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<T> destination) {
|
||||
destination.spell.set(spell.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() + "]";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Boolean> 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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<World> 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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -15,7 +15,6 @@ 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() {
|
||||
|
@ -26,6 +25,7 @@ public final class SpellReference<T extends Spell> implements NbtSerialisable {
|
|||
set(spell, null);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public boolean hasDirtySpell() {
|
||||
return spell != null && spell.isDirty();
|
||||
}
|
||||
|
@ -37,7 +37,6 @@ public final class SpellReference<T extends Spell> 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<T extends Spell> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<Boolean> dead = dataTracker.startTracking(TrackableDataType.BOOLEAN, false);
|
||||
private final DataTracker.Entry<Boolean> dying = dataTracker.startTracking(TrackableDataType.BOOLEAN, false);
|
||||
private boolean dirty;
|
||||
private final DataTracker.Entry<Boolean> 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
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -40,8 +40,6 @@ public class AwkwardSpell extends AbstractSpell implements TimedSpell {
|
|||
if (timer.getTicksRemaining() <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
setDirty();
|
||||
}
|
||||
|
||||
if (source.isClient()) {
|
||||
|
|
|
@ -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<Float> radius;
|
||||
private DataTracker.Entry<Integer> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Float> 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"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,8 +69,6 @@ public class FeatherFallSpell extends AbstractSpell implements TimedSpell {
|
|||
return false;
|
||||
}
|
||||
|
||||
setDirty();
|
||||
|
||||
List<Entity> targets = getTargets(caster).toList();
|
||||
|
||||
if (targets.isEmpty()) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -35,8 +35,6 @@ public class MimicSpell extends AbstractDisguiseSpell implements HomingSpell, Ti
|
|||
return false;
|
||||
}
|
||||
|
||||
setDirty();
|
||||
|
||||
return super.tick(caster, situation);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<UUID> targetPortalId = dataTracker.startTracking(TrackableDataType.UUID, Util.NIL_UUID);
|
||||
private final DataTracker.Entry<Float> targetPortalPitch = dataTracker.startTracking(TrackableDataType.FLOAT, 0F);
|
||||
private final DataTracker.Entry<Float> targetPortalYaw = dataTracker.startTracking(TrackableDataType.FLOAT, 0F);
|
||||
private final EntityReference<Entity> teleportationTarget = new EntityReference<>();
|
||||
|
||||
private boolean publishedPosition;
|
||||
|
||||
private float pitch;
|
||||
private float yaw;
|
||||
private final DataTracker.Entry<Float> pitch = dataTracker.startTracking(TrackableDataType.FLOAT, 0F);
|
||||
private final DataTracker.Entry<Float> 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<PortalSpell> 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<PortalSpell>)getType(), target.uuid(), targetPortalId))
|
||||
.map(target -> Ether.get(source.asWorld()).get((SpellType<PortalSpell>)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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Boolean> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
|
|||
private static final DynamicCommandExceptionType UNKNOWN_SPELL_TYPE_EXCEPTION = new DynamicCommandExceptionType(id -> Text.translatable("spell_type.unknown", id));
|
||||
|
||||
public static final SpellType<PlacementControlSpell> PLACE_CONTROL_SPELL = register("place_controller", SpellType.<PlacementControlSpell>builder(PlacementControlSpell::new).affinity(Affinity.NEUTRAL).unobtainable().stackable().shape(GemstoneItem.Shape.DONUT));
|
||||
public static final SpellType<ThrowableSpell> THROWN_SPELL = register("thrown", builder(ThrowableSpell::new).affinity(Affinity.NEUTRAL).unobtainable().shape(GemstoneItem.Shape.DONUT));
|
||||
public static final SpellType<ThrowableSpell> THROWN_SPELL = register("thrown", SpellType.<ThrowableSpell>builder(ThrowableSpell::new).affinity(Affinity.NEUTRAL).unobtainable().shape(GemstoneItem.Shape.DONUT));
|
||||
|
||||
public static final SpellType<DispersableDisguiseSpell> CHANGELING_DISGUISE = register("disguise", builder(DispersableDisguiseSpell::new).affinity(Affinity.BAD).color(0x19E48E).unobtainable().shape(GemstoneItem.Shape.ARROW));
|
||||
public static final SpellType<ChangelingFeedingSpell> FEED = register("feed", SpellType.<ChangelingFeedingSpell>builder(ChangelingFeedingSpell::new).affinity(Affinity.BAD).color(0xBDBDF9).unobtainable().shape(GemstoneItem.Shape.ARROW));
|
||||
|
|
|
@ -30,7 +30,7 @@ import net.minecraft.world.World;
|
|||
*
|
||||
* @param <T> The type of the entity this reference points to.
|
||||
*/
|
||||
public class EntityReference<T extends Entity> implements NbtSerialisable, TrackableObject {
|
||||
public class EntityReference<T extends Entity> implements NbtSerialisable, TrackableObject<EntityReference<T>> {
|
||||
private static final Serializer<?> SERIALIZER = Serializer.of(EntityReference::new);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
@ -122,12 +122,7 @@ public class EntityReference<T extends Entity> 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<T extends Entity> implements NbtSerialisable, Track
|
|||
}
|
||||
|
||||
@Override
|
||||
public NbtCompound toTrackedNbt() {
|
||||
public NbtCompound writeTrackedNbt() {
|
||||
return toNBT();
|
||||
}
|
||||
|
||||
|
@ -149,6 +144,12 @@ public class EntityReference<T extends Entity> implements NbtSerialisable, Track
|
|||
fromNBT(compound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyTo(EntityReference<T> destination) {
|
||||
destination.reference = reference;
|
||||
destination.directReference = directReference;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void discard(boolean immediate) {
|
||||
set(null);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Pair<?>> codecs = new ObjectArrayList<>();
|
||||
private IntSet dirtyIndices = new IntOpenHashSet();
|
||||
private Int2ObjectMap<TrackableObject> persistentObjects = new Int2ObjectOpenHashMap<>();
|
||||
private List<TrackableObject<?>> 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 <T extends TrackableObject> Entry<NbtCompound> startTracking(T value) {
|
||||
Entry<NbtCompound> entry = startTracking(TrackableDataType.COMPRESSED_NBT, value.toTrackedNbt());
|
||||
persistentObjects.put(entry.id(), value);
|
||||
return entry;
|
||||
public <T extends TrackableObject<T>> T startTracking(T value) {
|
||||
persistentObjects.add(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public <T> Entry<T> startTracking(TrackableDataType<T> type, T initialValue) {
|
||||
|
@ -50,10 +46,6 @@ public class DataTracker {
|
|||
}
|
||||
|
||||
private <T> void set(Entry<T> entry, T value) {
|
||||
if (manager.isClient) {
|
||||
return;
|
||||
}
|
||||
|
||||
Pair<T> 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<Object>)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<Object>)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<MsgTrackedValues.TrackerEntries> 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<MsgTrackedValues.TrackerEntries> getDirtyPairs() {
|
||||
public synchronized Optional<MsgTrackedValues.TrackerEntries> getDirtyPairs() {
|
||||
if (initial) {
|
||||
return getInitialPairs();
|
||||
}
|
||||
|
||||
updateTrackables();
|
||||
Map<Integer, PacketByteBuf> updates = writePersistentObjects(false);
|
||||
|
||||
if (dirtyIndices.isEmpty()) {
|
||||
if (dirtyIndices.isEmpty() && updates.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
|
@ -110,30 +90,38 @@ 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<Integer, PacketByteBuf> writePersistentObjects(boolean initial) {
|
||||
Map<Integer, PacketByteBuf> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var entry : values.objects().entrySet()) {
|
||||
TrackableObject<?> o = persistentObjects.get(entry.getKey());
|
||||
if (o != null) {
|
||||
o.readTrackedNbt((NbtCompound)value.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
o.read(entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 <T extends TrackableObject> ObjectTracker<T> checkoutTracker(Supplier<T> objFunction) {
|
||||
public synchronized <T extends TrackableObject<T>> ObjectTracker<T> checkoutTracker(Supplier<T> objFunction) {
|
||||
ObjectTracker<T> tracker = new ObjectTracker<>(objectTrackers.size(), objFunction);
|
||||
objectTrackers.add(tracker);
|
||||
packetEmitters.add((sender, initial) -> {
|
||||
|
|
|
@ -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<UUID> removedValues, Map<UUID, NbtCompound> values) {
|
||||
public record TrackerObjects(int id, Set<UUID> removedValues, Map<UUID, PacketByteBuf> 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<DataTracker.Pair<?>> values) {
|
||||
public record TrackerEntries(int id, boolean wipe, List<DataTracker.Pair<?>> values, Map<Integer, PacketByteBuf> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<T extends TrackableObject> implements NbtSerialisable {
|
||||
public class ObjectTracker<T extends TrackableObject<T>> {
|
||||
private final Map<UUID, T> trackedObjects = new Object2ObjectOpenHashMap<>();
|
||||
private volatile Map<UUID, T> quickAccess = Map.of();
|
||||
|
||||
|
@ -30,6 +27,10 @@ public class ObjectTracker<T extends TrackableObject> implements NbtSerialisable
|
|||
this.constructor = constructor;
|
||||
}
|
||||
|
||||
public Map<UUID, T> entries() {
|
||||
return quickAccess;
|
||||
}
|
||||
|
||||
public Set<UUID> keySet() {
|
||||
return quickAccess.keySet();
|
||||
}
|
||||
|
@ -68,15 +69,15 @@ public class ObjectTracker<T extends TrackableObject> 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<T> 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<T extends TrackableObject> implements NbtSerialisable
|
|||
return Optional.empty();
|
||||
}
|
||||
|
||||
Map<UUID, NbtCompound> trackableCompounds = new HashMap<>();
|
||||
Map<UUID, PacketByteBuf> 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<MsgTrackedValues.TrackerObjects> getDirtyPairs() {
|
||||
if (!trackedObjects.isEmpty()) {
|
||||
Map<UUID, NbtCompound> trackableCompounds = new HashMap<>();
|
||||
Map<UUID, PacketByteBuf> updates = new HashMap<>();
|
||||
Set<UUID> 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<T extends TrackableObject> 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<UUID, T> 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<UUID, T> values) {
|
||||
synchronized (this) {
|
||||
trackedObjects.clear();
|
||||
trackedObjects.putAll(values);
|
||||
|
|
|
@ -23,6 +23,7 @@ public record TrackableDataType<T>(int id, PacketCodec<T> codec) {
|
|||
public static final TrackableDataType<UUID> UUID = of(new Identifier("uuid"), PacketCodec.UUID);
|
||||
public static final TrackableDataType<NbtCompound> NBT = of(new Identifier("nbt"), PacketCodec.NBT);
|
||||
public static final TrackableDataType<NbtCompound> COMPRESSED_NBT = of(new Identifier("compressed_nbt"), PacketCodec.COMPRESSED_NBT);
|
||||
public static final TrackableDataType<Optional<PacketByteBuf>> RAW_BYTES = of(new Identifier("raw_bytes"), PacketCodec.RAW_BYTES.asOptional());
|
||||
|
||||
public static final TrackableDataType<Optional<BlockPos>> OPTIONAL_POS = of(new Identifier("optional_pos"), PacketCodec.OPTIONAL_POS);
|
||||
public static final TrackableDataType<Race> RACE = TrackableDataType.of(Unicopia.id("race"), PacketCodec.ofRegistry(Race.REGISTRY));
|
||||
|
|
|
@ -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<T extends TrackableObject<T>> {
|
||||
Status getStatus();
|
||||
|
||||
NbtCompound toTrackedNbt();
|
||||
default void read(PacketByteBuf buffer) {
|
||||
readTrackedNbt(PacketCodec.COMPRESSED_NBT.read(buffer));
|
||||
}
|
||||
|
||||
void readTrackedNbt(NbtCompound compound);
|
||||
default Optional<PacketByteBuf> 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,
|
||||
|
|
|
@ -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));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -69,14 +69,31 @@ public interface NbtSerialisable {
|
|||
}
|
||||
|
||||
static <K, V> Map<K, V> readMap(NbtCompound nbt, Function<String, K> keyFunction, Function<NbtElement, V> 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 <K, V> Map<K, V> readMap(NbtCompound nbt, Function<String, K> keyFunction, BiFunction<K, NbtElement, V> 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 <K, V> NbtCompound writeMap(Map<K, V> map, Function<K, String> keyFunction, Function<V, ? extends NbtElement> 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 <K, V> NbtCompound writeMap(NbtCompound nbt, Map<K, V> map, Function<K, String> keyFunction, Function<V, ? extends NbtElement> valueFunction) {
|
||||
map.forEach((k, v) -> nbt.put(keyFunction.apply(k), valueFunction.apply(v)));
|
||||
return nbt;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue