diff --git a/common/src/main/java/org/valkyrienskies/mod/mixin/feature/ai/MixinDefaultRandomPos.java b/common/src/main/java/org/valkyrienskies/mod/mixin/feature/ai/MixinDefaultRandomPos.java new file mode 100644 index 000000000..d66556f14 --- /dev/null +++ b/common/src/main/java/org/valkyrienskies/mod/mixin/feature/ai/MixinDefaultRandomPos.java @@ -0,0 +1,83 @@ +package org.valkyrienskies.mod.mixin.feature.ai; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import java.util.function.Supplier; +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.PathfinderMob; +import net.minecraft.world.entity.ai.util.DefaultRandomPos; +import net.minecraft.world.entity.ai.util.GoalUtils; +import net.minecraft.world.entity.ai.util.RandomPos; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import org.joml.Vector3d; +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.valkyrienskies.core.api.ships.LoadedShip; +import org.valkyrienskies.mod.common.VSGameUtilsKt; +import org.valkyrienskies.mod.common.util.VectorConversionsMCKt; + +/** + * @author Tomato + * Should allow for mobs to pathfind on ships. + */ +@Mixin(DefaultRandomPos.class) +public class MixinDefaultRandomPos { + @Inject( + method = "generateRandomPosTowardDirection", + at = @At( + value = "HEAD" + ), + cancellable = true + ) + private static void postGenerateRandomPosTowardDirection(PathfinderMob pathfinderMob, int i, boolean bl, + BlockPos blockPos, final CallbackInfoReturnable cir) { + if (pathfinderMob.level != null) { + final BlockPos blockPos3 = RandomPos.generateRandomPosTowardDirection(pathfinderMob, i, pathfinderMob.getRandom(), blockPos); + if (blockPos3 == null) { + return; + } + AABB checker = new AABB(blockPos3); + Iterable ships = VSGameUtilsKt.getShipObjectWorld(pathfinderMob.level).getLoadedShips().getIntersecting(VectorConversionsMCKt.toJOML(checker), VSGameUtilsKt.getDimensionId(pathfinderMob.level)); + if (ships.iterator().hasNext()) { + for (LoadedShip ship : ships) { + Vector3d posInShip = ship.getWorldToShip() + .transformPosition(VectorConversionsMCKt.toJOMLD(blockPos3), new Vector3d()); + BlockPos blockPosInShip = new BlockPos(VectorConversionsMCKt.toMinecraft(posInShip)); + if (!GoalUtils.isRestricted(bl, pathfinderMob, blockPosInShip) && + !GoalUtils.isNotStable(pathfinderMob.getNavigation(), blockPosInShip) && + !GoalUtils.hasMalus(pathfinderMob, blockPosInShip)) { + cir.setReturnValue(blockPosInShip); + break; + } + } + } + } + } + + @WrapOperation(method = "getPosTowards", at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/entity/ai/util/RandomPos;generateRandomPos(Lnet/minecraft/world/entity/PathfinderMob;Ljava/util/function/Supplier;)Lnet/minecraft/world/phys/Vec3;")) + private static Vec3 redirectGetPosInDirection(PathfinderMob arg, Supplier supplier, + Operation original) { + Vec3 result = original.call(arg, supplier); + if (result != null) { + return VSGameUtilsKt.toWorldCoordinates(arg.level, result); + } + return null; + } + + @WrapOperation(method = "getPos", at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/entity/ai/util/RandomPos;generateRandomPos(Lnet/minecraft/world/entity/PathfinderMob;Ljava/util/function/Supplier;)Lnet/minecraft/world/phys/Vec3;")) + private static Vec3 redirectGetPos(PathfinderMob arg, Supplier supplier, + Operation original) { + Vec3 result = original.call(arg, supplier); + if (result != null) { + return VSGameUtilsKt.toWorldCoordinates(arg.level, result); + } + return null; + } + + +} diff --git a/common/src/main/java/org/valkyrienskies/mod/mixin/feature/ai/MixinLandRandomPos.java b/common/src/main/java/org/valkyrienskies/mod/mixin/feature/ai/MixinLandRandomPos.java new file mode 100644 index 000000000..2346724cc --- /dev/null +++ b/common/src/main/java/org/valkyrienskies/mod/mixin/feature/ai/MixinLandRandomPos.java @@ -0,0 +1,88 @@ +package org.valkyrienskies.mod.mixin.feature.ai; + +import static net.minecraft.world.entity.ai.util.LandRandomPos.generateRandomPosTowardDirection; +import static net.minecraft.world.entity.ai.util.LandRandomPos.movePosUpOutOfSolid; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import java.util.function.Supplier; +import java.util.function.ToDoubleFunction; +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.PathfinderMob; +import net.minecraft.world.entity.ai.util.GoalUtils; +import net.minecraft.world.entity.ai.util.LandRandomPos; +import net.minecraft.world.entity.ai.util.RandomPos; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import org.joml.Vector3d; +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.valkyrienskies.core.api.ships.LoadedShip; +import org.valkyrienskies.mod.common.VSGameUtilsKt; +import org.valkyrienskies.mod.common.util.VectorConversionsMCKt; + +/** + * @author Tomato + * Should allow for mobs to pathfind on ships. + */ +@Mixin(LandRandomPos.class) +public class MixinLandRandomPos { + + @Inject( + method = "generateRandomPosTowardDirection", + at = @At( + value = "HEAD" + ), + cancellable = true + ) + private static void postGenerateRandomPosTowardDirection(PathfinderMob pathfinderMob, int i, boolean bl, + BlockPos blockPos, CallbackInfoReturnable cir) { + if (pathfinderMob.level != null) { + final BlockPos blockPos3 = RandomPos.generateRandomPosTowardDirection(pathfinderMob, i, pathfinderMob.getRandom(), blockPos); + AABB checker = new AABB(blockPos3); + Iterable ships = VSGameUtilsKt.getShipObjectWorld(pathfinderMob.level).getLoadedShips().getIntersecting(VectorConversionsMCKt.toJOML(checker), VSGameUtilsKt.getDimensionId(pathfinderMob.level)); + if (ships.iterator().hasNext()) { + for (LoadedShip ship : ships) { + Vector3d posInShip = ship.getWorldToShip() + .transformPosition(VectorConversionsMCKt.toJOMLD(blockPos3), new Vector3d()); + BlockPos blockPosInShip = new BlockPos(VectorConversionsMCKt.toMinecraft(posInShip)); + if (!GoalUtils.isRestricted(bl, pathfinderMob, blockPosInShip) && + !GoalUtils.isNotStable(pathfinderMob.getNavigation(), blockPosInShip) && + !GoalUtils.hasMalus(pathfinderMob, blockPosInShip)) { + cir.setReturnValue(blockPosInShip); + break; + } + } + } + } + } + + @WrapOperation(method = "getPosInDirection", at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/entity/ai/util/RandomPos;generateRandomPos(Lnet/minecraft/world/entity/PathfinderMob;Ljava/util/function/Supplier;)Lnet/minecraft/world/phys/Vec3;")) + private static Vec3 redirectGetPosInDirection(PathfinderMob arg, Supplier supplier, + Operation original) { + Vec3 result = original.call(arg, supplier); + if (result != null) { + return VSGameUtilsKt.toWorldCoordinates(arg.level, result); + } + return null; + } + + @Inject(method = "getPos(Lnet/minecraft/world/entity/PathfinderMob;IILjava/util/function/ToDoubleFunction;)Lnet/minecraft/world/phys/Vec3;", at = @At("HEAD"), cancellable = true) + private static void preGetPos(PathfinderMob pathfinderMob, int i, int j, + ToDoubleFunction toDoubleFunction, CallbackInfoReturnable cir) { + boolean bl = GoalUtils.mobRestricted(pathfinderMob, i); + Vec3 randomPos = RandomPos.generateRandomPos(() -> { + BlockPos blockPos = RandomPos.generateRandomDirection(pathfinderMob.getRandom(), i, j); + BlockPos blockPos2 = generateRandomPosTowardDirection(pathfinderMob, i, bl, blockPos); + return blockPos2 == null ? null : movePosUpOutOfSolid(pathfinderMob, blockPos2); + }, toDoubleFunction); + + if (randomPos != null) { + cir.setReturnValue(VSGameUtilsKt.toWorldCoordinates(pathfinderMob.level, randomPos)); + } + cir.setReturnValue(null); + } +} diff --git a/common/src/main/java/org/valkyrienskies/mod/mixin/feature/ai/MixinPOIManager.java b/common/src/main/java/org/valkyrienskies/mod/mixin/feature/ai/MixinPOIManager.java new file mode 100644 index 000000000..b993b8583 --- /dev/null +++ b/common/src/main/java/org/valkyrienskies/mod/mixin/feature/ai/MixinPOIManager.java @@ -0,0 +1,77 @@ +package org.valkyrienskies.mod.mixin.feature.ai; + +import java.util.function.Predicate; +import java.util.stream.Stream; +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.ai.village.poi.PoiManager; +import net.minecraft.world.entity.ai.village.poi.PoiManager.Occupancy; +import net.minecraft.world.entity.ai.village.poi.PoiRecord; +import net.minecraft.world.entity.ai.village.poi.PoiType; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import org.joml.Vector4ic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.valkyrienskies.core.api.ships.LoadedShip; +import org.valkyrienskies.mod.common.VSGameUtilsKt; +import org.valkyrienskies.mod.common.util.VectorConversionsMCKt; +import org.valkyrienskies.mod.common.world.POIChunkSearcher; +import org.valkyrienskies.mod.mixinducks.world.OfLevel; + +/** + * @author Tomato + * This atrocious mess of a mixin allows POIs in ship space to be detected, however it requires further mixins to Goals for the ship space positions to be correctly used, from what I can understand. + */ +@Mixin(PoiManager.class) +public abstract class MixinPOIManager implements OfLevel { + + @Unique + private Level valkyrienskies$sLevel; + + @Shadow + public abstract Stream getInChunk(Predicate predicate, ChunkPos chunkPos, Occupancy occupancy); + + /** + * @author Tomato + * @reason Allows for ships to be considered as a valid POI, also this method sucks anyway. + */ + @Overwrite + public Stream getInSquare(Predicate predicate, BlockPos blockPos, int i, Occupancy occupancy) { + int j = Math.floorDiv(i, 16) + 1; + final AABB aABB = new AABB(blockPos).inflate((double) j +1); + Stream chunkRange = ChunkPos.rangeClosed(new ChunkPos(blockPos), j); + Stream shipPOIs = Stream.empty(); + if (this.valkyrienskies$sLevel != null) { + for (LoadedShip ship : VSGameUtilsKt.getShipObjectWorld(this.valkyrienskies$sLevel).getLoadedShips().getIntersecting( + VectorConversionsMCKt.toJOML(aABB), VSGameUtilsKt.getDimensionId(this.valkyrienskies$sLevel))) { + Vector4ic chunkRangeBounds = POIChunkSearcher.INSTANCE.shipChunkBounds(ship.getActiveChunksSet()); + ChunkPos.rangeClosed(new ChunkPos(chunkRangeBounds.x(), chunkRangeBounds.z()), + new ChunkPos(chunkRangeBounds.y(), chunkRangeBounds.w())).flatMap((chunkPos) -> this.getInChunk(predicate, chunkPos, occupancy)).filter((poiRecord) -> { + BlockPos blockPos2 = poiRecord.getPos(); + Vec3 vecPos = new Vec3(blockPos2.getX(), blockPos2.getY(), blockPos2.getZ()); + VSGameUtilsKt.toWorldCoordinates(this.valkyrienskies$sLevel, vecPos); + return Math.abs(vecPos.x() - blockPos.getX()) <= i && Math.abs(vecPos.z() - blockPos.getZ()) <= i; + });; + } + } + final Stream worldPOIs = chunkRange.flatMap((chunkPos) -> this.getInChunk(predicate, chunkPos, occupancy)).filter((poiRecord) -> { + BlockPos blockPos2 = poiRecord.getPos(); + return Math.abs(blockPos2.getX() - blockPos.getX()) <= i && Math.abs(blockPos2.getZ() - blockPos.getZ()) <= i; + }); + return Stream.concat(worldPOIs, shipPOIs); + } + + @Override + public Level getLevel() { + return valkyrienskies$sLevel; + } + + @Override + public void setLevel(Level level) { + valkyrienskies$sLevel = level; + } +} diff --git a/common/src/main/java/org/valkyrienskies/mod/mixin/feature/ai/goal_redirector/MixinRandomStrollGoal.java b/common/src/main/java/org/valkyrienskies/mod/mixin/feature/ai/goal_redirector/MixinRandomStrollGoal.java deleted file mode 100644 index 4831a87c5..000000000 --- a/common/src/main/java/org/valkyrienskies/mod/mixin/feature/ai/goal_redirector/MixinRandomStrollGoal.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.valkyrienskies.mod.mixin.feature.ai.goal_redirector; - -import net.minecraft.world.entity.ai.goal.RandomStrollGoal; -import org.spongepowered.asm.mixin.Mixin; - -@Mixin(RandomStrollGoal.class) -public class MixinRandomStrollGoal { -} diff --git a/common/src/main/java/org/valkyrienskies/mod/mixin/feature/ai/goal_redirector/MixinWaterAvoidingRandomStrollGoal.java b/common/src/main/java/org/valkyrienskies/mod/mixin/feature/ai/goal_redirector/MixinWaterAvoidingRandomStrollGoal.java deleted file mode 100644 index 36c8fcd2b..000000000 --- a/common/src/main/java/org/valkyrienskies/mod/mixin/feature/ai/goal_redirector/MixinWaterAvoidingRandomStrollGoal.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.valkyrienskies.mod.mixin.feature.ai.goal_redirector; - -import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal; -import org.spongepowered.asm.mixin.Mixin; - -@Mixin(WaterAvoidingRandomStrollGoal.class) -public class MixinWaterAvoidingRandomStrollGoal { -} diff --git a/common/src/main/java/org/valkyrienskies/mod/mixin/server/world/MixinServerLevel.java b/common/src/main/java/org/valkyrienskies/mod/mixin/server/world/MixinServerLevel.java index af9fa6e19..32434b0ef 100644 --- a/common/src/main/java/org/valkyrienskies/mod/mixin/server/world/MixinServerLevel.java +++ b/common/src/main/java/org/valkyrienskies/mod/mixin/server/world/MixinServerLevel.java @@ -24,6 +24,7 @@ import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.progress.ChunkProgressListener; +import net.minecraft.world.entity.ai.village.poi.PoiManager; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; @@ -44,6 +45,7 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.valkyrienskies.core.api.ships.LoadedServerShip; import org.valkyrienskies.core.api.ships.Wing; import org.valkyrienskies.core.api.ships.WingManager; @@ -56,6 +58,7 @@ import org.valkyrienskies.mod.common.util.VectorConversionsMCKt; import org.valkyrienskies.mod.mixin.accessors.server.level.ChunkMapAccessor; import org.valkyrienskies.mod.mixin.accessors.server.level.DistanceManagerAccessor; +import org.valkyrienskies.mod.mixinducks.world.OfLevel; import org.valkyrienskies.mod.util.McMathUtilKt; @Mixin(ServerLevel.class) @@ -104,6 +107,13 @@ void onInit(final MinecraftServer minecraftServer, final Executor executor, } } + @Inject(method = "getPoiManager", at = @At("HEAD")) + void onGetPoiManager(CallbackInfoReturnable cir) { + if (chunkSource.getPoiManager() instanceof final OfLevel levelProvider) { + levelProvider.setLevel((ServerLevel) (Object) this); + } + } + /** * Include ships in particle distance check. Seems to only be used by /particle */ diff --git a/common/src/main/kotlin/org/valkyrienskies/mod/common/world/POIChunkSearcher.kt b/common/src/main/kotlin/org/valkyrienskies/mod/common/world/POIChunkSearcher.kt new file mode 100644 index 000000000..3a7e8a0d4 --- /dev/null +++ b/common/src/main/kotlin/org/valkyrienskies/mod/common/world/POIChunkSearcher.kt @@ -0,0 +1,47 @@ +package org.valkyrienskies.mod.common.world + +import net.minecraft.server.level.ServerLevel +import net.minecraft.world.entity.ai.village.poi.PoiRecord +import net.minecraft.world.level.ChunkPos +import net.minecraft.world.level.Level +import net.minecraft.world.phys.AABB +import net.minecraft.world.phys.Vec3 +import org.joml.Vector4i +import org.joml.Vector4ic +import org.valkyrienskies.core.api.ships.LoadedServerShip +import org.valkyrienskies.core.api.ships.QueryableShipData +import org.valkyrienskies.core.api.ships.properties.IShipActiveChunksSet +import org.valkyrienskies.core.impl.chunk_tracking.ShipActiveChunksSet +import org.valkyrienskies.mod.common.dimensionId +import org.valkyrienskies.mod.common.getShipObjectManagingPos +import org.valkyrienskies.mod.common.shipObjectWorld +import org.valkyrienskies.mod.common.util.toJOML +import org.valkyrienskies.mod.common.util.toMinecraft + +object POIChunkSearcher { + fun shipChunkBounds(chunkSet: IShipActiveChunksSet): Vector4ic { + var minChunkX = Integer.MIN_VALUE + var minChunkZ = Integer.MIN_VALUE + var maxChunkX = Integer.MAX_VALUE + var maxChunkZ = Integer.MAX_VALUE + chunkSet.forEach { chunkX, chunkZ -> + minChunkX = minChunkX.coerceAtLeast(chunkX) + minChunkZ = minChunkZ.coerceAtLeast(chunkZ) + maxChunkX = maxChunkX.coerceAtMost(chunkX) + maxChunkZ = maxChunkZ.coerceAtMost(chunkZ) + } + return Vector4i(minChunkX, minChunkZ, maxChunkX, maxChunkZ) + } + + fun PoiRecord.getWorldPos(level: Level): Vec3 { + val blockPos = this.pos + val vecPos = Vec3(blockPos.x.toDouble(), blockPos.y.toDouble(), blockPos.z.toDouble()) + if (level.shipObjectWorld.isBlockInShipyard(blockPos.x, blockPos.y, blockPos.z, level.dimensionId)) { + val ship = level.getShipObjectManagingPos(blockPos) + if (ship != null) { + return ship.shipToWorld.transformPosition(vecPos.toJOML()).toMinecraft() + } + } + return vecPos + } +} diff --git a/common/src/main/resources/valkyrienskies-common.mixins.json b/common/src/main/resources/valkyrienskies-common.mixins.json index 84114b2b1..85a755aba 100644 --- a/common/src/main/resources/valkyrienskies-common.mixins.json +++ b/common/src/main/resources/valkyrienskies-common.mixins.json @@ -16,6 +16,9 @@ "accessors.world.level.pathfinder.PathAccessor", "commands.argument.selector.MixinEntitySelectorParser", "entity.MixinEntity", + "feature.ai.MixinDefaultRandomPos", + "feature.ai.MixinLandRandomPos", + "feature.ai.MixinPOIManager", "feature.ai.node_evaluator.PathNavigationRegionAccessor", "feature.ai.node_evaluator.SwimNodeEvaluatorMixin", "feature.ai.node_evaluator.WalkNodeEvaluatorMixin",