Skip to content

Commit 42f846b

Browse files
authored
bugfix(pathfinder): Prevent infinite loop within PathfindCell::putOnSortedOpenList() (#1965)
1 parent 158b046 commit 42f846b

File tree

2 files changed

+48
-29
lines changed

2 files changed

+48
-29
lines changed

Generals/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,13 @@ inline Int IABS(Int x) { if (x>=0) return x; return -x;};
9292
//-----------------------------------------------------------------------------------
9393
static Int frameToShowObstacles;
9494

95+
constexpr const UnsignedInt MAX_CELL_COUNT = 500;
96+
constexpr const UnsignedInt MAX_ADJUSTMENT_CELL_COUNT = 400;
97+
constexpr const UnsignedInt MAX_SAFE_PATH_CELL_COUNT = 2000;
98+
99+
constexpr const UnsignedInt PATHFIND_CELLS_PER_FRAME = 5000; // Number of cells we will search pathfinding per frame.
100+
constexpr const UnsignedInt CELL_INFOS_TO_ALLOCATE = 30000;
101+
95102
//-----------------------------------------------------------------------------------
96103
PathNode::PathNode() :
97104
m_nextOpti(0),
@@ -1077,8 +1084,6 @@ Real Path::computeFlightDistToGoal( const Coord3D *pos, Coord3D& goalPos )
10771084
}
10781085
//-----------------------------------------------------------------------------------
10791086

1080-
enum { PATHFIND_CELLS_PER_FRAME=5000}; // Number of cells we will search pathfinding per frame.
1081-
enum {CELL_INFOS_TO_ALLOCATE = 30000};
10821087
PathfindCellInfo *PathfindCellInfo::s_infoArray = NULL;
10831088
PathfindCellInfo *PathfindCellInfo::s_firstFree = NULL;
10841089

