mirror of
https://github.com/Sollace/Unicopia.git
synced 2025-02-17 10:24:23 +01:00
Improve spell lookup performance by using the Ether
This commit is contained in:
parent
d22dd3bdf9
commit
ab3ac8cb0d
12 changed files with 263 additions and 198 deletions
|
@ -1,22 +1,17 @@
|
||||||
package com.minelittlepony.unicopia;
|
package com.minelittlepony.unicopia;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.ability.magic.CasterView;
|
|
||||||
import com.minelittlepony.unicopia.entity.player.dummy.DummyPlayerEntity;
|
import com.minelittlepony.unicopia.entity.player.dummy.DummyPlayerEntity;
|
||||||
import com.minelittlepony.unicopia.server.world.Ether;
|
|
||||||
import com.mojang.authlib.GameProfile;
|
import com.mojang.authlib.GameProfile;
|
||||||
|
|
||||||
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.network.PacketByteBuf;
|
import net.minecraft.network.PacketByteBuf;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.world.BlockView;
|
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public class InteractionManager {
|
public class InteractionManager {
|
||||||
|
@ -37,13 +32,6 @@ public class InteractionManager {
|
||||||
return INSTANCE;
|
return INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<CasterView> getCasterView(BlockView view) {
|
|
||||||
if (view instanceof ServerWorld world) {
|
|
||||||
return Optional.of(Ether.get(world));
|
|
||||||
}
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<Identifier, ?> readChapters(PacketByteBuf buf) {
|
public Map<Identifier, ?> readChapters(PacketByteBuf buf) {
|
||||||
throw new RuntimeException("Method not supported");
|
throw new RuntimeException("Method not supported");
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||||
import com.minelittlepony.unicopia.entity.*;
|
import com.minelittlepony.unicopia.entity.*;
|
||||||
import com.minelittlepony.unicopia.entity.damage.UDamageSources;
|
import com.minelittlepony.unicopia.entity.damage.UDamageSources;
|
||||||
import com.minelittlepony.unicopia.particle.ParticleSource;
|
import com.minelittlepony.unicopia.particle.ParticleSource;
|
||||||
|
import com.minelittlepony.unicopia.server.world.Ether;
|
||||||
import com.minelittlepony.unicopia.server.world.ModificationType;
|
import com.minelittlepony.unicopia.server.world.ModificationType;
|
||||||
import com.minelittlepony.unicopia.util.SoundEmitter;
|
import com.minelittlepony.unicopia.util.SoundEmitter;
|
||||||
import com.minelittlepony.unicopia.util.VecHelper;
|
import com.minelittlepony.unicopia.util.VecHelper;
|
||||||
|
@ -99,11 +100,7 @@ public interface Caster<E extends Entity> extends
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean canCastAt(Vec3d pos) {
|
default boolean canCastAt(Vec3d pos) {
|
||||||
return findAllSpellsInRange(500, SpellType.ARCANE_PROTECTION::isOn).noneMatch(caster -> caster
|
return !Ether.get(asWorld()).anyMatch(SpellType.ARCANE_PROTECTION, (spell, caster) -> spell.blocksMagicFor(caster, this, pos));
|
||||||
.getSpellSlot().get(SpellType.ARCANE_PROTECTION, false)
|
|
||||||
.filter(spell -> spell.blocksMagicFor(caster, this, pos))
|
|
||||||
.isPresent()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Stream<Caster<?>> stream(Stream<Entity> entities) {
|
static Stream<Caster<?>> stream(Stream<Entity> entities) {
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
package com.minelittlepony.unicopia.ability.magic;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.EquinePredicates;
|
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
|
||||||
import com.minelittlepony.unicopia.util.VecHelper;
|
|
||||||
|
|
||||||
import net.minecraft.entity.Entity;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.util.math.Vec3d;
|
|
||||||
import net.minecraft.world.*;
|
|
||||||
|
|
||||||
public interface CasterView {
|
|
||||||
EntityView getWorld();
|
|
||||||
|
|
||||||
default <S extends Spell> Stream<Map.Entry<Caster<?>, S>> findAllSpellsInRange(BlockPos pos, double radius, SpellPredicate<S> type) {
|
|
||||||
return findAllCastersInRange(pos, radius).flatMap(caster -> {
|
|
||||||
return caster.getSpellSlot().stream(type, false).map(spell -> {
|
|
||||||
return Map.entry(caster, spell);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
default Stream<Caster<?>> findAllCastersInRange(BlockPos pos, double radius) {
|
|
||||||
return findAllCastersInRange(pos, radius, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
default Stream<Caster<?>> findAllCastersInRange(BlockPos pos, double radius, @Nullable Predicate<Entity> test) {
|
|
||||||
return Caster.stream(findAllEntitiesInRange(pos, radius, test == null ? EquinePredicates.IS_CASTER : EquinePredicates.IS_CASTER.and(test)));
|
|
||||||
}
|
|
||||||
|
|
||||||
default Stream<Entity> findAllEntitiesInRange(BlockPos pos, double radius, @Nullable Predicate<Entity> test) {
|
|
||||||
return VecHelper.findInRange(null, getWorld(), Vec3d.ofCenter(pos), radius, test).stream();
|
|
||||||
}
|
|
||||||
|
|
||||||
default Stream<Entity> findAllEntitiesInRange(BlockPos pos, double radius) {
|
|
||||||
return findAllEntitiesInRange(pos, radius, null);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -45,6 +45,12 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
||||||
*/
|
*/
|
||||||
private final ParticleHandle particlEffect = new ParticleHandle();
|
private final ParticleHandle particlEffect = new ParticleHandle();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID of the placed counterpart of this spell.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
private UUID placedSpellId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The cast spell entity
|
* The cast spell entity
|
||||||
*/
|
*/
|
||||||
|
@ -82,10 +88,8 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean tick(Caster<?> source, Situation situation) {
|
public boolean tick(Caster<?> source, Situation situation) {
|
||||||
|
|
||||||
if (situation == Situation.BODY) {
|
if (situation == Situation.BODY) {
|
||||||
if (!source.isClient()) {
|
if (!source.isClient()) {
|
||||||
|
|
||||||
if (dimension == null) {
|
if (dimension == null) {
|
||||||
dimension = source.asWorld().getRegistryKey();
|
dimension = source.asWorld().getRegistryKey();
|
||||||
if (source instanceof Pony) {
|
if (source instanceof Pony) {
|
||||||
|
@ -105,8 +109,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
||||||
|
|
||||||
if (situation == Situation.GROUND_ENTITY) {
|
if (situation == Situation.GROUND_ENTITY) {
|
||||||
if (!source.isClient()) {
|
if (!source.isClient()) {
|
||||||
Ether ether = Ether.get(source.asWorld());
|
if (Ether.get(source.asWorld()).get(this, source) == null) {
|
||||||
if (ether.getEntry(getType(), source).isEmpty()) {
|
|
||||||
setDead();
|
setDead();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -127,7 +130,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkDetachment(Caster<?> source, EntityValues<?> target) {
|
private void checkDetachment(Caster<?> source, EntityValues<?> target) {
|
||||||
if (getWorld(source).map(Ether::get).flatMap(ether -> ether.getEntry(getType(), target.uuid())).isEmpty()) {
|
if (getWorld(source).map(Ether::get).map(ether -> ether.get(getType(), target, placedSpellId)).isEmpty()) {
|
||||||
setDead();
|
setDead();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,7 +146,8 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
||||||
entity.getSpellSlot().put(copy);
|
entity.getSpellSlot().put(copy);
|
||||||
entity.setCaster(source);
|
entity.setCaster(source);
|
||||||
entity.getWorld().spawnEntity(entity);
|
entity.getWorld().spawnEntity(entity);
|
||||||
Ether.get(entity.getWorld()).put(getType(), entity);
|
placedSpellId = copy.getUuid();
|
||||||
|
Ether.get(entity.getWorld()).getOrCreate(copy, entity);
|
||||||
|
|
||||||
castEntity.set(entity);
|
castEntity.set(entity);
|
||||||
setDirty();
|
setDirty();
|
||||||
|
@ -174,8 +178,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
||||||
if (!source.isClient()) {
|
if (!source.isClient()) {
|
||||||
castEntity.getTarget().ifPresent(target -> {
|
castEntity.getTarget().ifPresent(target -> {
|
||||||
getWorld(source).map(Ether::get)
|
getWorld(source).map(Ether::get)
|
||||||
.flatMap(ether -> ether.getEntry(getType(), target.uuid()))
|
.ifPresent(ether -> ether.remove(getType(), target.uuid()));
|
||||||
.ifPresent(Ether.Entry::markDead);
|
|
||||||
});
|
});
|
||||||
castEntity.set(null);
|
castEntity.set(null);
|
||||||
getSpellEntity(source).ifPresent(e -> {
|
getSpellEntity(source).ifPresent(e -> {
|
||||||
|
@ -183,7 +186,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
||||||
});
|
});
|
||||||
|
|
||||||
if (source.asEntity() instanceof CastSpellEntity spellcast) {
|
if (source.asEntity() instanceof CastSpellEntity spellcast) {
|
||||||
Ether.get(source.asWorld()).remove(getType(), source);
|
Ether.get(source.asWorld()).remove(this, source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.onDestroyed(source);
|
super.onDestroyed(source);
|
||||||
|
|
|
@ -9,6 +9,7 @@ import com.minelittlepony.unicopia.entity.mob.UEntities;
|
||||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||||
import com.minelittlepony.unicopia.item.FriendshipBraceletItem;
|
import com.minelittlepony.unicopia.item.FriendshipBraceletItem;
|
||||||
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
|
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
|
||||||
|
import com.minelittlepony.unicopia.server.world.Ether;
|
||||||
import com.minelittlepony.unicopia.util.shape.Sphere;
|
import com.minelittlepony.unicopia.util.shape.Sphere;
|
||||||
|
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
|
@ -42,6 +43,8 @@ public class AreaProtectionSpell extends AbstractAreaEffectSpell {
|
||||||
source.addParticle(new MagicParticleEffect(getType().getColor()), pos, Vec3d.ZERO);
|
source.addParticle(new MagicParticleEffect(getType().getColor()), pos, Vec3d.ZERO);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Ether.get(source.asWorld()).getOrCreate(this, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
source.findAllSpellsInRange(radius, e -> isValidTarget(source, e)).filter(caster -> !caster.hasCommonOwner(source)).forEach(caster -> {
|
source.findAllSpellsInRange(radius, e -> isValidTarget(source, e)).filter(caster -> !caster.hasCommonOwner(source)).forEach(caster -> {
|
||||||
|
@ -51,6 +54,11 @@ public class AreaProtectionSpell extends AbstractAreaEffectSpell {
|
||||||
return !isDead();
|
return !isDead();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroyed(Caster<?> caster) {
|
||||||
|
Ether.get(caster.asWorld()).remove(this, caster);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the maximum radius of the shield. aka The area of effect.
|
* Calculates the maximum radius of the shield. aka The area of effect.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -12,7 +12,7 @@ import com.minelittlepony.unicopia.util.shape.Sphere;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An area-effect spell that disperses illussions.
|
* An area-effect spell that disperses illusions.
|
||||||
*/
|
*/
|
||||||
public class DisperseIllusionSpell extends AbstractAreaEffectSpell {
|
public class DisperseIllusionSpell extends AbstractAreaEffectSpell {
|
||||||
protected DisperseIllusionSpell(CustomisedSpellType<?> type) {
|
protected DisperseIllusionSpell(CustomisedSpellType<?> type) {
|
||||||
|
|
|
@ -5,14 +5,16 @@ import java.util.Set;
|
||||||
|
|
||||||
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.CasterView;
|
import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod;
|
||||||
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.trait.SpellTraits;
|
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
|
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
|
||||||
import com.minelittlepony.unicopia.advancement.UCriteria;
|
import com.minelittlepony.unicopia.advancement.UCriteria;
|
||||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||||
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.server.world.Ether;
|
||||||
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
||||||
import com.minelittlepony.unicopia.util.shape.*;
|
import com.minelittlepony.unicopia.util.shape.*;
|
||||||
|
|
||||||
|
@ -21,8 +23,10 @@ import net.minecraft.fluid.*;
|
||||||
import net.minecraft.nbt.*;
|
import net.minecraft.nbt.*;
|
||||||
import net.minecraft.state.property.Properties;
|
import net.minecraft.state.property.Properties;
|
||||||
import net.minecraft.registry.tag.TagKey;
|
import net.minecraft.registry.tag.TagKey;
|
||||||
|
import net.minecraft.server.world.ServerWorld;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
import net.minecraft.world.BlockView;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public class HydrophobicSpell extends AbstractSpell {
|
public class HydrophobicSpell extends AbstractSpell {
|
||||||
|
@ -41,16 +45,15 @@ public class HydrophobicSpell extends AbstractSpell {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Caster<?> source) {
|
public Spell prepareForCast(Caster<?> caster, CastingMethod method) {
|
||||||
if (getTraits().get(Trait.GENEROSITY) > 0) {
|
if ((method == CastingMethod.DIRECT || method == CastingMethod.STAFF) && getTraits().get(Trait.GENEROSITY) > 0) {
|
||||||
return toPlaceable().apply(source);
|
return toPlaceable();
|
||||||
}
|
}
|
||||||
return super.apply(source);
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean tick(Caster<?> source, Situation situation) {
|
public boolean tick(Caster<?> source, Situation situation) {
|
||||||
|
|
||||||
if (!source.isClient()) {
|
if (!source.isClient()) {
|
||||||
World world = source.asWorld();
|
World world = source.asWorld();
|
||||||
|
|
||||||
|
@ -86,7 +89,11 @@ public class HydrophobicSpell extends AbstractSpell {
|
||||||
setDead();
|
setDead();
|
||||||
}
|
}
|
||||||
|
|
||||||
source.spawnParticles(new Sphere(true, getRange(source)), 10, pos -> {
|
double range = getRange(source);
|
||||||
|
var entry = Ether.get(source.asWorld()).getOrCreate(this, source);
|
||||||
|
entry.radius = (float)range;
|
||||||
|
|
||||||
|
source.spawnParticles(new Sphere(true, range), 10, pos -> {
|
||||||
BlockPos bp = BlockPos.ofFloored(pos);
|
BlockPos bp = BlockPos.ofFloored(pos);
|
||||||
if (source.asWorld().getFluidState(bp.up()).isIn(affectedFluid)) {
|
if (source.asWorld().getFluidState(bp.up()).isIn(affectedFluid)) {
|
||||||
source.addParticle(UParticles.RAIN_DROPS, pos, Vec3d.ZERO);
|
source.addParticle(UParticles.RAIN_DROPS, pos, Vec3d.ZERO);
|
||||||
|
@ -107,6 +114,7 @@ public class HydrophobicSpell extends AbstractSpell {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroyed(Caster<?> caster) {
|
protected void onDestroyed(Caster<?> caster) {
|
||||||
|
Ether.get(caster.asWorld()).remove(this, caster);
|
||||||
storedFluidPositions.removeIf(entry -> {
|
storedFluidPositions.removeIf(entry -> {
|
||||||
entry.restore(caster.asWorld());
|
entry.restore(caster.asWorld());
|
||||||
return true;
|
return true;
|
||||||
|
@ -162,13 +170,20 @@ public class HydrophobicSpell extends AbstractSpell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean blocksFlow(Caster<?> caster, BlockPos pos, FluidState fluid) {
|
public boolean blocksFlow(Ether.Entry<?> entry, Vec3d center, BlockPos pos, FluidState fluid) {
|
||||||
return fluid.isIn(affectedFluid) && pos.isWithinDistance(caster.getOrigin(), getRange(caster) + 1);
|
return fluid.isIn(affectedFluid) && pos.isWithinDistance(center, (double)entry.radius + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean blocksFluidFlow(CasterView world, BlockPos pos, FluidState state) {
|
public static boolean blocksFluidFlow(BlockView world, BlockPos pos, FluidState state) {
|
||||||
return world.findAllSpellsInRange(pos, 500, SpellType.HYDROPHOBIC).anyMatch(pair -> {
|
if (world instanceof ServerWorld sw) {
|
||||||
return pair.getValue().blocksFlow(pair.getKey(), pos, state);
|
return Ether.get(sw).anyMatch(SpellType.HYDROPHOBIC, entry -> {
|
||||||
});
|
var spell = entry.getSpell();
|
||||||
|
var target = entry.entity.getTarget().orElse(null);
|
||||||
|
return spell != null && target != null && spell.blocksFlow(entry, target.pos(), pos, state);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package com.minelittlepony.unicopia.ability.magic.spell.effect;
|
package com.minelittlepony.unicopia.ability.magic.spell.effect;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.USounds;
|
import com.minelittlepony.unicopia.USounds;
|
||||||
import com.minelittlepony.unicopia.Unicopia;
|
import com.minelittlepony.unicopia.Unicopia;
|
||||||
|
@ -34,6 +37,8 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
|
||||||
.build();
|
.build();
|
||||||
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
|
||||||
|
private UUID targetPortalId;
|
||||||
private final EntityReference<Entity> teleportationTarget = new EntityReference<>();
|
private final EntityReference<Entity> teleportationTarget = new EntityReference<>();
|
||||||
|
|
||||||
private boolean publishedPosition;
|
private boolean publishedPosition;
|
||||||
|
@ -63,10 +68,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
|
||||||
Vec3d origin = source.getOriginVector();
|
Vec3d origin = source.getOriginVector();
|
||||||
|
|
||||||
ParticleEffect effect = teleportationTarget.getTarget()
|
ParticleEffect effect = teleportationTarget.getTarget()
|
||||||
.map(target -> {
|
.map(target -> (ParticleEffect)new FollowingParticleEffect(UParticles.HEALTH_DRAIN, target.pos(), 0.2F).withChild(ParticleTypes.ELECTRIC_SPARK))
|
||||||
getType();
|
|
||||||
return (ParticleEffect)new FollowingParticleEffect(UParticles.HEALTH_DRAIN, target.pos(), 0.2F).withChild(ParticleTypes.ELECTRIC_SPARK);
|
|
||||||
})
|
|
||||||
.orElse(ParticleTypes.ELECTRIC_SPARK);
|
.orElse(ParticleTypes.ELECTRIC_SPARK);
|
||||||
|
|
||||||
source.spawnParticles(origin, particleArea, 5, pos -> {
|
source.spawnParticles(origin, particleArea, 5, pos -> {
|
||||||
|
@ -82,7 +84,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
teleportationTarget.getTarget().ifPresent(target -> {
|
teleportationTarget.getTarget().ifPresent(target -> {
|
||||||
if (Ether.get(source.asWorld()).getEntry(getType(), target.uuid()).isEmpty()) {
|
if (Ether.get(source.asWorld()).get(getType(), target, targetPortalId) != null) {
|
||||||
Unicopia.LOGGER.debug("Lost sibling, breaking connection to " + target.uuid());
|
Unicopia.LOGGER.debug("Lost sibling, breaking connection to " + target.uuid());
|
||||||
teleportationTarget.set(null);
|
teleportationTarget.set(null);
|
||||||
setDirty();
|
setDirty();
|
||||||
|
@ -96,18 +98,15 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!publishedPosition) {
|
var entry = Ether.get(source.asWorld()).getOrCreate(this, source);
|
||||||
publishedPosition = true;
|
entry.pitch = pitch;
|
||||||
Ether.Entry entry = Ether.get(source.asWorld()).put(getType(), source);
|
entry.yaw = yaw;
|
||||||
entry.pitch = pitch;
|
|
||||||
entry.yaw = yaw;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return !isDead();
|
return !isDead();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tickWithTargetLink(Caster<?> source, Ether.Entry destination) {
|
private void tickWithTargetLink(Caster<?> source, Ether.Entry<?> destination) {
|
||||||
|
|
||||||
destination.entity.getTarget().ifPresent(target -> {
|
destination.entity.getTarget().ifPresent(target -> {
|
||||||
source.findAllEntitiesInRange(1).forEach(entity -> {
|
source.findAllEntitiesInRange(1).forEach(entity -> {
|
||||||
|
@ -142,20 +141,21 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ether ether = Ether.get(source.asWorld());
|
Ether.get(source.asWorld()).anyMatch(getType(), entry -> {
|
||||||
ether.getEntries(getType())
|
if (entry.isAvailable() && !entry.entity.referenceEquals(source.asEntity()) && entry.entity.isSet()) {
|
||||||
.stream()
|
|
||||||
.filter(entry -> entry.isAvailable() && !entry.entity.referenceEquals(source.asEntity()) && entry.entity.isSet())
|
|
||||||
.findAny()
|
|
||||||
.ifPresent(entry -> {
|
|
||||||
entry.setTaken(true);
|
entry.setTaken(true);
|
||||||
teleportationTarget.copyFrom(entry.entity);
|
teleportationTarget.copyFrom(entry.entity);
|
||||||
|
targetPortalId = entry.getSpellId();
|
||||||
setDirty();
|
setDirty();
|
||||||
});
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<Ether.Entry> getTarget(Caster<?> source) {
|
@SuppressWarnings("unchecked")
|
||||||
return teleportationTarget.getTarget().flatMap(target -> Ether.get(source.asWorld()).getEntry(getType(), target.uuid()));
|
private Optional<Ether.Entry<PortalSpell>> getTarget(Caster<?> source) {
|
||||||
|
return teleportationTarget.getTarget()
|
||||||
|
.map(target -> Ether.get(source.asWorld()).get((SpellType<PortalSpell>)getType(), target, targetPortalId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -189,7 +189,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
|
||||||
protected void onDestroyed(Caster<?> caster) {
|
protected void onDestroyed(Caster<?> caster) {
|
||||||
particleEffect.destroy();
|
particleEffect.destroy();
|
||||||
Ether ether = Ether.get(caster.asWorld());
|
Ether ether = Ether.get(caster.asWorld());
|
||||||
ether.remove(getType(), caster.asEntity().getUuid());
|
ether.remove(getType(), caster);
|
||||||
getTarget(caster).ifPresent(e -> e.setTaken(false));
|
getTarget(caster).ifPresent(e -> e.setTaken(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ package com.minelittlepony.unicopia.client;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
@ -13,14 +12,12 @@ import com.minelittlepony.unicopia.EquinePredicates;
|
||||||
import com.minelittlepony.unicopia.FlightType;
|
import com.minelittlepony.unicopia.FlightType;
|
||||||
import com.minelittlepony.unicopia.InteractionManager;
|
import com.minelittlepony.unicopia.InteractionManager;
|
||||||
import com.minelittlepony.unicopia.USounds;
|
import com.minelittlepony.unicopia.USounds;
|
||||||
import com.minelittlepony.unicopia.ability.magic.CasterView;
|
|
||||||
import com.minelittlepony.unicopia.client.gui.DismissSpellScreen;
|
import com.minelittlepony.unicopia.client.gui.DismissSpellScreen;
|
||||||
import com.minelittlepony.unicopia.client.gui.spellbook.ClientChapters;
|
import com.minelittlepony.unicopia.client.gui.spellbook.ClientChapters;
|
||||||
import com.minelittlepony.unicopia.client.sound.*;
|
import com.minelittlepony.unicopia.client.sound.*;
|
||||||
import com.minelittlepony.unicopia.entity.player.PlayerPhysics;
|
import com.minelittlepony.unicopia.entity.player.PlayerPhysics;
|
||||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||||
import com.minelittlepony.unicopia.entity.player.dummy.DummyClientPlayerEntity;
|
import com.minelittlepony.unicopia.entity.player.dummy.DummyClientPlayerEntity;
|
||||||
import com.minelittlepony.unicopia.server.world.Ether;
|
|
||||||
import com.mojang.authlib.GameProfile;
|
import com.mojang.authlib.GameProfile;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
|
@ -37,29 +34,17 @@ import net.minecraft.entity.passive.BeeEntity;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.entity.vehicle.AbstractMinecartEntity;
|
import net.minecraft.entity.vehicle.AbstractMinecartEntity;
|
||||||
import net.minecraft.network.PacketByteBuf;
|
import net.minecraft.network.PacketByteBuf;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
|
||||||
import net.minecraft.sound.SoundCategory;
|
import net.minecraft.sound.SoundCategory;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.util.math.random.Random;
|
import net.minecraft.util.math.random.Random;
|
||||||
import net.minecraft.world.BlockView;
|
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public class ClientInteractionManager extends InteractionManager {
|
public class ClientInteractionManager extends InteractionManager {
|
||||||
|
|
||||||
private final MinecraftClient client = MinecraftClient.getInstance();
|
private final MinecraftClient client = MinecraftClient.getInstance();
|
||||||
|
|
||||||
private final Optional<CasterView> clientWorld = Optional.of(() -> MinecraftClient.getInstance().world);
|
|
||||||
|
|
||||||
private final Int2ObjectMap<WeakReference<TickableSoundInstance>> playingSounds = new Int2ObjectOpenHashMap<>();
|
private final Int2ObjectMap<WeakReference<TickableSoundInstance>> playingSounds = new Int2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<CasterView> getCasterView(BlockView view) {
|
|
||||||
if (view instanceof ServerWorld world) {
|
|
||||||
return Optional.of(Ether.get(world));
|
|
||||||
}
|
|
||||||
return clientWorld;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<Identifier, ?> readChapters(PacketByteBuf buffer) {
|
public Map<Identifier, ?> readChapters(PacketByteBuf buffer) {
|
||||||
return buffer.readMap(PacketByteBuf::readIdentifier, ClientChapters::loadChapter);
|
return buffer.readMap(PacketByteBuf::readIdentifier, ClientChapters::loadChapter);
|
||||||
|
|
|
@ -5,7 +5,6 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.InteractionManager;
|
|
||||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.HydrophobicSpell;
|
import com.minelittlepony.unicopia.ability.magic.spell.effect.HydrophobicSpell;
|
||||||
|
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
|
@ -17,7 +16,7 @@ import net.minecraft.world.BlockView;
|
||||||
abstract class MixinFlowableFluid {
|
abstract class MixinFlowableFluid {
|
||||||
@Inject(method = "canFill", at = @At("HEAD"), cancellable = true)
|
@Inject(method = "canFill", at = @At("HEAD"), cancellable = true)
|
||||||
private void onCanFill(BlockView world, BlockPos pos, BlockState state, Fluid fluid, CallbackInfoReturnable<Boolean> info) {
|
private void onCanFill(BlockView world, BlockPos pos, BlockState state, Fluid fluid, CallbackInfoReturnable<Boolean> info) {
|
||||||
if (InteractionManager.instance().getCasterView(world).filter(view -> HydrophobicSpell.blocksFluidFlow(view, pos, fluid.getDefaultState())).isPresent()) {
|
if (HydrophobicSpell.blocksFluidFlow(world, pos, fluid.getDefaultState())) {
|
||||||
info.setReturnValue(false);
|
info.setReturnValue(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,88 +1,93 @@
|
||||||
package com.minelittlepony.unicopia.server.world;
|
package com.minelittlepony.unicopia.server.world;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.function.BiPredicate;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.minelittlepony.unicopia.Unicopia;
|
import com.minelittlepony.unicopia.Unicopia;
|
||||||
import com.minelittlepony.unicopia.ability.magic.CasterView;
|
|
||||||
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.effect.SpellType;
|
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||||
import com.minelittlepony.unicopia.entity.EntityReference;
|
import com.minelittlepony.unicopia.entity.EntityReference;
|
||||||
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
||||||
|
|
||||||
import net.minecraft.nbt.*;
|
import net.minecraft.nbt.*;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.world.PersistentState;
|
import net.minecraft.world.PersistentState;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public class Ether extends PersistentState implements CasterView {
|
public class Ether extends PersistentState {
|
||||||
private static final Identifier ID = Unicopia.id("ether");
|
private static final Identifier ID = Unicopia.id("ether");
|
||||||
|
|
||||||
public static Ether get(World world) {
|
public static Ether get(World world) {
|
||||||
return WorldOverlay.getPersistableStorage(world, ID, Ether::new, Ether::new);
|
return WorldOverlay.getPersistableStorage(world, ID, Ether::new, Ether::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Map<Identifier, Set<Entry>> advertisingEndpoints = new HashMap<>();
|
private final Map<Identifier, Map<UUID, Map<UUID, Entry<?>>>> endpoints;
|
||||||
|
|
||||||
private final Object locker = new Object();
|
private final Object locker = new Object();
|
||||||
|
|
||||||
private final World world;
|
private final World world;
|
||||||
|
|
||||||
Ether(World world, NbtCompound compound) {
|
Ether(World world, NbtCompound compound) {
|
||||||
this(world);
|
this.world = world;
|
||||||
compound.getKeys().forEach(key -> {
|
this.endpoints = NbtSerialisable.readMap(compound.getCompound("endpoints"), Identifier::tryParse, typeNbt -> {
|
||||||
Identifier typeId = Identifier.tryParse(key);
|
return NbtSerialisable.readMap((NbtCompound)typeNbt, UUID::fromString, entityNbt -> {
|
||||||
if (typeId != null) {
|
return NbtSerialisable.readMap((NbtCompound)entityNbt, UUID::fromString, Entry::new);
|
||||||
Set<Entry> uuids = getEntries(typeId);
|
});
|
||||||
compound.getList(key, NbtElement.COMPOUND_TYPE).forEach(entry -> {
|
|
||||||
Entry e = new Entry();
|
|
||||||
e.fromNBT((NbtCompound)entry);
|
|
||||||
uuids.add(e);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Ether(World world) {
|
Ether(World world) {
|
||||||
this.world = world;
|
this.world = world;
|
||||||
|
this.endpoints = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NbtCompound writeNbt(NbtCompound compound) {
|
public NbtCompound writeNbt(NbtCompound compound) {
|
||||||
synchronized (locker) {
|
synchronized (locker) {
|
||||||
advertisingEndpoints.forEach((id, uuids) -> {
|
pruneNodes();
|
||||||
NbtList list = new NbtList();
|
compound.put("endpoints", NbtSerialisable.writeMap(endpoints, Identifier::toString, entities -> {
|
||||||
uuids.forEach(uuid -> {
|
return NbtSerialisable.writeMap(entities, UUID::toString, spells -> {
|
||||||
if (uuid.isAlive()) {
|
return NbtSerialisable.writeMap(spells, UUID::toString, Entry::toNBT);
|
||||||
list.add(uuid.toNBT());
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
compound.put(id.toString(), list);
|
}));
|
||||||
});
|
|
||||||
|
|
||||||
return compound;
|
return compound;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Entry put(SpellType<?> spellType, Caster<?> caster) {
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T extends Spell> Entry<T> getOrCreate(T spell, Caster<?> caster) {
|
||||||
synchronized (locker) {
|
synchronized (locker) {
|
||||||
var entry = new Entry(caster);
|
Entry<T> entry = (Entry<T>)endpoints
|
||||||
getEntries(spellType.getId()).add(entry);
|
.computeIfAbsent(spell.getType().getId(), typeId -> new HashMap<>())
|
||||||
markDirty();
|
.computeIfAbsent(caster.asEntity().getUuid(), entityId -> new HashMap<>())
|
||||||
|
.computeIfAbsent(spell.getUuid(), spellid -> {
|
||||||
|
markDirty();
|
||||||
|
return new Entry<>(spell, caster);
|
||||||
|
});
|
||||||
|
if (entry.removed) {
|
||||||
|
entry.removed = false;
|
||||||
|
markDirty();
|
||||||
|
}
|
||||||
|
if (entry.spell.get() != spell) {
|
||||||
|
entry.spell = new WeakReference<>(spell);
|
||||||
|
markDirty();
|
||||||
|
}
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void remove(SpellType<?> spellType, UUID id) {
|
public <T extends Spell> void remove(SpellType<T> spellType, UUID entityId) {
|
||||||
synchronized (locker) {
|
synchronized (locker) {
|
||||||
Identifier typeId = spellType.getId();
|
endpoints.computeIfPresent(spellType.getId(), (typeId, entries) -> {
|
||||||
Set<Entry> refs = advertisingEndpoints.get(typeId);
|
if (entries.remove(entityId) != null) {
|
||||||
if (refs != null) {
|
markDirty();
|
||||||
refs.removeIf(ref -> ref.isDead() || ref.entity.getTarget().filter(target -> id.equals(target.uuid())).isPresent());
|
|
||||||
if (refs.isEmpty()) {
|
|
||||||
advertisingEndpoints.remove(typeId);
|
|
||||||
}
|
}
|
||||||
markDirty();
|
return entries.isEmpty() ? null : entries;
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,72 +95,123 @@ public class Ether extends PersistentState implements CasterView {
|
||||||
remove(spellType, caster.asEntity().getUuid());
|
remove(spellType, caster.asEntity().getUuid());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<Entry> getEntries(SpellType<?> spellType) {
|
public <T extends Spell> void remove(T spell, Caster<?> caster) {
|
||||||
return getEntries(spellType.getId());
|
Entry<T> entry = get(spell, caster);
|
||||||
|
if (entry != null) {
|
||||||
|
entry.markDead();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<Entry> getEntries(Identifier typeId) {
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T extends Spell> Entry<T> get(T spell, Caster<?> caster) {
|
||||||
|
return get((SpellType<T>)spell.getType(), caster.asEntity().getUuid(), spell.getUuid());
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends Spell> Entry<T> get(SpellType<T> spell, EntityReference.EntityValues<?> entityId, @Nullable UUID spellId) {
|
||||||
|
return get(spell, entityId.uuid(), spellId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Nullable
|
||||||
|
private <T extends Spell> Entry<T> get(SpellType<T> spell, UUID entityId, @Nullable UUID spellId) {
|
||||||
|
if (spellId == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
synchronized (locker) {
|
synchronized (locker) {
|
||||||
return advertisingEndpoints.compute(typeId, (k, old) -> {
|
Entry<?> entry = endpoints
|
||||||
if (old == null) {
|
.getOrDefault(spell.getId(), Map.of())
|
||||||
old = new HashSet<>();
|
.getOrDefault(entityId, Map.of())
|
||||||
} else {
|
.get(spellId);
|
||||||
old.removeIf(Entry::isDead);
|
return entry == null || entry.isDead() ? null : (Entry<T>)entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends Spell> boolean anyMatch(SpellType<T> spellType, BiPredicate<T, Caster<?>> condition) {
|
||||||
|
return anyMatch(spellType, entry -> {
|
||||||
|
var spell = entry.getSpell();
|
||||||
|
var caster = entry.getCaster();
|
||||||
|
return spell != null && caster != null && condition.test(spell, caster);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T extends Spell> boolean anyMatch(SpellType<T> spellType, Predicate<Entry<T>> condition) {
|
||||||
|
synchronized (locker) {
|
||||||
|
for (var entries : endpoints.getOrDefault(spellType.getId(), Map.of()).values()) {
|
||||||
|
for (var entry : entries.values()) {
|
||||||
|
if (!entry.isDead() && condition.test((Entry<T>)entry)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return old;
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pruneNodes() {
|
||||||
|
this.endpoints.values().removeIf(entities -> {
|
||||||
|
entities.values().removeIf(spells -> {
|
||||||
|
spells.values().removeIf(Entry::isDead);
|
||||||
|
return spells.isEmpty();
|
||||||
});
|
});
|
||||||
}
|
return entities.isEmpty();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Entry> getEntry(SpellType<?> spellType, Caster<?> caster) {
|
public class Entry<T extends Spell> implements NbtSerialisable {
|
||||||
synchronized (locker) {
|
|
||||||
return getEntries(spellType).stream().filter(e -> e.entity.referenceEquals(caster.asEntity())).findFirst();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<Entry> getEntry(SpellType<?> spellType, UUID uuid) {
|
|
||||||
synchronized (locker) {
|
|
||||||
return getEntries(spellType).stream().filter(e -> e.equals(uuid)).findFirst();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public World getWorld() {
|
|
||||||
return world;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Entry implements NbtSerialisable {
|
|
||||||
public final EntityReference<?> entity;
|
public final EntityReference<?> entity;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private UUID spellId;
|
||||||
|
private WeakReference<T> spell;
|
||||||
|
|
||||||
private boolean removed;
|
private boolean removed;
|
||||||
private boolean taken;
|
private boolean taken;
|
||||||
|
|
||||||
public float pitch;
|
public float pitch;
|
||||||
public float yaw;
|
public float yaw;
|
||||||
|
public float radius;
|
||||||
|
|
||||||
public Entry() {
|
private Entry(NbtElement nbt) {
|
||||||
entity = new EntityReference<>();
|
this.entity = new EntityReference<>();
|
||||||
|
this.spell = new WeakReference<>(null);
|
||||||
|
this.fromNBT((NbtCompound)nbt);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Entry(Caster<?> caster) {
|
public Entry(T spell, Caster<?> caster) {
|
||||||
entity = new EntityReference<>(caster.asEntity());
|
this.entity = new EntityReference<>(caster.asEntity());
|
||||||
|
this.spell = new WeakReference<>(spell);
|
||||||
|
spellId = spell.getUuid();
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isAlive() {
|
boolean isAlive() {
|
||||||
return !removed;
|
return !isDead();
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isDead() {
|
boolean isDead() {
|
||||||
|
if (!removed) {
|
||||||
|
getSpell();
|
||||||
|
}
|
||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public UUID getSpellId() {
|
||||||
|
return spellId;
|
||||||
|
}
|
||||||
|
|
||||||
public void markDead() {
|
public void markDead() {
|
||||||
Unicopia.LOGGER.debug("Marking " + entity.getTarget().orElse(null) + " as dead");
|
Unicopia.LOGGER.debug("Marking " + entity.getTarget().orElse(null) + " as dead");
|
||||||
removed = true;
|
removed = true;
|
||||||
markDirty();
|
markDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean entityMatches(UUID uuid) {
|
||||||
|
return entity.getTarget().filter(target -> uuid.equals(target.uuid())).isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isAvailable() {
|
public boolean isAvailable() {
|
||||||
return !removed && !taken;
|
return !isDead() && !taken;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTaken(boolean taken) {
|
public void setTaken(boolean taken) {
|
||||||
|
@ -163,6 +219,43 @@ public class Ether extends PersistentState implements CasterView {
|
||||||
markDirty();
|
markDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public T getSpell() {
|
||||||
|
if (removed) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
T spell = this.spell.get();
|
||||||
|
if (spell == null) {
|
||||||
|
if (spellId != null) {
|
||||||
|
spell = entity
|
||||||
|
.getOrEmpty(world)
|
||||||
|
.flatMap(Caster::of)
|
||||||
|
.flatMap(caster -> caster.getSpellSlot().<T>get(s -> s.getUuid().equals(spellId), true))
|
||||||
|
.orElse(null);
|
||||||
|
|
||||||
|
if (spell != null) {
|
||||||
|
this.spell = new WeakReference<>(spell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spell != null && spell.isDead()) {
|
||||||
|
spellId = null;
|
||||||
|
spell = null;
|
||||||
|
markDead();
|
||||||
|
}
|
||||||
|
|
||||||
|
return spell;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Caster<?> getCaster() {
|
||||||
|
if (removed) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Caster.of(this.entity.get(world)).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toNBT(NbtCompound compound) {
|
public void toNBT(NbtCompound compound) {
|
||||||
entity.toNBT(compound);
|
entity.toNBT(compound);
|
||||||
|
@ -170,6 +263,10 @@ public class Ether extends PersistentState implements CasterView {
|
||||||
compound.putBoolean("taken", taken);
|
compound.putBoolean("taken", taken);
|
||||||
compound.putFloat("pitch", pitch);
|
compound.putFloat("pitch", pitch);
|
||||||
compound.putFloat("yaw", yaw);
|
compound.putFloat("yaw", yaw);
|
||||||
|
compound.putFloat("radius", radius);
|
||||||
|
if (spellId != null) {
|
||||||
|
compound.putUuid("spellId", spellId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -179,20 +276,24 @@ public class Ether extends PersistentState implements CasterView {
|
||||||
taken = compound.getBoolean("taken");
|
taken = compound.getBoolean("taken");
|
||||||
pitch = compound.getFloat("pitch");
|
pitch = compound.getFloat("pitch");
|
||||||
yaw = compound.getFloat("yaw");
|
yaw = compound.getFloat("yaw");
|
||||||
|
radius = compound.getFloat("radius");
|
||||||
|
spellId = compound.containsUuid("spellid") ? compound.getUuid("spellId") : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object other) {
|
public boolean equals(Object other) {
|
||||||
return other instanceof Entry e && e.entity.referenceEquals(entity);
|
return other instanceof Entry<?> e
|
||||||
|
&& e.entity.referenceEquals(entity)
|
||||||
|
&& Objects.equals(e.spell.get(), spell.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean equals(UUID uuid) {
|
public boolean equals(UUID entityId, UUID spellId) {
|
||||||
return entity.referenceEquals(uuid);
|
return entity.referenceEquals(entityId) && spellId.equals(this.spellId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return entity.hashCode();
|
return Objects.hash(entity, spell.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.util;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.*;
|
import java.util.function.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Pair;
|
import com.mojang.datafixers.util.Pair;
|
||||||
|
@ -65,6 +66,18 @@ public interface NbtSerialisable {
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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))));
|
||||||
|
}
|
||||||
|
|
||||||
|
static <K, V> NbtCompound writeMap(Map<K, V> map, Function<K, String> keyFunction, Function<V, ? extends NbtElement> valueFunction) {
|
||||||
|
NbtCompound nbt = new NbtCompound();
|
||||||
|
map.forEach((k, v) -> {
|
||||||
|
nbt.put(keyFunction.apply(k), valueFunction.apply(v));
|
||||||
|
});
|
||||||
|
return nbt;
|
||||||
|
}
|
||||||
|
|
||||||
interface Serializer<T> {
|
interface Serializer<T> {
|
||||||
T read(NbtCompound compound);
|
T read(NbtCompound compound);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue