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;
|
package com.minelittlepony.unicopia.ability.magic;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.Unicopia;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.SpellReference;
|
import com.minelittlepony.unicopia.ability.magic.spell.SpellReference;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
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.ObjectTracker;
|
||||||
import com.minelittlepony.unicopia.network.track.Trackable;
|
import com.minelittlepony.unicopia.network.track.Trackable;
|
||||||
import com.minelittlepony.unicopia.network.track.TrackableObject;
|
import com.minelittlepony.unicopia.network.track.TrackableObject;
|
||||||
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
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.nbt.NbtCompound;
|
||||||
|
import net.minecraft.network.PacketByteBuf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Container for multiple spells
|
* Container for multiple spells
|
||||||
|
@ -21,7 +28,7 @@ import net.minecraft.nbt.NbtCompound;
|
||||||
*/
|
*/
|
||||||
class MultiSpellSlot implements SpellSlots, NbtSerialisable {
|
class MultiSpellSlot implements SpellSlots, NbtSerialisable {
|
||||||
private final Caster<?> owner;
|
private final Caster<?> owner;
|
||||||
private final ObjectTracker<Entry<?>> tracker;
|
private final ObjectTracker<Entry<Spell>> tracker;
|
||||||
|
|
||||||
public MultiSpellSlot(Caster<?> owner) {
|
public MultiSpellSlot(Caster<?> owner) {
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
|
@ -41,7 +48,7 @@ class MultiSpellSlot implements SpellSlots, NbtSerialisable {
|
||||||
@Override
|
@Override
|
||||||
public void put(@Nullable Spell effect) {
|
public void put(@Nullable Spell effect) {
|
||||||
if (effect != null) {
|
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
|
@Override
|
||||||
public void toNBT(NbtCompound compound) {
|
public void toNBT(NbtCompound compound) {
|
||||||
compound.put("spells", tracker.toNBT());
|
compound.put("spells", NbtSerialisable.writeMap(tracker.entries(), UUID::toString, entry -> entry.spell.toNBT()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fromNBT(NbtCompound compound) {
|
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;
|
private final Caster<?> owner;
|
||||||
final SpellReference<T> spell = new SpellReference<>();
|
final SpellReference<T> spell = new SpellReference<>();
|
||||||
private Status status = Status.NEW;
|
private boolean hasValue;
|
||||||
|
|
||||||
public Entry(Caster<?> owner) {
|
public Entry(Caster<?> owner) {
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
|
@ -99,34 +120,63 @@ class MultiSpellSlot implements SpellSlots, NbtSerialisable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public UUID getUuid() {
|
|
||||||
return spell.get().getUuid();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Status getStatus() {
|
public Status getStatus() {
|
||||||
try {
|
boolean hasValue = spell.get() != null;
|
||||||
if (spell.get() == null) {
|
if (hasValue != this.hasValue) {
|
||||||
return Status.REMOVED;
|
this.hasValue = hasValue;
|
||||||
}
|
return hasValue ? Status.NEW : Status.REMOVED;
|
||||||
if (spell.hasDirtySpell()) {
|
|
||||||
return Status.UPDATED;
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
} finally {
|
|
||||||
status = Status.DEFAULT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return spell.hasDirtySpell() ? Status.UPDATED : Status.DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NbtCompound toTrackedNbt() {
|
public void readTrackedNbt(NbtCompound nbt) {
|
||||||
|
spell.fromNBT(nbt);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NbtCompound writeTrackedNbt() {
|
||||||
return spell.toNBT();
|
return spell.toNBT();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readTrackedNbt(NbtCompound compound) {
|
public void read(PacketByteBuf buffer) {
|
||||||
spell.fromNBT(compound);
|
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
|
@Override
|
||||||
public void toNBT(NbtCompound compound) {
|
public void toNBT(NbtCompound compound) {
|
||||||
compound.put("effect", entry.toTrackedNbt());
|
compound.put("effect", entry.spell.toNBT());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fromNBT(NbtCompound compound) {
|
public void fromNBT(NbtCompound compound) {
|
||||||
entry.readTrackedNbt(compound.getCompound("effect"));
|
entry.spell.fromNBT(compound.getCompound("effect"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -7,15 +7,12 @@ import com.google.common.base.MoreObjects;
|
||||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||||
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
|
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
|
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
|
||||||
|
import com.minelittlepony.unicopia.network.track.DataTracker;
|
||||||
import com.minelittlepony.unicopia.server.world.Ether;
|
import com.minelittlepony.unicopia.server.world.Ether;
|
||||||
|
|
||||||
import net.minecraft.nbt.NbtCompound;
|
import net.minecraft.nbt.NbtCompound;
|
||||||
|
|
||||||
public abstract class AbstractDelegatingSpell implements Spell {
|
public abstract class AbstractDelegatingSpell implements Spell {
|
||||||
private boolean dirty;
|
|
||||||
private boolean hidden;
|
|
||||||
private boolean destroyed;
|
|
||||||
|
|
||||||
private UUID uuid = UUID.randomUUID();
|
private UUID uuid = UUID.randomUUID();
|
||||||
|
|
||||||
private final CustomisedSpellType<?> type;
|
private final CustomisedSpellType<?> type;
|
||||||
|
@ -25,10 +22,20 @@ public abstract class AbstractDelegatingSpell implements Spell {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AbstractDelegatingSpell(CustomisedSpellType<?> type, Spell delegate) {
|
||||||
|
this.type = type;
|
||||||
|
this.delegate.set(delegate);
|
||||||
|
}
|
||||||
|
|
||||||
public final Spell getDelegate() {
|
public final Spell getDelegate() {
|
||||||
return delegate.get();
|
return delegate.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final DataTracker getDataTracker() {
|
||||||
|
return getOrEmpty().getDataTracker();
|
||||||
|
}
|
||||||
|
|
||||||
private Spell getOrEmpty() {
|
private Spell getOrEmpty() {
|
||||||
return MoreObjects.firstNonNull(delegate.get(), EmptySpell.INSTANCE);
|
return MoreObjects.firstNonNull(delegate.get(), EmptySpell.INSTANCE);
|
||||||
}
|
}
|
||||||
|
@ -73,19 +80,21 @@ public abstract class AbstractDelegatingSpell implements Spell {
|
||||||
return getOrEmpty().isDying();
|
return getOrEmpty().isDying();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public boolean isDirty() {
|
public boolean isDirty() {
|
||||||
return dirty || delegate.hasDirtySpell();
|
return delegate.hasDirtySpell();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public void setDirty() {
|
public void setDirty() {
|
||||||
dirty = true;
|
getOrEmpty().setDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isHidden() {
|
public boolean isHidden() {
|
||||||
return hidden || getOrEmpty().isHidden();
|
return getOrEmpty().isHidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -95,15 +104,6 @@ public abstract class AbstractDelegatingSpell implements Spell {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void destroy(Caster<?> caster) {
|
public final void destroy(Caster<?> caster) {
|
||||||
if (destroyed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
destroyed = true;
|
|
||||||
setDead();
|
|
||||||
onDestroyed(caster);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void onDestroyed(Caster<?> caster) {
|
|
||||||
if (!caster.isClient()) {
|
if (!caster.isClient()) {
|
||||||
Ether.get(caster.asWorld()).remove(this, caster);
|
Ether.get(caster.asWorld()).remove(this, caster);
|
||||||
}
|
}
|
||||||
|
@ -123,14 +123,11 @@ public abstract class AbstractDelegatingSpell implements Spell {
|
||||||
@Override
|
@Override
|
||||||
public void toNBT(NbtCompound compound) {
|
public void toNBT(NbtCompound compound) {
|
||||||
compound.putUuid("uuid", uuid);
|
compound.putUuid("uuid", uuid);
|
||||||
compound.putBoolean("hidden", hidden);
|
|
||||||
compound.put("spell", delegate.toNBT());
|
compound.put("spell", delegate.toNBT());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fromNBT(NbtCompound compound) {
|
public void fromNBT(NbtCompound compound) {
|
||||||
dirty = false;
|
|
||||||
hidden = compound.getBoolean("hidden");
|
|
||||||
if (compound.contains("uuid")) {
|
if (compound.contains("uuid")) {
|
||||||
uuid = compound.getUuid("uuid");
|
uuid = compound.getUuid("uuid");
|
||||||
}
|
}
|
||||||
|
@ -139,6 +136,6 @@ public abstract class AbstractDelegatingSpell implements Spell {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final String toString() {
|
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.ability.magic.spell.trait.Trait;
|
||||||
import com.minelittlepony.unicopia.entity.behaviour.EntityAppearance;
|
import com.minelittlepony.unicopia.entity.behaviour.EntityAppearance;
|
||||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
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.MagicParticleEffect;
|
||||||
import com.minelittlepony.unicopia.particle.UParticles;
|
import com.minelittlepony.unicopia.particle.UParticles;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
|
@ -21,6 +23,7 @@ import net.minecraft.nbt.NbtCompound;
|
||||||
*/
|
*/
|
||||||
public class DispersableDisguiseSpell extends AbstractDisguiseSpell implements IllusionarySpell {
|
public class DispersableDisguiseSpell extends AbstractDisguiseSpell implements IllusionarySpell {
|
||||||
|
|
||||||
|
private final DataTracker.Entry<Boolean> suppressed = dataTracker.startTracking(TrackableDataType.BOOLEAN, false);
|
||||||
private int suppressionCounter;
|
private int suppressionCounter;
|
||||||
|
|
||||||
public DispersableDisguiseSpell(CustomisedSpellType<?> type) {
|
public DispersableDisguiseSpell(CustomisedSpellType<?> type) {
|
||||||
|
@ -37,7 +40,7 @@ public class DispersableDisguiseSpell extends AbstractDisguiseSpell implements I
|
||||||
public void onSuppressed(Caster<?> otherSource, float time) {
|
public void onSuppressed(Caster<?> otherSource, float time) {
|
||||||
time /= getTraits().getOrDefault(Trait.STRENGTH, 1);
|
time /= getTraits().getOrDefault(Trait.STRENGTH, 1);
|
||||||
suppressionCounter = (int)time;
|
suppressionCounter = (int)time;
|
||||||
setDirty();
|
suppressed.set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -64,7 +67,9 @@ public class DispersableDisguiseSpell extends AbstractDisguiseSpell implements I
|
||||||
Entity appearance = getDisguise().getAppearance();
|
Entity appearance = getDisguise().getAppearance();
|
||||||
|
|
||||||
if (isSuppressed()) {
|
if (isSuppressed()) {
|
||||||
suppressionCounter--;
|
if (--suppressionCounter <= 0) {
|
||||||
|
suppressed.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
owner.setInvisible(false);
|
owner.setInvisible(false);
|
||||||
if (source instanceof Pony) {
|
if (source instanceof Pony) {
|
||||||
|
@ -92,6 +97,9 @@ public class DispersableDisguiseSpell extends AbstractDisguiseSpell implements I
|
||||||
public void fromNBT(NbtCompound compound) {
|
public void fromNBT(NbtCompound compound) {
|
||||||
super.fromNBT(compound);
|
super.fromNBT(compound);
|
||||||
suppressionCounter = compound.getInt("suppressionCounter");
|
suppressionCounter = compound.getInt("suppressionCounter");
|
||||||
|
if (suppressionCounter > 0) {
|
||||||
|
suppressed.set(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -5,6 +5,7 @@ import java.util.UUID;
|
||||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
|
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||||
|
import com.minelittlepony.unicopia.network.track.DataTracker;
|
||||||
|
|
||||||
import net.minecraft.nbt.NbtCompound;
|
import net.minecraft.nbt.NbtCompound;
|
||||||
import net.minecraft.util.Util;
|
import net.minecraft.util.Util;
|
||||||
|
@ -74,4 +75,9 @@ public final class EmptySpell implements Spell {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "EmptySpell{}";
|
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.Caster;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.AbstractSpell;
|
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.CustomisedSpellType;
|
||||||
|
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||||
import com.minelittlepony.unicopia.entity.mob.CastSpellEntity;
|
import com.minelittlepony.unicopia.entity.mob.CastSpellEntity;
|
||||||
import com.minelittlepony.unicopia.server.world.Ether;
|
import com.minelittlepony.unicopia.server.world.Ether;
|
||||||
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
||||||
|
@ -34,8 +35,8 @@ public class PlacementControlSpell extends AbstractSpell implements OrientedSpel
|
||||||
super(type);
|
super(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
PlacementControlSpell(CustomisedSpellType<?> type, Spell delegate) {
|
PlacementControlSpell(Spell delegate) {
|
||||||
this(type);
|
this(SpellType.PLACE_CONTROL_SPELL.withTraits(delegate.getTypeAndTraits().traits()));
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,10 +51,12 @@ public class PlacementControlSpell extends AbstractSpell implements OrientedSpel
|
||||||
|
|
||||||
public void setDimension(RegistryKey<World> dimension) {
|
public void setDimension(RegistryKey<World> dimension) {
|
||||||
this.dimension = Optional.of(dimension);
|
this.dimension = Optional.of(dimension);
|
||||||
|
setDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPosition(Vec3d position) {
|
public void setPosition(Vec3d position) {
|
||||||
this.position = Optional.of(position);
|
this.position = Optional.of(position);
|
||||||
|
setDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -98,7 +101,6 @@ public class PlacementControlSpell extends AbstractSpell implements OrientedSpel
|
||||||
entity.getWorld().spawnEntity(entity);
|
entity.getWorld().spawnEntity(entity);
|
||||||
|
|
||||||
placedEntityId = entity.getUuid();
|
placedEntityId = entity.getUuid();
|
||||||
setDirty();
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,6 @@ public class RageAbilitySpell extends AbstractSpell {
|
||||||
ticksExtenguishing++;
|
ticksExtenguishing++;
|
||||||
source.playSound(USounds.Vanilla.ENTITY_GENERIC_EXTINGUISH_FIRE, 1);
|
source.playSound(USounds.Vanilla.ENTITY_GENERIC_EXTINGUISH_FIRE, 1);
|
||||||
source.spawnParticles(ParticleTypes.CLOUD, 12);
|
source.spawnParticles(ParticleTypes.CLOUD, 12);
|
||||||
setDirty();
|
|
||||||
} else {
|
} else {
|
||||||
ticksExtenguishing = 0;
|
ticksExtenguishing = 0;
|
||||||
}
|
}
|
||||||
|
@ -127,9 +126,6 @@ public class RageAbilitySpell extends AbstractSpell {
|
||||||
|
|
||||||
age++;
|
age++;
|
||||||
source.asEntity().setInvulnerable(age < 25);
|
source.asEntity().setInvulnerable(age < 25);
|
||||||
|
|
||||||
|
|
||||||
setDirty();
|
|
||||||
return true;
|
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.SpellPredicate;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
|
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
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.server.world.Ether;
|
||||||
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
||||||
|
|
||||||
|
@ -30,6 +31,8 @@ public interface Spell extends NbtSerialisable, Affine {
|
||||||
*/
|
*/
|
||||||
CustomisedSpellType<?> getTypeAndTraits();
|
CustomisedSpellType<?> getTypeAndTraits();
|
||||||
|
|
||||||
|
DataTracker getDataTracker();
|
||||||
|
|
||||||
default boolean isOf(SpellType<?> type) {
|
default boolean isOf(SpellType<?> type) {
|
||||||
return getTypeAndTraits().type() == 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.
|
* Returns true if this effect has changes that need to be sent to the client.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
boolean isDirty();
|
boolean isDirty();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks this effect as dirty.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
void setDirty();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies this spell to the supplied caster.
|
* Applies this spell to the supplied caster.
|
||||||
* @param caster The caster to apply the spell to
|
* @param caster The caster to apply the spell to
|
||||||
|
@ -110,11 +120,6 @@ public interface Spell extends NbtSerialisable, Affine {
|
||||||
*/
|
*/
|
||||||
void tickDying(Caster<?> caster);
|
void tickDying(Caster<?> caster);
|
||||||
|
|
||||||
/**
|
|
||||||
* Marks this effect as dirty.
|
|
||||||
*/
|
|
||||||
void setDirty();
|
|
||||||
|
|
||||||
boolean isHidden();
|
boolean isHidden();
|
||||||
|
|
||||||
void setHidden(boolean hidden);
|
void setHidden(boolean hidden);
|
||||||
|
@ -128,7 +133,7 @@ public interface Spell extends NbtSerialisable, Affine {
|
||||||
* Converts this spell into a placeable spell.
|
* Converts this spell into a placeable spell.
|
||||||
*/
|
*/
|
||||||
default PlacementControlSpell toPlaceable() {
|
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
|
* @return
|
||||||
*/
|
*/
|
||||||
default ThrowableSpell toThrowable() {
|
default ThrowableSpell toThrowable() {
|
||||||
return SpellType.THROWN_SPELL.withTraits().create().setSpell(this);
|
return new ThrowableSpell(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|
|
@ -15,7 +15,6 @@ import net.minecraft.nbt.NbtCompound;
|
||||||
public final class SpellReference<T extends Spell> implements NbtSerialisable {
|
public final class SpellReference<T extends Spell> implements NbtSerialisable {
|
||||||
@Nullable
|
@Nullable
|
||||||
private transient T spell;
|
private transient T spell;
|
||||||
private int nbtHash;
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public T get() {
|
public T get() {
|
||||||
|
@ -26,6 +25,7 @@ public final class SpellReference<T extends Spell> implements NbtSerialisable {
|
||||||
set(spell, null);
|
set(spell, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public boolean hasDirtySpell() {
|
public boolean hasDirtySpell() {
|
||||||
return spell != null && spell.isDirty();
|
return spell != null && spell.isDirty();
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,6 @@ public final class SpellReference<T extends Spell> implements NbtSerialisable {
|
||||||
}
|
}
|
||||||
T oldValue = this.spell;
|
T oldValue = this.spell;
|
||||||
this.spell = spell;
|
this.spell = spell;
|
||||||
nbtHash = 0;
|
|
||||||
if (owner != null && oldValue != null && (spell == null || !oldValue.getUuid().equals(spell.getUuid()))) {
|
if (owner != null && oldValue != null && (spell == null || !oldValue.getUuid().equals(spell.getUuid()))) {
|
||||||
oldValue.destroy(owner);
|
oldValue.destroy(owner);
|
||||||
}
|
}
|
||||||
|
@ -65,19 +64,9 @@ public final class SpellReference<T extends Spell> implements NbtSerialisable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fromNBT(NbtCompound compound) {
|
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())) {
|
if (spell == null || !Objects.equals(Spell.getUuid(compound), spell.getUuid())) {
|
||||||
spell = Spell.readNbt(compound);
|
spell = Spell.readNbt(compound);
|
||||||
} else if (force || !spell.isDirty()) {
|
} else {
|
||||||
spell.fromNBT(compound);
|
spell.fromNBT(compound);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import java.util.Optional;
|
||||||
import com.minelittlepony.unicopia.USounds;
|
import com.minelittlepony.unicopia.USounds;
|
||||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
|
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.MagicBeamEntity;
|
||||||
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
|
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
|
||||||
import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
|
import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
|
||||||
|
@ -20,9 +21,8 @@ public final class ThrowableSpell extends AbstractDelegatingSpell implements
|
||||||
super(type);
|
super(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ThrowableSpell setSpell(Spell spell) {
|
public ThrowableSpell(Spell delegate) {
|
||||||
delegate.set(spell);
|
super(SpellType.THROWN_SPELL.withTraits(delegate.getTypeAndTraits().traits()), delegate);
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -5,26 +5,34 @@ import java.util.UUID;
|
||||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
|
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 com.minelittlepony.unicopia.server.world.Ether;
|
||||||
|
|
||||||
import net.minecraft.nbt.NbtCompound;
|
import net.minecraft.nbt.NbtCompound;
|
||||||
|
|
||||||
public abstract class AbstractSpell implements Spell {
|
public abstract class AbstractSpell implements Spell {
|
||||||
|
|
||||||
private boolean dead;
|
private UUID uuid = UUID.randomUUID();
|
||||||
private boolean dying;
|
|
||||||
private boolean dirty;
|
|
||||||
private boolean hidden;
|
|
||||||
private boolean destroyed;
|
|
||||||
|
|
||||||
private final CustomisedSpellType<?> type;
|
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) {
|
protected AbstractSpell(CustomisedSpellType<?> type) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final DataTracker getDataTracker() {
|
||||||
|
return dataTracker;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final UUID getUuid() {
|
public final UUID getUuid() {
|
||||||
return uuid;
|
return uuid;
|
||||||
|
@ -45,25 +53,26 @@ public abstract class AbstractSpell implements Spell {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void setDead() {
|
public final void setDead() {
|
||||||
dying = true;
|
dying.set(true);
|
||||||
setDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean isDead() {
|
public final boolean isDead() {
|
||||||
return dead;
|
return dead.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean isDying() {
|
public final boolean isDying() {
|
||||||
return dying;
|
return dying.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public final boolean isDirty() {
|
public final boolean isDirty() {
|
||||||
return dirty;
|
return dirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public final void setDirty() {
|
public final void setDirty() {
|
||||||
dirty = true;
|
dirty = true;
|
||||||
|
@ -71,17 +80,17 @@ public abstract class AbstractSpell implements Spell {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean isHidden() {
|
public final boolean isHidden() {
|
||||||
return hidden;
|
return hidden.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void setHidden(boolean hidden) {
|
public final void setHidden(boolean hidden) {
|
||||||
this.hidden = hidden;
|
this.hidden.set(hidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void tickDying(Caster<?> caster) {
|
public void tickDying(Caster<?> caster) {
|
||||||
dead = true;
|
dead.set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -102,9 +111,9 @@ public abstract class AbstractSpell implements Spell {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toNBT(NbtCompound compound) {
|
public void toNBT(NbtCompound compound) {
|
||||||
compound.putBoolean("dying", dying);
|
compound.putBoolean("dying", dying.get());
|
||||||
compound.putBoolean("dead", dead);
|
compound.putBoolean("dead", dead.get());
|
||||||
compound.putBoolean("hidden", hidden);
|
compound.putBoolean("hidden", hidden.get());
|
||||||
compound.putUuid("uuid", uuid);
|
compound.putUuid("uuid", uuid);
|
||||||
compound.put("traits", getTraits().toNbt());
|
compound.put("traits", getTraits().toNbt());
|
||||||
}
|
}
|
||||||
|
@ -115,9 +124,9 @@ public abstract class AbstractSpell implements Spell {
|
||||||
if (compound.containsUuid("uuid")) {
|
if (compound.containsUuid("uuid")) {
|
||||||
uuid = compound.getUuid("uuid");
|
uuid = compound.getUuid("uuid");
|
||||||
}
|
}
|
||||||
dying = compound.getBoolean("dying");
|
dying.set(compound.getBoolean("dying"));
|
||||||
dead = compound.getBoolean("dead");
|
dead.set(compound.getBoolean("dead"));
|
||||||
hidden = compound.getBoolean("hidden");
|
hidden.set(compound.getBoolean("hidden"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -42,6 +42,7 @@ public class AttractiveSpell extends ShieldSpell implements HomingSpell, TimedSp
|
||||||
protected AttractiveSpell(CustomisedSpellType<?> type) {
|
protected AttractiveSpell(CustomisedSpellType<?> type) {
|
||||||
super(type);
|
super(type);
|
||||||
timer = new Timer(BASE_DURATION + TimedSpell.getExtraDuration(getTraits()));
|
timer = new Timer(BASE_DURATION + TimedSpell.getExtraDuration(getTraits()));
|
||||||
|
dataTracker.startTracking(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -57,8 +58,6 @@ public class AttractiveSpell extends ShieldSpell implements HomingSpell, TimedSp
|
||||||
if (timer.getTicksRemaining() <= 0) {
|
if (timer.getTicksRemaining() <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
setDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
target.getOrEmpty(caster.asWorld())
|
target.getOrEmpty(caster.asWorld())
|
||||||
|
|
|
@ -40,8 +40,6 @@ public class AwkwardSpell extends AbstractSpell implements TimedSpell {
|
||||||
if (timer.getTicksRemaining() <= 0) {
|
if (timer.getTicksRemaining() <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
setDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source.isClient()) {
|
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.*;
|
||||||
import com.minelittlepony.unicopia.entity.mob.UEntityAttributes;
|
import com.minelittlepony.unicopia.entity.mob.UEntityAttributes;
|
||||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
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.particle.UParticles;
|
||||||
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
|
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
|
||||||
import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
|
import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
|
||||||
|
@ -55,15 +57,15 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell,
|
||||||
|
|
||||||
private final Timer timer;
|
private final Timer timer;
|
||||||
|
|
||||||
private int struggles;
|
|
||||||
|
|
||||||
private float prevRadius;
|
private float prevRadius;
|
||||||
private float radius;
|
private DataTracker.Entry<Float> radius;
|
||||||
|
private DataTracker.Entry<Integer> struggles;
|
||||||
|
|
||||||
protected BubbleSpell(CustomisedSpellType<?> type) {
|
protected BubbleSpell(CustomisedSpellType<?> type) {
|
||||||
super(type);
|
super(type);
|
||||||
timer = new Timer(BASE_DURATION + TimedSpell.getExtraDuration(getTraits()));
|
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
|
@Override
|
||||||
|
@ -72,7 +74,7 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell,
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getRadius(float tickDelta) {
|
public float getRadius(float tickDelta) {
|
||||||
return MathHelper.lerp(tickDelta, prevRadius, radius);
|
return MathHelper.lerp(tickDelta, prevRadius, radius.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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);
|
source.playSound(USounds.ENTITY_PLAYER_UNICORN_TELEPORT, 1);
|
||||||
entity.addVelocity(0, 0.2F * source.getPhysics().getGravitySignum(), 0);
|
entity.addVelocity(0, 0.2F * source.getPhysics().getGravitySignum(), 0);
|
||||||
Living.updateVelocity(entity);
|
Living.updateVelocity(entity);
|
||||||
|
@ -111,7 +113,7 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell,
|
||||||
|
|
||||||
boolean done = timer.getTicksRemaining() <= 0;
|
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);
|
source.addParticle(done ? ParticleTypes.BUBBLE_POP : UParticles.BUBBLE, pos, Vec3d.ZERO);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -119,8 +121,6 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
setDirty();
|
|
||||||
|
|
||||||
source.asEntity().addVelocity(
|
source.asEntity().addVelocity(
|
||||||
MathHelper.sin(source.asEntity().age / 6F) / 50F,
|
MathHelper.sin(source.asEntity().age / 6F) / 50F,
|
||||||
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;
|
source.asEntity().fallDistance = 0;
|
||||||
|
|
||||||
prevRadius = radius;
|
prevRadius = radius.get();
|
||||||
|
|
||||||
if (source instanceof Pony pony && pony.sneakingChanged() && pony.asEntity().isSneaking()) {
|
if (source instanceof Pony pony && pony.sneakingChanged() && pony.asEntity().isSneaking()) {
|
||||||
setDirty();
|
radius.set(radius.get() + 0.5F);
|
||||||
radius += 0.5F;
|
|
||||||
source.playSound(USounds.SPELL_BUBBLE_DISTURB, 1);
|
source.playSound(USounds.SPELL_BUBBLE_DISTURB, 1);
|
||||||
if (struggles-- <= 0) {
|
int s = struggles.get() - 1;
|
||||||
|
struggles.set(s);
|
||||||
|
if (s <= 0) {
|
||||||
setDead();
|
setDead();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -168,16 +169,16 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell,
|
||||||
@Override
|
@Override
|
||||||
public void toNBT(NbtCompound compound) {
|
public void toNBT(NbtCompound compound) {
|
||||||
super.toNBT(compound);
|
super.toNBT(compound);
|
||||||
compound.putInt("struggles", struggles);
|
compound.putInt("struggles", struggles.get());
|
||||||
compound.putFloat("radius", radius);
|
compound.putFloat("radius", radius.get());
|
||||||
timer.toNBT(compound);
|
timer.toNBT(compound);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fromNBT(NbtCompound compound) {
|
public void fromNBT(NbtCompound compound) {
|
||||||
super.fromNBT(compound);
|
super.fromNBT(compound);
|
||||||
struggles = compound.getInt("struggles");
|
struggles.set(compound.getInt("struggles"));
|
||||||
radius = compound.getFloat("radius");
|
radius.set(compound.getFloat("radius"));
|
||||||
timer.fromNBT(compound);
|
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.Living;
|
||||||
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
|
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
|
||||||
import com.minelittlepony.unicopia.entity.mob.CastSpellEntity;
|
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.FollowingParticleEffect;
|
||||||
import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect;
|
import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect;
|
||||||
import com.minelittlepony.unicopia.particle.ParticleUtils;
|
import com.minelittlepony.unicopia.particle.ParticleUtils;
|
||||||
|
@ -56,7 +58,7 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate
|
||||||
.with(Trait.DARKNESS, 100)
|
.with(Trait.DARKNESS, 100)
|
||||||
.build();
|
.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);
|
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
|
// 3. force reaches 0 at distance of drawDropOffRange
|
||||||
|
|
||||||
private double getMass() {
|
private double getMass() {
|
||||||
return 0.1F + accumulatedMass / 10F;
|
return 0.1F + accumulatedMass.get() / 10F;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getEventHorizonRadius() {
|
public double getEventHorizonRadius() {
|
||||||
|
@ -174,11 +176,12 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void tickDying(Caster<?> source) {
|
public void tickDying(Caster<?> source) {
|
||||||
accumulatedMass -= 0.8F;
|
float m = accumulatedMass.get() - 0.8F;
|
||||||
|
accumulatedMass.set(m);
|
||||||
double mass = getMass() * 0.1;
|
double mass = getMass() * 0.1;
|
||||||
double logarithm = 1 - (1D / (1 + (mass * mass)));
|
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);
|
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);
|
super.tickDying(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,7 +205,7 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFriendlyTogether(Affine other) {
|
public boolean isFriendlyTogether(Affine other) {
|
||||||
return accumulatedMass < 4;
|
return accumulatedMass.get() < 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isValidTarget(Caster<?> source, Entity entity) {
|
private boolean isValidTarget(Caster<?> source, Entity entity) {
|
||||||
|
@ -274,8 +277,7 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate
|
||||||
double massOfTarget = AttractionUtils.getMass(target);
|
double massOfTarget = AttractionUtils.getMass(target);
|
||||||
|
|
||||||
if (!source.isClient() && massOfTarget != 0) {
|
if (!source.isClient() && massOfTarget != 0) {
|
||||||
accumulatedMass += massOfTarget;
|
accumulatedMass.set((float)(accumulatedMass.get() + massOfTarget));
|
||||||
setDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
target.damage(source.damageOf(UDamageTypes.GAVITY_WELL_RECOIL, source), Integer.MAX_VALUE);
|
target.damage(source.damageOf(UDamageTypes.GAVITY_WELL_RECOIL, source), Integer.MAX_VALUE);
|
||||||
|
@ -303,12 +305,12 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate
|
||||||
@Override
|
@Override
|
||||||
public void toNBT(NbtCompound compound) {
|
public void toNBT(NbtCompound compound) {
|
||||||
super.toNBT(compound);
|
super.toNBT(compound);
|
||||||
compound.putFloat("accumulatedMass", accumulatedMass);
|
compound.putFloat("accumulatedMass", accumulatedMass.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fromNBT(NbtCompound compound) {
|
public void fromNBT(NbtCompound compound) {
|
||||||
super.fromNBT(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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
setDirty();
|
|
||||||
|
|
||||||
List<Entity> targets = getTargets(caster).toList();
|
List<Entity> targets = getTargets(caster).toList();
|
||||||
|
|
||||||
if (targets.isEmpty()) {
|
if (targets.isEmpty()) {
|
||||||
|
|
|
@ -63,8 +63,6 @@ public class LightSpell extends AbstractSpell implements TimedSpell, ProjectileD
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
setDirty();
|
|
||||||
|
|
||||||
if (!caster.isClient()) {
|
if (!caster.isClient()) {
|
||||||
if (lights.isEmpty()) {
|
if (lights.isEmpty()) {
|
||||||
int size = 2 + caster.asWorld().random.nextInt(2) + (int)(getTraits().get(Trait.LIFE, 10, 20) - 10)/10;
|
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);
|
entity.getWorld().spawnEntity(entity);
|
||||||
|
|
||||||
ref.set(entity);
|
ref.set(entity);
|
||||||
setDirty();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,8 +35,6 @@ public class MimicSpell extends AbstractDisguiseSpell implements HomingSpell, Ti
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
setDirty();
|
|
||||||
|
|
||||||
return super.tick(caster, situation);
|
return super.tick(caster, situation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,7 +135,6 @@ public class MindSwapSpell extends MimicSpell implements ProjectileDelegate.Enti
|
||||||
caster.playSound(USounds.SPELL_MINDSWAP_SWAP, 1);
|
caster.playSound(USounds.SPELL_MINDSWAP_SWAP, 1);
|
||||||
});
|
});
|
||||||
initialized = true;
|
initialized = true;
|
||||||
setDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (counterpart.isSet()) {
|
if (counterpart.isSet()) {
|
||||||
|
@ -143,13 +142,12 @@ public class MindSwapSpell extends MimicSpell implements ProjectileDelegate.Enti
|
||||||
|
|
||||||
if (other == null) {
|
if (other == null) {
|
||||||
caster.getOriginatingCaster().asEntity().damage(caster.asWorld().getDamageSources().magic(), Float.MAX_VALUE);
|
caster.getOriginatingCaster().asEntity().damage(caster.asWorld().getDamageSources().magic(), Float.MAX_VALUE);
|
||||||
setDead();
|
destroy(caster);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Caster.of(other).get().getSpellSlot().contains(SpellType.MIMIC)) {
|
if (!Caster.of(other).get().getSpellSlot().contains(SpellType.MIMIC)) {
|
||||||
onDestroyed(caster);
|
destroy(caster);
|
||||||
setDead();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,8 +156,7 @@ public class MindSwapSpell extends MimicSpell implements ProjectileDelegate.Enti
|
||||||
counterpart.ifPresent(caster.asWorld(), e -> {
|
counterpart.ifPresent(caster.asWorld(), e -> {
|
||||||
e.damage(e.getDamageSources().magic(), Float.MAX_VALUE);
|
e.damage(e.getDamageSources().magic(), Float.MAX_VALUE);
|
||||||
});
|
});
|
||||||
onDestroyed(caster);
|
destroy(caster);
|
||||||
setDead();
|
|
||||||
return false;
|
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);
|
float additional = source.asWorld().getLocalDifficulty(source.getOrigin()).getLocalDifficulty() + getTraits().get(Trait.CHAOS, 0, 10);
|
||||||
|
|
||||||
setDirty();
|
|
||||||
if (--spawnCountdown > 0 && !summonedEntities.isEmpty()) {
|
if (--spawnCountdown > 0 && !summonedEntities.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -213,7 +212,6 @@ public class NecromancySpell extends AbstractAreaEffectSpell implements Projecti
|
||||||
|
|
||||||
source.asWorld().spawnEntity(minion);
|
source.asWorld().spawnEntity(minion);
|
||||||
summonedEntities.add(new EntityReference<>(minion));
|
summonedEntities.add(new EntityReference<>(minion));
|
||||||
setDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -14,6 +14,8 @@ import com.minelittlepony.unicopia.entity.Living;
|
||||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||||
import com.minelittlepony.unicopia.network.Channel;
|
import com.minelittlepony.unicopia.network.Channel;
|
||||||
import com.minelittlepony.unicopia.network.MsgCasterLookRequest;
|
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.particle.*;
|
||||||
import com.minelittlepony.unicopia.server.world.Ether;
|
import com.minelittlepony.unicopia.server.world.Ether;
|
||||||
import com.minelittlepony.unicopia.util.shape.*;
|
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.particle.ParticleTypes;
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
import net.minecraft.server.world.ServerWorld;
|
||||||
|
import net.minecraft.util.Util;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.MathHelper;
|
import net.minecraft.util.math.MathHelper;
|
||||||
import net.minecraft.util.math.Vec3d;
|
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);
|
private static final Shape PARTICLE_AREA = new Sphere(true, 2, 1, 1, 0);
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private UUID targetPortalId;
|
private final DataTracker.Entry<UUID> targetPortalId = dataTracker.startTracking(TrackableDataType.UUID, Util.NIL_UUID);
|
||||||
private float targetPortalPitch;
|
private final DataTracker.Entry<Float> targetPortalPitch = dataTracker.startTracking(TrackableDataType.FLOAT, 0F);
|
||||||
private float targetPortalYaw;
|
private final DataTracker.Entry<Float> targetPortalYaw = dataTracker.startTracking(TrackableDataType.FLOAT, 0F);
|
||||||
private final EntityReference<Entity> teleportationTarget = new EntityReference<>();
|
private final EntityReference<Entity> teleportationTarget = new EntityReference<>();
|
||||||
|
|
||||||
private boolean publishedPosition;
|
private final DataTracker.Entry<Float> pitch = dataTracker.startTracking(TrackableDataType.FLOAT, 0F);
|
||||||
|
private final DataTracker.Entry<Float> yaw = dataTracker.startTracking(TrackableDataType.FLOAT, 0F);
|
||||||
private float pitch;
|
|
||||||
private float yaw;
|
|
||||||
|
|
||||||
private Shape particleArea = PARTICLE_AREA;
|
private Shape particleArea = PARTICLE_AREA;
|
||||||
|
|
||||||
|
@ -61,30 +62,30 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell.
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getPitch() {
|
public float getPitch() {
|
||||||
return pitch;
|
return pitch.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getYaw() {
|
public float getYaw() {
|
||||||
return yaw;
|
return yaw.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getTargetPitch() {
|
public float getTargetPitch() {
|
||||||
return targetPortalPitch;
|
return targetPortalPitch.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getTargetYaw() {
|
public float getTargetYaw() {
|
||||||
return targetPortalYaw;
|
return targetPortalYaw.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getYawDifference() {
|
public float getYawDifference() {
|
||||||
return MathHelper.wrapDegrees(180 + targetPortalYaw - yaw);
|
return MathHelper.wrapDegrees(180 + getTargetYaw() - getYaw());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Ether.Entry<PortalSpell> getDestination(Caster<?> source) {
|
private Ether.Entry<PortalSpell> getDestination(Caster<?> source) {
|
||||||
return targetPortalId == null ? null : getDestinationReference()
|
return Util.NIL_UUID.equals(targetPortalId.get()) ? null : getDestinationReference()
|
||||||
.getTarget()
|
.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()))
|
.filter(destination -> destination.isClaimedBy(getUuid()))
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
@ -94,6 +95,18 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell.
|
||||||
return toPlaceable().apply(caster);
|
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
|
@Override
|
||||||
public boolean tick(Caster<?> source, Situation situation) {
|
public boolean tick(Caster<?> source, Situation situation) {
|
||||||
if (situation == Situation.GROUND) {
|
if (situation == Situation.GROUND) {
|
||||||
|
@ -108,9 +121,7 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell.
|
||||||
|
|
||||||
if (targetEntry == null) {
|
if (targetEntry == null) {
|
||||||
if (teleportationTarget.isSet()) {
|
if (teleportationTarget.isSet()) {
|
||||||
teleportationTarget.set(null);
|
setDestination(null);
|
||||||
targetPortalId = null;
|
|
||||||
setDirty();
|
|
||||||
source.asWorld().syncWorldEvent(WorldEvents.BLOCK_BROKEN, source.getOrigin(), Block.getRawIdFromState(Blocks.GLASS.getDefaultState()));
|
source.asWorld().syncWorldEvent(WorldEvents.BLOCK_BROKEN, source.getOrigin(), Block.getRawIdFromState(Blocks.GLASS.getDefaultState()));
|
||||||
} else {
|
} else {
|
||||||
Ether.get(source.asWorld()).anyMatch(getType(), entry -> {
|
Ether.get(source.asWorld()).anyMatch(getType(), entry -> {
|
||||||
|
@ -119,18 +130,10 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell.
|
||||||
ownEntry.claim(entry.getSpellId());
|
ownEntry.claim(entry.getSpellId());
|
||||||
synchronized (entry) {
|
synchronized (entry) {
|
||||||
if (entry.getSpell() instanceof PortalSpell portal) {
|
if (entry.getSpell() instanceof PortalSpell portal) {
|
||||||
portal.teleportationTarget.copyFrom(ownEntry.entity);
|
portal.setDestination(ownEntry);
|
||||||
portal.targetPortalId = getUuid();
|
|
||||||
portal.targetPortalPitch = pitch;
|
|
||||||
portal.targetPortalYaw = yaw;
|
|
||||||
portal.setDirty();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
teleportationTarget.copyFrom(entry.entity);
|
setDestination(entry);
|
||||||
targetPortalId = entry.getSpellId();
|
|
||||||
targetPortalPitch = entry.getPitch();
|
|
||||||
targetPortalYaw = entry.getYaw();
|
|
||||||
setDirty();
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
@ -142,8 +145,8 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell.
|
||||||
}
|
}
|
||||||
|
|
||||||
var entry = Ether.get(source.asWorld()).getOrCreate(this, source);
|
var entry = Ether.get(source.asWorld()).getOrCreate(this, source);
|
||||||
entry.setPitch(pitch);
|
entry.setPitch(pitch.get());
|
||||||
entry.setYaw(yaw);
|
entry.setYaw(yaw.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
return !isDead();
|
return !isDead();
|
||||||
|
@ -154,7 +157,7 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell.
|
||||||
source.findAllEntitiesInRange(1).forEach(entity -> {
|
source.findAllEntitiesInRange(1).forEach(entity -> {
|
||||||
if (!entity.hasPortalCooldown()) {
|
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) {
|
if (approachYaw > 80) {
|
||||||
return;
|
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.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.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);
|
entity.getWorld().playSoundFromEntity(null, entity, USounds.ENTITY_PLAYER_UNICORN_TELEPORT, entity.getSoundCategory(), 1, 1);
|
||||||
setDirty();
|
|
||||||
|
|
||||||
Living.updateVelocity(entity);
|
Living.updateVelocity(entity);
|
||||||
|
|
||||||
|
@ -193,13 +195,12 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell.
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setOrientation(Caster<?> caster, float pitch, float yaw) {
|
public void setOrientation(Caster<?> caster, float pitch, float yaw) {
|
||||||
this.pitch = 90 - pitch;
|
this.pitch.set(90 - pitch);
|
||||||
this.yaw = -yaw;
|
this.yaw.set(-yaw);
|
||||||
particleArea = PARTICLE_AREA.rotate(
|
particleArea = PARTICLE_AREA.rotate(
|
||||||
this.pitch * MathHelper.RADIANS_PER_DEGREE,
|
this.pitch.get() * MathHelper.RADIANS_PER_DEGREE,
|
||||||
this.yaw * MathHelper.RADIANS_PER_DEGREE
|
(180 - this.yaw.get()) * MathHelper.RADIANS_PER_DEGREE
|
||||||
);
|
);
|
||||||
setDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -227,30 +228,26 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell.
|
||||||
@Override
|
@Override
|
||||||
public void toNBT(NbtCompound compound) {
|
public void toNBT(NbtCompound compound) {
|
||||||
super.toNBT(compound);
|
super.toNBT(compound);
|
||||||
if (targetPortalId != null) {
|
compound.putUuid("targetPortalId", targetPortalId.get());
|
||||||
compound.putUuid("targetPortalId", targetPortalId);
|
|
||||||
}
|
|
||||||
compound.putBoolean("publishedPosition", publishedPosition);
|
|
||||||
compound.put("teleportationTarget", teleportationTarget.toNBT());
|
compound.put("teleportationTarget", teleportationTarget.toNBT());
|
||||||
compound.putFloat("pitch", pitch);
|
compound.putFloat("pitch", getPitch());
|
||||||
compound.putFloat("yaw", yaw);
|
compound.putFloat("yaw", getYaw());
|
||||||
compound.putFloat("targetPortalPitch", targetPortalPitch);
|
compound.putFloat("targetPortalPitch", getTargetPitch());
|
||||||
compound.putFloat("targetPortalYaw", targetPortalYaw);
|
compound.putFloat("targetPortalYaw", getTargetYaw());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fromNBT(NbtCompound compound) {
|
public void fromNBT(NbtCompound compound) {
|
||||||
super.fromNBT(compound);
|
super.fromNBT(compound);
|
||||||
targetPortalId = compound.containsUuid("targetPortalId") ? compound.getUuid("targetPortalId") : null;
|
targetPortalId.set(compound.containsUuid("targetPortalId") ? compound.getUuid("targetPortalId") : Util.NIL_UUID);
|
||||||
publishedPosition = compound.getBoolean("publishedPosition");
|
|
||||||
teleportationTarget.fromNBT(compound.getCompound("teleportationTarget"));
|
teleportationTarget.fromNBT(compound.getCompound("teleportationTarget"));
|
||||||
pitch = compound.getFloat("pitch");
|
pitch.set(compound.getFloat("pitch"));
|
||||||
yaw = compound.getFloat("yaw");
|
yaw.set(compound.getFloat("yaw"));
|
||||||
targetPortalPitch = compound.getFloat("targetPortalPitch");
|
targetPortalPitch.set(compound.getFloat("targetPortalPitch"));
|
||||||
targetPortalYaw = compound.getFloat("targetPortalYaw");
|
targetPortalYaw.set(compound.getFloat("targetPortalYaw"));
|
||||||
particleArea = PARTICLE_AREA.rotate(
|
particleArea = PARTICLE_AREA.rotate(
|
||||||
pitch * MathHelper.RADIANS_PER_DEGREE,
|
pitch.get() * MathHelper.RADIANS_PER_DEGREE,
|
||||||
(180 - yaw) * 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.ability.magic.spell.trait.Trait;
|
||||||
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
|
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
|
||||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
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.FollowingParticleEffect;
|
||||||
import com.minelittlepony.unicopia.particle.ParticleUtils;
|
import com.minelittlepony.unicopia.particle.ParticleUtils;
|
||||||
import com.minelittlepony.unicopia.particle.UParticles;
|
import com.minelittlepony.unicopia.particle.UParticles;
|
||||||
|
@ -43,6 +45,7 @@ public class SiphoningSpell extends AbstractAreaEffectSpell {
|
||||||
tooltip.add(SpellAttributes.of(SpellAttributes.RANGE, 4));
|
tooltip.add(SpellAttributes.of(SpellAttributes.RANGE, 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final DataTracker.Entry<Boolean> upset = dataTracker.startTracking(TrackableDataType.BOOLEAN, false);
|
||||||
private int ticksUpset;
|
private int ticksUpset;
|
||||||
|
|
||||||
protected SiphoningSpell(CustomisedSpellType<?> type) {
|
protected SiphoningSpell(CustomisedSpellType<?> type) {
|
||||||
|
@ -57,8 +60,8 @@ public class SiphoningSpell extends AbstractAreaEffectSpell {
|
||||||
@Override
|
@Override
|
||||||
public boolean tick(Caster<?> source, Situation situation) {
|
public boolean tick(Caster<?> source, Situation situation) {
|
||||||
|
|
||||||
if (ticksUpset > 0) {
|
if (ticksUpset > 0 && --ticksUpset <= 0) {
|
||||||
ticksUpset--;
|
upset.set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source.isClient()) {
|
if (source.isClient()) {
|
||||||
|
@ -108,7 +111,7 @@ public class SiphoningSpell extends AbstractAreaEffectSpell {
|
||||||
} else {
|
} else {
|
||||||
e.damage(damage, e.getHealth() / 4);
|
e.damage(damage, e.getHealth() / 4);
|
||||||
ticksUpset = 100;
|
ticksUpset = 100;
|
||||||
setDirty();
|
upset.set(true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
e.heal((float)Math.min(source.getLevel().getScaled(e.getHealth()) / 2F, maxHealthGain * 0.6));
|
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) {
|
public void fromNBT(NbtCompound compound) {
|
||||||
super.fromNBT(compound);
|
super.fromNBT(compound);
|
||||||
ticksUpset = compound.getInt("upset");
|
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));
|
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<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<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));
|
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.
|
* @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);
|
private static final Serializer<?> SERIALIZER = Serializer.of(EntityReference::new);
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -122,12 +122,7 @@ public class EntityReference<T extends Entity> implements NbtSerialisable, Track
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return getUuid().hashCode();
|
return getTarget().map(EntityValues::uuid).orElse(Util.NIL_UUID).hashCode();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UUID getUuid() {
|
|
||||||
return getTarget().map(EntityValues::uuid).orElse(Util.NIL_UUID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -140,7 +135,7 @@ public class EntityReference<T extends Entity> implements NbtSerialisable, Track
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NbtCompound toTrackedNbt() {
|
public NbtCompound writeTrackedNbt() {
|
||||||
return toNBT();
|
return toNBT();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,6 +144,12 @@ public class EntityReference<T extends Entity> implements NbtSerialisable, Track
|
||||||
fromNBT(compound);
|
fromNBT(compound);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void copyTo(EntityReference<T> destination) {
|
||||||
|
destination.reference = reference;
|
||||||
|
destination.directReference = directReference;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void discard(boolean immediate) {
|
public void discard(boolean immediate) {
|
||||||
set(null);
|
set(null);
|
||||||
|
|
|
@ -38,12 +38,10 @@ public class CorruptionHandler implements Tickable {
|
||||||
if (entity.age % (10 * ItemTracker.SECONDS) == 0) {
|
if (entity.age % (10 * ItemTracker.SECONDS) == 0) {
|
||||||
if (random.nextInt(100) == 0) {
|
if (random.nextInt(100) == 0) {
|
||||||
pony.getCorruption().add(-1);
|
pony.getCorruption().add(-1);
|
||||||
pony.setDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entity.getHealth() >= entity.getMaxHealth() - 1 && !entity.getHungerManager().isNotFull()) {
|
if (entity.getHealth() >= entity.getMaxHealth() - 1 && !entity.getHungerManager().isNotFull()) {
|
||||||
pony.getCorruption().add(-random.nextInt(4));
|
pony.getCorruption().add(-random.nextInt(4));
|
||||||
pony.setDirty();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,6 +77,5 @@ public class CorruptionHandler implements Tickable {
|
||||||
MagicReserves reserves = pony.getMagicalReserves();
|
MagicReserves reserves = pony.getMagicalReserves();
|
||||||
reserves.getExertion().addPercent(10);
|
reserves.getExertion().addPercent(10);
|
||||||
reserves.getEnergy().add(10);
|
reserves.getEnergy().add(10);
|
||||||
pony.setDirty();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +1,33 @@
|
||||||
package com.minelittlepony.unicopia.network.track;
|
package com.minelittlepony.unicopia.network.track;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
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.IntOpenHashSet;
|
||||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||||
import net.minecraft.nbt.NbtCompound;
|
|
||||||
import net.minecraft.network.PacketByteBuf;
|
import net.minecraft.network.PacketByteBuf;
|
||||||
|
|
||||||
public class DataTracker {
|
public class DataTracker {
|
||||||
private final List<Pair<?>> codecs = new ObjectArrayList<>();
|
private final List<Pair<?>> codecs = new ObjectArrayList<>();
|
||||||
private IntSet dirtyIndices = new IntOpenHashSet();
|
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;
|
private boolean initial = true;
|
||||||
|
|
||||||
final int id;
|
final int id;
|
||||||
|
|
||||||
public DataTracker(DataTrackerManager manager, int id) {
|
public DataTracker(int id) {
|
||||||
this.manager = manager;
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends TrackableObject> Entry<NbtCompound> startTracking(T value) {
|
public <T extends TrackableObject<T>> T startTracking(T value) {
|
||||||
Entry<NbtCompound> entry = startTracking(TrackableDataType.COMPRESSED_NBT, value.toTrackedNbt());
|
persistentObjects.add(value);
|
||||||
persistentObjects.put(entry.id(), value);
|
return value;
|
||||||
return entry;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> Entry<T> startTracking(TrackableDataType<T> type, T initialValue) {
|
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) {
|
private <T> void set(Entry<T> entry, T value) {
|
||||||
if (manager.isClient) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Pair<T> pair = getPair(entry);
|
Pair<T> pair = getPair(entry);
|
||||||
if (!Objects.equals(pair.value, value)) {
|
if (!Objects.equals(pair.value, value)) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
|
@ -63,25 +55,14 @@ public class DataTracker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
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")
|
|
||||||
synchronized void copyTo(DataTracker destination) {
|
synchronized void copyTo(DataTracker destination) {
|
||||||
for (int i = 0; i < codecs.size(); i++) {
|
for (int i = 0; i < codecs.size(); i++) {
|
||||||
((Pair<Object>)destination.codecs.get(i)).value = codecs.get(i).value;
|
((Pair<Object>)destination.codecs.get(i)).value = codecs.get(i).value;
|
||||||
TrackableObject o = destination.persistentObjects.get(i);
|
TrackableObject<?> a = persistentObjects.get(i);
|
||||||
if (o != null) {
|
TrackableObject<?> b = destination.persistentObjects.get(i);
|
||||||
o.readTrackedNbt((NbtCompound)codecs.get(i).value);
|
if (a != null && b != null) {
|
||||||
|
((TrackableObject)a).copyTo(b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,18 +70,17 @@ public class DataTracker {
|
||||||
synchronized Optional<MsgTrackedValues.TrackerEntries> getInitialPairs() {
|
synchronized Optional<MsgTrackedValues.TrackerEntries> getInitialPairs() {
|
||||||
initial = false;
|
initial = false;
|
||||||
dirtyIndices = new IntOpenHashSet();
|
dirtyIndices = new IntOpenHashSet();
|
||||||
updateTrackables();
|
return Optional.of(new MsgTrackedValues.TrackerEntries(id, true, codecs, writePersistentObjects(true)));
|
||||||
return Optional.of(new MsgTrackedValues.TrackerEntries(id, true, codecs));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized Optional<MsgTrackedValues.TrackerEntries> getDirtyPairs() {
|
public synchronized Optional<MsgTrackedValues.TrackerEntries> getDirtyPairs() {
|
||||||
if (initial) {
|
if (initial) {
|
||||||
return getInitialPairs();
|
return getInitialPairs();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTrackables();
|
Map<Integer, PacketByteBuf> updates = writePersistentObjects(false);
|
||||||
|
|
||||||
if (dirtyIndices.isEmpty()) {
|
if (dirtyIndices.isEmpty() && updates.isEmpty()) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,32 +90,40 @@ public class DataTracker {
|
||||||
for (int i : toSend) {
|
for (int i : toSend) {
|
||||||
pairs.add(codecs.get(i));
|
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()) {
|
if (values.wipe()) {
|
||||||
codecs.clear();
|
codecs.clear();
|
||||||
codecs.addAll(values.values());
|
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 {
|
} else {
|
||||||
for (var value : values.values()) {
|
for (var value : values.values()) {
|
||||||
if (value.id >= 0 && value.id < codecs.size()) {
|
if (value.id >= 0 && value.id < codecs.size()) {
|
||||||
if (codecs.get(value.id).type == value.type) {
|
if (codecs.get(value.id).type == value.type) {
|
||||||
codecs.set(value.id, value);
|
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<T>(DataTracker tracker, int id) {
|
public record Entry<T>(DataTracker tracker, int id) {
|
||||||
|
|
|
@ -41,7 +41,7 @@ public class DataTrackerManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized DataTracker checkoutTracker() {
|
public synchronized DataTracker checkoutTracker() {
|
||||||
DataTracker tracker = new DataTracker(this, trackers.size());
|
DataTracker tracker = new DataTracker(trackers.size());
|
||||||
trackers.add(tracker);
|
trackers.add(tracker);
|
||||||
packetEmitters.add((sender, initial) -> {
|
packetEmitters.add((sender, initial) -> {
|
||||||
var update = initial ? tracker.getInitialPairs() : tracker.getDirtyPairs();
|
var update = initial ? tracker.getInitialPairs() : tracker.getDirtyPairs();
|
||||||
|
@ -56,7 +56,7 @@ public class DataTrackerManager {
|
||||||
return tracker;
|
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);
|
ObjectTracker<T> tracker = new ObjectTracker<>(objectTrackers.size(), objFunction);
|
||||||
objectTrackers.add(tracker);
|
objectTrackers.add(tracker);
|
||||||
packetEmitters.add((sender, initial) -> {
|
packetEmitters.add((sender, initial) -> {
|
||||||
|
|
|
@ -14,7 +14,6 @@ import com.sollace.fabwork.api.packets.HandledPacket;
|
||||||
|
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.nbt.NbtCompound;
|
|
||||||
import net.minecraft.network.PacketByteBuf;
|
import net.minecraft.network.PacketByteBuf;
|
||||||
|
|
||||||
public record MsgTrackedValues(
|
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) {
|
public TrackerObjects(PacketByteBuf buffer) {
|
||||||
this(
|
this(
|
||||||
buffer.readInt(),
|
buffer.readInt(),
|
||||||
buffer.readCollection(HashSet::new, PacketByteBuf::readUuid),
|
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) {
|
public void write(PacketByteBuf buffer) {
|
||||||
buffer.writeInt(id);
|
buffer.writeInt(id);
|
||||||
buffer.writeCollection(removedValues, PacketByteBuf::writeUuid);
|
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) {
|
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) {
|
public void write(PacketByteBuf buffer) {
|
||||||
buffer.writeInt(id);
|
buffer.writeInt(id);
|
||||||
buffer.writeBoolean(wipe);
|
buffer.writeBoolean(wipe);
|
||||||
buffer.writeCollection(values, (buf, pair) -> pair.write(buf));
|
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 org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.Unicopia;
|
import com.minelittlepony.unicopia.network.track.TrackableObject.Status;
|
||||||
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
import net.minecraft.nbt.NbtCompound;
|
import net.minecraft.network.PacketByteBuf;
|
||||||
import net.minecraft.util.Util;
|
|
||||||
|
|
||||||
public class ObjectTracker<T extends TrackableObject> implements NbtSerialisable {
|
public class ObjectTracker<T extends TrackableObject<T>> {
|
||||||
private final Map<UUID, T> trackedObjects = new Object2ObjectOpenHashMap<>();
|
private final Map<UUID, T> trackedObjects = new Object2ObjectOpenHashMap<>();
|
||||||
private volatile Map<UUID, T> quickAccess = Map.of();
|
private volatile Map<UUID, T> quickAccess = Map.of();
|
||||||
|
|
||||||
|
@ -30,6 +27,10 @@ public class ObjectTracker<T extends TrackableObject> implements NbtSerialisable
|
||||||
this.constructor = constructor;
|
this.constructor = constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<UUID, T> entries() {
|
||||||
|
return quickAccess;
|
||||||
|
}
|
||||||
|
|
||||||
public Set<UUID> keySet() {
|
public Set<UUID> keySet() {
|
||||||
return quickAccess.keySet();
|
return quickAccess.keySet();
|
||||||
}
|
}
|
||||||
|
@ -68,15 +69,15 @@ public class ObjectTracker<T extends TrackableObject> implements NbtSerialisable
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void add(T obj) {
|
public synchronized void add(UUID id, T obj) {
|
||||||
trackedObjects.put(obj.getUuid(), obj);
|
trackedObjects.put(id, obj);
|
||||||
quickAccess = Map.copyOf(trackedObjects);
|
quickAccess = Map.copyOf(trackedObjects);
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void copyTo(ObjectTracker<T> destination) {
|
synchronized void copyTo(ObjectTracker<T> destination) {
|
||||||
for (var entry : trackedObjects.entrySet()) {
|
for (var entry : trackedObjects.entrySet()) {
|
||||||
T copy = destination.constructor.get();
|
T copy = destination.constructor.get();
|
||||||
copy.readTrackedNbt(entry.getValue().toTrackedNbt());
|
entry.getValue().copyTo(copy);
|
||||||
destination.trackedObjects.put(entry.getKey(), copy);
|
destination.trackedObjects.put(entry.getKey(), copy);
|
||||||
}
|
}
|
||||||
destination.quickAccess = Map.copyOf(destination.trackedObjects);
|
destination.quickAccess = Map.copyOf(destination.trackedObjects);
|
||||||
|
@ -87,31 +88,35 @@ public class ObjectTracker<T extends TrackableObject> implements NbtSerialisable
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<UUID, NbtCompound> trackableCompounds = new HashMap<>();
|
Map<UUID, PacketByteBuf> updates = new HashMap<>();
|
||||||
quickAccess.entrySet().forEach(object -> {
|
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() {
|
synchronized Optional<MsgTrackedValues.TrackerObjects> getDirtyPairs() {
|
||||||
if (!trackedObjects.isEmpty()) {
|
if (!trackedObjects.isEmpty()) {
|
||||||
Map<UUID, NbtCompound> trackableCompounds = new HashMap<>();
|
Map<UUID, PacketByteBuf> updates = new HashMap<>();
|
||||||
Set<UUID> removedTrackableObjects = new HashSet<>();
|
Set<UUID> removedTrackableObjects = new HashSet<>();
|
||||||
trackedObjects.entrySet().removeIf(object -> {
|
trackedObjects.entrySet().removeIf(object -> {
|
||||||
TrackableObject.Status status = object.getValue().getStatus();
|
TrackableObject.Status status = object.getValue().getStatus();
|
||||||
if (status == TrackableObject.Status.REMOVED) {
|
if (status == TrackableObject.Status.REMOVED) {
|
||||||
removedTrackableObjects.add(object.getKey());
|
removedTrackableObjects.add(object.getKey());
|
||||||
} else if (status != TrackableObject.Status.DEFAULT) {
|
return true;
|
||||||
trackableCompounds.put(object.getKey(), object.getValue().toTrackedNbt());
|
|
||||||
}
|
}
|
||||||
return status == TrackableObject.Status.REMOVED;
|
object.getValue().write(status).ifPresent(data -> {
|
||||||
|
updates.put(object.getKey(), data);
|
||||||
|
});
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
quickAccess = Map.copyOf(trackedObjects);
|
quickAccess = Map.copyOf(trackedObjects);
|
||||||
|
|
||||||
if (!trackableCompounds.isEmpty() || !removedTrackableObjects.isEmpty()) {
|
if (!updates.isEmpty() || !removedTrackableObjects.isEmpty()) {
|
||||||
return Optional.of(new MsgTrackedValues.TrackerObjects(id, removedTrackableObjects, trackableCompounds));
|
return Optional.of(new MsgTrackedValues.TrackerObjects(id, removedTrackableObjects, updates));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,44 +130,18 @@ public class ObjectTracker<T extends TrackableObject> implements NbtSerialisable
|
||||||
o.discard(true);
|
o.discard(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
objects.values().forEach((id, nbt) -> {
|
objects.values().forEach((id, data) -> {
|
||||||
T o = trackedObjects.get(id);
|
T o = trackedObjects.get(id);
|
||||||
if (o == null) {
|
if (o == null) {
|
||||||
o = constructor.get();
|
o = constructor.get();
|
||||||
trackedObjects.put(id, o);
|
trackedObjects.put(id, o);
|
||||||
}
|
}
|
||||||
o.readTrackedNbt(nbt);
|
o.read(data);
|
||||||
});
|
});
|
||||||
quickAccess = Map.copyOf(trackedObjects);
|
quickAccess = Map.copyOf(trackedObjects);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void load(Map<UUID, T> values) {
|
||||||
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());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
trackedObjects.clear();
|
trackedObjects.clear();
|
||||||
trackedObjects.putAll(values);
|
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<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> 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<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<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));
|
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;
|
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.nbt.NbtCompound;
|
||||||
|
import net.minecraft.network.PacketByteBuf;
|
||||||
|
|
||||||
public interface TrackableObject {
|
public interface TrackableObject<T extends TrackableObject<T>> {
|
||||||
UUID getUuid();
|
|
||||||
|
|
||||||
Status getStatus();
|
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 discard(boolean immediate);
|
||||||
|
|
||||||
|
void copyTo(T destination);
|
||||||
|
|
||||||
public enum Status {
|
public enum Status {
|
||||||
DEFAULT,
|
DEFAULT,
|
||||||
NEW,
|
NEW,
|
||||||
|
|
|
@ -37,7 +37,7 @@ public class Ether extends PersistentState {
|
||||||
this.world = world;
|
this.world = world;
|
||||||
this.endpoints = NbtSerialisable.readMap(compound.getCompound("endpoints"), Identifier::tryParse, typeNbt -> {
|
this.endpoints = NbtSerialisable.readMap(compound.getCompound("endpoints"), Identifier::tryParse, typeNbt -> {
|
||||||
return NbtSerialisable.readMap((NbtCompound)typeNbt, UUID::fromString, entityNbt -> {
|
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) {
|
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) {
|
static <K, V> NbtCompound writeMap(Map<K, V> map, Function<K, String> keyFunction, Function<V, ? extends NbtElement> valueFunction) {
|
||||||
NbtCompound nbt = new NbtCompound();
|
return writeMap(new NbtCompound(), map, keyFunction, valueFunction);
|
||||||
map.forEach((k, v) -> {
|
}
|
||||||
nbt.put(keyFunction.apply(k), valueFunction.apply(v));
|
|
||||||
});
|
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;
|
return nbt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue