Skip to content

Commit 98b5355

Browse files
committed
bugfix(radar): Add hero radar objects into its own list to get rid of hero cache updates and its related issues (#1893)
1 parent 4e05e18 commit 98b5355

File tree

5 files changed

+110
-98
lines changed

5 files changed

+110
-98
lines changed

GeneralsMD/Code/GameEngine/Include/Common/Radar.h

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,6 @@ enum RadarEventType CPP_11(: Int)
7777

7878
};
7979

80-
enum RadarObjectType CPP_11(: Int)
81-
{
82-
RadarObjectType_None = 0,
83-
RadarObjectType_Regular,
84-
RadarObjectType_Local,
85-
};
86-
8780
// PROTOTYPES /////////////////////////////////////////////////////////////////////////////////////
8881

8982
//-------------------------------------------------------------------------------------------------
@@ -196,8 +189,8 @@ class Radar : public Snapshot,
196189
Bool tryEvent( RadarEventType event, const Coord3D *pos ); ///< try to make a "stealth" event
197190

198191
// adding and removing objects from the radar
199-
virtual RadarObjectType addObject( Object *obj ); ///< add object to radar
200-
virtual RadarObjectType removeObject( Object *obj ); ///< remove object from radar
192+
virtual Bool addObject( Object *obj ); ///< add object to radar
193+
virtual Bool removeObject( Object *obj ); ///< remove object from radar
201194

202195
// radar options
203196
void hide( Int playerIndex, Bool hide ) { m_radarHidden[playerIndex] = hide; } ///< hide/show the radar
@@ -245,8 +238,6 @@ class Radar : public Snapshot,
245238

246239
inline Real getTerrainAverageZ() const { return m_terrainAverageZ; }
247240
inline Real getWaterAverageZ() const { return m_waterAverageZ; }
248-
inline const RadarObject* getObjectList() const { return m_objectList; }
249-
inline const RadarObject* getLocalObjectList() const { return m_localObjectList; }
250241

251242
void clearAllEvents( void ); ///< remove all radar events in progress
252243

@@ -258,12 +249,17 @@ class Radar : public Snapshot,
258249

259250
Bool m_radarHidden[MAX_PLAYER_COUNT]; ///< true when radar is not visible
260251
Bool m_radarForceOn[MAX_PLAYER_COUNT]; ///< true when radar is forced to be on
252+
261253
RadarObject *m_objectList; ///< list of objects in the radar
262254
RadarObject *m_localObjectList; /** list of objects for the local player, sorted
263255
* in exactly the same priority as the regular
264256
* object list for all other objects */
265-
// typedef std::list<Object*> HeroList;
266-
// HeroList m_heroList; //< list of pointers to objects with radar icon representations
257+
258+
// TheSuperHackers @bugfix xezon 22/11/2025 Now stores local heroes in a separate list,
259+
// because they are treated with special icons but should otherwise work like all other
260+
// radar objects. In retail version, the cached hero object data was able to dangle
261+
// for a few frames and cause undefined behavior.
262+
RadarObject *m_localHeroObjectList; ///< list of hero objects for the local player
267263

268264
Real m_terrainAverageZ; ///< average Z for terrain samples
269265
Real m_waterAverageZ; ///< average Z for water samples

GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,12 +1086,12 @@ void Player::becomingLocalPlayer(Bool yes)
10861086
{
10871087
// Added support for updating the perceptions of garrisoned buildings containing enemy stealth units.
10881088
// When changing teams, it is necessary to update this information.
1089+
Bool requireRadarRefresh = false;
10891090
ContainModuleInterface *contain = object->getContain();
10901091
if( contain )
10911092
{
10921093
contain->recalcApparentControllingPlayer();
1093-
TheRadar->removeObject( object );
1094-
TheRadar->addObject( object );
1094+
requireRadarRefresh = true;
10951095
}
10961096

10971097
if( object->isKindOf( KINDOF_DISGUISER ) )
@@ -1102,7 +1102,7 @@ void Player::becomingLocalPlayer(Bool yes)
11021102
if( draw )
11031103
{
11041104

1105-
StealthUpdate *update = object->getStealth();
1105+
StealthUpdate *update = object->getStealth();
11061106

11071107
if( update && update->isDisguised() )
11081108
{
@@ -1123,11 +1123,16 @@ void Player::becomingLocalPlayer(Bool yes)
11231123
else
11241124
draw->setIndicatorColor( object->getIndicatorColor() );
11251125
}
1126-
TheRadar->removeObject( object );
1127-
TheRadar->addObject( object );
1126+
requireRadarRefresh = true;
11281127
}
11291128
}
11301129
}
1130+
1131+
if (requireRadarRefresh)
1132+
{
1133+
TheRadar->removeObject( object );
1134+
TheRadar->addObject( object );
1135+
}
11311136
}
11321137
deleteInstance(iter);
11331138
}

GeneralsMD/Code/GameEngine/Source/Common/System/Radar.cpp

