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

View File

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

View File

@ -14,44 +14,56 @@
#include "aiWorld.h"
AIWorld::AIWorld(NodePath render) {
_ai_char_pool = new AICharPool();
_render = render;
_render = move(render);
}
AIWorld::~AIWorld() {
}
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->_world = this;
}
void AIWorld::remove_ai_char(string name) {
_ai_char_pool->del(name);
remove_ai_char_from_flock(name);
AICharPool::iterator it;
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) {
AICharPool::node *ai_pool;
ai_pool = _ai_char_pool->_head;
while((ai_pool) != NULL) {
for(unsigned int i = 0; i < _flock_pool.size(); ++i) {
if(ai_pool->_ai_char->_ai_char_flock_id == _flock_pool[i]->get_id()) {
for(unsigned int j = 0; j<_flock_pool[i]->_ai_char_list.size(); ++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);
for (AICharacter *ai_char : _ai_char_pool) {
for (Flock *flock : _flock_pool) {
if (ai_char->_ai_char_flock_id == flock->get_id()) {
for (size_t j = 0; j < flock->_ai_char_list.size(); ++j) {
if (flock->_ai_char_list[j]->_name == name) {
flock->_ai_char_list.erase(flock->_ai_char_list.begin() + j);
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() {
_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.
*/
void AIWorld::update() {
AICharPool::node *ai_pool;
ai_pool = _ai_char_pool->_head;
while((ai_pool)!=NULL) {
ai_pool->_ai_char->update();
ai_pool = ai_pool->_next;
for (AICharacter *ai_char : _ai_char_pool) {
ai_char->update();
}
}
@ -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
* obstacle avoidance behavior.

View File

@ -21,27 +21,6 @@
class AICharacter;
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
* 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 {
private:
AICharPool * _ai_char_pool;
typedef std::vector<PT(AICharacter)> AICharPool;
AICharPool _ai_char_pool;
NodePath _render;
public:
vector<NodePath> _obstacles;

View File

@ -40,7 +40,7 @@ public:
unsigned int _alignment_wt;
// 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;
PUBLISHED: