Iterators in event manager for game becoming invalidated upon unregister - c++

I am writing an EventManager class for a game I am making for school. It utilizes a double queue system for events and holds an unordered_map consisting of events types and vectors of ID's registered to respond to the particular events. The problem I am having is that certain events (like when you click the play button for the main menu)may cause an actor to unregister events. While this is intentional it causes problems with the iterators which is not intentional. In the example where it switches from the main menu to the game it destroys all the actors(buttons and such) that are part of the main menu. These, in turn, unregister themselves with the event manager which deletes their id from the corresponding vector stored in the unordered_map and invalidates the iterator throwing an exception at the beginning of the loop. As such no event can cause an actor to unregister anything. This is undesirable because some trigger objects in the game may be one time triggers at which point they need to unregister themselves from receiving more events. Any help or ideas would be greatly appreciated.
void EventManager::Flush()
{
Event* current;
while (m_eventList[m_current].size() > 0)
{
current = m_eventList[m_current].front();
m_eventList[m_current].pop_front();
unordered_map<string, vector<IDTYPE>>::iterator it = m_registeredEvents.find(current->GetType());
if (it == m_registeredEvents.end())
continue;
vector<IDTYPE>* toProcessAct = &(it->second);
vector<IDTYPE>::iterator actIt = toProcessAct->begin();
while (actIt != toProcessAct->end()) //this becomes invalid
{
Actor* temp = ACTORS->GetActor(*actIt);
if (temp == NULL)
actIt = toProcessAct->erase(actIt);
else
{
actIt++;
temp->Process(current); //Because this may unregister events
}
}
delete current; current = 0;
}
Swap();
}
void EventManager::UnregisterEvent(string Event, IDTYPE actor)
{
unordered_map<string, vector<IDTYPE>>::iterator it = m_registeredEvents.find(Event);
//Found
if (it != m_registeredEvents.end())
{
//Find if actor is already registered
vector<IDTYPE>::iterator actIt = it->second.begin();
while (actIt != it->second.end())
{
if (actor == *actIt)
{
it->second.erase(actIt);
return;
}
actIt++;
}
}
}

