Skip to content

Commit fd63484

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 766afe4 commit fd63484

File tree

5 files changed

+110
-96
lines changed

5 files changed

+110
-96
lines changed

Core/GameEngine/Include/Common/Radar.h

Lines changed: 9 additions & 11 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,11 +249,18 @@ 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 */
265257

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
263+
266264
Real m_terrainAverageZ; ///< average Z for terrain samples
267265
Real m_waterAverageZ; ///< average Z for water samples
268266

Core/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;
@@ -379,13 +381,13 @@ void Radar::newMap( TerrainLogic *terrain )
379381
/** Add an object to the radar list. The object will be sorted in the list to be grouped
380382
* using it's radar priority */
381383
//-------------------------------------------------------------------------------------------------
382-
RadarObjectType Radar::addObject( Object *obj )
384+
Bool Radar::addObject( Object *obj )
383385
{
384386

385387
// get the radar priority for this object
386388
RadarPriorityType newPriority = obj->getRadarPriority();
387389
if( isPriorityVisible( newPriority ) == FALSE )
388-
return RadarObjectType_None;
390+
return FALSE;
389391

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

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

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

430-
return objectType;
432+
return TRUE;
431433
}
432434

433435
//-------------------------------------------------------------------------------------------------
@@ -474,24 +476,24 @@ Bool Radar::deleteFromList( Object *obj, RadarObject **list )
474476
//-------------------------------------------------------------------------------------------------
475477
/** Remove an object from the radar, the object may reside in any list */
476478
//-------------------------------------------------------------------------------------------------
477-
RadarObjectType Radar::removeObject( Object *obj )
479+
Bool Radar::removeObject( Object *obj )
478480
{
479481

480482
// sanity
481483
if( obj->friend_getRadarData() == NULL )
482-
return RadarObjectType_None;
484+
return FALSE;
483485

486+
if( deleteFromList( obj, &m_localHeroObjectList ) == TRUE )
487+
return TRUE;
484488
if( deleteFromList( obj, &m_localObjectList ) == TRUE )
485-
return RadarObjectType_Local;
489+
return TRUE;
486490
else if( deleteFromList( obj, &m_objectList ) == TRUE )
487-
return RadarObjectType_Regular;
491+
return TRUE;
488492
else
489493
{
490-
491-
// sanity
492494
DEBUG_ASSERTCRASH( 0, ("Radar: Tried to remove object '%s' which was not found",
493495
obj->getTemplate()->getName().str()) );
494-
return RadarObjectType_None;
496+
return FALSE;
495497
}
496498

497499
}
@@ -716,10 +718,14 @@ Object *Radar::objectUnderRadarPixel( const ICoord2D *pixel )
716718
// to the radar location
717719
//
718720

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

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

@@ -1359,6 +1365,7 @@ static void xferRadarObjectList( Xfer *xfer, RadarObject **head )
13591365
* Version Info:
13601366
* 1: Initial version
13611367
* 2: TheSuperHackers @tweak Serialize m_radarHidden, m_radarForceOn for each player
1368+
* 3: TheSuperHackers @tweak Serialize m_localHeroObjectList
13621369
*/
13631370
// ------------------------------------------------------------------------------------------------
13641371
void Radar::xfer( Xfer *xfer )
@@ -1368,13 +1375,13 @@ void Radar::xfer( Xfer *xfer )
13681375
#if RETAIL_COMPATIBLE_XFER_SAVE
13691376
XferVersion currentVersion = 1;
13701377
#else
1371-
XferVersion currentVersion = 2;
1378+
XferVersion currentVersion = 3;
13721379
#endif
13731380
XferVersion version = currentVersion;
13741381
xfer->xferVersion( &version, currentVersion );
13751382

13761383

1377-
if (version <= 1)
1384+
if (version < 2)
13781385
{
13791386
const Int localPlayerIndex = ThePlayerList->getLocalPlayer()->getPlayerIndex();
13801387
Bool value;
@@ -1398,12 +1405,66 @@ void Radar::xfer( Xfer *xfer )
13981405
xfer->xferUser(&m_radarForceOn, sizeof(m_radarForceOn));
13991406
}
14001407

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

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

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

Core/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
};

Core/GameEngineDevice/Source/W3DDevice/Common/System/W3DRadar.cpp

