Browse Source

More Entity Component System Work, Flatbuffers Experiments

- I tested writing Flatbuffers binaries out to JSON, which is cool. 
- I made some changes to the ECS interface, added EntityList helpers, and implemented a simple pooled component manager. 
- Note that ObjectPool is unfinished, and may be discarded (I'm having troubles coming up with a way to do handles).
combatComponentRefactor
Macoy Madson 7 years ago
parent
commit
7509ff734c
  1. 5
      Jamrules
  2. 11
      src/entityComponentSystem/EntityComponentManager.cpp
  3. 17
      src/entityComponentSystem/EntityComponentManager.hpp
  4. 49
      src/entityComponentSystem/EntityTypes.cpp
  5. 9
      src/entityComponentSystem/EntityTypes.hpp
  6. 160
      src/entityComponentSystem/PooledComponentManager.hpp
  7. 6
      src/experiments/flatbuffers/Jamfile
  8. BIN
      src/experiments/flatbuffers/SavedHelloForWrite.bin
  9. 20
      src/experiments/flatbuffers/testFlatbuffers.cpp
  10. 102
      src/experiments/flatbuffers/testFlatbuffers_WriteOut.cpp
  11. 94
      src/unitTesting/EntityComponentSystem_test.cpp
  12. 4
      src/unitTesting/Jamfile
  13. 29
      src/unitTesting/ObjectPoolTest.cpp
  14. 9
      src/util/FragmentedPool.hpp
  15. 107
      src/util/ObjectPool.hpp

5
Jamrules

@ -7,4 +7,7 @@ LINK = g++ ;
C++FLAGS = -std=c++11 -fPIC -g ;
OBJECTC++FLAGS = -std=c++11 ;
HDRS = thirdParty/flatbuffers/include ;
HDRS = thirdParty/flatbuffers/include ;
# TODO: add project-specific filetype rules for things like flatbuffer .json compilation (see "UserObject rule":
# https://swarm.workshop.perforce.com/view/guest/perforce_software/jam/src/Jamfile.html ) ?

11
src/entityComponentSystem/EntityComponentManager.cpp

@ -14,7 +14,7 @@ EntityComponentManager::~EntityComponentManager()
// Sets the ComponentManager for a ComponentType. Returns false if there is already a manager
// for that type (it will not be set)
bool EntityComponentManager::AddComponentManagerOfType(ComponentType type,
ComponentManager *manager)
ComponentManager *manager)
{
if (manager)
{
@ -27,6 +27,11 @@ bool EntityComponentManager::AddComponentManagerOfType(ComponentType type,
return false;
}
bool EntityComponentManager::AddComponentManager(ComponentManager *manager)
{
return AddComponentManagerOfType(manager->GetType(), manager);
}
// Returns the ComponentManager assigned to the provided type, or nullptr if there isn't one
// assigned. If your ComponentManager needs another, it is preferable to get its dependencies
// directly (i.e. passed in during a initialize() function)
@ -54,7 +59,7 @@ void EntityComponentManager::GetNewEntities(EntityList &list, int count)
void EntityComponentManager::MarkDestroyEntities(EntityList &entities)
{
EntitiesPendingDestruction.insert(EntitiesPendingDestruction.end(), entities.begin(),
entities.end());
entities.end());
}
void EntityComponentManager::UnsubscribeEntitiesFromAllManagers(EntityList &entitiesToUnsubscribe)
@ -63,7 +68,7 @@ void EntityComponentManager::UnsubscribeEntitiesFromAllManagers(EntityList &enti
// Some component managers will not actually have the Entity being destroyed subscribed, but
// that's fine
for (EntityComponentManager::ComponentManagerMapIterator it = ComponentManagers.begin();
it != ComponentManagers.end(); ++it)
it != ComponentManagers.end(); ++it)
{
ComponentManager *currentComponentManager = it->second;

17
src/entityComponentSystem/EntityComponentManager.hpp

@ -6,6 +6,16 @@
#include "EntityTypes.hpp"
#include "ComponentManager.hpp"
/* --EntityComponentManager--
EntityComponentManager is intended to be a very minimal managing class for the Entity Component
System. Its primary task is to facilitate the creation and destruction of Entities.
Note that EntityComponentManager does not track nor handle subscribing of entities to
ComponentManagers - that is handled directly by ComponentManagers. This means that when an Entity is
destroyed, the EntityComponentManager must send the entire list of all unsubscribing entities to
every ComponentManager which is registered with this ECM. This is less than optimal, but
permissable.
*/
class EntityComponentManager
{
private:
@ -31,16 +41,17 @@ public:
// Sets the ComponentManager for a ComponentType. Returns false if there is already a manager
// for that type (it will not be set)
// Maybe this shouldn't take a type - just get it from the manager?
bool AddComponentManagerOfType(ComponentType type, ComponentManager *manager);
bool AddComponentManager(ComponentManager *manager);
// Returns the ComponentManager assigned to the provided type, or nullptr if there isn't one
// assigned. If your ComponentManager needs another, it is preferable to get its dependencies
// directly (i.e. passed in during a initialize() function) rather than using this function
ComponentManager *GetComponentManagerForType(ComponentType type);
// Creates the given number of entities and adds them to the ActiveEntities list as well as the
// provided list
// Creates the given number of entities, adds them to the ActiveEntities list, and appends them
// to the provided list
void GetNewEntities(EntityList &list, int count);
// Mark Entities for destruction. They are not destroyed immediately; rather, they is destroyed

49
src/entityComponentSystem/EntityTypes.cpp

@ -9,6 +9,11 @@ bool EntityComparator(Entity a, Entity b)
return a < b;
}
void EntityListAppendList(EntityList& list, const EntityList& listToAdd)
{
list.insert(list.begin(), listToAdd.begin(), listToAdd.end());
}
void EntityListSort(EntityList& list)
{
std::sort(list.begin(), list.end(), EntityComparator);
@ -39,11 +44,11 @@ void EntityListSortAndRemoveDuplicates(EntityList& list)
}
// Remove all entities from suspectList which are already in list
void EntityListRemoveNonUniqueEntitiesInSuspect(EntityList& list, EntityList& suspectList)
void EntityListRemoveNonUniqueEntitiesInSuspect(const EntityList& list, EntityList& suspectList)
{
for (EntityListIterator it = list.begin(); it != list.end(); ++it)
for (EntityListConstIterator it = list.begin(); it != list.end(); ++it)
{
Entity currentEntity = (*it);
const Entity currentEntity = (*it);
for (EntityListIterator sIt = suspectList.begin(); sIt != suspectList.end();)
{
Entity suspectEntity = (*sIt);
@ -59,4 +64,42 @@ void EntityListRemoveNonUniqueEntitiesInSuspect(EntityList& list, EntityList& su
++sIt;
}
}
}
// Remove all entities from suspectList which are not already in list
void EntityListRemoveUniqueEntitiesInSuspect(const EntityList& list, EntityList& suspectList)
{
for (EntityListIterator it = suspectList.begin(); it != suspectList.end();)
{
bool found = false;
Entity suspectEntity = (*it);
for (EntityListConstIterator sIt = list.begin(); sIt != list.end();)
{
const Entity currentEntity = (*sIt);
if (currentEntity == suspectEntity)
{
found = true;
break;
}
else
++sIt;
}
if (found)
it++;
else
suspectList.erase(it);
}
}
bool EntityListFindEntity(EntityList& list, Entity entity)
{
for (EntityListIterator it = list.begin(); it != list.end(); ++it)
{
Entity currentEntity = (*it);
if (currentEntity == entity)
return true;
}
return false;
}

9
src/entityComponentSystem/EntityTypes.hpp

@ -16,12 +16,19 @@ typedef std::vector<Entity>::iterator EntityListIterator;
typedef std::vector<Entity>::const_iterator EntityListConstIterator;
typedef std::vector<Entity>::reverse_iterator EntityListReverseIterator;
void EntityListAppendList(EntityList& list, const EntityList& listToAdd);
void EntityListSort(EntityList& list);
void EntityListSortAndRemoveDuplicates(EntityList& list);
// Remove all entities from suspectList which are already in list
// This is bad. At least the name, possibly also the function itself.
void EntityListRemoveNonUniqueEntitiesInSuspect(EntityList& list, EntityList& suspectList);
void EntityListRemoveNonUniqueEntitiesInSuspect(const EntityList& list, EntityList& suspectList);
void EntityListRemoveUniqueEntitiesInSuspect(const EntityList& list, EntityList& suspectList);
// Linear search for entity. I'll eventually add a binary search function if it can be assumed that
// the list is sorted
bool EntityListFindEntity(EntityList& list, Entity entity);
// This should probably become an enum at some point.
typedef unsigned int ComponentType;

160
src/entityComponentSystem/PooledComponentManager.hpp

@ -1,58 +1,202 @@
#ifndef POOLEDCOMPONENTMANAGER_H__
#define POOLEDCOMPONENTMANAGER_H__
// TODO: Handle full pool better (will be done when FragmentedPool is replaced)
#include <cassert>
#include <vector>
#include <iostream>
// TODO: Replace FragmentedPool with a better pool
#include "../util/FragmentedPool.hpp"
#include "EntityTypes.hpp"
#include "ComponentManager.hpp"
/* --PooledComponentManager--
PooledComponentManager is a general purpose ComponentManager that assumes you're managing your
PooledComponentManager is a general purpose PooledComponentManager that assumes you're managing your
components in a standard way.
You should only use PooledComponentManager if it is a good fit for your manager (i.e. you don't need
to do anything special in Subscribe/Unsubscribe etc.) - in other words, use the right tool for the
job. Writing a ComponentManager from scratch is not difficult. Read through the assumptions. If your
job. Writing a PooledComponentManager from scratch is not difficult. Read through the assumptions.
If your
use case doesn't match all of those assumptions, you should write a specialized manager instead.
Assumptions
------------
All components have identical data.
Whatever subscribes entities knows all of the data needed to create a Component.
Whatever subscribes entities knows all of the data needed to create a PooledComponent.
All components are treated the exact same way. For example, if you have a field
GiveMeSpecialTreatment on your component, it would be better to remove that field and instead have
all components desiring special treatment in a SpecialTreatment list. This makes it so you don't
have to have complicated branching deep in your Component update loop to give special treatment. If
have to have complicated branching deep in your PooledComponent update loop to give special
treatment. If
this is the case, you probably shouldn't be using PooledComponentManager.
Whatever your data is can be copied via the assignment operator '='
*/
template <class T>
struct Component
struct PooledComponent
{
Entity entity;
T data;
void operator=(const PooledComponent<T>& component)
{
entity = component.entity;
data = component.data;
}
};
template <class T>
class PooledComponentManager : public ComponentManager
{
private:
// This list is used only to quickly check whether an entity is subscribed; data should actually
// be stored in PooledComponents
EntityList Subscribers;
// TODO: Replace FragmentedPool with a better pool
FragmentedPool<Component<T> > Components;
FragmentedPool<PooledComponent<T> > PooledComponents;
protected:
typedef int FragmentedPoolIterator;
const int NULL_POOL_ITERATOR = -1;
// Return the index of the first active data, or NULL_POOL_ITERATOR if the pool is inactive
PooledComponent<T>* ActivePoolBegin(FragmentedPoolIterator& it)
{
for (int i = 0; i < PooledComponents.GetPoolSize(); i++)
{
FragmentedPoolData<PooledComponent<T> >* currentPooledComponent =
PooledComponents.GetActiveDataAtIndex(i);
if (currentPooledComponent)
{
it = i;
return &currentPooledComponent->data;
}
}
it = NULL_POOL_ITERATOR;
return nullptr;
}
// Increments iterator until an active component is found.
// Returns nullptr at end of list
PooledComponent<T>* GetNextActivePooledComponent(FragmentedPoolIterator& it)
{
it++;
for (; it < PooledComponents.GetPoolSize(); it++)
{
FragmentedPoolData<PooledComponent<T> >* currentPooledComponent =
PooledComponents.GetActiveDataAtIndex(it);
if (currentPooledComponent)
return &currentPooledComponent->data;
}
it = NULL_POOL_ITERATOR;
return nullptr;
}
PooledComponent<T>* GetComponent(FragmentedPoolIterator& it)
{
FragmentedPoolData<PooledComponent<T> >* pooledComponent =
PooledComponents.GetActiveDataAtIndex(it);
if (pooledComponent)
return &pooledComponent->data;
return nullptr;
}
// This function is executed once for each entity which is being subscribed
// This should only be used if your manager must do something per-component - otherwise, you
// should write a custom solution
// The component is already in the pool.
virtual void SubscribeEntity(PooledComponent<T>& component)
{
}
// Do whatever your custom manager does for unsubscribing here. Don't implement if you don't
// have to
virtual void UnsubscribeEntity(PooledComponent<T>& component)
{
}
public:
PooledComponentManager(int poolSize) : PooledComponents(poolSize)
{
}
virtual ~PooledComponentManager()
{
Reset();
}
// If the entity is already subscribed, the input component will be tossed out
void SubscribeEntities(const std::vector<PooledComponent<T> >& components)
{
for (typename std::vector<PooledComponent<T> >::const_iterator it = components.begin();
it != components.end(); ++it)
{
const PooledComponent<T> currentPooledComponent = (*it);
// Make sure the Entity isn't already subscribed
if (EntityListFindEntity(Subscribers, currentPooledComponent.entity))
continue;
FragmentedPoolData<PooledComponent<T> >* newPooledComponentPooled =
PooledComponents.GetNewData();
// Pool is full!
// TODO: handle this elegantly
assert(newPooledComponentPooled);
newPooledComponentPooled->data = currentPooledComponent;
Subscribers.push_back(currentPooledComponent.entity);
SubscribeEntity(newPooledComponentPooled->data);
}
}
virtual void SubscribeEntities(Component<T>& components)
virtual void UnsubscribeEntities(const EntityList& entities)
{
EntityList entitiesToUnsubscribe;
EntityListAppendList(entitiesToUnsubscribe, entities);
// Ensure that we only unsubscribe entities which are actually Subscribers
EntityListRemoveUniqueEntitiesInSuspect(Subscribers, entitiesToUnsubscribe);
for (EntityListConstIterator it = entitiesToUnsubscribe.begin();
it != entitiesToUnsubscribe.end(); ++it)
{
Entity currentEntity = (*it);
for (int i = 0; i < PooledComponents.GetPoolSize(); i++)
{
FragmentedPoolData<PooledComponent<T> >* currentPooledComponent =
PooledComponents.GetActiveDataAtIndex(i);
if (currentPooledComponent && currentPooledComponent->data.entity == currentEntity)
{
UnsubscribeEntity(currentPooledComponent->data);
PooledComponents.RemoveData(currentPooledComponent);
}
}
}
// Remove all entities which were unsubscribed from the Subscribers list
EntityListRemoveNonUniqueEntitiesInSuspect(entitiesToUnsubscribe, Subscribers);
}
virtual void UnsubscribeEntities(EntityList& entities)
void Reset(void)
{
PooledComponents.Clear();
Subscribers.clear();
}
};

6
src/experiments/flatbuffers/Jamfile

@ -2,4 +2,10 @@ SubDir . src experiments flatbuffers ;
Main testFlatbuffers : testFlatbuffers.cpp ;
Main testFlatbuffers_write : testFlatbuffers_WriteOut.cpp ;
LinkLibraries testFlatbuffers_write : ./thirdParty/flatbuffers/libflatbuffers.a ;
# Note that we're not moving testFlatbuffers_write to bin because it's
# dependent on SavedHelloForWrite.bin (this is how lazy I am)
MakeLocate testFlatbuffers : bin ;

BIN
src/experiments/flatbuffers/SavedHelloForWrite.bin

Binary file not shown.

20
src/experiments/flatbuffers/testFlatbuffers.cpp

@ -13,7 +13,7 @@ void printHello(const Galavant::Test::Hello *hello)
{
std::string message = hello->message() ? hello->message()->message()->str() : "";
std::cout << "From buffer:\n\tstatus: " << Galavant::Test::EnumNameFuckYou(hello->status())
<< " value: " << hello->value() << " message: \"" << message << "\"\n";
<< " value: " << hello->value() << " message: \"" << message << "\"\n";
}
}
@ -64,18 +64,18 @@ char *readBuffer()
}
void createPlaceholderHelloArray(std::vector<flatbuffers::Offset<Galavant::Test::Hello> > &array,
flatbuffers::FlatBufferBuilder &builder, int count)
flatbuffers::FlatBufferBuilder &builder, int count)
{
for (int i = 0; i < count; i++)
{
flatbuffers::Offset<flatbuffers::String> messageString =
builder.CreateString("This is the message!!!1 hello codegomad, mckenna and 123ran");
builder.CreateString("This is the message!!!1 hello codegomad, mckenna and 123ran");
flatbuffers::Offset<Galavant::Test::HelloReply> message =
Galavant::Test::CreateHelloReply(builder, messageString);
Galavant::Test::CreateHelloReply(builder, messageString);
flatbuffers::Offset<Galavant::Test::Hello> testHello = Galavant::Test::CreateHello(
builder, Galavant::Test::FuckYou::FuckYou_FuckYouToo, i, message);
builder, Galavant::Test::FuckYou::FuckYou_FuckYouToo, i, message);
array.push_back(testHello);
}
@ -86,13 +86,13 @@ void testHellos()
flatbuffers::FlatBufferBuilder builder;
flatbuffers::Offset<flatbuffers::String> messageString =
builder.CreateString("This is the message!!!1 hello codegomad and 123ran");
builder.CreateString("This is the message!!!1 hello codegomad and 123ran");
flatbuffers::Offset<Galavant::Test::HelloReply> message =
Galavant::Test::CreateHelloReply(builder, messageString);
Galavant::Test::CreateHelloReply(builder, messageString);
flatbuffers::Offset<Galavant::Test::Hello> testHello = Galavant::Test::CreateHello(
builder, Galavant::Test::FuckYou::FuckYou_FuckYouToo, 58008, message);
builder, Galavant::Test::FuckYou::FuckYou_FuckYouToo, 58008, message);
builder.Finish(testHello);
@ -119,7 +119,7 @@ void testHelloDict()
createPlaceholderHelloArray(helloArray, builder, 10000);
flatbuffers::Offset<Galavant::Test::HelloDict> helloDict =
Galavant::Test::CreateHelloDictDirect(builder, &helloArray);
Galavant::Test::CreateHelloDictDirect(builder, &helloArray);
builder.Finish(helloDict);
@ -133,7 +133,7 @@ void testHelloDict()
if (readInHelloDict)
{
const flatbuffers::Vector<flatbuffers::Offset<Galavant::Test::Hello> > *helloArray =
readInHelloDict->helloArray();
readInHelloDict->helloArray();
if (helloArray)
{

102
src/experiments/flatbuffers/testFlatbuffers_WriteOut.cpp

@ -0,0 +1,102 @@
#include <vector>
#include <iostream>
#include <fstream>
#include "flatbuffers/idl.h"
#include "bogusSchema_generated.h"
char *readSchema(const char *filename)
{
std::ifstream inputFile;
std::cout << "Reading in " << filename << "...\n";
// open with std::ios::ate so buffer pointer starts at end. tellg() tells us the size,
// then we move the pointer back to the start and read in the entire file
// This sucks and I should be ashamed to have written it
inputFile.open(filename, std::ios::ate);
if (inputFile.is_open())
{
std::streampos size = inputFile.tellg();
char *memblock = new char[size];
inputFile.seekg(0, std::ios::beg);
inputFile.read(memblock, size);
inputFile.close();
std::cout << "Successfully read in " << filename << "\n\t(" << size << " bytes)\n";
return memblock;
}
else
std::cout << "Failed to read in " << filename << "\n";
return nullptr;
}
char *readBinary(const char *filename)
{
std::ifstream inputFile;
std::cout << "Reading in " << filename << "...\n";
// open with std::ios::ate so buffer pointer starts at end. tellg() tells us the size,
// then we move the pointer back to the start and read in the entire file
// This sucks and I should be ashamed to have written it
inputFile.open(filename, std::ios::binary | std::ios::ate);
if (inputFile.is_open())
{
std::streampos size = inputFile.tellg();
char *memblock = new char[size];
inputFile.seekg(0, std::ios::beg);
inputFile.read(memblock, size);
inputFile.close();
std::cout << "Successfully read in " << filename << "\n\t(" << size << " bytes)\n";
return memblock;
}
else
std::cout << "Failed to read in " << filename << "\n";
return nullptr;
}
void testFlatbufferToJSON(void)
{
const char *outputFilename = "Output.json";
const char *flatbufferFilename = "SavedHelloForWrite.bin";
const char *schemaFilename = "bogusSchema.flb";
const char *includePaths = {
"/home/macoy/Development/code/repositories/galavant/src/experiments/flatbuffers"};
char *schemaBlock = readSchema(schemaFilename);
char *memblock = readBinary(flatbufferFilename);
if (memblock && schemaBlock)
{
const Galavant::Test::Hello *readInHello = Galavant::Test::GetHello(memblock);
// printHello(readInHello);
flatbuffers::Parser parser;
parser.Parse(schemaBlock, &includePaths, schemaFilename);
std::string outputString = "";
flatbuffers::GenerateText(parser, memblock, &outputString);
std::cout << outputString << "\n";
//std::cout << "Generating text file...\n";
//flatbuffers::GenerateTextFile(parser, memblock, outputFilename);
delete memblock;
delete schemaBlock;
}
}
int main()
{
std::cout << "Test Flatbuffers\n";
testFlatbufferToJSON();
return 1;
}

94
src/unitTesting/EntityComponentSystem_test.cpp

@ -4,6 +4,7 @@
#include "../entityComponentSystem/EntityTypes.hpp"
#include "../entityComponentSystem/EntityComponentManager.hpp"
#include "../entityComponentSystem/ComponentManager.hpp"
#include "../entityComponentSystem/PooledComponentManager.hpp"
void TestEntityLists(void)
{
@ -41,7 +42,7 @@ void TestEntityCreationAndDestruction(void)
{
EntityList unsubscribeList;
unsubscribeList.insert(unsubscribeList.end(), Subscribers.begin(),
Subscribers.end());
Subscribers.end());
UnsubscribeEntities(unsubscribeList);
}
};
@ -50,13 +51,13 @@ void TestEntityCreationAndDestruction(void)
{
EntityList entitiesToSubscribe;
entitiesToSubscribe.insert(entitiesToSubscribe.begin(), entities.begin(),
entities.end());
entities.end());
// Ensure that we don't resubscribe any entities by removing anything in entities which
// is already in Subscribers
EntityListRemoveNonUniqueEntitiesInSuspect(Subscribers, entitiesToSubscribe);
for (EntityListIterator it = entitiesToSubscribe.begin();
it != entitiesToSubscribe.end(); ++it)
it != entitiesToSubscribe.end(); ++it)
{
Entity currentEntity = (*it);
@ -112,7 +113,7 @@ void TestEntityCreationAndDestruction(void)
std::cout << "Testing Entity Creation and Destruction...\n";
entityComponentManager.AddComponentManagerOfType(testComponentManager.GetType(),
&testComponentManager);
&testComponentManager);
EntityList newEntities;
@ -148,10 +149,95 @@ void TestEntityCreationAndDestruction(void)
std::cout << "Done\n\n";
}
void TestEntityComponentTypes(void)
{
struct TestComponent
{
int value;
};
class TestPooledComponentManager : public PooledComponentManager<TestComponent>
{
public:
TestPooledComponentManager() : PooledComponentManager<TestComponent>(1000)
{
}
virtual ~TestPooledComponentManager()
{
}
virtual void Update(float ms)
{
std::cout << "\tUpdating TestPooledComponentManager...\n";
PooledComponentManager::FragmentedPoolIterator it =
PooledComponentManager::NULL_POOL_ITERATOR;
for (PooledComponent<TestComponent>* currentComponent = ActivePoolBegin(it);
currentComponent != nullptr && it != PooledComponentManager::NULL_POOL_ITERATOR;
currentComponent = GetNextActivePooledComponent(it))
{
std::cout << "\t\t" << currentComponent->entity
<< " value: " << currentComponent->data.value << "\n";
}
std::cout << "\tDone\n";
}
virtual void UnsubscribeEntity(PooledComponent<TestComponent>& component)
{
std::cout << "\t\tUnsubscribing " << component.entity << "\n";
}
};
std::cout << "\nTesting Entity Component Types...\n";
EntityList newEntities;
EntityComponentManager entityComponentManager;
TestPooledComponentManager testComponentManager;
entityComponentManager.GetNewEntities(newEntities, 10);
// Simulate a game loop (poorly)
EntityList createList;
for (int i = 0; i < 100; i++)
{
testComponentManager.Update(i);
// Do some weird creation/destruction of entities
if (rand() % 4 && !createList.empty())
{
EntityList destroyList;
destroyList.push_back(createList[i]);
entityComponentManager.MarkDestroyEntities(destroyList);
}
else
{
std::vector<PooledComponent<TestComponent> > newComponents;
// Note that I'm resubscribing entities here. The ComponentManager needs to know how to
// handle this (maybe by using EntityListRemoveNonUniqueEntitiesInSuspect() etc.)
entityComponentManager.GetNewEntities(createList, rand() % 10);
newComponents.resize(createList.size());
int i = 0;
for (EntityListIterator it = createList.begin(); it != createList.end(); ++it)
{
PooledComponent<TestComponent>* newComponent = &newComponents[i++];
newComponent->entity = (*it);
newComponent->data.value = rand() % 10000;
}
std::cout << "\tAttempting to subscribe " << newComponents.size()
<< " components (some duplicates)\n";
testComponentManager.SubscribeEntities(newComponents);
}
entityComponentManager.DestroyEntitiesPendingDestruction();
}
std::cout << "Done\n\n";
}
int main()
{
TestEntityLists();
TestEntityCreationAndDestruction();
TestEntityComponentTypes();
return 1;
}

