Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Core/GameEngine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ set(GAMEENGINE_SRC
# Include/Common/StateMachine.h
# Include/Common/StatsCollector.h
# Include/Common/STLTypedefs.h
Include/Common/STLUtils.h
# Include/Common/StreamingArchiveFile.h
# Include/Common/SubsystemInterface.h
# Include/Common/SystemInfo.h
Expand Down
61 changes: 61 additions & 0 deletions Core/GameEngine/Include/Common/STLUtils.h
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why was this not added in the utility folder instead and why are two copies of the same file being added? Also why are we not using standard library algorithms instead?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also why are we not using standard library algorithms instead?

What do you propose we use instead?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove erase idiom

bool W3DRadar::removeObject(Object* obj)
{
    bool removed = Radar::removeObject(obj);
    if (!removed)
	    return false;
    if (obj->isHero())
	    m_cachedHeroObjectList.erase(std::remove(m_cachedHeroObjectList.begin(), m_cachedHeroObjectList.end(), obj), m_cachedHeroObjectList.end());
    return true;
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As for find_and_erase_unordered, after we drop vc6 we can create an rts::unstable_remove function and use it in places in the code where it can be demonstrated with profiling that it yields a significant performance improvement.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this is to be considered an improvement:
stl::find_and_erase_unordered(m_cachedHeroObjectList, obj);
->
m_cachedHeroObjectList.erase(std::remove(m_cachedHeroObjectList.begin(), m_cachedHeroObjectList.end(), obj), m_cachedHeroObjectList.end());

we can create an rts::unstable_remove function and use it in places in the code where it can be demonstrated with profiling that it yields a significant performance improvement

Frankly, I disagree as not every improvement needs to be measured. In my opinion, improvements should not affect correctness naturally, and should not be disproportionate to the scope of the change. The scope of the change here is trivial.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is O n/2 not a thing :D . It is to me. Going through half the range on average is faster than going through the full range always.

But why not use std::set instead

Because std::set is not space efficient/contiguous. We use std::vector because READ is most important per frame. Insert/erase happens sparingly and is therefore not performance priority.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is O n/2 not a thing :D . It is to me. Going through half the range on average is faster than going through the full range always.

But why not use std::set instead

Because std::set is not space efficient/contiguous. We use std::vector because READ is most important per frame. Insert/erase happens sparingly and is therefore not performance priority.

Because thats how Big O notation works. Its about how the value scales in respect to scaling the input size n. Coefficients are dropped because they would be the same in the scaled value and the previous value and cancel out.

If it wasn't this way then I could write a helper function bar that only searches 10 elements. Obiously the performance of bar is always fixed ie constant time. Now I could write a function foo that iterates in steps of size 10 and calls bar on the next 10 elements. Technically foo would need 10 times less iterations than a function that that iterated with a step size of 1. Now I could claim I've created a search function thats 10 times faster because it has a time complexity of O (n/10) instead of O (n)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I will name that C n/2 then.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well foo is C n/20 then

Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 TheSuperHackers
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

#include <Utility/CppMacros.h>
#include <utility>

namespace stl
{

// Finds first matching element in vector-like container and erases it.
template <typename Container>
bool find_and_erase(Container& container, const typename Container::value_type& value)
{
typename Container::const_iterator it = container.begin();
for (; it != container.end(); ++it)
{
if (*it == value)
{
container.erase(it);
return true;
}
}
return false;
}

// Finds first matching element in vector-like container and removes it by swapping it with the last element.
// This is generally faster than erasing from a vector, but will change the element sorting.
template <typename Container>
bool find_and_erase_unordered(Container& container, const typename Container::value_type& value)
{
typename Container::iterator it = container.begin();
for (; it != container.end(); ++it)
{
if (*it == value)
{
*it = CPP_11(std::move)(container.back());
container.pop_back();
return true;
}
}
return false;
}

} // namespace stl
2 changes: 2 additions & 0 deletions Generals/Code/GameEngine/Include/Common/GameEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ class GameEngine : public SubsystemInterface

protected:

virtual void resetSubsystems( void );

virtual FileSystem *createFileSystem( void ); ///< Factory for FileSystem classes
virtual LocalFileSystem *createLocalFileSystem( void ) = 0; ///< Factory for LocalFileSystem classes
virtual ArchiveFileSystem *createArchiveFileSystem( void ) = 0; ///< Factory for ArchiveFileSystem classes
Expand Down
5 changes: 2 additions & 3 deletions Generals/Code/GameEngine/Include/Common/Radar.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,8 @@ class Radar : public Snapshot,
Bool tryEvent( RadarEventType event, const Coord3D *pos ); ///< try to make a "stealth" event

// adding and removing objects from the radar
void addObject( Object *obj ); ///< add object to radar
void removeObject( Object *obj ); ///< remove object from radar
void examineObject( Object *obj ); ///< re-examine object and resort if needed
virtual bool addObject( Object *obj ); ///< add object to radar
virtual bool removeObject( Object *obj ); ///< remove object from radar

// radar options
void hide( Bool hide ) { m_radarHidden = hide; } ///< hide/unhide the radar
Expand Down
1 change: 1 addition & 0 deletions Generals/Code/GameEngine/Include/Common/STLTypedefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class STLSpecialAlloc;
#include "Common/UnicodeString.h"
#include "Common/GameCommon.h"
#include "Common/GameMemory.h"
#include "Common/STLUtils.h"

//-----------------------------------------------------------------------------

Expand Down
15 changes: 13 additions & 2 deletions Generals/Code/GameEngine/Source/Common/GameEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,8 @@ void GameEngine::init()

initDisabledMasks();

TheSubsystemList->resetAll();
resetSubsystems();

HideControlBar();
} // end init

Expand All @@ -528,7 +529,7 @@ void GameEngine::reset( void )
if (TheGameLogic->isInMultiplayerGame())
deleteNetwork = true;

TheSubsystemList->resetAll();
resetSubsystems();

if (deleteNetwork)
{
Expand All @@ -545,6 +546,16 @@ void GameEngine::reset( void )
}
}

