Implement a custom data tracker to sync values dynamically. Fixes #14

This commit is contained in:
Sollace 2024-05-20 20:56:44 +01:00
parent c1bcaaa78e
commit 771da650c4
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
9 changed files with 47 additions and 112 deletions

View file

@ -32,7 +32,9 @@ public interface SpellContainer {
/**
* Gets the active effect for this caster updating it if needed.
*/
<T extends Spell> Optional<T> get(@Nullable SpellPredicate<T> type);
default <T extends Spell> Optional<T> get(@Nullable SpellPredicate<T> type) {
return stream(type).findFirst();
}
/**
* Sets the active effect.
@ -73,10 +75,12 @@ public interface SpellContainer {
/**
* Gets all active effects for this caster updating it if needed.
*/
Stream<Spell> stream();
default Stream<Spell> stream() {
return stream(null);
}
/**
* Gets all active effects for this caster that match the given type updating it if needed.
* Gets all active effects for this caster that match the given type.
*/
<T extends Spell> Stream<T> stream(@Nullable SpellPredicate<T> type);

View file

@ -31,8 +31,6 @@ import net.minecraft.entity.SpawnGroup;
import net.minecraft.entity.ai.goal.*;
import net.minecraft.entity.attribute.DefaultAttributeContainer;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.entity.data.TrackedData;
import net.minecraft.entity.data.TrackedDataHandlerRegistry;
import net.minecraft.entity.mob.*;
import net.minecraft.entity.passive.*;
import net.minecraft.entity.player.PlayerEntity;
@ -41,7 +39,6 @@ import net.minecraft.nbt.NbtElement;
import net.minecraft.util.math.MathHelper;
public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutable<LivingEntity> {
static final TrackedData<NbtCompound> EFFECT = net.minecraft.entity.data.DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.NBT_COMPOUND);
public static void boostrap() {}
private final EntityPhysics<LivingEntity> physics;

View file

@ -19,8 +19,6 @@ public interface Equine<T extends Entity> extends NbtSerialisable, Tickable, Pro
void setSpecies(Race race);
void initDataTracker();
/**
* Called at the beginning of an update cycle.
*/

View file

@ -47,10 +47,6 @@ public class ItemImpl implements Equine<ItemEntity> {
race = tracker.startTracking(TrackableDataType.of(Race.PACKET_CODEC), Race.HUMAN);
}
@Override
public void initDataTracker() {
}
@Override
public boolean onProjectileImpact(ProjectileEntity projectile) {
return false;

View file

@ -76,8 +76,6 @@ import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
public abstract class Living<T extends LivingEntity> implements Equine<T>, Caster<T>, Transmittable {
//private static final TrackedData<Optional<UUID>> CARRIER_ID = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.OPTIONAL_UUID);
protected final T entity;
private final EffectSync effectDelegate;
@ -113,7 +111,7 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
this.entity = entity;
this.trackers = Trackable.of(entity).getDataTrackers();
this.tracker = trackers.getPrimaryTracker();
this.effectDelegate = new EffectSync(this, Creature.EFFECT);
this.effectDelegate = new EffectSync(this, tracker);
this.sneakingHeuristic = addTicker(new Interactable(entity::isSneaking));
this.landedHeuristic = addTicker(new Interactable(entity::isOnGround));
this.jumpingHeuristic = addTicker(new Interactable(((LivingEntityDuck)entity)::isJumping));
@ -121,11 +119,6 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
carrierId = tracker.startTracking(TrackableDataType.of(PacketCodec.UUID), Util.NIL_UUID);
}
@Override
public void initDataTracker() {
entity.getDataTracker().startTracking(Creature.EFFECT, new NbtCompound());
}
public <Q extends Tickable> Q addTicker(Q tickable) {
tickers.add(Objects.requireNonNull(tickable, "tickable cannot be null"));
return tickable;

View file

@ -19,19 +19,15 @@ import net.minecraft.entity.EntityDimensions;
import net.minecraft.entity.EntityPose;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.data.DataTracker;
import net.minecraft.entity.data.TrackedData;
import net.minecraft.entity.data.TrackedDataHandlerRegistry;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.text.Text;
import net.minecraft.world.World;
public class CastSpellEntity extends LightEmittingEntity implements Caster<CastSpellEntity>, WeaklyOwned.Mutable<LivingEntity>, MagicImmune {
private static final TrackedData<NbtCompound> EFFECT = DataTracker.registerData(CastSpellEntity.class, TrackedDataHandlerRegistry.NBT_COMPOUND);
private final EntityPhysics<CastSpellEntity> physics = new EntityPhysics<>(this, Trackable.of(this).getDataTrackers().getPrimaryTracker());
private final EffectSync effectDelegate = new EffectSync(this, EFFECT);
private final EffectSync effectDelegate = new EffectSync(this, Trackable.of(this).getDataTrackers().getPrimaryTracker());
private final EntityReference<LivingEntity> owner = new EntityReference<>();
@ -45,7 +41,6 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster<CastS
@Override
protected void initDataTracker() {
getDataTracker().startTracking(EFFECT, new NbtCompound());
}
@Override

View file

@ -30,7 +30,6 @@ import net.minecraft.fluid.Fluid;
import net.minecraft.item.ItemStack;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
@Mixin(Entity.class)
abstract class MixinEntity implements EntityDuck, Trackable {
@ -80,14 +79,6 @@ abstract class MixinEntity implements EntityDuck, Trackable {
return self.hasVehicle() && self.getVehicle() instanceof LavaAffine affine && affine.isLavaAffine();
}
@Inject(method = "<init>", at = @At(value = "INVOKE", target = "net/minecraft/entity/Entity.initDataTracker()V"))
private void onInstanceInit(EntityType<?> type, World world, CallbackInfo info) {
if (this instanceof Equine.Container c) {
c.get().initDataTracker();
}
}
@Inject(method = "isFireImmune", at = @At("HEAD"), cancellable = true)
private void onIsFireImmune(CallbackInfoReturnable<Boolean> info) {
if (isLavaAffine() || (this instanceof Equine.Container c) && c.get().getCompositeRace().includes(Race.KIRIN)) {

View file

@ -1,7 +1,7 @@
package com.minelittlepony.unicopia.network.datasync;
import java.util.Optional;
import java.util.UUID;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
@ -14,9 +14,11 @@ import com.minelittlepony.unicopia.ability.magic.SpellContainer;
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.network.track.DataTracker;
import com.minelittlepony.unicopia.network.track.TrackableDataType;
import com.minelittlepony.unicopia.util.NbtSerialisable;
import com.minelittlepony.unicopia.util.serialization.PacketCodec;
import net.minecraft.entity.data.TrackedData;
import net.minecraft.nbt.NbtCompound;
/**
@ -32,19 +34,21 @@ public class EffectSync implements SpellContainer, NbtSerialisable {
private final Caster<?> owner;
private final TrackedData<NbtCompound> param;
private final DataTracker tracker;
private final DataTracker.Entry<NbtCompound> param;
@Nullable
private NbtCompound lastValue;
public EffectSync(Caster<?> owner, TrackedData<NbtCompound> param) {
spells = new NetworkedReferenceSet<>(Spell::getUuid, () -> new SpellNetworkedReference<>(owner));
public EffectSync(Caster<?> owner, DataTracker tracker) {
this.owner = owner;
this.param = param;
}
this.tracker = tracker;
this.param = tracker.startTracking(TrackableDataType.of(PacketCodec.NBT), new NbtCompound());
spells = new NetworkedReferenceSet<>(Spell::getUuid, () -> new SpellNetworkedReference<>(owner));
public void initDataTracker() {
owner.asEntity().getDataTracker().startTracking(param, new NbtCompound());
tracker.onBeforeSend(param, () -> {
if (spells.isDirty()) {
tracker.set(param, spells.toNbt());
}
});
tracker.onReceive(param, nbt -> spells.fromNbt(nbt));
}
public boolean tick(Situation situation) {
@ -78,20 +82,9 @@ public class EffectSync implements SpellContainer, NbtSerialisable {
return spells.containsReference(id) || spells.getReferences().anyMatch(s -> s.equalsOrContains(id));
}
@Override
public boolean contains(@Nullable SpellPredicate<?> type) {
return read(type).findFirst().isPresent();
}
@Override
public <T extends Spell> Optional<T> get(@Nullable SpellPredicate<T> type) {
return read(type).findFirst();
}
@Override
public void put(@Nullable Spell effect) {
spells.addReference(effect);
write();
if (owner instanceof UpdateCallback callback) {
callback.onSpellSet(effect);
}
@ -99,12 +92,7 @@ public class EffectSync implements SpellContainer, NbtSerialisable {
@Override
public void remove(UUID id) {
Spell spell = spells.getReference(id);
spell.setDead();
spell.tickDying(owner);
if (spell.isDead()) {
spells.removeReference(id);
}
discard(spells.getReference(id));
}
@Override
@ -113,13 +101,19 @@ public class EffectSync implements SpellContainer, NbtSerialisable {
if (!test.test(spell)) {
return initial;
}
discard(spell);
return true;
});
}
private void discard(Spell spell) {
if (spell != null) {
spell.setDead();
spell.tickDying(owner);
if (spell.isDead()) {
spells.removeReference(spell);
}
return true;
});
}
}
@Override
@ -135,55 +129,28 @@ public class EffectSync implements SpellContainer, NbtSerialisable {
});
}
@Override
public Stream<Spell> stream() {
return stream(null);
}
@SuppressWarnings("unchecked")
@Override
public <T extends Spell> Stream<T> stream(@Nullable SpellPredicate<T> type) {
return read(type);
}
@Override
public boolean clear() {
if (spells.clear()) {
write();
if (owner instanceof UpdateCallback) {
((UpdateCallback)owner).onSpellSet(null);
}
return true;
}
return false;
}
@SuppressWarnings("unchecked")
private <T extends Spell> Stream<T> read(@Nullable SpellPredicate<T> type) {
if (owner.isClient()) {
spells.fromNbt(owner.asEntity().getDataTracker().get(param));
}
write();
if (type == null) {
return (Stream<T>)spells.getReferences();
}
return (Stream<T>)spells.getReferences().flatMap(s -> s.findMatches(type));
}
private boolean reduce(Alteration alteration) {
boolean initial = false;
for (Spell i : read(null).toList()) {
initial = alteration.apply(initial, i);
@Override
public boolean clear() {
if (spells.clear()) {
if (owner instanceof UpdateCallback c) {
c.onSpellSet(null);
}
return true;
}
write();
return initial;
return false;
}
private void write() {
if (spells.isDirty() && !owner.isClient()) {
owner.asEntity().getDataTracker().set(param, spells.toNbt());
}
private boolean reduce(BiFunction<Boolean, Spell, Boolean> alteration) {
return stream().toList().stream().reduce(false, alteration, (a, b) -> b);
}
@Override
@ -194,14 +161,10 @@ public class EffectSync implements SpellContainer, NbtSerialisable {
@Override
public void fromNBT(NbtCompound compound) {
spells.fromNbt(compound.getCompound("spells"));
owner.asEntity().getDataTracker().set(param, spells.toNbt());
tracker.set(param, spells.toNbt());
}
public interface UpdateCallback {
void onSpellSet(@Nullable Spell spell);
}
private interface Alteration {
boolean apply(boolean initial, Spell spell);
}
}

View file

@ -32,10 +32,9 @@ import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
public class MagicBeamEntity extends MagicProjectileEntity implements Caster<MagicBeamEntity>, MagicImmune {
private static final TrackedData<NbtCompound> EFFECT = DataTracker.registerData(MagicBeamEntity.class, TrackedDataHandlerRegistry.NBT_COMPOUND);
private static final TrackedData<Boolean> HYDROPHOBIC = DataTracker.registerData(MagicBeamEntity.class, TrackedDataHandlerRegistry.BOOLEAN);
private final EffectSync effectDelegate = new EffectSync(this, EFFECT);
private final EffectSync effectDelegate = new EffectSync(this, Trackable.of(this).getDataTrackers().getPrimaryTracker());
private final EntityPhysics<MagicProjectileEntity> physics = new EntityPhysics<>(this, Trackable.of(this).getDataTrackers().getPrimaryTracker());
public MagicBeamEntity(EntityType<MagicBeamEntity> type, World world) {
@ -55,7 +54,6 @@ public class MagicBeamEntity extends MagicProjectileEntity implements Caster<Mag
protected void initDataTracker() {
super.initDataTracker();
getDataTracker().startTracking(HYDROPHOBIC, false);
getDataTracker().startTracking(EFFECT, new NbtCompound());
}
@Override