4
src/unitTesting/Jamfile

@ -4,8 +4,10 @@ Main objectComponentTest : ObjectComponent_test.cpp ../objectComponent/Component
Main entityComponentTest : EntityComponentSystem_test.cpp ;
Main objectPoolTest : ObjectPoolTest.cpp ;
LinkLibraries entityComponentTest : libGalaEntityComponent ;
MakeLocate objectComponentTest entityComponentTest : bin ;
MakeLocate objectComponentTest entityComponentTest objectPoolTest : bin ;
SubInclude . src entityComponentSystem ;

29
src/unitTesting/ObjectPoolTest.cpp

@ -0,0 +1,29 @@
#include <iostream>
#include "../util/ObjectPool.hpp"
int main()
{
ObjectPool<int> testPool(100);
for (int i = 0; i < testPool.GetSize(); i++)
{
int* newData = testPool.GetNewData();
if (newData)
*newData = i;
else
break;
}
for (auto data:testPool)
{
std::cout << data << "\n";
}
// for (PoolContainer<int>::iterator it = testPool.begin(); it != testPool.end(); ++it)
// {
// std::cout << (*it) << "\n";
// }
return 1;
}

9
src/util/FragmentedPool.hpp

@ -50,7 +50,7 @@ private:
unsigned int totalActiveData; // The number of active data in the pool
// Prepare the data pointers as a linked list
void resetPool()
void ResetPool()
{
// Iterate through all data, resetting pointers and isActive status
firstFreeData = &pool[0];
@ -80,7 +80,7 @@ public:
firstFreeData = NULL;
firstUsedData = NULL;
pool.resize(size);
resetPool();
ResetPool();
}
// Resets the pool. Note that this isn't cheap because it must fix up the linked list
// that underlies the FragmentedPool structure
@ -159,5 +159,10 @@ public:
{
return totalActiveData;
}
int GetPoolSize()
{
return size;
}
};
#endif

