Skip to content

Commit

Permalink
Serveral pathfinding fixes (#10409)
Browse files Browse the repository at this point in the history
Add planks, slabs and carpets as pathblocks
Pathblocks no longer increase pathing cost when they have a nonempty collision shape(carpets)
Doors and pathblocks are now cheaper to traverse


Re-visiting a pathfinding Node no longer counts towards the maximum allowed nodes to visit
Pathfinding Nodes now got a visited counter, which limits how many times the cost of a node is allowed to update. Heuristic rebalancing allows an additional revisit
Reevaluating the heuristic in pathfinding is now more accurate during search, using the distance to destination provided by the new IDestinationPathJob interface, and are also applied gradually instead of at once
Fix a bug where corner nodes could connect to eachother
Fix a bug where corner nodes that get their cost updated would not be recognized as such


Add fast collision existance check to ShapeUtil
PathfindingAIHelper#walkCloseToXNearY now runs the pathjob at least once, even if the entity may still already be in working range
Stuckhandler now at minimum walks the entity 20 blocks away when trying to resolve stuck
PathJobFindWater now discourages pathfinding from going too far out of its search radius
PathJobMoveCloseToXNearY got a slightly adjusted heuristic to prefer staying close to the near position
  • Loading branch information
someaddons authored Nov 6, 2024
1 parent c78628f commit 7c4642e
Show file tree
Hide file tree
Showing 30 changed files with 353 additions and 154 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
"enchantments": [
{
"enchantment": "minecraft:silk_touch",
"levels": 1
"levels": {
"min": 1
}
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"values": [
"#minecolonies:concrete",
"#minecraft:stone_bricks",
"#minecraft:planks",
"#minecraft:wooden_slabs",
"#minecraft:wool_carpets",
"minecraft:stone_brick_stairs",
"minecraft:stone_brick_slab",
"minecraft:mossy_stone_brick_slab",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ protected ServerConfiguration(final ForgeConfigSpec.Builder builder)

pathfindingDebugVerbosity = defineInteger(builder, "pathfindingdebugverbosity", 0, 0, 10);
minimumRailsToPath = defineInteger(builder, "minimumrailstopath", 8, 5, 100);
pathfindingMaxThreadCount = defineInteger(builder, "pathfindingmaxthreadcount", 2, 1, 10);
pathfindingMaxThreadCount = defineInteger(builder, "pathfindingmaxthreadcount", 1, 1, 10);

swapToCategory(builder, "requestSystem");

Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/minecolonies/api/util/EntityUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.minecolonies.api.crafting.ItemStorage;
import com.minecolonies.api.entity.citizen.AbstractEntityCitizen;
import com.minecolonies.api.items.ModTags;
import com.minecolonies.core.entity.pathfinding.PathfindingUtils;
import com.minecolonies.core.entity.pathfinding.SurfaceType;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
Expand Down Expand Up @@ -205,7 +206,7 @@ private static boolean checkValidSpawn(@NotNull final BlockGetter world, final B
for (int dy = 0; dy < height; dy++)
{
final BlockState state = world.getBlockState(pos.above(dy));
if (!state.is(ModTags.validSpawn) && BlockUtils.isAnySolid(state))
if (!state.is(ModTags.validSpawn) && (PathfindingUtils.isLiquid(state) || ShapeUtil.hasCollision(world, pos.above(dy), state)))
{
return false;
}
Expand Down
38 changes: 38 additions & 0 deletions src/main/java/com/minecolonies/api/util/ShapeUtil.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.minecolonies.api.util;

import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;

Expand Down Expand Up @@ -99,4 +102,39 @@ public static double getEndY(final VoxelShape bb, final double def)
{
return isEmpty(bb) ? def : max(bb, Direction.Axis.Y);
}

/**
* Check if the given block has a collision shape
*
* @param world
* @param pos
* @param state
* @return
*/
public static boolean hasCollision(final BlockGetter world, final BlockPos pos, final BlockState state)
{
if (!state.getBlock().hasCollision)
{
return false;
}

return hasCollision(state, state.getCollisionShape(world, pos));
}

/**
* Check if the given block has a collision shape
*
* @param state
* @param collisionShape
* @return
*/
public static boolean hasCollision(final BlockState state, final VoxelShape collisionShape)
{
if (!state.getBlock().hasCollision)
{
return false;
}

return !ShapeUtil.isEmpty(collisionShape);
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
package com.minecolonies.core.entity.mobs.aitasks;

import com.minecolonies.api.entity.ai.combat.threat.IThreatTableEntity;
import com.minecolonies.api.entity.ai.statemachine.states.IState;
import com.minecolonies.api.entity.ai.statemachine.tickratestatemachine.ITickRateStateMachine;
import com.minecolonies.api.entity.ai.combat.threat.IThreatTableEntity;
import com.minecolonies.api.entity.mobs.AbstractEntityRaiderMob;
import com.minecolonies.api.util.DamageSourceKeys;
import com.minecolonies.core.entity.pathfinding.pathresults.PathResult;
import com.minecolonies.api.util.SoundUtils;
import com.minecolonies.api.util.constant.Constants;
import com.minecolonies.core.MineColonies;
import com.minecolonies.core.entity.ai.combat.AttackMoveAI;
import com.minecolonies.core.entity.citizen.EntityCitizen;
import com.minecolonies.core.entity.pathfinding.pathresults.PathResult;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.chat.contents.TranslatableContents;
import net.minecraft.resources.ResourceKey;
Expand Down Expand Up @@ -79,6 +78,7 @@ protected double getAttackDistance()
@Override
protected int getAttackDelay()
{
// TODO: use own difficulty
return MAX_ATTACK_DELAY - MineColonies.getConfig().getServer().raidDifficulty.get() * 4;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
package com.minecolonies.core.entity.mobs.aitasks;

import com.minecolonies.api.entity.mobs.ICustomAttackSound;
import com.minecolonies.api.entity.ai.combat.threat.IThreatTableEntity;
import com.minecolonies.api.entity.ai.statemachine.states.IState;
import com.minecolonies.api.entity.ai.statemachine.tickratestatemachine.ITickRateStateMachine;
import com.minecolonies.api.entity.ai.combat.threat.IThreatTableEntity;
import com.minecolonies.api.entity.mobs.AbstractEntityRaiderMob;
import com.minecolonies.api.entity.mobs.ICustomAttackSound;
import com.minecolonies.api.entity.mobs.IRangedMobEntity;
import com.minecolonies.core.entity.pathfinding.pathresults.PathResult;
import com.minecolonies.api.util.EntityUtils;
import com.minecolonies.core.MineColonies;
import com.minecolonies.core.entity.other.CustomArrowEntity;
import com.minecolonies.core.entity.ai.combat.AttackMoveAI;
import com.minecolonies.core.entity.ai.combat.CombatUtils;
import com.minecolonies.core.entity.citizen.EntityCitizen;
import com.minecolonies.core.entity.other.CustomArrowEntity;
import com.minecolonies.core.entity.pathfinding.pathresults.PathResult;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.world.InteractionHand;
Expand Down Expand Up @@ -153,7 +153,7 @@ protected int getAttackDelay()
{
return 10;
}

// TODO: config is included in own difficulty
return (int) (Math.max(MIN_ATTACK_DELAY, MAX_ATTACK_DELAY - MineColonies.getConfig().getServer().raidDifficulty.get() * 4 * user.getDifficulty())
* user.getAttackDelayModifier());
}
Expand Down
24 changes: 20 additions & 4 deletions src/main/java/com/minecolonies/core/entity/pathfinding/MNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public class MNode implements Comparable<MNode>
/**
* Checks if the node has been closed already.
*/
private boolean visited = false;
private int visitedCount = 0;

/**
* Checks if the node is on a ladder.
Expand Down Expand Up @@ -190,7 +190,17 @@ public boolean equals(@Nullable final Object o)
*/
public boolean isVisited()
{
return visited;
return visitedCount != 0;
}

/**
* Get the visited count
*
* @return
*/
public int getVisitedCount()
{
return visitedCount;
}

/**
Expand All @@ -216,9 +226,9 @@ public boolean isSwimming()
/**
* Sets the node as closed.
*/
public void setVisited()
public void increaseVisited()
{
visited = true;
visitedCount++;
}

/**
Expand Down Expand Up @@ -388,4 +398,10 @@ public static int computeNodeKey(final int x, final int y, final int z)
| ((y & 0xFF) << SHIFT_Y_BY)
| (z & 0xFFF);
}

@Override
public String toString()
{
return "Node: [" + x + "," + y + "," + z + "] visited:" + visitedCount + " cost:" + cost + " heuristic:" + heuristic;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class PathingOptions
/**
* Cost improvement of paths - base 1.
*/
public double onPathCost = 1 / 4d;
public double onPathCost = 1 / 6d;

/**
* Cost improvement of paths - base 1.
Expand Down Expand Up @@ -55,7 +55,7 @@ public class PathingOptions
/**
* Cost to traverse trap doors
*/
public double traverseToggleAbleCost = 5D;
public double traverseToggleAbleCost = 3D;

/**
* Cost to climb a non ladder.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,11 +256,6 @@ public PathResult<AbstractPathJob> setPathJob(
return null;
}

if (PathfindingUtils.trackingMap.containsValue(ourEntity.getUUID()))
{
Log.getLogger().info(ourEntity + " started pathjob to:" + dest + " job type:" + job.getClass().getSimpleName());
}

stop();

this.destination = dest;
Expand Down Expand Up @@ -319,6 +314,7 @@ public void tick()
else if (pathResult.getStatus() == PathFindingStatus.CALCULATION_COMPLETE)
{
processCompletedCalculationResult();
wantedPosition = null;
}
}

Expand Down Expand Up @@ -675,10 +671,11 @@ private void processCompletedCalculationResult()
}

// Calculate an overtime-heuristic adjustment for pathfinding to use which fits the terrain
if (pathResult.costPerDist != 1)
if (pathResult.getPathLength() > 2 && pathResult.costPerDist != 1)
{
heuristicAvg -= heuristicAvg / 20;
heuristicAvg += pathResult.costPerDist / 20;
final double factor = 1 + pathResult.getPathLength() / 30.0;
heuristicAvg -= heuristicAvg / (50 / factor);
heuristicAvg += pathResult.costPerDist / (50 / factor);
}

if (pathResult.failedToReachDestination())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,28 @@ public static boolean walkCloseToXNearY(
{
final MinecoloniesAdvancedPathNavigate nav = ((MinecoloniesAdvancedPathNavigate) entity.getNavigation());

if (nav.isDone() || (nav.getPathResult() != null
&& !(nav.getPathResult().getJob() instanceof PathJobMoveCloseToXNearY job
&& job.nearbyPosition.equals(nearbyPosition)
&& job.desiredPosition.equals(desiredPosition)
&& job.distToDesired == distToDesired)))
// Three cases
// 1. Navigation Finished
// 2. Navigation is progressing towards a previous task
// 3. Navigation did not try once
boolean isOnRightTask = (nav.getPathResult() != null
&& nav.getPathResult().getJob() instanceof PathJobMoveCloseToXNearY job
&& job.nearbyPosition.equals(nearbyPosition)
&& job.desiredPosition.equals(desiredPosition));

if (nav.isDone() || !isOnRightTask)
{
// Check distance once navigation is done, to let the entity walk
if (BlockPosUtil.dist(entity.blockPosition(), desiredPosition) < distToDesired)
if (isOnRightTask)
{
return false;
// Check distance once navigation is done, to let the entity walk
if (BlockPosUtil.dist(entity.blockPosition(), desiredPosition) < distToDesired)
{
nav.stop();
return false;
}
}

PathJobMoveCloseToXNearY pathJob = new PathJobMoveCloseToXNearY(entity.level, desiredPosition, nearbyPosition, distToDesired, entity);
PathJobMoveCloseToXNearY pathJob = new PathJobMoveCloseToXNearY(entity.level, desiredPosition, nearbyPosition, 1, entity);
nav.setPathJob(pathJob, desiredPosition, 1.0, false);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ private void tryUnstuck(final AbstractAdvancedPathNavigate navigator)
}

navigator.stop();
final int range = ColonyConstants.rand.nextInt(20) + Math.min(100, BlockPosUtil.distManhattan(navigator.ourEntity.blockPosition(), prevDestination));
final int range = ColonyConstants.rand.nextInt(20) + Math.min(100, Math.max(20, BlockPosUtil.distManhattan(navigator.ourEntity.blockPosition(), prevDestination)));
navigator.moveTowards(navigator.getOurEntity().blockPosition().relative(movingAwayDir, 40), range, 1.0f);
movingAwayDir = movingAwayDir.getClockWise();
navigator.setPauseTicks(range * TICKS_PER_BLOCK);
Expand Down
Loading

0 comments on commit 7c4642e

Please sign in to comment.