Lines changed: 10 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -602,12 +602,13 @@ void W3DRadar::drawEvents( Int pixelX, Int pixelY, Int width, Int height )
602602
//-------------------------------------------------------------------------------------------------
603603
void W3DRadar::drawIcons( Int pixelX, Int pixelY, Int width, Int height )
604604
{
605-
// draw the hero icons
606-
std::vector<const Object *>::const_iterator iter = m_cachedHeroObjectList.begin();
607-
while (iter != m_cachedHeroObjectList.end())
605+
Player *player = rts::getObservedOrLocalPlayer();
606+
for (RadarObject *heroObj = m_localHeroObjectList; heroObj; heroObj = heroObj->friend_getNext())
608607
{
609-
drawHeroIcon( pixelX, pixelY, width, height, (*iter)->getPosition() );
610-
++iter;
608+
if (canRenderObject(heroObj, player))
609+
{
610+
drawHeroIcon(pixelX, pixelY, width, height, heroObj->friend_getObject()->getPosition());
611+
}
611612
}
612613
}
613614

@@ -621,8 +622,9 @@ void W3DRadar::updateObjectTexture(TextureClass *texture)
621622
REF_PTR_RELEASE(surface);
622623

623624
// rebuild the object overlay
624-
renderObjectList( getObjectList(), texture );
625-
renderObjectList( getLocalObjectList(), texture, TRUE );
625+
renderObjectList( m_objectList, texture );
626+
renderObjectList( m_localObjectList, texture );
627+
renderObjectList( m_localHeroObjectList, texture );
626628
}
627629

628630
//-------------------------------------------------------------------------------------------------
@@ -676,7 +678,7 @@ Bool W3DRadar::canRenderObject( const RadarObject *rObj, const Player *localPlay
676678
//-------------------------------------------------------------------------------------------------
677679
/** Render an object list into the texture passed in */
678680
//-------------------------------------------------------------------------------------------------
679-
void W3DRadar::renderObjectList( const RadarObject *listHead, TextureClass *texture, Bool calcHero )
681+
void W3DRadar::renderObjectList( const RadarObject *listHead, TextureClass *texture )
680682
{
681683

682684
// sanity
@@ -691,12 +693,6 @@ void W3DRadar::renderObjectList( const RadarObject *listHead, TextureClass *text
691693

692694
Player *player = rts::getObservedOrLocalPlayer();
693695

694-
if( calcHero )
695-
{
696-
// clear all entries from the cached hero object list
697-
m_cachedHeroObjectList.clear();
698-
}
699-
700696
for( const RadarObject *rObj = listHead; rObj; rObj = rObj->friend_getNext() )
701697
{
702698
if (!canRenderObject(rObj, player))
@@ -734,12 +730,6 @@ void W3DRadar::renderObjectList( const RadarObject *listHead, TextureClass *text
734730

735731
}
736732

737-
// cache hero objects for drawing in icon layer
738-
if( calcHero && obj->isHero() )
739-
{
740-
m_cachedHeroObjectList.push_back(obj);
741-
}
742-
743733
// draw the blip, but make sure the points are legal
744734
if( legalRadarPoint( radarPoint.x, radarPoint.y ) )
745735
surface->DrawPixel( radarPoint.x, radarPoint.y, c );
@@ -988,8 +978,6 @@ void W3DRadar::reset( void )
988978
// extending functionality, call base class
989979
Radar::reset();
990980

991-
m_cachedHeroObjectList.clear();
992-
993981
// clear our texture data, but do not delete the resources
994982
SurfaceClass *surface;
995983

@@ -1024,39 +1012,6 @@ void W3DRadar::update( void )
10241012

10251013
}
10261014

1027-
//-------------------------------------------------------------------------------------------------
1028-
//-------------------------------------------------------------------------------------------------
1029-
RadarObjectType W3DRadar::addObject( Object* obj )
1030-
{
1031-
RadarObjectType addedType = Radar::addObject(obj);
1032-
1033-
if (addedType == RadarObjectType_Local)
1034-
{
1035-
if (obj->isHero() && !RadarObject::isTemporarilyHidden(obj))
1036-
{
1037-
m_cachedHeroObjectList.push_back(obj);
1038-
}
1039-
}
1040-
1041-
return addedType;
1042-
}
1043-
1044-
//-------------------------------------------------------------------------------------------------
1045-
// TheSuperHackers @bugfix xezon 05/07/2025 Now removes the cached hero immediately because
1046-
// otherwise the object pointer could be dangling and used for a bit too long.
1047-
//-------------------------------------------------------------------------------------------------
1048-
RadarObjectType W3DRadar::removeObject( Object* obj )
1049-
{
1050-
RadarObjectType removedType = Radar::removeObject(obj);
1051-
1052-
if (removedType == RadarObjectType_Local)
1053-
{
1054-
stl::find_and_erase_unordered(m_cachedHeroObjectList, obj);
1055-
}
1056-
1057-
return removedType;
1058-
}
1059-
10601015
//-------------------------------------------------------------------------------------------------
10611016
/** Reset the radar for the new map data being given to it */
10621017
//-------------------------------------------------------------------------------------------------

0 commit comments

Comments
 (0)