/// -----------------------------------------------------------------------------------------------
void GameEngine::resetSubsystems( void )
{
// TheSuperHackers @fix xezon 09/06/2025 Reset GameLogic first to purge all world objects early.
// This avoids potentially catastrophic issues when objects and subsystems have cross dependencies.
TheGameLogic->reset();

TheSubsystemList->resetAll();
}

/// -----------------------------------------------------------------------------------------------
DECLARE_PERF_TIMER(GameEngine_update)

Expand Down
15 changes: 8 additions & 7 deletions Generals/Code/GameEngine/Source/Common/System/Radar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -394,13 +394,13 @@ void Radar::newMap( TerrainLogic *terrain )
/** Add an object to the radar list. The object will be sorted in the list to be grouped
* using it's radar priority */
//-------------------------------------------------------------------------------------------------
void Radar::addObject( Object *obj )
bool Radar::addObject( Object *obj )
{

// get the radar priority for this object
RadarPriorityType newPriority = obj->getRadarPriority();
if( isPriorityVisible( newPriority ) == FALSE )
return;
return false;

// if this object is on the radar, remove it in favor of the new add
RadarObject **list;
Expand Down Expand Up @@ -544,6 +544,7 @@ void Radar::addObject( Object *obj )

} // end else

return true;
} // end addObject

//-------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -590,24 +591,24 @@ Bool Radar::deleteFromList( Object *obj, RadarObject **list )
//-------------------------------------------------------------------------------------------------
/** Remove an object from the radar, the object may reside in any list */
//-------------------------------------------------------------------------------------------------
void Radar::removeObject( Object *obj )
bool Radar::removeObject( Object *obj )
{

// sanity
if( obj->friend_getRadarData() == NULL )
return;
return false;

if( deleteFromList( obj, &m_localObjectList ) == TRUE )
return;
return true;
else if( deleteFromList( obj, &m_objectList ) == TRUE )
return;
return true;
else
{

// sanity
DEBUG_ASSERTCRASH( 0, ("Radar: Tried to remove object '%s' which was not found\n",
obj->getTemplate()->getName().str()) );

return false;
} // end else

} // end removeObject
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ class W3DRadar : public Radar
virtual void update( void ); ///< subsystem update
virtual void reset( void ); ///< subsystem reset

virtual bool addObject( Object *obj ); ///< add object to radar
virtual bool removeObject( Object *obj ); ///< remove object from radar

virtual void newMap( TerrainLogic *terrain ); ///< reset radar for new map