@@ -1547,8 +1552,18 @@ PathfindCell *PathfindCell::putOnSortedOpenList( PathfindCell *list )
15471552
{
15481553
// insertion sort
15491554
PathfindCell *c, *lastCell = NULL;
1550-
for( c = list; c; c = c->getNextOpen() )
1555+
#if RETAIL_COMPATIBLE_PATHFINDING
1556+
// TheSuperHackers @bugfix In the retail compatible pathfinding, on rare ocassions, we get stuck in an infinite loop
1557+
// External code should pickup on the bad behaviour and cleanup properly, but we need to explicitly break out here
1558+
// The fixed pathfinding does not have this issue due to the proper cleanup of pathfindCells and their pathfindCellInfos
1559+
UnsignedInt cellCount = 0;
1560+
for (c = list; c && cellCount < PATHFIND_CELLS_PER_FRAME; c = c->getNextOpen())
1561+
{
1562+
cellCount++;
1563+
#else
1564+
for (c = list; c; c = c->getNextOpen())
15511565
{
1566+
#endif
15521567
if (c->m_info->m_totalCost > m_info->m_totalCost)
15531568
break;
15541569

@@ -4715,8 +4730,7 @@ Bool Pathfinder::adjustToLandingDestination(Object *obj, Coord3D *dest)
47154730
}
47164731
worldToCell( &adjustDest, &cell );
47174732

4718-
enum {MAX_CELLS_TO_TRY=400};
4719-
Int limit = MAX_CELLS_TO_TRY;
4733+
Int limit = MAX_ADJUSTMENT_CELL_COUNT;
47204734
Int i, j;
47214735
i = cell.x;
47224736
j = cell.y;
@@ -4793,8 +4807,7 @@ Bool Pathfinder::adjustDestination(Object *obj, const LocomotorSet& locomotorSet
47934807
layer = TheTerrainLogic->getLayerForDestination(groupDest);
47944808
}
47954809

4796-
enum {MAX_CELLS_TO_TRY=400};
4797-
Int limit = MAX_CELLS_TO_TRY;
4810+
Int limit = MAX_ADJUSTMENT_CELL_COUNT;
47984811
Int i, j;
47994812
i = cell.x;
48004813
j = cell.y;
@@ -4877,8 +4890,8 @@ Bool Pathfinder::adjustTargetDestination(const Object *obj, const Object *target
48774890
if (worldToCell( &adjustDest, &cell )) {
48784891
return false; // outside of bounds.
48794892
}
4880-
enum {MAX_CELLS_TO_TRY=400};
4881-
Int limit = MAX_CELLS_TO_TRY;
4893+
4894+
Int limit = MAX_ADJUSTMENT_CELL_COUNT;
48824895
Int i, j;
48834896
i = cell.x;
48844897
j = cell.y;
@@ -5001,8 +5014,7 @@ Bool Pathfinder::adjustToPossibleDestination(Object *obj, const LocomotorSet& lo
50015014
}
50025015
}
50035016

5004-
enum {MAX_CELLS_TO_TRY=400};
5005-
Int limit = MAX_CELLS_TO_TRY;
5017+
Int limit = MAX_ADJUSTMENT_CELL_COUNT;
50065018
Int i, j;
50075019
i = goalCellNdx.x;
50085020
j = goalCellNdx.y;
@@ -7511,7 +7523,6 @@ Bool Pathfinder::pathDestination( Object *obj, const LocomotorSet& locomotorSet
75117523
if (!obj) return false;
75127524

75137525
Int cellCount = 0;
7514-
#define MAX_CELL_COUNT 500
75157526

75167527
Coord3D adjustTo = *groupDest;
75177528
Coord3D *to = &adjustTo;
@@ -7806,7 +7817,6 @@ Int Pathfinder::checkPathCost(Object *obj, const LocomotorSet& locomotorSet, con
78067817
if (!obj) return MAX_COST;
78077818

78087819
Int cellCount = 0;
7809-
#define MAX_CELL_COUNT 500
78107820

78117821
Coord3D adjustTo = *rawTo;
78127822
Coord3D *to = &adjustTo;
@@ -10328,7 +10338,7 @@ Path *Pathfinder::findSafePath( const Object *obj, const LocomotorSet& locomotor
1032810338
// Int startTimeMS = ::GetTickCount();
1032910339
#endif
1033010340

10331-
const Int MAX_CELLS = 2000; // this is a rather expensive operation, so limit the search.
10341+
const Int MAX_CELLS = MAX_SAFE_PATH_CELL_COUNT; // this is a rather expensive operation, so limit the search.
1033210342

1033310343
Bool centerInCell;
1033410344
Int radius;

GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIPathfind.cpp

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,13 @@ inline Int IABS(Int x) { if (x>=0) return x; return -x;};
106106
static Int frameToShowObstacles;
107107

108108

109-
static UnsignedInt ZONE_UPDATE_FREQUENCY = 300;
109+
constexpr const UnsignedInt ZONE_UPDATE_FREQUENCY = 300;
110+
constexpr const UnsignedInt MAX_CELL_COUNT = 500;
111+
constexpr const UnsignedInt MAX_ADJUSTMENT_CELL_COUNT = 400;
112+
constexpr const UnsignedInt MAX_SAFE_PATH_CELL_COUNT = 2000;
113+
114+
constexpr const UnsignedInt PATHFIND_CELLS_PER_FRAME = 5000; // Number of cells we will search pathfinding per frame.
115+
constexpr const UnsignedInt CELL_INFOS_TO_ALLOCATE = 30000;
110116

111117
//-----------------------------------------------------------------------------------
112118
PathNode::PathNode() :
@@ -1093,8 +1099,6 @@ Real Path::computeFlightDistToGoal( const Coord3D *pos, Coord3D& goalPos )
10931099
}
10941100
//-----------------------------------------------------------------------------------
10951101

1096-
enum { PATHFIND_CELLS_PER_FRAME=5000}; // Number of cells we will search pathfinding per frame.
1097-
enum {CELL_INFOS_TO_ALLOCATE = 30000};
10981102
PathfindCellInfo *PathfindCellInfo::s_infoArray = NULL;
10991103
PathfindCellInfo *PathfindCellInfo::s_firstFree = NULL;
11001104

@@ -1565,8 +1569,18 @@ PathfindCell *PathfindCell::putOnSortedOpenList( PathfindCell *list )
15651569
{
15661570
// insertion sort
15671571
PathfindCell *c, *lastCell = NULL;
1568-
for( c = list; c; c = c->getNextOpen() )
1572+
#if RETAIL_COMPATIBLE_PATHFINDING
1573+
// TheSuperHackers @bugfix In the retail compatible pathfinding, on rare ocassions, we get stuck in an infinite loop
1574+
// External code should pickup on the bad behaviour and cleanup properly, but we need to explicitly break out here
1575+
// The fixed pathfinding does not have this issue due to the proper cleanup of pathfindCells and their pathfindCellInfos
1576+
UnsignedInt cellCount = 0;
1577+
for (c = list; c && cellCount < PATHFIND_CELLS_PER_FRAME; c = c->getNextOpen())
15691578
{
1579+
cellCount++;
1580+
#else
1581+
for (c = list; c; c = c->getNextOpen())
1582+
{
1583+
#endif
15701584
if (c->m_info->m_totalCost > m_info->m_totalCost)
15711585
break;
15721586

@@ -4998,8 +5012,7 @@ Bool Pathfinder::adjustToLandingDestination(Object *obj, Coord3D *dest)
49985012
}
49995013
worldToCell( &adjustDest, &cell );
50005014

5001-
enum {MAX_CELLS_TO_TRY=400};
5002-
Int limit = MAX_CELLS_TO_TRY;
5015+
Int limit = MAX_ADJUSTMENT_CELL_COUNT;
50035016
Int i, j;
50045017
i = cell.x;
50055018
j = cell.y;
@@ -5076,8 +5089,7 @@ Bool Pathfinder::adjustDestination(Object *obj, const LocomotorSet& locomotorSet
50765089
layer = TheTerrainLogic->getLayerForDestination(groupDest);
50775090
}
50785091

5079-
enum {MAX_CELLS_TO_TRY=400};
5080-
Int limit = MAX_CELLS_TO_TRY;
5092+
Int limit = MAX_ADJUSTMENT_CELL_COUNT;
50815093
Int i, j;
50825094
i = cell.x;
50835095
j = cell.y;
@@ -5160,8 +5172,8 @@ Bool Pathfinder::adjustTargetDestination(const Object *obj, const Object *target
51605172
if (worldToCell( &adjustDest, &cell )) {
51615173
return false; // outside of bounds.
51625174
}
5163-
enum {MAX_CELLS_TO_TRY=400};
5164-
Int limit = MAX_CELLS_TO_TRY;
5175+
5176+
Int limit = MAX_ADJUSTMENT_CELL_COUNT;
51655177
Int i, j;
51665178
i = cell.x;
51675179
j = cell.y;
@@ -5284,8 +5296,7 @@ Bool Pathfinder::adjustToPossibleDestination(Object *obj, const LocomotorSet& lo
52845296
}
52855297
}
52865298

5287-
enum {MAX_CELLS_TO_TRY=400};
5288-
Int limit = MAX_CELLS_TO_TRY;
5299+
Int limit = MAX_ADJUSTMENT_CELL_COUNT;
52895300
Int i, j;
52905301
i = goalCellNdx.x;
52915302
j = goalCellNdx.y;
@@ -7910,7 +7921,6 @@ Bool Pathfinder::pathDestination( Object *obj, const LocomotorSet& locomotorSet
79107921
if (!obj) return false;
79117922

79127923
Int cellCount = 0;
7913-
#define MAX_CELL_COUNT 500
79147924

79157925
Coord3D adjustTo = *groupDest;
79167926
Coord3D *to = &adjustTo;
@@ -8205,7 +8215,6 @@ Int Pathfinder::checkPathCost(Object *obj, const LocomotorSet& locomotorSet, con
82058215
if (!obj) return MAX_COST;
82068216

82078217
Int cellCount = 0;
8208-
#define MAX_CELL_COUNT 500
82098218

82108219
Coord3D adjustTo = *rawTo;
82118220
Coord3D *to = &adjustTo;
@@ -10805,7 +10814,7 @@ Path *Pathfinder::findSafePath( const Object *obj, const LocomotorSet& locomotor
1080510814
// Int startTimeMS = ::GetTickCount();
1080610815
#endif
1080710816

10808-
const Int MAX_CELLS = 2000; // this is a rather expensive operation, so limit the search.
10817+
const Int MAX_CELLS = MAX_SAFE_PATH_CELL_COUNT; // this is a rather expensive operation, so limit the search.
1080910818

1081010819
Bool centerInCell;
1081110820
Int radius;

0 commit comments

Comments
 (0)