Просмотр исходного кода

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
rdb 7 лет назад
Родитель
Сommit
0de981d0b8

+ 3 - 0
contrib/src/ai/aiCharacter.cxx

@@ -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);
 }
 
 /**

+ 2 - 1
contrib/src/ai/aiCharacter.h

@@ -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;

+ 29 - 101
contrib/src/ai/aiWorld.cxx

@@ -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.

+ 2 - 22
contrib/src/ai/aiWorld.h

@@ -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;

+ 1 - 1
contrib/src/ai/flock.h

@@ -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: