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. * 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. * Sets the active effect.
@ -73,10 +75,12 @@ public interface SpellContainer {
/** /**
* Gets all active effects for this caster updating it if needed. * 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); <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.ai.goal.*;
import net.minecraft.entity.attribute.DefaultAttributeContainer; import net.minecraft.entity.attribute.DefaultAttributeContainer;
import net.minecraft.entity.attribute.EntityAttributes; 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.mob.*;
import net.minecraft.entity.passive.*; import net.minecraft.entity.passive.*;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
@ -41,7 +39,6 @@ import net.minecraft.nbt.NbtElement;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutable<LivingEntity> { 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() {} public static void boostrap() {}
private final EntityPhysics<LivingEntity> physics; 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 setSpecies(Race race);
void initDataTracker();
/** /**
* Called at the beginning of an update cycle. * 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); race = tracker.startTracking(TrackableDataType.of(Race.PACKET_CODEC), Race.HUMAN);
} }
@Override
public void initDataTracker() {
}
@Override @Override
public boolean onProjectileImpact(ProjectileEntity projectile) { public boolean onProjectileImpact(ProjectileEntity projectile) {
return false; return false;

View file

@ -76,8 +76,6 @@ import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
public abstract class Living<T extends LivingEntity> implements Equine<T>, Caster<T>, Transmittable { 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; protected final T entity;
private final EffectSync effectDelegate; private final EffectSync effectDelegate;
@ -113,7 +111,7 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
this.entity = entity; this.entity = entity;
this.trackers = Trackable.of(entity).getDataTrackers(); this.trackers = Trackable.of(entity).getDataTrackers();
this.tracker = trackers.getPrimaryTracker(); 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.sneakingHeuristic = addTicker(new Interactable(entity::isSneaking));
this.landedHeuristic = addTicker(new Interactable(entity::isOnGround)); this.landedHeuristic = addTicker(new Interactable(entity::isOnGround));
this.jumpingHeuristic = addTicker(new Interactable(((LivingEntityDuck)entity)::isJumping)); 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); 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) { public <Q extends Tickable> Q addTicker(Q tickable) {
tickers.add(Objects.requireNonNull(tickable, "tickable cannot be null")); tickers.add(Objects.requireNonNull(tickable, "tickable cannot be null"));
return tickable; return tickable;

View file

@ -19,19 +19,15 @@ import net.minecraft.entity.EntityDimensions;
import net.minecraft.entity.EntityPose; import net.minecraft.entity.EntityPose;
import net.minecraft.entity.EntityType; import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity; 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.nbt.NbtCompound;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.world.World; import net.minecraft.world.World;
public class CastSpellEntity extends LightEmittingEntity implements Caster<CastSpellEntity>, WeaklyOwned.Mutable<LivingEntity>, MagicImmune { 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 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<>(); private final EntityReference<LivingEntity> owner = new EntityReference<>();
@ -45,7 +41,6 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster<CastS
@Override @Override
protected void initDataTracker() { protected void initDataTracker() {
getDataTracker().startTracking(EFFECT, new NbtCompound());
} }
@Override @Override

View file

@ -30,7 +30,6 @@ import net.minecraft.fluid.Fluid;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.registry.tag.TagKey; import net.minecraft.registry.tag.TagKey;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
@Mixin(Entity.class) @Mixin(Entity.class)
abstract class MixinEntity implements EntityDuck, Trackable { 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(); 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) @Inject(method = "isFireImmune", at = @At("HEAD"), cancellable = true)
private void onIsFireImmune(CallbackInfoReturnable<Boolean> info) { private void onIsFireImmune(CallbackInfoReturnable<Boolean> info) {
if (isLavaAffine() || (this instanceof Equine.Container c) && c.get().getCompositeRace().includes(Race.KIRIN)) { 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; package com.minelittlepony.unicopia.network.datasync;
import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.function.BiFunction;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Stream; 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.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.spell.Situation; import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.Spell; 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.NbtSerialisable;
import com.minelittlepony.unicopia.util.serialization.PacketCodec;
import net.minecraft.entity.data.TrackedData;
import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtCompound;
/** /**
@ -32,19 +34,21 @@ public class EffectSync implements SpellContainer, NbtSerialisable {
private final Caster<?> owner; private final Caster<?> owner;
private final TrackedData<NbtCompound> param; private final DataTracker tracker;
private final DataTracker.Entry<NbtCompound> param;
@Nullable public EffectSync(Caster<?> owner, DataTracker tracker) {
private NbtCompound lastValue;
public EffectSync(Caster<?> owner, TrackedData<NbtCompound> param) {
spells = new NetworkedReferenceSet<>(Spell::getUuid, () -> new SpellNetworkedReference<>(owner));
this.owner = owner; 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() { tracker.onBeforeSend(param, () -> {
owner.asEntity().getDataTracker().startTracking(param, new NbtCompound()); if (spells.isDirty()) {
tracker.set(param, spells.toNbt());
}
});
tracker.onReceive(param, nbt -> spells.fromNbt(nbt));
} }
public boolean tick(Situation situation) { 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)); 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 @Override
public void put(@Nullable Spell effect) { public void put(@Nullable Spell effect) {
spells.addReference(effect); spells.addReference(effect);
write();
if (owner instanceof UpdateCallback callback) { if (owner instanceof UpdateCallback callback) {
callback.onSpellSet(effect); callback.onSpellSet(effect);
} }
@ -99,12 +92,7 @@ public class EffectSync implements SpellContainer, NbtSerialisable {
@Override @Override
public void remove(UUID id) { public void remove(UUID id) {
Spell spell = spells.getReference(id); discard(spells.getReference(id));
spell.setDead();
spell.tickDying(owner);
if (spell.isDead()) {
spells.removeReference(id);
}
} }
@Override @Override
@ -113,13 +101,19 @@ public class EffectSync implements SpellContainer, NbtSerialisable {
if (!test.test(spell)) { if (!test.test(spell)) {
return initial; return initial;
} }
discard(spell);
return true;
});
}
private void discard(Spell spell) {
if (spell != null) {
spell.setDead(); spell.setDead();
spell.tickDying(owner); spell.tickDying(owner);
if (spell.isDead()) { if (spell.isDead()) {
spells.removeReference(spell); spells.removeReference(spell);
} }
return true; }
});
} }
@Override @Override
@ -135,55 +129,28 @@ public class EffectSync implements SpellContainer, NbtSerialisable {
}); });
} }
@Override @SuppressWarnings("unchecked")
public Stream<Spell> stream() {
return stream(null);
}
@Override @Override
public <T extends Spell> Stream<T> stream(@Nullable SpellPredicate<T> type) { 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) { if (type == null) {
return (Stream<T>)spells.getReferences(); return (Stream<T>)spells.getReferences();
} }
return (Stream<T>)spells.getReferences().flatMap(s -> s.findMatches(type)); return (Stream<T>)spells.getReferences().flatMap(s -> s.findMatches(type));
} }
private boolean reduce(Alteration alteration) { @Override
boolean initial = false; public boolean clear() {
for (Spell i : read(null).toList()) { if (spells.clear()) {
initial = alteration.apply(initial, i); if (owner instanceof UpdateCallback c) {
c.onSpellSet(null);
}
return true;
}
return false;
} }
write(); private boolean reduce(BiFunction<Boolean, Spell, Boolean> alteration) {
return initial; return stream().toList().stream().reduce(false, alteration, (a, b) -> b);
}
private void write() {
if (spells.isDirty() && !owner.isClient()) {
owner.asEntity().getDataTracker().set(param, spells.toNbt());
}
} }
@Override @Override
@ -194,14 +161,10 @@ public class EffectSync implements SpellContainer, NbtSerialisable {
@Override @Override
public void fromNBT(NbtCompound compound) { public void fromNBT(NbtCompound compound) {
spells.fromNbt(compound.getCompound("spells")); spells.fromNbt(compound.getCompound("spells"));
owner.asEntity().getDataTracker().set(param, spells.toNbt()); tracker.set(param, spells.toNbt());
} }
public interface UpdateCallback { public interface UpdateCallback {
void onSpellSet(@Nullable Spell spell); 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; import net.minecraft.world.World;
public class MagicBeamEntity extends MagicProjectileEntity implements Caster<MagicBeamEntity>, MagicImmune { 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 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()); private final EntityPhysics<MagicProjectileEntity> physics = new EntityPhysics<>(this, Trackable.of(this).getDataTrackers().getPrimaryTracker());
public MagicBeamEntity(EntityType<MagicBeamEntity> type, World world) { public MagicBeamEntity(EntityType<MagicBeamEntity> type, World world) {
@ -55,7 +54,6 @@ public class MagicBeamEntity extends MagicProjectileEntity implements Caster<Mag
protected void initDataTracker() { protected void initDataTracker() {
super.initDataTracker(); super.initDataTracker();
getDataTracker().startTracking(HYDROPHOBIC, false); getDataTracker().startTracking(HYDROPHOBIC, false);
getDataTracker().startTracking(EFFECT, new NbtCompound());
} }
@Override @Override