Skip to content

Commit 41559c0

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 ce717ef commit 41559c0

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
@@ -81,6 +81,7 @@ void Radar::deleteListResources( void )
8181
{
8282
deleteList(&m_objectList);
8383
deleteList(&m_localObjectList);
84+
deleteList(&m_localHeroObjectList);
8485

8586
#ifdef DEBUG_CRASHING
8687
for( Object *obj = TheGameLogic->getFirstObject(); obj; obj = obj->getNextObject() )
@@ -191,6 +192,7 @@ Radar::Radar( void )
191192
m_radarWindow = NULL;
192193
m_objectList = NULL;
193194
m_localObjectList = NULL;
195+
m_localHeroObjectList = NULL;
194196
std::fill(m_radarHidden, m_radarHidden + ARRAY_SIZE(m_radarHidden), false);
195197
std::fill(m_radarForceOn, m_radarForceOn + ARRAY_SIZE(m_radarForceOn), false);
196198
m_terrainAverageZ = 0.0f;
@@ -380,13 +382,13 @@ void Radar::newMap( TerrainLogic *terrain )
380382
/** Add an object to the radar list. The object will be sorted in the list to be grouped
381383
* using it's radar priority */
382384
//-------------------------------------------------------------------------------------------------
383-
RadarObjectType Radar::addObject( Object *obj )
385+
Bool Radar::addObject( Object *obj )
384386
{
385387

386388
// get the radar priority for this object
387389
RadarPriorityType newPriority = obj->getRadarPriority();
388390
if( isPriorityVisible( newPriority ) == FALSE )
389-
return RadarObjectType_None;
391+
return FALSE;
390392

391393
// if this object is on the radar, remove it in favor of the new add
392394
RadarObject **list;
@@ -409,26 +411,26 @@ RadarObjectType Radar::addObject( Object *obj )
409411
// set a chunk of radar data in the object
410412
obj->friend_setRadarData( newObj );
411413

412-
RadarObjectType objectType;
413414
//
414415
// we will put this on either the local object list for objects that belong to the
415416
// local player, or on the regular object list for all other objects
416417
//
417418
if( obj->isLocallyControlled() )
418419
{
419-
list = &m_localObjectList;
420-
objectType = RadarObjectType_Local;
420+
if ( obj->isHero() )
421+
list = &m_localHeroObjectList;
422+
else
423+
list = &m_localObjectList;
421424
}
422425
else
423426
{
424427
list = &m_objectList;
425-
objectType = RadarObjectType_Regular;
426428
}
427429

428430
// link object to master list at the head of it's priority section
429431
linkRadarObject(newObj, list);
430432

431-
return objectType;
433+
return TRUE;
432434
}
433435

434436
//-------------------------------------------------------------------------------------------------
@@ -475,24 +477,24 @@ Bool Radar::deleteFromList( Object *obj, RadarObject **list )
475477
//-------------------------------------------------------------------------------------------------
476478
/** Remove an object from the radar, the object may reside in any list */
477479
//-------------------------------------------------------------------------------------------------
478-
RadarObjectType Radar::removeObject( Object *obj )
480+
Bool Radar::removeObject( Object *obj )
479481
{
480482

481483
// sanity
482484
if( obj->friend_getRadarData() == NULL )
483-
return RadarObjectType_None;
485+
return FALSE;
484486

487+
if( deleteFromList( obj, &m_localHeroObjectList ) == TRUE )
488+
return TRUE;
485489
if( deleteFromList( obj, &m_localObjectList ) == TRUE )
486-
return RadarObjectType_Local;
490+
return TRUE;
487491
else if( deleteFromList( obj, &m_objectList ) == TRUE )
488-
return RadarObjectType_Regular;
492+
return TRUE;
489493
else
490494
{
491-
492-
// sanity
493495
DEBUG_ASSERTCRASH( 0, ("Radar: Tried to remove object '%s' which was not found",
494496
obj->getTemplate()->getName().str()) );
495-
return RadarObjectType_None;
497+
return FALSE;
496498
}
497499

498500
}
@@ -717,10 +719,14 @@ Object *Radar::objectUnderRadarPixel( const ICoord2D *pixel )
717719
// to the radar location
718720
//
719721

720-
// search the local object list
721-
obj = searchListForRadarLocationMatch( m_localObjectList, &radar );
722+
// search the local hero object list
723+
obj = searchListForRadarLocationMatch( m_localHeroObjectList, &radar );
724+
725+
// search the local object list if not found
726+
if( obj == NULL )
727+
obj = searchListForRadarLocationMatch( m_localObjectList, &radar );
722728

723-
// search all other objects if not found
729+
// search all other objects if still not found
724730
if( obj == NULL )
725731
obj = searchListForRadarLocationMatch( m_objectList, &radar );
726732

@@ -1360,6 +1366,7 @@ static void xferRadarObjectList( Xfer *xfer, RadarObject **head )
13601366
* Version Info:
13611367
* 1: Initial version
13621368
* 2: TheSuperHackers @tweak Serialize m_radarHidden, m_radarForceOn for each player
1369+
* 3: TheSuperHackers @tweak Serialize m_localHeroObjectList
13631370
*/
13641371
// ------------------------------------------------------------------------------------------------
13651372
void Radar::xfer( Xfer *xfer )
@@ -1369,13 +1376,13 @@ void Radar::xfer( Xfer *xfer )
13691376
#if RETAIL_COMPATIBLE_XFER_SAVE
13701377
XferVersion currentVersion = 1;
13711378
#else
1372-
XferVersion currentVersion = 2;
1379+
XferVersion currentVersion = 3;
13731380
#endif
13741381
XferVersion version = currentVersion;
13751382
xfer->xferVersion( &version, currentVersion );
13761383

13771384

1378-
if (version <= 1)
1385+
if (version < 2)
13791386
{
13801387
const Int localPlayerIndex = ThePlayerList->getLocalPlayer()->getPlayerIndex();
13811388
Bool value;
@@ -1399,12 +1406,66 @@ void Radar::xfer( Xfer *xfer )
13991406
xfer->xferUser(&m_radarForceOn, sizeof(m_radarForceOn));
14001407
}
14011408

1409+
if (version < 3)
1410+
{
1411+
if (xfer->getXferMode() == XFER_SAVE)
1412+
{
1413+
// TheSuperHackers @info For legacy xfer compatibility.
1414+
// Transfer all local hero objects to local object list.
1415+
RadarObject **fromList = &m_localHeroObjectList;
1416+
RadarObject **toList = &m_localObjectList;
1417+
while (*fromList != NULL)
1418+
{
1419+
RadarObject* nextObject = (*fromList)->friend_getNext();
1420+
(*fromList)->friend_setNext(NULL);
1421+
linkRadarObject(*fromList, toList);
1422+
*fromList = nextObject;
1423+
}
1424+
}
1425+
}
1426+
else
1427+
{
1428+
xferRadarObjectList( xfer, &m_localHeroObjectList );
1429+
}
1430+
14021431
// save our local object list
14031432
xferRadarObjectList( xfer, &m_localObjectList );
14041433

14051434
// save the regular object list
14061435
xferRadarObjectList( xfer, &m_objectList );
14071436

1437+
if (version < 3)
1438+
{
1439+
// TheSuperHackers @info For legacy xfer compatibility.
1440+
// Transfer hero local object(s) back to local hero object list.
1441+
// This needs to be done on both load and save.
1442+
RadarObject **fromList = &m_localObjectList;
1443+
RadarObject **toList = &m_localHeroObjectList;
1444+
RadarObject *currObject;
1445+
RadarObject *prevObject;
1446+
RadarObject *nextObject;
1447+
prevObject = NULL;
1448+
for (currObject = *fromList; currObject != NULL; currObject = nextObject)
1449+
{
1450+
nextObject = currObject->friend_getNext();
1451+
if (currObject->friend_getObject()->isHero())
1452+
{
1453+
if (prevObject != NULL)
1454+
{
1455+
prevObject->friend_setNext(nextObject);
1456+
}
1457+
else
1458+
{
1459+
*fromList = nextObject;
1460+
}
1461+
currObject->friend_setNext(NULL);
1462+
linkRadarObject(currObject, toList);
1463+
continue;
1464+
}
1465+
prevObject = currObject;
1466+
}
1467+
}
1468+
14081469
// save the radar event count and data
14091470
UnsignedShort eventCountVerify = MAX_RADAR_EVENTS;
14101471
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)