diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntitySelectorMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntitySelectorMixin.java index 97ce4428c50..72eff0eb084 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntitySelectorMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntitySelectorMixin.java @@ -26,6 +26,7 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntitySelector; +import net.minecraft.world.entity.player.Player; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mutable; @@ -38,11 +39,20 @@ public abstract class EntitySelectorMixin { @Shadow @Final @Mutable public static Predicate NO_SPECTATORS = entity -> { - if (entity instanceof VanishableBridge && ((VanishableBridge) entity).bridge$vanishState().invisible()) { + if (entity instanceof VanishableBridge vb && vb.bridge$vanishState().invisible()) { // Sponge: Count vanished entities as spectating return false; } return !entity.isSpectator(); }; + @Shadow @Final @Mutable public static Predicate NO_CREATIVE_OR_SPECTATOR = $$0 -> { + if ($$0 instanceof VanishableBridge vb && vb.bridge$vanishState().invisible()) { + // Sponge: Count vanished entities as spectating + return false; + } + return !($$0 instanceof Player) || !$$0.isSpectator() && !((Player)$$0).isCreative(); + }; + + } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ai/sensing/NearestLivingEntitySensorMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ai/sensing/NearestLivingEntitySensorMixin.java new file mode 100644 index 00000000000..f677c1e7ed9 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ai/sensing/NearestLivingEntitySensorMixin.java @@ -0,0 +1,58 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.world.entity.ai.sensing; + +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.sensing.NearestLivingEntitySensor; +import net.minecraft.world.phys.AABB; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.common.bridge.data.VanishableBridge; + +import java.util.List; +import java.util.function.Predicate; + +@Mixin(NearestLivingEntitySensor.class) +public class NearestLivingEntitySensorMixin extends SensorMixin { + + @Redirect(method = "doTick", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerLevel;getEntitiesOfClass(Ljava/lang/Class;Lnet/minecraft/world/phys/AABB;Ljava/util/function/Predicate;)Ljava/util/List;")) + private List impl$ignoreVanishedEntities( + final ServerLevel instance, final Class aClass, final AABB aabb, + final Predicate predicate + ) { + return instance.getEntitiesOfClass(aClass, aabb, entity -> { + if (entity instanceof VanishableBridge v) { + final var state = v.bridge$vanishState(); + if (state.invisible() || state.untargetable()) { + return false; + } + } + return predicate.test(entity); + }); + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ai/sensing/SensorMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ai/sensing/SensorMixin.java new file mode 100644 index 00000000000..dbcf9ecd5af --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/ai/sensing/SensorMixin.java @@ -0,0 +1,54 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.core.world.entity.ai.sensing; + +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.sensing.Sensor; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.common.bridge.data.VanishableBridge; + +@Mixin(Sensor.class) +public class SensorMixin { + + @Inject(method = { + "isEntityTargetable", + "isEntityAttackable", + "isEntityAttackableIgnoringLineOfSight" + }, at = @At("HEAD"), cancellable = true) + private static void impl$cancelForVanishedEntities(LivingEntity $$0, LivingEntity $$1, CallbackInfoReturnable cir) { + final var vs = ((VanishableBridge) $$0).bridge$vanishState(); + if (vs.invisible() && vs.untargetable()) { + cir.setReturnValue(false); + } + final var vsOther = ((VanishableBridge) $$1).bridge$vanishState(); + if (vsOther.invisible() && vsOther.untargetable()) { + cir.setReturnValue(false); + } + } + +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/LevelMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/LevelMixin.java index 229cfe2fcfa..64ed80cf796 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/level/LevelMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/level/LevelMixin.java @@ -46,6 +46,7 @@ import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.storage.LevelData; import net.minecraft.world.level.storage.WritableLevelData; +import net.minecraft.world.phys.AABB; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.ResourceKey; @@ -59,9 +60,14 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Coerce; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.accessor.world.entity.MobAccessor; import org.spongepowered.common.accessor.world.entity.item.FallingBlockEntityAccessor; +import org.spongepowered.common.bridge.data.VanishableBridge; import org.spongepowered.common.bridge.world.level.LevelBridge; import org.spongepowered.common.data.persistence.NBTTranslator; import org.spongepowered.common.entity.projectile.UnknownProjectileSource; @@ -75,7 +81,7 @@ @Mixin(net.minecraft.world.level.Level.class) public abstract class LevelMixin implements LevelBridge, LevelAccessor { - // @formatter: off + //@formatter: off @Mutable @Shadow @Final private Holder dimensionTypeRegistration; @Shadow protected float oRainLevel; @Shadow protected float rainLevel; @@ -98,8 +104,6 @@ public abstract class LevelMixin implements LevelBridge, LevelAccessor { // @formatter on - - @Override public boolean bridge$isFake() { return this.isClientSide(); @@ -113,13 +117,14 @@ public abstract class LevelMixin implements LevelBridge, LevelAccessor { } @SuppressWarnings("unchecked") + @Override public E bridge$createEntity( - final DataContainer dataContainer, - final @Nullable Vector3d position, - final @Nullable Predicate positionCheck) throws IllegalArgumentException, IllegalStateException { + final DataContainer dataContainer, + final @Nullable Vector3d position, + final @Nullable Predicate positionCheck) throws IllegalArgumentException, IllegalStateException { final EntityType<@NonNull ?> type = dataContainer.getRegistryValue(Constants.Entity.TYPE, RegistryTypes.ENTITY_TYPE) - .orElseThrow(() -> new IllegalArgumentException("DataContainer does not contain a valid entity type.")); + .orElseThrow(() -> new IllegalArgumentException("DataContainer does not contain a valid entity type.")); final Vector3d proposedPosition; if (position == null) { proposedPosition = DataUtil.getPosition3d(dataContainer, Constants.Sponge.SNAPSHOT_WORLD_POSITION); @@ -129,9 +134,9 @@ public abstract class LevelMixin implements LevelBridge, LevelAccessor { if (positionCheck != null && !positionCheck.test(proposedPosition)) { throw new IllegalArgumentException(String.format("Position (%.2f, %.2f, %.2f) is not a valid position in this context.", - proposedPosition.x(), - proposedPosition.y(), - proposedPosition.z())); + proposedPosition.x(), + proposedPosition.y(), + proposedPosition.z())); } final @Nullable Vector3d rotation; @@ -150,15 +155,15 @@ public abstract class LevelMixin implements LevelBridge, LevelAccessor { final Entity createdEntity = this.bridge$createEntity(type, position, false); dataContainer.getView(Constants.Sponge.UNSAFE_NBT) - .map(NBTTranslator.INSTANCE::translate) - .ifPresent(x -> { - final net.minecraft.world.entity.Entity e = ((net.minecraft.world.entity.Entity) createdEntity); - // mimicing Entity#restoreFrom - x.remove("Dimension"); - e.load(x); - // position needs a reset - e.moveTo(proposedPosition.x(), proposedPosition.y(), proposedPosition.z()); - }); + .map(NBTTranslator.INSTANCE::translate) + .ifPresent(x -> { + final net.minecraft.world.entity.Entity e = ((net.minecraft.world.entity.Entity) createdEntity); + // mimicing Entity#restoreFrom + x.remove("Dimension"); + e.load(x); + // position needs a reset + e.moveTo(proposedPosition.x(), proposedPosition.y(), proposedPosition.z()); + }); if (rotation != null) { createdEntity.setRotation(rotation); } @@ -225,7 +230,7 @@ public abstract class LevelMixin implements LevelBridge, LevelAccessor { if (naturally && entity instanceof Mob) { // Adding the default equipment final DifficultyInstance difficulty = this.shadow$getCurrentDifficultyAt(new BlockPos((int) x, (int) y, (int) z)); - ((MobAccessor)entity).invoker$populateDefaultEquipmentSlots(this.random, difficulty); + ((MobAccessor) entity).invoker$populateDefaultEquipmentSlots(this.random, difficulty); } if (entity instanceof Painting) { @@ -236,4 +241,29 @@ public abstract class LevelMixin implements LevelBridge, LevelAccessor { return (E) entity; } + + @Inject(method = { + "getEntities(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/AABB;Ljava/util/function/Predicate;)Ljava/util/List;", + "getEntities(Lnet/minecraft/world/level/entity/EntityTypeTest;Lnet/minecraft/world/phys/AABB;Ljava/util/function/Predicate;)Ljava/util/List;", + }, at = @At("RETURN")) + private void impl$IgnoreTargetingOfVanishedEntities( + final @Coerce Object entityIn, final AABB aabb, + final Predicate filter, final CallbackInfoReturnable> cir + ) { + if (this.bridge$isFake()) { + return; + } + final List entities = cir.getReturnValue(); + if (entities == null || entities.isEmpty()) { + return; + } + entities.removeIf(entity -> { + if (entity instanceof VanishableBridge vb) { + final var state = vb.bridge$vanishState(); + return state.invisible() && state.untargetable(); + } + return false; + }); + } + } diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/LevelMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/LevelMixin_Tracker.java index b3a539d5098..c513fc0d26b 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/LevelMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/world/level/LevelMixin_Tracker.java @@ -89,7 +89,6 @@ public abstract class LevelMixin_Tracker implements LevelBridge, LevelAccessor { tileEntity.tick(); } - @SuppressWarnings("InvalidInjectorMethodSignature") @Inject(method = { "getEntities(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/AABB;Ljava/util/function/Predicate;)Ljava/util/List;", "getEntities(Lnet/minecraft/world/level/entity/EntityTypeTest;Lnet/minecraft/world/phys/AABB;Ljava/util/function/Predicate;)Ljava/util/List;", diff --git a/src/mixins/resources/mixins.sponge.core.json b/src/mixins/resources/mixins.sponge.core.json index d49dd358440..3071ababe1d 100644 --- a/src/mixins/resources/mixins.sponge.core.json +++ b/src/mixins/resources/mixins.sponge.core.json @@ -124,6 +124,8 @@ "world.entity.ai.goal.GoalSelectorMixin", "world.entity.ai.goal.RangedAttackGoalMixin", "world.entity.ai.goal.RunAroundLikeCrazyGoalMixin", + "world.entity.ai.sensing.NearestLivingEntitySensorMixin", + "world.entity.ai.sensing.SensorMixin", "world.entity.animal.AnimalMixin", "world.entity.animal.Cat_CatRelaxOnOwnerGoalMixin", "world.entity.animal.OcelotMixin",