void draw( Int pixelX, Int pixelY, Int width, Int height ); ///< draw the radar
Expand All @@ -80,7 +83,7 @@ class W3DRadar : public Radar
void drawViewBox( Int pixelX, Int pixelY, Int width, Int height ); ///< draw view box
void buildTerrainTexture( TerrainLogic *terrain ); ///< create the terrain texture of the radar
void drawIcons( Int pixelX, Int pixelY, Int width, Int height ); ///< draw all of the radar icons
void renderObjectList( const RadarObject *listHead, TextureClass *texture, Bool calcHero = FALSE ); ///< render an object list to the texture
void renderObjectList( const RadarObject *listHead, TextureClass *texture ); ///< render an object list to the texture
void interpolateColorForHeight( RGBColor *color,
Real height,
Real hiZ,
Expand Down Expand Up @@ -118,7 +121,7 @@ class W3DRadar : public Radar
Real m_viewZoom; ///< camera zoom used for the view box we have
ICoord2D m_viewBox[ 4 ]; ///< radar cell points for the 4 corners of view box

std::list<const Coord3D *> m_cachedHeroPosList; //< cache of hero positions for drawing icons in radar overlay
std::vector<const Object *> m_cachedHeroObjectList; //< cache of hero objects for drawing icons in radar overlay
};


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -608,18 +608,18 @@ void W3DRadar::drawEvents( Int pixelX, Int pixelY, Int width, Int height )
void W3DRadar::drawIcons( Int pixelX, Int pixelY, Int width, Int height )
{
// draw the hero icons
std::list<const Coord3D *>::const_iterator iter = m_cachedHeroPosList.begin();
while (iter != m_cachedHeroPosList.end())
std::vector<const Object *>::const_iterator iter = m_cachedHeroObjectList.begin();
while (iter != m_cachedHeroObjectList.end())
{
drawHeroIcon( pixelX, pixelY, width, height, (*iter) );
drawHeroIcon( pixelX, pixelY, width, height, (*iter)->getPosition() );
++iter;
}
}

//-------------------------------------------------------------------------------------------------
/** Render an object list into the texture passed in */
//-------------------------------------------------------------------------------------------------
void W3DRadar::renderObjectList( const RadarObject *listHead, TextureClass *texture, Bool calcHero )
void W3DRadar::renderObjectList( const RadarObject *listHead, TextureClass *texture )
{

// sanity
Expand All @@ -637,12 +637,6 @@ void W3DRadar::renderObjectList( const RadarObject *listHead, TextureClass *text
if (player)
playerIndex=player->getPlayerIndex();

if( calcHero )
{
// clear all entries from the cached hero object list
m_cachedHeroPosList.clear();
}

for( const RadarObject *rObj = listHead; rObj; rObj = rObj->friend_getNext() )
{

Expand All @@ -652,11 +646,6 @@ void W3DRadar::renderObjectList( const RadarObject *listHead, TextureClass *text
// get object
const Object *obj = rObj->friend_getObject();

// cache hero object positions for drawing in icon layer
if( calcHero && obj->isHero() )
{
m_cachedHeroPosList.push_back(obj->getPosition());
}
Bool skip = FALSE;

// check for shrouded status
Expand Down Expand Up @@ -1001,6 +990,34 @@ void W3DRadar::update( void )

} // end update

//-------------------------------------------------------------------------------------------------
bool W3DRadar::addObject( Object *obj )
{
if (Radar::addObject(obj))
{
if (obj->isHero())
{
m_cachedHeroObjectList.push_back(obj);
}
return true;
}
return false;
}

//-------------------------------------------------------------------------------------------------
bool W3DRadar::removeObject( Object *obj )
{
if (Radar::removeObject(obj))
{
if (obj->isHero())
{
stl::find_and_erase_unordered(m_cachedHeroObjectList, obj);
}
return true;
}
return false;
}

//-------------------------------------------------------------------------------------------------
/** Reset the radar for the new map data being given to it */
//-------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -1416,7 +1433,7 @@ void W3DRadar::draw( Int pixelX, Int pixelY, Int width, Int height )

// rebuild the object overlay
renderObjectList( getObjectList(), m_overlayTexture );
renderObjectList( getLocalObjectList(), m_overlayTexture, TRUE );
renderObjectList( getLocalObjectList(), m_overlayTexture );

} // end if

Expand Down Expand Up @@ -1475,7 +1492,7 @@ void W3DRadar::refreshTerrain( TerrainLogic *terrain )

/*
*
void W3DRadar::renderObjectList( const RadarObject *listHead, TextureClass *texture, Bool calcHero )
void W3DRadar::renderObjectList( const RadarObject *listHead, TextureClass *texture )
{

// sanity
Expand All @@ -1495,12 +1512,6 @@ void W3DRadar::refreshTerrain( TerrainLogic *terrain )

UnsignedByte minAlpha = 8;

if( calcHero )
{
// clear all entries from the cached hero object list
m_cachedHeroPosList.clear();
}

for( const RadarObject *rObj = listHead; rObj; rObj = rObj->friend_getNext() )
{
UnsignedByte h = (UnsignedByte)(rObj->isTemporarilyHidden());
Expand All @@ -1513,12 +1524,6 @@ void W3DRadar::refreshTerrain( TerrainLogic *terrain )
const Object *obj = rObj->friend_getObject();
UnsignedByte r = 1; // all decoys

// cache hero object positions for drawing in icon layer
if( calcHero && obj->isHero() )
{
m_cachedHeroPosList.push_back(obj->getPosition());
}

// get the color we're going to draw in
UnsignedInt c = 0xfe000000;// this is a decoy
c |= (UnsignedInt)( obj->testStatus( OBJECT_STATUS_STEALTHED ) );//so is this
Expand Down
Loading
Loading