Skip to content

Commit

Permalink
The great pathfinding nightmare
Browse files Browse the repository at this point in the history
  • Loading branch information
ThePlasticPotato committed Dec 17, 2024
1 parent 9045c93 commit a3e51eb
Show file tree
Hide file tree
Showing 8 changed files with 308 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -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<BlockPos> 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<LoadedShip> 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<BlockPos> supplier,
Operation<Vec3> 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<BlockPos> supplier,
Operation<Vec3> original) {
Vec3 result = original.call(arg, supplier);
if (result != null) {
return VSGameUtilsKt.toWorldCoordinates(arg.level, result);
}
return null;
}


}
Original file line number Diff line number Diff line change
@@ -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<BlockPos> cir) {
if (pathfinderMob.level != null) {
final BlockPos blockPos3 = RandomPos.generateRandomPosTowardDirection(pathfinderMob, i, pathfinderMob.getRandom(), blockPos);
AABB checker = new AABB(blockPos3);
Iterable<LoadedShip> 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<BlockPos> supplier,
Operation<Vec3> 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<BlockPos> toDoubleFunction, CallbackInfoReturnable<Vec3> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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<PoiRecord> getInChunk(Predicate<PoiType> 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<PoiRecord> getInSquare(Predicate<PoiType> 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<ChunkPos> chunkRange = ChunkPos.rangeClosed(new ChunkPos(blockPos), j);
Stream<PoiRecord> 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<PoiRecord> 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;
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -104,6 +107,13 @@ void onInit(final MinecraftServer minecraftServer, final Executor executor,
}
}

@Inject(method = "getPoiManager", at = @At("HEAD"))
void onGetPoiManager(CallbackInfoReturnable<PoiManager> 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
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
}
3 changes: 3 additions & 0 deletions common/src/main/resources/valkyrienskies-common.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down

0 comments on commit a3e51eb

Please sign in to comment.