107
src/util/ObjectPool.hpp

@ -0,0 +1,107 @@
#ifndef OBJECTPOOL_H__
#define OBJECTPOOL_H__
#include <vector>
typedef unsigned int ObjectPoolHandle;
template <class T>
class ObjectPool
{
public:
template <class R>
using PoolContainer = std::vector<R>;
private:
typedef unsigned int PoolIndex;
// Provide a layer of indirection so ObjectPool can move data as it pleases
// Fuck - what if you run out of handles? Don't allow that? What if pool resizes? Multiple
// pools? How would handles work?
typedef std::vector<PoolIndex> HandleTable;
PoolContainer<T> Pool;
HandleTable Handles;
unsigned int NextFreeData;
ObjectPoolHandle CreateHandleInternal(PoolIndex index)
{
Handles.push_back(index);
return Handles.size() - 1;
}
public:
ObjectPool(unsigned int size)
{
Pool.resize(size);
// Somewhat arbitrary size for handles
Handles.resize(size);
NextFreeData = 0;
}
typename PoolContainer<T>::iterator begin(void)
{
return Pool.begin();
}
typename PoolContainer<T>::iterator end(void)
{
// Only return the range of data which are actually active in the pool
return Pool.begin() + NextFreeData;
}
ObjectPoolHandle GetHandleFromIterator(typename PoolContainer<T>::iterator& it)
{
return 0; // TODO
}
unsigned int GetSize(void)
{
return Pool.size();
}
unsigned int GetNumActiveElements(void)
{
// Because NextFreeData is the index of the free data nearest to the last active data, it is
// equivalent to the number of active data
return NextFreeData;
}
T* GetNewData(void)
{
T* newData = GetUnsafe(NextFreeData);
NextFreeData++;
return newData;
}
// Use if you know you want to have the handle afterwards
T* GetNewDataWithHandle(ObjectPoolHandle& handle)
{
}
// Returns nullptr if the data at handle isn't active
// Note that this pointer should NOT be relied upon if you are removing other data
T* Get(ObjectPoolHandle handle)
{
if (handle < NextFreeData)
return GetUnsafe(handle);
return nullptr;
}
// Don't perform bounds checking and don't make sure the data is active
inline T* GetUnsafe(ObjectPoolHandle handle)
{
return &Pool[handle];
}
// Remove the element and replace its space with the element at the end of the pool (swap)
void Remove(ObjectPoolHandle handle)
{
}
void Remove(typename PoolContainer<T>::iterator& it)
{
}
};
#endif /* end of include guard: OBJECTPOOL_H__ */
Loading…
Cancel
Save