mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-11-23 21:38:00 +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;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.ability.magic.CasterView;
|
||||
import com.minelittlepony.unicopia.entity.player.dummy.DummyPlayerEntity;
|
||||
import com.minelittlepony.unicopia.server.world.Ether;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class InteractionManager {
|
||||
|
@ -37,13 +32,6 @@ public class InteractionManager {
|
|||
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) {
|
||||
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.damage.UDamageSources;
|
||||
import com.minelittlepony.unicopia.particle.ParticleSource;
|
||||
import com.minelittlepony.unicopia.server.world.Ether;
|
||||
import com.minelittlepony.unicopia.server.world.ModificationType;
|
||||
import com.minelittlepony.unicopia.util.SoundEmitter;
|
||||
import com.minelittlepony.unicopia.util.VecHelper;
|
||||
|
@ -99,11 +100,7 @@ public interface Caster<E extends Entity> extends
|
|||
}
|
||||
|
||||
default boolean canCastAt(Vec3d pos) {
|
||||
return findAllSpellsInRange(500, SpellType.ARCANE_PROTECTION::isOn).noneMatch(caster -> caster
|
||||
.getSpellSlot().get(SpellType.ARCANE_PROTECTION, false)
|
||||
.filter(spell -> spell.blocksMagicFor(caster, this, pos))
|
||||
.isPresent()
|
||||
);
|
||||
return !Ether.get(asWorld()).anyMatch(SpellType.ARCANE_PROTECTION, (spell, caster) -> spell.blocksMagicFor(caster, this, pos));
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
/**
|
||||
* ID of the placed counterpart of this spell.
|
||||
*/
|
||||
@Nullable
|
||||
private UUID placedSpellId;
|
||||
|
||||
/**
|
||||
* The cast spell entity
|
||||
*/
|
||||
|
@ -82,10 +88,8 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
|||
|
||||
@Override
|
||||
public boolean tick(Caster<?> source, Situation situation) {
|
||||
|
||||
if (situation == Situation.BODY) {
|
||||
if (!source.isClient()) {
|
||||
|
||||
if (dimension == null) {
|
||||
dimension = source.asWorld().getRegistryKey();
|
||||
if (source instanceof Pony) {
|
||||
|
@ -105,8 +109,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
|||
|
||||
if (situation == Situation.GROUND_ENTITY) {
|
||||
if (!source.isClient()) {
|
||||
Ether ether = Ether.get(source.asWorld());
|
||||
if (ether.getEntry(getType(), source).isEmpty()) {
|
||||
if (Ether.get(source.asWorld()).get(this, source) == null) {
|
||||
setDead();
|
||||
return false;
|
||||
}
|
||||
|
@ -127,7 +130,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
|||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -143,7 +146,8 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
|||
entity.getSpellSlot().put(copy);
|
||||
entity.setCaster(source);
|
||||
entity.getWorld().spawnEntity(entity);
|
||||
Ether.get(entity.getWorld()).put(getType(), entity);
|
||||
placedSpellId = copy.getUuid();
|
||||
Ether.get(entity.getWorld()).getOrCreate(copy, entity);
|
||||
|
||||
castEntity.set(entity);
|
||||
setDirty();
|
||||
|
@ -174,8 +178,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
|||
if (!source.isClient()) {
|
||||
castEntity.getTarget().ifPresent(target -> {
|
||||
getWorld(source).map(Ether::get)
|
||||
.flatMap(ether -> ether.getEntry(getType(), target.uuid()))
|
||||
.ifPresent(Ether.Entry::markDead);
|
||||
.ifPresent(ether -> ether.remove(getType(), target.uuid()));
|
||||
});
|
||||
castEntity.set(null);
|
||||
getSpellEntity(source).ifPresent(e -> {
|
||||
|
@ -183,7 +186,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
|||
});
|
||||
|
||||
if (source.asEntity() instanceof CastSpellEntity spellcast) {
|
||||
Ether.get(source.asWorld()).remove(getType(), source);
|
||||
Ether.get(source.asWorld()).remove(this, 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.item.FriendshipBraceletItem;
|
||||
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
|
||||
import com.minelittlepony.unicopia.server.world.Ether;
|
||||
import com.minelittlepony.unicopia.util.shape.Sphere;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
|
@ -42,6 +43,8 @@ public class AreaProtectionSpell extends AbstractAreaEffectSpell {
|
|||
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 -> {
|
||||
|
@ -51,6 +54,11 @@ public class AreaProtectionSpell extends AbstractAreaEffectSpell {
|
|||
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.
|
||||
*/
|
||||
|
|
|
@ -12,7 +12,7 @@ import com.minelittlepony.unicopia.util.shape.Sphere;
|
|||
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 {
|
||||
protected DisperseIllusionSpell(CustomisedSpellType<?> type) {
|
||||
|
|
|
@ -5,14 +5,16 @@ import java.util.Set;
|
|||
|
||||
import com.minelittlepony.unicopia.USounds;
|
||||
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.Spell;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
|
||||
import com.minelittlepony.unicopia.advancement.UCriteria;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
import com.minelittlepony.unicopia.particle.UParticles;
|
||||
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
|
||||
import com.minelittlepony.unicopia.server.world.Ether;
|
||||
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
||||
import com.minelittlepony.unicopia.util.shape.*;
|
||||
|
||||
|
@ -21,8 +23,10 @@ import net.minecraft.fluid.*;
|
|||
import net.minecraft.nbt.*;
|
||||
import net.minecraft.state.property.Properties;
|
||||
import net.minecraft.registry.tag.TagKey;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class HydrophobicSpell extends AbstractSpell {
|
||||
|
@ -41,16 +45,15 @@ public class HydrophobicSpell extends AbstractSpell {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Caster<?> source) {
|
||||
if (getTraits().get(Trait.GENEROSITY) > 0) {
|
||||
return toPlaceable().apply(source);
|
||||
public Spell prepareForCast(Caster<?> caster, CastingMethod method) {
|
||||
if ((method == CastingMethod.DIRECT || method == CastingMethod.STAFF) && getTraits().get(Trait.GENEROSITY) > 0) {
|
||||
return toPlaceable();
|
||||
}
|
||||
return super.apply(source);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tick(Caster<?> source, Situation situation) {
|
||||
|
||||
if (!source.isClient()) {
|
||||
World world = source.asWorld();
|
||||
|
||||
|
@ -86,7 +89,11 @@ public class HydrophobicSpell extends AbstractSpell {
|
|||
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);
|
||||
if (source.asWorld().getFluidState(bp.up()).isIn(affectedFluid)) {
|
||||
source.addParticle(UParticles.RAIN_DROPS, pos, Vec3d.ZERO);
|
||||
|
@ -107,6 +114,7 @@ public class HydrophobicSpell extends AbstractSpell {
|
|||
|
||||
@Override
|
||||
protected void onDestroyed(Caster<?> caster) {
|
||||
Ether.get(caster.asWorld()).remove(this, caster);
|
||||
storedFluidPositions.removeIf(entry -> {
|
||||
entry.restore(caster.asWorld());
|
||||
return true;
|
||||
|
@ -162,13 +170,20 @@ public class HydrophobicSpell extends AbstractSpell {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean blocksFlow(Caster<?> caster, BlockPos pos, FluidState fluid) {
|
||||
return fluid.isIn(affectedFluid) && pos.isWithinDistance(caster.getOrigin(), getRange(caster) + 1);
|
||||
public boolean blocksFlow(Ether.Entry<?> entry, Vec3d center, BlockPos pos, FluidState fluid) {
|
||||
return fluid.isIn(affectedFluid) && pos.isWithinDistance(center, (double)entry.radius + 1);
|
||||
}
|
||||
|
||||
public static boolean blocksFluidFlow(CasterView world, BlockPos pos, FluidState state) {
|
||||
return world.findAllSpellsInRange(pos, 500, SpellType.HYDROPHOBIC).anyMatch(pair -> {
|
||||
return pair.getValue().blocksFlow(pair.getKey(), pos, state);
|
||||
});
|
||||
public static boolean blocksFluidFlow(BlockView world, BlockPos pos, FluidState state) {
|
||||
if (world instanceof ServerWorld sw) {
|
||||
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;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.USounds;
|
||||
import com.minelittlepony.unicopia.Unicopia;
|
||||
|
@ -34,6 +37,8 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
|
|||
.build();
|
||||
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 boolean publishedPosition;
|
||||
|
@ -63,10 +68,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
|
|||
Vec3d origin = source.getOriginVector();
|
||||
|
||||
ParticleEffect effect = teleportationTarget.getTarget()
|
||||
.map(target -> {
|
||||
getType();
|
||||
return (ParticleEffect)new FollowingParticleEffect(UParticles.HEALTH_DRAIN, target.pos(), 0.2F).withChild(ParticleTypes.ELECTRIC_SPARK);
|
||||
})
|
||||
.map(target -> (ParticleEffect)new FollowingParticleEffect(UParticles.HEALTH_DRAIN, target.pos(), 0.2F).withChild(ParticleTypes.ELECTRIC_SPARK))
|
||||
.orElse(ParticleTypes.ELECTRIC_SPARK);
|
||||
|
||||
source.spawnParticles(origin, particleArea, 5, pos -> {
|
||||
|
@ -82,7 +84,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
|
|||
});
|
||||
} else {
|
||||
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());
|
||||
teleportationTarget.set(null);
|
||||
setDirty();
|
||||
|
@ -96,18 +98,15 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
|
|||
);
|
||||
}
|
||||
|
||||
if (!publishedPosition) {
|
||||
publishedPosition = true;
|
||||
Ether.Entry entry = Ether.get(source.asWorld()).put(getType(), source);
|
||||
entry.pitch = pitch;
|
||||
entry.yaw = yaw;
|
||||
}
|
||||
var entry = Ether.get(source.asWorld()).getOrCreate(this, source);
|
||||
entry.pitch = pitch;
|
||||
entry.yaw = yaw;
|
||||
}
|
||||
|
||||
return !isDead();
|
||||
}
|
||||
|
||||
private void tickWithTargetLink(Caster<?> source, Ether.Entry destination) {
|
||||
private void tickWithTargetLink(Caster<?> source, Ether.Entry<?> destination) {
|
||||
|
||||
destination.entity.getTarget().ifPresent(target -> {
|
||||
source.findAllEntitiesInRange(1).forEach(entity -> {
|
||||
|
@ -142,20 +141,21 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
|
|||
return;
|
||||
}
|
||||
|
||||
Ether ether = Ether.get(source.asWorld());
|
||||
ether.getEntries(getType())
|
||||
.stream()
|
||||
.filter(entry -> entry.isAvailable() && !entry.entity.referenceEquals(source.asEntity()) && entry.entity.isSet())
|
||||
.findAny()
|
||||
.ifPresent(entry -> {
|
||||
Ether.get(source.asWorld()).anyMatch(getType(), entry -> {
|
||||
if (entry.isAvailable() && !entry.entity.referenceEquals(source.asEntity()) && entry.entity.isSet()) {
|
||||
entry.setTaken(true);
|
||||
teleportationTarget.copyFrom(entry.entity);
|
||||
targetPortalId = entry.getSpellId();
|
||||
setDirty();
|
||||
});
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
private Optional<Ether.Entry> getTarget(Caster<?> source) {
|
||||
return teleportationTarget.getTarget().flatMap(target -> Ether.get(source.asWorld()).getEntry(getType(), target.uuid()));
|
||||
@SuppressWarnings("unchecked")
|
||||
private Optional<Ether.Entry<PortalSpell>> getTarget(Caster<?> source) {
|
||||
return teleportationTarget.getTarget()
|
||||
.map(target -> Ether.get(source.asWorld()).get((SpellType<PortalSpell>)getType(), target, targetPortalId));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -189,7 +189,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
|
|||
protected void onDestroyed(Caster<?> caster) {
|
||||
particleEffect.destroy();
|
||||
Ether ether = Ether.get(caster.asWorld());
|
||||
ether.remove(getType(), caster.asEntity().getUuid());
|
||||
ether.remove(getType(), caster);
|
||||
getTarget(caster).ifPresent(e -> e.setTaken(false));
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package com.minelittlepony.unicopia.client;
|
|||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
@ -13,14 +12,12 @@ import com.minelittlepony.unicopia.EquinePredicates;
|
|||
import com.minelittlepony.unicopia.FlightType;
|
||||
import com.minelittlepony.unicopia.InteractionManager;
|
||||
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.spellbook.ClientChapters;
|
||||
import com.minelittlepony.unicopia.client.sound.*;
|
||||
import com.minelittlepony.unicopia.entity.player.PlayerPhysics;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
import com.minelittlepony.unicopia.entity.player.dummy.DummyClientPlayerEntity;
|
||||
import com.minelittlepony.unicopia.server.world.Ether;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
|
||||
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.vehicle.AbstractMinecartEntity;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class ClientInteractionManager extends InteractionManager {
|
||||
|
||||
private final MinecraftClient client = MinecraftClient.getInstance();
|
||||
|
||||
private final Optional<CasterView> clientWorld = Optional.of(() -> MinecraftClient.getInstance().world);
|
||||
|
||||
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
|
||||
public Map<Identifier, ?> readChapters(PacketByteBuf buffer) {
|
||||
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.callback.CallbackInfoReturnable;
|
||||
|
||||
import com.minelittlepony.unicopia.InteractionManager;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.HydrophobicSpell;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
|
@ -17,7 +16,7 @@ import net.minecraft.world.BlockView;
|
|||
abstract class MixinFlowableFluid {
|
||||
@Inject(method = "canFill", at = @At("HEAD"), cancellable = true)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,88 +1,93 @@
|
|||
package com.minelittlepony.unicopia.server.world;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
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.ability.magic.CasterView;
|
||||
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.entity.EntityReference;
|
||||
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
||||
|
||||
import net.minecraft.nbt.*;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.world.PersistentState;
|
||||
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");
|
||||
|
||||
public static Ether get(World world) {
|
||||
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 World world;
|
||||
|
||||
Ether(World world, NbtCompound compound) {
|
||||
this(world);
|
||||
compound.getKeys().forEach(key -> {
|
||||
Identifier typeId = Identifier.tryParse(key);
|
||||
if (typeId != null) {
|
||||
Set<Entry> uuids = getEntries(typeId);
|
||||
compound.getList(key, NbtElement.COMPOUND_TYPE).forEach(entry -> {
|
||||
Entry e = new Entry();
|
||||
e.fromNBT((NbtCompound)entry);
|
||||
uuids.add(e);
|
||||
});
|
||||
}
|
||||
this.world = world;
|
||||
this.endpoints = NbtSerialisable.readMap(compound.getCompound("endpoints"), Identifier::tryParse, typeNbt -> {
|
||||
return NbtSerialisable.readMap((NbtCompound)typeNbt, UUID::fromString, entityNbt -> {
|
||||
return NbtSerialisable.readMap((NbtCompound)entityNbt, UUID::fromString, Entry::new);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Ether(World world) {
|
||||
this.world = world;
|
||||
this.endpoints = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NbtCompound writeNbt(NbtCompound compound) {
|
||||
synchronized (locker) {
|
||||
advertisingEndpoints.forEach((id, uuids) -> {
|
||||
NbtList list = new NbtList();
|
||||
uuids.forEach(uuid -> {
|
||||
if (uuid.isAlive()) {
|
||||
list.add(uuid.toNBT());
|
||||
}
|
||||
pruneNodes();
|
||||
compound.put("endpoints", NbtSerialisable.writeMap(endpoints, Identifier::toString, entities -> {
|
||||
return NbtSerialisable.writeMap(entities, UUID::toString, spells -> {
|
||||
return NbtSerialisable.writeMap(spells, UUID::toString, Entry::toNBT);
|
||||
});
|
||||
compound.put(id.toString(), list);
|
||||
});
|
||||
|
||||
}));
|
||||
return compound;
|
||||
}
|
||||
}
|
||||
|
||||
public Entry put(SpellType<?> spellType, Caster<?> caster) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Spell> Entry<T> getOrCreate(T spell, Caster<?> caster) {
|
||||
synchronized (locker) {
|
||||
var entry = new Entry(caster);
|
||||
getEntries(spellType.getId()).add(entry);
|
||||
markDirty();
|
||||
Entry<T> entry = (Entry<T>)endpoints
|
||||
.computeIfAbsent(spell.getType().getId(), typeId -> new HashMap<>())
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(SpellType<?> spellType, UUID id) {
|
||||
public <T extends Spell> void remove(SpellType<T> spellType, UUID entityId) {
|
||||
synchronized (locker) {
|
||||
Identifier typeId = spellType.getId();
|
||||
Set<Entry> refs = advertisingEndpoints.get(typeId);
|
||||
if (refs != null) {
|
||||
refs.removeIf(ref -> ref.isDead() || ref.entity.getTarget().filter(target -> id.equals(target.uuid())).isPresent());
|
||||
if (refs.isEmpty()) {
|
||||
advertisingEndpoints.remove(typeId);
|
||||
endpoints.computeIfPresent(spellType.getId(), (typeId, entries) -> {
|
||||
if (entries.remove(entityId) != null) {
|
||||
markDirty();
|
||||
}
|
||||
markDirty();
|
||||
}
|
||||
return entries.isEmpty() ? null : entries;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,72 +95,123 @@ public class Ether extends PersistentState implements CasterView {
|
|||
remove(spellType, caster.asEntity().getUuid());
|
||||
}
|
||||
|
||||
public Set<Entry> getEntries(SpellType<?> spellType) {
|
||||
return getEntries(spellType.getId());
|
||||
public <T extends Spell> void remove(T spell, Caster<?> caster) {
|
||||
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) {
|
||||
return advertisingEndpoints.compute(typeId, (k, old) -> {
|
||||
if (old == null) {
|
||||
old = new HashSet<>();
|
||||
} else {
|
||||
old.removeIf(Entry::isDead);
|
||||
Entry<?> entry = endpoints
|
||||
.getOrDefault(spell.getId(), Map.of())
|
||||
.getOrDefault(entityId, Map.of())
|
||||
.get(spellId);
|
||||
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) {
|
||||
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 class Entry<T extends Spell> implements NbtSerialisable {
|
||||
public final EntityReference<?> entity;
|
||||
|
||||
@Nullable
|
||||
private UUID spellId;
|
||||
private WeakReference<T> spell;
|
||||
|
||||
private boolean removed;
|
||||
private boolean taken;
|
||||
|
||||
public float pitch;
|
||||
public float yaw;
|
||||
public float radius;
|
||||
|
||||
public Entry() {
|
||||
entity = new EntityReference<>();
|
||||
private Entry(NbtElement nbt) {
|
||||
this.entity = new EntityReference<>();
|
||||
this.spell = new WeakReference<>(null);
|
||||
this.fromNBT((NbtCompound)nbt);
|
||||
}
|
||||
|
||||
public Entry(Caster<?> caster) {
|
||||
entity = new EntityReference<>(caster.asEntity());
|
||||
public Entry(T spell, Caster<?> caster) {
|
||||
this.entity = new EntityReference<>(caster.asEntity());
|
||||
this.spell = new WeakReference<>(spell);
|
||||
spellId = spell.getUuid();
|
||||
}
|
||||
|
||||
boolean isAlive() {
|
||||
return !removed;
|
||||
return !isDead();
|
||||
}
|
||||
|
||||
boolean isDead() {
|
||||
if (!removed) {
|
||||
getSpell();
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public UUID getSpellId() {
|
||||
return spellId;
|
||||
}
|
||||
|
||||
public void markDead() {
|
||||
Unicopia.LOGGER.debug("Marking " + entity.getTarget().orElse(null) + " as dead");
|
||||
removed = true;
|
||||
markDirty();
|
||||
}
|
||||
|
||||
public boolean entityMatches(UUID uuid) {
|
||||
return entity.getTarget().filter(target -> uuid.equals(target.uuid())).isPresent();
|
||||
}
|
||||
|
||||
public boolean isAvailable() {
|
||||
return !removed && !taken;
|
||||
return !isDead() && !taken;
|
||||
}
|
||||
|
||||
public void setTaken(boolean taken) {
|
||||
|
@ -163,6 +219,43 @@ public class Ether extends PersistentState implements CasterView {
|
|||
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
|
||||
public void toNBT(NbtCompound compound) {
|
||||
entity.toNBT(compound);
|
||||
|
@ -170,6 +263,10 @@ public class Ether extends PersistentState implements CasterView {
|
|||
compound.putBoolean("taken", taken);
|
||||
compound.putFloat("pitch", pitch);
|
||||
compound.putFloat("yaw", yaw);
|
||||
compound.putFloat("radius", radius);
|
||||
if (spellId != null) {
|
||||
compound.putUuid("spellId", spellId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -179,20 +276,24 @@ public class Ether extends PersistentState implements CasterView {
|
|||
taken = compound.getBoolean("taken");
|
||||
pitch = compound.getFloat("pitch");
|
||||
yaw = compound.getFloat("yaw");
|
||||
radius = compound.getFloat("radius");
|
||||
spellId = compound.containsUuid("spellid") ? compound.getUuid("spellId") : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
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) {
|
||||
return entity.referenceEquals(uuid);
|
||||
public boolean equals(UUID entityId, UUID spellId) {
|
||||
return entity.referenceEquals(entityId) && spellId.equals(this.spellId);
|
||||
}
|
||||
|
||||
@Override
|
||||
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.function.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
|
@ -65,6 +66,18 @@ public interface NbtSerialisable {
|
|||
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> {
|
||||
T read(NbtCompound compound);
|
||||
|
||||
|
|
Loading…
Reference in a new issue