Lines changed: 80 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ void Radar::deleteListResources( void )
8888
{
8989
deleteList(&m_objectList);
9090
deleteList(&m_localObjectList);
91+
deleteList(&m_localHeroObjectList);
9192

9293
#ifdef DEBUG_CRASHING
9394
for( Object *obj = TheGameLogic->getFirstObject(); obj; obj = obj->getNextObject() )
@@ -197,6 +198,7 @@ Radar::Radar( void )
197198
m_radarWindow = NULL;
198199
m_objectList = NULL;
199200
m_localObjectList = NULL;
201+
m_localHeroObjectList = NULL;
200202
std::fill(m_radarHidden, m_radarHidden + ARRAY_SIZE(m_radarHidden), false);
201203
std::fill(m_radarForceOn, m_radarForceOn + ARRAY_SIZE(m_radarForceOn), false);
202204
m_terrainAverageZ = 0.0f;
@@ -386,13 +388,13 @@ void Radar::newMap( TerrainLogic *terrain )
386388
/** Add an object to the radar list. The object will be sorted in the list to be grouped
387389
* using it's radar priority */
388390
//-------------------------------------------------------------------------------------------------
389-
RadarObjectType Radar::addObject( Object *obj )
391+
Bool Radar::addObject( Object *obj )
390392
{
391393

392394
// get the radar priority for this object
393395
RadarPriorityType newPriority = obj->getRadarPriority();
394396
if( isPriorityVisible( newPriority ) == FALSE )
395-
return RadarObjectType_None;
397+
return FALSE;
396398

397399
// if this object is on the radar, remove it in favor of the new add
398400
RadarObject **list;
@@ -415,26 +417,26 @@ RadarObjectType Radar::addObject( Object *obj )
415417
// set a chunk of radar data in the object
416418
obj->friend_setRadarData( newObj );
417419

418-
RadarObjectType objectType;
419420
//
420421
// we will put this on either the local object list for objects that belong to the
421422
// local player, or on the regular object list for all other objects
422423
//
423424
if( obj->isLocallyControlled() )
424425
{
425-
list = &m_localObjectList;
426-
objectType = RadarObjectType_Local;
426+
if ( obj->isHero() )
427+
list = &m_localHeroObjectList;
428+
else
429+
list = &m_localObjectList;
427430
}
428431
else
429432
{
430433
list = &m_objectList;
431-
objectType = RadarObjectType_Regular;
432434
}
433435

434436
// link object to master list at the head of it's priority section
435437
linkRadarObject(newObj, list);
436438

437-
return objectType;
439+
return TRUE;
438440
}
439441

440442
//-------------------------------------------------------------------------------------------------
@@ -481,24 +483,24 @@ Bool Radar::deleteFromList( Object *obj, RadarObject **list )
481483
//-------------------------------------------------------------------------------------------------
482484
/** Remove an object from the radar, the object may reside in any list */
483485
//-------------------------------------------------------------------------------------------------
484-
RadarObjectType Radar::removeObject( Object *obj )
486+
Bool Radar::removeObject( Object *obj )
485487
{
486488

487489
// sanity
488490
if( obj->friend_getRadarData() == NULL )
489-
return RadarObjectType_None;
491+
return FALSE;
490492

493+
if( deleteFromList( obj, &m_localHeroObjectList ) == TRUE )
494+
return TRUE;
491495
if( deleteFromList( obj, &m_localObjectList ) == TRUE )
492-
return RadarObjectType_Local;
496+
return TRUE;
493497
else if( deleteFromList( obj, &m_objectList ) == TRUE )
494-
return RadarObjectType_Regular;
498+
return TRUE;
495499
else
496500
{
497-
498-
// sanity
499501
DEBUG_ASSERTCRASH( 0, ("Radar: Tried to remove object '%s' which was not found",
500502
obj->getTemplate()->getName().str()) );
501-
return RadarObjectType_None;
503+
return FALSE;
502504
}
503505

504506
}
@@ -723,10 +725,14 @@ Object *Radar::objectUnderRadarPixel( const ICoord2D *pixel )
723725
// to the radar location
724726
//
725727

726-
// search the local object list
727-
obj = searchListForRadarLocationMatch( m_localObjectList, &radar );
728+
// search the local hero object list
729+
obj = searchListForRadarLocationMatch( m_localHeroObjectList, &radar );
730+
731+
// search the local object list if not found
732+
if( obj == NULL )
733+
obj = searchListForRadarLocationMatch( m_localObjectList, &radar );
728734

729-
// search all other objects if not found
735+
// search all other objects if still not found
730736
if( obj == NULL )
731737
obj = searchListForRadarLocationMatch( m_objectList, &radar );
732738

@@ -1366,6 +1372,7 @@ static void xferRadarObjectList( Xfer *xfer, RadarObject **head )
13661372
* Version Info:
13671373
* 1: Initial version
13681374
* 2: TheSuperHackers @tweak Serialize m_radarHidden, m_radarForceOn for each player
1375+
* 3: TheSuperHackers @tweak Serialize m_localHeroObjectList
13691376
*/
13701377
// ------------------------------------------------------------------------------------------------
13711378
void Radar::xfer( Xfer *xfer )
@@ -1375,13 +1382,13 @@ void Radar::xfer( Xfer *xfer )
13751382
#if RETAIL_COMPATIBLE_XFER_SAVE
13761383
XferVersion currentVersion = 1;
13771384
#else
1378-
XferVersion currentVersion = 2;
1385+
XferVersion currentVersion = 3;
13791386
#endif
13801387
XferVersion version = currentVersion;
13811388
xfer->xferVersion( &version, currentVersion );
13821389

13831390

1384-
if (version <= 1)
1391+
if (version < 2)
13851392
{
13861393
const Int localPlayerIndex = ThePlayerList->getLocalPlayer()->getPlayerIndex();
13871394
Bool value;
@@ -1405,12 +1412,66 @@ void Radar::xfer( Xfer *xfer )
14051412
xfer->xferUser(&m_radarForceOn, sizeof(m_radarForceOn));
14061413
}
14071414

1415+
if (version < 3)
1416+
{
1417+
if (xfer->getXferMode() == XFER_SAVE)
1418+
{
1419+
// TheSuperHackers @info For legacy xfer compatibility.
1420+
// Transfer all local hero objects to local object list.
1421+
RadarObject **fromList = &m_localHeroObjectList;
1422+
RadarObject **toList = &m_localObjectList;
1423+
while (*fromList != NULL)
1424+
{
1425+
RadarObject* nextObject = (*fromList)->friend_getNext();
1426+
(*fromList)->friend_setNext(NULL);
1427+
linkRadarObject(*fromList, toList);
1428+
*fromList = nextObject;
1429+
}
1430+
}
1431+
}
1432+
else
1433+
{
1434+
xferRadarObjectList( xfer, &m_localHeroObjectList );
1435+
}
1436+
14081437
// save our local object list
14091438
xferRadarObjectList( xfer, &m_localObjectList );
14101439

14111440
// save the regular object list
14121441
xferRadarObjectList( xfer, &m_objectList );
14131442

1443+
if (version < 3)
1444+
{
1445+
// TheSuperHackers @info For legacy xfer compatibility.
1446+
// Transfer hero local object(s) back to local hero object list.
1447+
// This needs to be done on both load and save.
1448+
RadarObject **fromList = &m_localObjectList;
1449+
RadarObject **toList = &m_localHeroObjectList;
1450+
RadarObject *currObject;
1451+
RadarObject *prevObject;
1452+
RadarObject *nextObject;
1453+
prevObject = NULL;
1454+
for (currObject = *fromList; currObject != NULL; currObject = nextObject)
1455+
{
1456+
nextObject = currObject->friend_getNext();
1457+
if (currObject->friend_getObject()->isHero())
1458+
{
1459+
if (prevObject != NULL)
1460+
{
1461+
prevObject->friend_setNext(nextObject);
1462+
}
1463+
else
1464+
{
1465+
*fromList = nextObject;
1466+
}
1467+
currObject->friend_setNext(NULL);
1468+
linkRadarObject(currObject, toList);
1469+
continue;
1470+
}
1471+
prevObject = currObject;
1472+
}
1473+
}
1474+
14141475
// save the radar event count and data
14151476
UnsignedShort eventCountVerify = MAX_RADAR_EVENTS;
14161477
UnsignedShort eventCount = eventCountVerify;

GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/Common/W3DRadar.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,6 @@ class W3DRadar : public Radar
5858
virtual void update( void ); ///< subsystem update
5959
virtual void reset( void ); ///< subsystem reset
6060

61-
virtual RadarObjectType addObject( Object *obj ); ///< add object to radar
62-
virtual RadarObjectType removeObject( Object *obj ); ///< remove object from radar
63-
6461
virtual void newMap( TerrainLogic *terrain ); ///< reset radar for new map
6562

6663
virtual void draw( Int pixelX, Int pixelY, Int width, Int height ); ///< draw the radar
@@ -85,7 +82,7 @@ class W3DRadar : public Radar
8582
void drawIcons( Int pixelX, Int pixelY, Int width, Int height ); ///< draw all of the radar icons
8683
void updateObjectTexture(TextureClass *texture);
8784
static Bool canRenderObject( const RadarObject *rObj, const Player *localPlayer );
88-
void renderObjectList( const RadarObject *listHead, TextureClass *texture, Bool calcHero = FALSE ); ///< render an object list to the texture
85+
void renderObjectList( const RadarObject *listHead, TextureClass *texture );
8986
void interpolateColorForHeight( RGBColor *color,
9087
Real height,
9188
Real hiZ,
@@ -122,6 +119,4 @@ class W3DRadar : public Radar
122119
Real m_viewAngle; ///< camera angle used for the view box we have
123120
Real m_viewZoom; ///< camera zoom used for the view box we have
124121
ICoord2D m_viewBox[ 4 ]; ///< radar cell points for the 4 corners of view box
125-
126-
std::vector<const Object *> m_cachedHeroObjectList; //< cache of hero objects for drawing icons in radar overlay
127122
};

0 commit comments

Comments
 (0)