This is a good way to shoot yourself in the foot
Event* current; // not the issue here but you should always initialize your variables.
while (m_eventList[m_current].size() > 0) {
current = m_eventList[m_current].front(); // current points the the first element
m_eventList[m_current].pop_front(); // now the first element gets destroyed
unordered_map<string, vector<IDTYPE>>::iterator it = m_registeredEvents.find(current->GetType()); // its pure coincident if this works.
pop_front() destroys the front element making the pointer invalid and you enter undefined behaviour. Make a copy of the record instead, then you also don't have problems with destroying it.
Further all iterators to elements after the a delete are invalid, so if your code saves iterators as a state you will get hurt, just presume all deletes makes all iterators to that or related containers invalid to be safe.
If you are running a multi-threaded program you have the further problem that other threads could change the content, use at least a mutex to protect it.
OK, now for what you actually asked for:
while (actIt != toProcessAct->end()) { // end() should update but actIt doesn't.
Actor* temp = ACTORS->GetActor(*actIt);
if (temp == NULL)
actIt = toProcessAct->erase(actIt);
else {
actIt = temp->Process(actIt, current); // make Process return the new valid It
}
}
delete current; // would fail as there is a continue higher up!!!
current = 0; // if using C++11 use nullptr instead else NULL
Make Process return the new valid Iterator or toProcessAct->end() if no more are valid after actIt.

One solution can be to iterate over a copy of event handlers vector, that is instead of
vector<IDTYPE>* toProcessAct = &(it->second);
write
vector<IDTYPE> toProcessAct = it->second;
(and make corresponding changes for the code to compile).
Also probably it will be faster if you make a copy of the whole m_registeredEvents in the start of Flush(), so you won't have to copy vectors several times.
Another solution would be to make unregistration deferred, that is UnregisterEvent() instead of executing instantly would enqueue the unregistration request into separate queue and execute it later, for example at the end of Flush().

Related

Machine Dependent Corrupted Double Linked List Error

I am making a game for my software engineering class and my group is experiencing an odd issue. We create items as a doubley linked list in the level and as as soon as we touch an item we call delete on that item. But when I touch the item pointed to by itemHead in the linked list I get a corrupted double linked list error. For some reason the itemHead next seems to be pointing to some garbage memory.
This wouldn't be so odd if it was on every machine but it only seems to happen on some of the laptops in my group. We all compared our specs, we are all running 64bit linux mint, we all have the same version of g++, and we all did a fresh clone off of gitHub so we all have the same version of the game. And we can not seem to get why the segfault happens on only some machines.
If you would like to look at the code post your gitHub account name and we will give you access. But simply telling why this might happen should point us in the right direction to figure out how this is messing up.
Here is how we are creating the items in a circular doubley linked list, detecting the item collision with the character, and how we are deleting. in that order.
Items *createDiamond() {
Items *it = new Diamond;
it->next = itemHead;
if (itemHead != NULL)
itemHead->prev = it;
itemHead = it;
nItems++;
return it;
}
void checkItemCollision(Game * g) {
Items *item = g->itemHead;
while(item!=NULL) {
int itemY = item->getCenterY();
int itemX = item->getCenterX();
int itemH = item->getHeight();
int itemW = item->getWidth();
//top
if ( (g->player.getPosX() + g->player.getWidth() > itemX
&& g->player.getPosX() -g->player.getWidth() < itemX )
&& (g->player.getPosY() - g->player.getHeight() <= itemY )
&& (g->player.getPosY() + g->player.getHeight() >= itemY) )
{
if (item->type == "diamond") {
g->player.addToScore(500);
} else if (item->type == "heart") {
g->player.addToScore(200);
g->player.incHealth();
}
if (item->type == "dagger"){
g->player.setDagActive();
g->player.hasDaggers = true;
g->player.daggers =+ 7;
}
if (item->type == "axe"){
g->player.setAxeActive();
g->player.hasAxes = true;
g->player.axes =+ 7;
}
Items *saveItem = item;
g->deleteItem(item);
item = saveItem;
}
item = item->next;
}
}
void deleteItem(Items *node) {
if(node->prev == NULL){enter code here`
if (node->next == NULL){
itemHead = NULL;
}
else {
node->next->prev = NULL;
itemHead = node->next;
}
}
else {
if (node->next == NULL){
node->prev->next = NULL;
}
else {
node->prev->next = node->next;
node->next->prev = node->prev;
}
}
free(node);
nItems--;
node = NULL;
}
Accessing uninitialized memory may cause effects like these. Usually in C++ standard this is specified by "Unidentified behavior", so everything might happen, from working correctly to seg-faults. I suggest you start your program through memory validator.. Valgrind seems good candidate.
http://valgrind.org/docs/manual/quick-start.html
One error I see is this:
Items *it = new Diamond;
Then you do this in your deleteItem function:
free(node);
where node is of type Item*.
You are mixing allocation and deallocation routines. When you allocate with new, you must use delete, not free. You use free on functions in the malloc family (malloc, calloc, realloc, etc). By mixing up your allocation / deallocation routines, you introduced undefined behavior in your application.
That line should be this:
delete node
Now even with that change, this may not guarantee that your program will work. If you made an error like this, more than likely you may have other issues with your code.
For example, this may have several things wrong with it:
if (item->type == "diamond")
and similar lines.
If type is a std::string, then it's ok (and probably is a reason for your seg fault earlier when you called free instead of delete). If it's a char * that is dynamically allocated, then not only is this the wrong way to compare char arrays for equality, your Item type may not have followed the rule of 3, thus causing undefined behavior if used in a copy.
Another issue that could cause a problem:
Items *it = new Diamond;
Now, assuming that Diamond is derived from Items, and since you are deleting Items (even though your original code is wrong), the Items class must have a virtual destructor declared (even if it's an empty virtual destructor).
The reason is that you are (or will be) calling delete using an Items pointer, which is the base class. It is (again) undefined behavior to issue a call to delete on a base class pointer through a derived class if the base class does not have a virtual destructor. So make sure that Items has a virtual destructor.

Vector iterators incompatible... but why?

I receive the message "Vector iterators incompatible". I tried to wrap my head around it, but nothing. I did it before. Same code, just not used in a class that receives "cWORLD* World". What am I doing wrong?
Thank you!
else if (Click[2] == true)
{
//go through objects and check collision
for (vector<cOBJECT*>::iterator it = World->ReturnWorldObjects().begin(); it != World->ReturnWorldObjects().end();)
{
//Check for collision and delete object
if (PointInRect(MouseX + offX, MouseY + offY, (*it)->getrect()) == true)
{
// delete object, delete slot, pick up next slot
delete *it;
it = World->ReturnWorldObjects().erase(it);
}
else
{ // no action, move to next
++it;
}
}//for
}//else if (Click[2] == true)
Looks like ReturnWorldObjects returns copy of vector, not reference. In this case, you are trying to compare iterators of different objects, that is not checked by standard, but can be checked by checked iterators (in this case, I think it's MSVC checked iterators).
Like #ForEveR already mentioned, you possibly return a copy of a vector in the function ReturnWorldObjects(). Without seeing the declaration of this method I can only assume it's something like vector<cOBJECT*> ReturnWorldObject();
You can come around this with 2 Solutions, I think:
1. Return a reference to the vector in your World Class
const vector<cOBJECT*>& ReturnWorldObjects()
{
return m_vecWorldObjects; // Your vector here
}
2. Get one copy of that function and use that in your code
...
vector<cOBJECT*> worldObjects = World->ReturnWorldObjects();
for (vector<cOBJECT*>::iterator it = worldObjects.begin(); it != worldObjects.end(); it++)
{
...
}
...

priority_queue becomes extremely slow in debug mode

I am currently writing an A* pathfinding algorithm for a game and came across a very strange performance problem regarding priority_queue's.
I am using a typical 'open nodes list', where I store found, but yet unprocessed nodes. This is implemented as an STL priority_queue (openList) of pointers to PathNodeRecord objects, which store information about a visited node. They are sorted by the estimated cost to get there (estimatedTotalCost).
Now I noticed that whenever the pathfinding method is called, the respective AI thread gets completely stuck and takes several (~5) seconds to process the algorithm and calculate the path. Subsequently I used the VS2013 profiler to see, why and where it was taking so long.
As it turns out, the pushing to and popping from the open list (the priority_queue) takes up a very large amount of time. I am no expert in STL containers, but I never had problems with their efficiency before and this is just weird to me.
The strange thing is that this only occurs while using VS's 'Debug' build configuration. The 'Release' conf. works fine for me and the times are back to normal.
Am I doing something fundamentally wrong here or why is the priority_queue performing so badly for me? The current situation is unacceptable to me, so if I cannot resolve it soon, I will need to fall back to using a simpler container and inserting it to the right place manually.
Any pointers to why this might be occuring would be very helpful!
.
Here is a snippet of what the profiler shows me:
http://i.stack.imgur.com/gEyD3.jpg
.
Code parts:
Here is the relevant part of the pathfinding algorithm, where it loops the open list until there are no open nodes:
// set up arrays and other variables
PathNodeRecord** records = new PathNodeRecord*[graph->getNodeAmount()]; // holds records for all nodes
std::priority_queue<PathNodeRecord*> openList; // holds records of open nodes, sorted by estimated rest cost (most promising node first)
// null all record pointers
memset(records, NULL, sizeof(PathNodeRecord*) * graph->getNodeAmount());
// set up record for start node and put into open list
PathNodeRecord* startNodeRecord = new PathNodeRecord();
startNodeRecord->node = startNode;
startNodeRecord->connection = NULL;
startNodeRecord->closed = false;
startNodeRecord->costToHere = 0.f;
startNodeRecord->estimatedTotalCost = heuristic->estimate(startNode, goalNode);
records[startNode] = startNodeRecord;
openList.push(startNodeRecord);
// ### pathfind algorithm ###
// declare current node variable
PathNodeRecord* currentNode = NULL;
// loop-process open nodes
while (openList.size() > 0) // while there are open nodes to process
{
// retrieve most promising node and immediately remove from open list
currentNode = openList.top();
openList.pop(); // ### THIS IS, WHERE IT GETS STUCK
// if current node is the goal node, end the search here
if (currentNode->node == goalNode)
break;
// look at connections outgoing from this node
for (auto connection : graph->getConnections(currentNode->node))
{
// get end node
PathNodeRecord* toNodeRecord = records[connection->toNode];
if (toNodeRecord == NULL) // UNVISITED -> path record needs to be created and put into open list
{
// set up path node record
toNodeRecord = new PathNodeRecord();
toNodeRecord->node = connection->toNode;
toNodeRecord->connection = connection;
toNodeRecord->closed = false;
toNodeRecord->costToHere = currentNode->costToHere + connection->cost;
toNodeRecord->estimatedTotalCost = toNodeRecord->costToHere + heuristic->estimate(connection->toNode, goalNode);
// store in record array
records[connection->toNode] = toNodeRecord;
// put into open list for future processing
openList.push(toNodeRecord);
}
else if (!toNodeRecord->closed) // OPEN -> evaluate new cost to here and, if better, update open list entry; otherwise skip
{
float newCostToHere = currentNode->costToHere + connection->cost;
if (newCostToHere < toNodeRecord->costToHere)
{
// update record
toNodeRecord->connection = connection;
toNodeRecord->estimatedTotalCost = newCostToHere + (toNodeRecord->estimatedTotalCost - toNodeRecord->costToHere);
toNodeRecord->costToHere = newCostToHere;
}
}
else // CLOSED -> evaluate new cost to here and, if better, put back on open list and reset closed status; otherwise skip
{
float newCostToHere = currentNode->costToHere + connection->cost;
if (newCostToHere < toNodeRecord->costToHere)
{
// update record
toNodeRecord->connection = connection;
toNodeRecord->estimatedTotalCost = newCostToHere + (toNodeRecord->estimatedTotalCost - toNodeRecord->costToHere);
toNodeRecord->costToHere = newCostToHere;
// reset node to open and push into open list
toNodeRecord->closed = false;
openList.push(toNodeRecord); // ### THIS IS, WHERE IT GETS STUCK
}
}
}
// set node to closed
currentNode->closed = true;
}
Here is my PathNodeRecord with the 'less' operator overloading to enable sorting in priority_queue:
namespace AI
{
struct PathNodeRecord
{
Node node;
NodeConnection* connection;
float costToHere;
float estimatedTotalCost;
bool closed;
// overload less operator comparing estimated total cost; used by priority queue
// nodes with a higher estimated total cost are considered "less"
bool operator < (const PathNodeRecord &otherRecord)
{
return this->estimatedTotalCost > otherRecord.estimatedTotalCost;
}
};
}
std::priority_queue<PathNodeRecord*> openList
I think the reason is that you have a priority_queue of pointers to PathNodeRecord.
and there is no ordering defined for the pointers.
try changing it to std::priority_queue<PathNodeRecord> first, if it makes a difference then all you need is passing on your own comparator that knows how to compare pointers to PathNodeRecord, it will just dereference the pointers first and then do the comparison.
EDIT:
taking a wild guess about why did you get an extremely slow execution time, I think the pointers were compared based on their address. and the addresses were allocated starting from one point in memory and going up.
and so that resulted in the extreme case of your heap (the heap as in data structure not the memory part), so your heap was actually a list, (a tree where each node had one children node and so on).
and so you operation took a linear time, again just a guess.
You cannot expect a debug build to be as fast as a release optimized one, but you seems to do a lot of dynamic allocation that may interact badly with the debug runtime.
I suggest you to add _NO_DEBUG_HEAP=1 in the environment setting of the debug property page of your project.

Box2d destroy all objects in a set

I'm using cocos2d and box2d and I have up to 5 b2bodies that need to be destroyed at the same time. They are all added to a set std::set<b2Body*>row1RedArray; and added by row1RedArray.insert(spriteBody);, and i've deleted all the items in the array through iteration, but my program just crashes when I touch the screen after they are removed. Am I destroying the b2Bodies correctly?
//if that array is empty, then remove all objects from this array (row4)
if ((row4BlueArray.count == 0) && (row4.count >> 0) && (row4Removed == NO)) {
std::set<b2Body *>::iterator pos04;
for(pos04 = row4RedArray.begin(); pos04 != row4RedArray.end(); ++pos04) {
b2Body *rowBody = *pos04;
if (rowBody->GetUserData() != NULL)
{
for (CCSprite* sprite4 in row4) {
[self removeChild:sprite4 cleanup:YES];
}
//Adding the b2Body to the toDelete Set and then removing it from the set of b2Bodies
toDestroy.insert(rowBody);
row4RedArray.erase(rowBody);
row4Removed = YES;
}
}
}
std::set<b2Body *>::iterator pos2;
for(pos2 = toDestroy.begin(); pos2 != toDestroy.end(); ++pos2) {
b2Body *body = *pos2;
if (body->GetUserData() != NULL)
{
//Then removing the b2Body completely (this is all at the end of the tick method)
_world->DestroyBody(body);
}
}
Solution of Captain Obvlious in comments is obvious, but is not correct. Bodies should be destroyed by world->DestroyBody(). Its needed simple iterate through bodies, and destroy each by calling this method (and never call delete for b2Body, b2Fixture or b2Joint). There no way to destroy them all at once.
You should destroy the bodies via world->DestroyBody(), do not dispose of them in any other way. There is no way to remove them at the same time, but the removal of the body has to be done outside of the box2d world step. This means that if you iterate through your list and destroy the bodies you want next time the box2d world will be updated it will look like the bodies have been disposed of at the same time.
There are few issues on the C++ side which might cause undefined behaviour. One of them is removing from an container when iterating over it. Once you used erase on any container the iterators to that container become invalid. This is more less the code I would propose:
std::vector<b2Body *> toDestroy;
if ((row4BlueArray.count == 0) && (row4.count >> 0) && (row4Removed == NO))
{
for(std::set<b2Body *>::iterator pos04 = row4RedArray.begin(); pos04 != row4RedArray.end(); ++pos04)
{
b2Body *rowBody = *pos04;
if (rowBody->GetUserData() != NULL)
{
toDestroy.push_back(rowBody);
row4Removed = YES;
}
}
for (CCSprite* sprite4 in row4)
{
[self removeChild:sprite4 cleanup:YES];
}
}
for( std::set<b2Body *>::iterator pos2 = toDestroy.begin(); pos2 != toDestroy.end(); ++pos2)
{
row4RedArray.erase( (*body) );
_world->DestroyBody( (*body) );
}
//I have put the toDestroy vector in as a local variable so this is not needed, but if you
//are having it as a member variable etc. you need to clear it before you are going to use
//it in next loop, otherwise it will try to delete the same elements a second time.
toDestroy.clear();
I'm not sure why you used a std::set for storing the b2Body pointers. Sets are generally slower then any unordered container, such us vectors. I have also removed the if (rowBody->GetUserData() != NULL) from the second for loop as you do that check when you add the objects to the toDestroy vector, it can be assumed that the objects passed the criteria for removal.
You also remove sprites from the scene when iterating over the row4 container ( I assume from the code it is a container of some sort ), but you never clear it. It might wise to do so after you deleted all of the elements in that container, which is happening here from what I can see. You also attempt to remove those sprites multiple times, with the "for (CCSprite* sprite4 in row4)" inside the for loop iterating the bodies, so if more then one body has passed the criteria to be removed you iterate through the row4 again to remove the sprites.
I'm not sure why you are removing the bodies based on "if (rowBody->GetUserData() != NULL) criteria", but that might be something that you require in your game, which isn't apparent from the provided code.

For Looping Link List using Templates

Having used the various search engines (and the wonderful stackoverflow database), I have found some similar situations, but they are either far more complex, or not nearly as complex as what I'm trying to accomplish.
C++ List Looping
Link Error Using Templates
C++:Linked List Ordering
Pointer Address Does Not Change In A Link List
I'm trying to work with Link List and Node templates to store and print non-standard class objects (in this case, a collection of categorized contacts). Particularly, I want to print multiple objects that have the same category, out of a bunch of objects with different categories. When printing by category, I compare an sub-object tmpCategory (= "business") with the category part of a categorized contact.
But how to extract this data for comparison in int main()?
Here's what I'm thinking. I create a GetItem member function in LinkList.tem This would initialize the pointer cursor and then run a For loop until the function input matches the iteration number. At which point, GetItem returns object Type using (cursor -> data).
template <class Type>
Type LinkList<Type>::GetItem(int itemNumber) const
{
Node<Type>* cursor = NULL;
for(cursor = first;
cursor != NULL;
cursor = (cursor -> next))
{
for(int i = 0; i < used; i++)
{
if(itemNumber == i)
{
return(cursor -> data);
}
}
}
}
Here's where int main() comes in. I set my comparison object tmpCategory to a certain value (in this case, "Business"). Then, I run a For loop that iterates for cycles equal to the number of Nodes I have (as determined by a function GetUsed()). Inside that loop, I call GetItem, using the current iteration number. Theoretically, this would let the int main loop return the corresponding Node from LinkList.tem. From there, I call the category from the object inside that Node's data (which currently works), which would be compared with tmpCategory. If there's a match, the loop will print out the entire Node's data object.
tmpCategory = "Business";
for(int i = 0; i < myCategorizedContact.GetUsed(); i++)
{
if(myCategorizedContact.GetItem(i).getCategory() == tmpCategory)
cout << myCategorizedContact.GetItem(i);
}
The problem is that the currently setup (while it does run), it returns nothing at all. Upon further testing ( cout << myCategorizedContact.GetItem(i).getCategory() ), I found that it's just printing out the category of the first Node over and over again. I want the overall scheme to evaluate for every Node and print out matching data, not just spit out the same Node.
Any ideas/suggestions are greatly appreciated.
Please look at this very carefully:
template <class Type>
Type LinkList<Type>::GetItem(int itemNumber) const
{
Node<Type>* cursor = NULL;
// loop over all items in the linked list
for(cursor = first;
cursor != NULL;
cursor = (cursor -> next))
{
// for each item in the linked list, run a for-loop
// counter from 0 to (used-1).
for(int i = 0; i < used; i++)
{
// if the passed in itemNumber matches 'i' anytime
// before we reach the end of the for-loop, return
// whatever the current cursor is.
if(itemNumber == i)
{
return(cursor -> data);
}
}
}
}
You're not walking the cursor down the list itemNumber times. The very first item cursor references will kick off the inner-for-loop. The moment that loop index reaches itemNumber you return. You never advance your cursor if the linked list has at least itemNumber items in the list.. In fact, the two of them (cursor and itemNumber) are entirely unrelated in your implementation of this function. And to really add irony, since used and cursor are entirely unrelated, if used is ever less than itemNumber, it will ALWAYS be so, since used doesn't change when cursor advances through the outer loop. Thus cursor eventually becomes NULL and the results of this function are undefined (no return value). In summary, as written you will always either return the first item (if itemNumber < used), or undefined behavior since you have no return value.
I believe you need something like the following instead:
template< class Type >
Type LinkList<Type>::GetItem(int itemNumber) const
{
const Node<Type>* cursor = first;
while (cursor && itemNumber-- > 0)
cursor = cursor->next;
if (cursor)
return cursor->data;
// note: this is here because you're definition is to return
// an item COPY. This case would be better off returning a
// `const Type*` instead, which would at least allow you to
// communicate to the caller that there was no item at the
// proposed index (because the list is undersized) and return
// NULL, which the caller could check.
return Type();
}