ai: make AICharacter reference counted

This lets one drop the reference to AICharacter after adding it to a flock or AIWorld without crashes.

Also replace the silly and inefficient custom-rolled linked list implementation of AICharPool with a vector, and add additional safety checks.

Fixes #318
This commit is contained in:
rdb 2018-05-27 12:59:41 +02:00
parent 0ce9dc98b1
commit 0de981d0b8
5 changed files with 37 additions and 125 deletions

View File

@ -24,6 +24,8 @@ AICharacter::AICharacter(string model_name, NodePath model_np, double mass, doub
_velocity = LVecBase3(0.0, 0.0, 0.0); _velocity = LVecBase3(0.0, 0.0, 0.0);
_steering_force = LVecBase3(0.0, 0.0, 0.0); _steering_force = LVecBase3(0.0, 0.0, 0.0);
_world = nullptr;
_steering = new AIBehaviors(); _steering = new AIBehaviors();
_steering->_ai_char = this; _steering->_ai_char = this;
@ -31,6 +33,7 @@ AICharacter::AICharacter(string model_name, NodePath model_np, double mass, doub
} }
AICharacter::~AICharacter() { AICharacter::~AICharacter() {
nassertv(_world == nullptr);
} }
/** /**

View File

@ -15,6 +15,7 @@
#define _AICHARACTER_H #define _AICHARACTER_H
#include "aiBehaviors.h" #include "aiBehaviors.h"
#include "referenceCount.h"
/** /**
* This class is used for creating the AI characters. It assigns both physics * This class is used for creating the AI characters. It assigns both physics
@ -25,7 +26,7 @@
class AIBehaviors; class AIBehaviors;
class AIWorld; class AIWorld;
class EXPCL_PANDAAI AICharacter { class EXPCL_PANDAAI AICharacter : public ReferenceCount {
public: public:
double _mass; double _mass;
double _max_force; double _max_force;

View File

@ -14,44 +14,56 @@
#include "aiWorld.h" #include "aiWorld.h"
AIWorld::AIWorld(NodePath render) { AIWorld::AIWorld(NodePath render) {
_ai_char_pool = new AICharPool(); _render = move(render);
_render = render;
} }
AIWorld::~AIWorld() { AIWorld::~AIWorld() {
} }
void AIWorld::add_ai_char(AICharacter *ai_char) { void AIWorld::add_ai_char(AICharacter *ai_char) {
_ai_char_pool->append(ai_char); _ai_char_pool.push_back(ai_char);
ai_char->_window_render = _render; ai_char->_window_render = _render;
ai_char->_world = this; ai_char->_world = this;
} }
void AIWorld::remove_ai_char(string name) { void AIWorld::remove_ai_char(string name) {
_ai_char_pool->del(name); AICharPool::iterator it;
remove_ai_char_from_flock(name); for (it = _ai_char_pool.begin(); it != _ai_char_pool.end(); ++it) {
AICharacter *ai_char = *it;
if (ai_char->_name == name) {
nassertv(ai_char->_world == this);
ai_char->_world = nullptr;
_ai_char_pool.erase(it);
break;
}
}
remove_ai_char_from_flock(move(name));
} }
void AIWorld::remove_ai_char_from_flock(string name) { void AIWorld::remove_ai_char_from_flock(string name) {
AICharPool::node *ai_pool; for (AICharacter *ai_char : _ai_char_pool) {
ai_pool = _ai_char_pool->_head; for (Flock *flock : _flock_pool) {
while((ai_pool) != NULL) { if (ai_char->_ai_char_flock_id == flock->get_id()) {
for(unsigned int i = 0; i < _flock_pool.size(); ++i) { for (size_t j = 0; j < flock->_ai_char_list.size(); ++j) {
if(ai_pool->_ai_char->_ai_char_flock_id == _flock_pool[i]->get_id()) { if (flock->_ai_char_list[j]->_name == name) {
for(unsigned int j = 0; j<_flock_pool[i]->_ai_char_list.size(); ++j) { flock->_ai_char_list.erase(flock->_ai_char_list.begin() + j);
if(_flock_pool[i]->_ai_char_list[j]->_name == name) {
_flock_pool[i]->_ai_char_list.erase(_flock_pool[i]->_ai_char_list.begin() + j);
return; return;
} }
} }
} }
} }
ai_pool = ai_pool->_next;
} }
} }
/**
* This function prints the names of the AI characters that have been added to
* the AIWorld. Useful for debugging purposes.
*/
void AIWorld::print_list() { void AIWorld::print_list() {
_ai_char_pool->print_list(); for (AICharacter *ai_char : _ai_char_pool) {
cout << ai_char->_name << endl;
}
} }
/** /**
@ -59,12 +71,8 @@ void AIWorld::print_list() {
* characters which have been added to the AIWorld. * characters which have been added to the AIWorld.
*/ */
void AIWorld::update() { void AIWorld::update() {
AICharPool::node *ai_pool; for (AICharacter *ai_char : _ai_char_pool) {
ai_pool = _ai_char_pool->_head; ai_char->update();
while((ai_pool)!=NULL) {
ai_pool->_ai_char->update();
ai_pool = ai_pool->_next;
} }
} }
@ -142,86 +150,6 @@ void AIWorld::flock_on(unsigned int flock_id) {
} }
} }
AICharPool::AICharPool() {
_head = NULL;
}
AICharPool::~AICharPool() {
}
void AICharPool::append(AICharacter *ai_ch) {
node *q;
node *t;
if(_head == NULL) {
q = new node();
q->_ai_char = ai_ch;
q->_next = NULL;
_head = q;
}
else {
q = _head;
while( q->_next != NULL) {
q = q->_next;
}
t = new node();
t->_ai_char = ai_ch;
t->_next = NULL;
q->_next = t;
}
}
void AICharPool::del(string name) {
node *q;
node *r;
q = _head;
if(_head==NULL) {
return;
}
// Only one node in the linked list
if(q->_next == NULL) {
if(q->_ai_char->_name == name) {
_head = NULL;
delete q;
}
return;
}
r = q;
while( q != NULL) {
if( q->_ai_char->_name == name) {
// Special case
if(q == _head) {
_head = q->_next;
delete q;
return;
}
r->_next = q->_next;
delete q;
return;
}
r = q;
q = q->_next;
}
}
/**
* This function prints the ai characters in the AICharPool. Used for
* debugging purposes.
*/
void AICharPool::print_list() {
node* q;
q = _head;
while(q != NULL) {
cout<<q->_ai_char->_name<<endl;
q = q->_next;
}
}
/** /**
* This function adds the nodepath as an obstacle that is needed by the * This function adds the nodepath as an obstacle that is needed by the
* obstacle avoidance behavior. * obstacle avoidance behavior.

View File

@ -21,27 +21,6 @@
class AICharacter; class AICharacter;
class Flock; class Flock;
/**
* This class implements a linked list of AI Characters allowing the user to
* add and delete characters from the linked list. This will be used in the
* AIWorld class.
*/
class EXPCL_PANDAAI AICharPool {
public:
struct node {
AICharacter * _ai_char;
node * _next;
} ;
node* _head;
AICharPool();
~AICharPool();
void append(AICharacter *ai_ch);
void del(string name);
void print_list();
};
/** /**
* A class that implements the virtual AI world which keeps track of the AI * A class that implements the virtual AI world which keeps track of the AI
* characters active at any given time. It contains a linked list of AI * characters active at any given time. It contains a linked list of AI
@ -51,7 +30,8 @@ class EXPCL_PANDAAI AICharPool {
*/ */
class EXPCL_PANDAAI AIWorld { class EXPCL_PANDAAI AIWorld {
private: private:
AICharPool * _ai_char_pool; typedef std::vector<PT(AICharacter)> AICharPool;
AICharPool _ai_char_pool;
NodePath _render; NodePath _render;
public: public:
vector<NodePath> _obstacles; vector<NodePath> _obstacles;

View File

@ -40,7 +40,7 @@ public:
unsigned int _alignment_wt; unsigned int _alignment_wt;
// This vector will hold all the ai characters which belong to this flock. // This vector will hold all the ai characters which belong to this flock.
typedef std::vector<AICharacter*> AICharList; typedef std::vector<PT(AICharacter)> AICharList;
AICharList _ai_char_list; AICharList _ai_char_list;
PUBLISHED: PUBLISHED: