Skip to content

Commit

Permalink
Switch FEATURE to use PagedEntityContainer<FEATURE> as backing st…
Browse files Browse the repository at this point in the history
…orage

1. Split the feature storage into pages containing 128 features
2. Disable slot reuse to guard against memory-related issues
   when some object pointers won't get updated properly,
   e.g. when transitioning between the base and offworld missions.

Signed-off-by: Pavel Solodovnikov <[email protected]>
  • Loading branch information
ManManson authored and past-due committed Mar 26, 2024
1 parent c71a682 commit 62cea65
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 12 deletions.
16 changes: 9 additions & 7 deletions src/feature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,14 +183,10 @@ FEATURE *buildFeature(FEATURE_STATS *psStats, UDWORD x, UDWORD y, bool FromSave)
/* Create a feature on the map */
FEATURE *buildFeature(FEATURE_STATS *psStats, UDWORD x, UDWORD y, bool FromSave, uint32_t id)
{
//try and create the Feature
FEATURE *psFeature = new FEATURE(id, psStats);
//try and create the Feature, obtain stable address.
FEATURE& feature = GlobalFeatureContainer().emplace(id, psStats);
FEATURE* psFeature = &feature;

if (psFeature == nullptr)
{
debug(LOG_WARNING, "Feature couldn't be built.");
return nullptr;
}
//add the feature to the list - this enables it to be drawn whilst being built
addFeature(psFeature);

Expand Down Expand Up @@ -556,3 +552,9 @@ StructureBounds getStructureBounds(FEATURE_STATS const *stats, Vector2i pos)
const Vector2i map = map_coord(pos) - size / 2;
return StructureBounds(map, size);
}

FeatureContainer& GlobalFeatureContainer()
{
static FeatureContainer instance;
return instance;
}
7 changes: 7 additions & 0 deletions src/feature.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

#include "objectdef.h"
#include "lib/framework/wzconfig.h"
#include "lib/framework/paged_entity_container.h"

/* The statistics for the features */
extern std::vector<FEATURE_STATS> asFeatureStats;
Expand Down Expand Up @@ -82,4 +83,10 @@ static inline FEATURE const *castFeature(SIMPLE_OBJECT const *psObject)
return isFeature(psObject) ? (FEATURE const *)psObject : (FEATURE const *)nullptr;
}

// Split the feature storage into pages containing 128 features, disable slot reuse
// to guard against memory-related issues when some object pointers won't get
// updated properly, e.g. when transitioning between the base and offworld missions.
using FeatureContainer = PagedEntityContainer<FEATURE, 128, false>;
FeatureContainer& GlobalFeatureContainer();

#endif // __INCLUDED_SRC_FEATURE_H__
33 changes: 28 additions & 5 deletions src/objmem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,9 @@ bool objmemInitialise()
/* Release the object heaps */
void objmemShutdown()
{
auto& droidContainer = GlobalDroidContainer();
droidContainer.clear();
auto& structContainer = GlobalStructContainer();
structContainer.clear();
GlobalDroidContainer().clear();
GlobalStructContainer().clear();
GlobalFeatureContainer().clear();
}

// Check that psVictim is not referred to by any other object in the game. We can dump out some extra data in debug builds that help track down sources of dangling pointer errors.
Expand Down Expand Up @@ -244,6 +243,14 @@ bool objmemDestroy(BASE_OBJECT *psObj, bool checkRefs)
ASSERT(it != structContainer.end(), "Structure not found in the global container!");
structContainer.erase(it);
}
else if (psObj->type == OBJ_FEATURE)
{
// Features are managed by a separate feature container.
auto& featureContainer = GlobalFeatureContainer();
auto it = featureContainer.find(*static_cast<FEATURE*>(psObj));
ASSERT(it != featureContainer.end(), "Feature not found in the global container!");
featureContainer.erase(it);
}
else
{
delete psObj;
Expand Down Expand Up @@ -497,6 +504,22 @@ struct GlobalEntityContainerTraits<STRUCTURE>
}
};

template <>
struct GlobalEntityContainerTraits<FEATURE>
{
using StorageType = FeatureContainer;

static FeatureContainer& getContainer()
{
return GlobalFeatureContainer();
}

static const char* entityName()
{
return "Feature";
}
};

template <typename Entity, unsigned PlayerCount>
static void freeAllEntitiesImpl(PerPlayerObjectLists<Entity, PlayerCount>& entityLists)
{
Expand Down Expand Up @@ -692,7 +715,7 @@ void killFeature(FEATURE *psDel)
/* Remove all features */
void freeAllFeatures()
{
releaseAllObjectsInList(apsFeatureLists);
freeAllEntitiesImpl<FEATURE, MAX_PLAYERS>(apsFeatureLists);
}

/************************** FLAG_POSITION ********************************/
Expand Down

0 comments on commit 62cea65

Please sign in to comment.