Browse Source

move AIDistClass etc. into C++

David Rose 21 years ago
parent
commit
8625787a5e

+ 420 - 2
direct/src/dcparser/dcAtomicField.cxx

@@ -236,6 +236,366 @@ output(ostream &out, bool brief) const {
   }
 }
 
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCAtomicField::ElementType::pack_arg
+//       Access: Public
+//  Description: Packs the Python object into the datagram, appending
+//               to the end of the datagram.
+////////////////////////////////////////////////////////////////////
+void DCAtomicField::ElementType::
+pack_arg(Datagram &datagram, PyObject *item, DCSubatomicType type) const {
+  char *str;
+  int size;
+
+  if (type == ST_invalid) {
+    type = _type;
+  }
+
+  // Check for an array type.  These are handled recursively.
+  DCSubatomicType array_subtype;
+  int num_bytes = 0;
+  switch (type) {
+  case ST_int16array:
+    array_subtype = ST_int16;
+    num_bytes = 2;
+    break;
+
+  case ST_int32array:
+    array_subtype = ST_int32;
+    num_bytes = 4;
+    break;
+
+  case ST_uint16array:
+    array_subtype = ST_uint16;
+    num_bytes = 2;
+    break;
+
+  case ST_uint32array:
+    array_subtype = ST_uint32;
+    num_bytes = 4;
+    break;
+
+  case ST_int8array:
+    array_subtype = ST_int8;
+    num_bytes = 1;
+    break;
+
+  case ST_uint8array:
+    array_subtype = ST_uint8;
+    num_bytes = 1;
+    break;
+
+  case ST_uint32uint8array:
+    array_subtype = ST_uint32;
+    num_bytes = 5;
+    break;
+
+  default:
+    array_subtype = ST_invalid;
+  }
+
+  if (array_subtype != ST_invalid) {
+    int size = PySequence_Size(item);
+    datagram.add_uint16(size * num_bytes);
+    if (type == ST_uint32uint8array) {
+      // This one is a special case: an array of tuples.
+      for (int i = 0; i < size; i++) {
+        PyObject *tuple = PySequence_GetItem(item, i);
+        pack_arg(datagram, PyTuple_GetItem(tuple, 0), ST_uint32);
+        pack_arg(datagram, PyTuple_GetItem(tuple, 1), ST_uint8);
+        Py_DECREF(tuple);
+      }
+    } else {
+      for (int i = 0; i < size; i++) {
+        PyObject *element = PySequence_GetItem(item, i);
+        pack_arg(datagram, element, array_subtype);
+        Py_DECREF(element);
+      }
+    }
+
+    return;
+  }
+
+  if (_divisor == 1) {
+    switch (type) {
+    case ST_int8:
+      datagram.add_int8(PyInt_AsLong(item));
+      break;
+
+    case ST_int16:
+      datagram.add_int16(PyInt_AsLong(item));
+      break;
+
+    case ST_int32:
+      datagram.add_int32(PyInt_AsLong(item));
+      break;
+
+    case ST_int64:
+      datagram.add_int64(PyLong_AsLongLong(item));
+      break;
+
+    case ST_uint8:
+      datagram.add_uint8(PyInt_AsLong(item));
+      break;
+
+    case ST_uint16:
+      datagram.add_uint16(PyInt_AsLong(item));
+      break;
+
+    case ST_uint32:
+      datagram.add_uint32(PyInt_AsLong(item));
+      break;
+
+    case ST_uint64:
+      datagram.add_uint64(PyLong_AsUnsignedLongLong(item));
+      break;
+
+    case ST_float64:
+      datagram.add_float64(PyFloat_AsDouble(item));
+      break;
+
+    case ST_string:
+    case ST_blob:
+      PyString_AsStringAndSize(item, &str, &size);
+      datagram.add_string(string(str, size));
+      break;
+      
+    case ST_blob32:
+      PyString_AsStringAndSize(item, &str, &size);
+      datagram.add_string32(string(str, size));
+      break;
+
+    default:
+      break;
+    }
+
+  } else {
+    switch (type) {
+    case ST_int8:
+      datagram.add_int8(floor(PyFloat_AsDouble(item) * _divisor + 0.5));
+      break;
+
+    case ST_int16:
+      datagram.add_int16(floor(PyFloat_AsDouble(item) * _divisor + 0.5));
+      break;
+
+    case ST_int32:
+      datagram.add_int32(floor(PyFloat_AsDouble(item) * _divisor + 0.5));
+      break;
+
+    case ST_int64:
+      datagram.add_int64(floor(PyFloat_AsDouble(item) * _divisor + 0.5));
+      break;
+
+    case ST_uint8:
+      datagram.add_uint8(floor(PyFloat_AsDouble(item) * _divisor + 0.5));
+      break;
+
+    case ST_uint16:
+      datagram.add_uint16(floor(PyFloat_AsDouble(item) * _divisor + 0.5));
+      break;
+
+    case ST_uint32:
+      datagram.add_uint32(floor(PyFloat_AsDouble(item) * _divisor + 0.5));
+      break;
+
+    case ST_uint64:
+      datagram.add_uint64(floor(PyFloat_AsDouble(item) * _divisor + 0.5));
+      break;
+
+    case ST_float64:
+      datagram.add_float64(PyFloat_AsDouble(item) * _divisor);
+      break;
+
+    case ST_string:
+    case ST_blob:
+      PyString_AsStringAndSize(item, &str, &size);
+      datagram.add_string(string(str, size));
+      break;
+      
+    case ST_blob32:
+      PyString_AsStringAndSize(item, &str, &size);
+      datagram.add_string32(string(str, size));
+      break;
+    }
+  }
+}
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCAtomicField::ElementType::unpack_arg
+//       Access: Public
+//  Description: Unpacks a Python object from the datagram, beginning
+//               at the current point in the interator, and returns a
+//               new reference, or NULL if there was not enough data
+//               in the datagram.
+////////////////////////////////////////////////////////////////////
+PyObject *DCAtomicField::ElementType::
+unpack_arg(DatagramIterator &iterator, DCSubatomicType type) const {
+  string str;
+
+  if (type == ST_invalid) {
+    type = _type;
+  }
+
+  // Check for an array type.  These are handled recursively.
+  DCSubatomicType array_subtype;
+  int num_bytes = 0;
+  switch (type) {
+  case ST_int16array:
+    array_subtype = ST_int16;
+    num_bytes = 2;
+    break;
+
+  case ST_int32array:
+    array_subtype = ST_int32;
+    num_bytes = 4;
+    break;
+
+  case ST_uint16array:
+    array_subtype = ST_uint16;
+    num_bytes = 2;
+    break;
+
+  case ST_uint32array:
+    array_subtype = ST_uint32;
+    num_bytes = 4;
+    break;
+
+  case ST_int8array:
+    array_subtype = ST_int8;
+    num_bytes = 1;
+    break;
+
+  case ST_uint8array:
+    array_subtype = ST_uint8;
+    num_bytes = 1;
+    break;
+
+  case ST_uint32uint8array:
+    array_subtype = ST_uint32;
+    num_bytes = 5;
+    break;
+
+  default:
+    array_subtype = ST_invalid;
+  }
+
+  if (array_subtype != ST_invalid) {
+    int size_bytes = iterator.get_uint16();
+    int size = size_bytes / num_bytes;
+    nassertr(size * num_bytes == size_bytes, NULL);
+
+    PyObject *list = PyList_New(size);
+    if (type == ST_uint32uint8array) {
+      // This one is a special case: an array of tuples.
+      for (int i = 0; i < size; i++) {
+        PyObject *a = unpack_arg(iterator, ST_uint32);
+        PyObject *b = unpack_arg(iterator, ST_uint8);
+        PyObject *tuple = PyTuple_New(2);
+        PyTuple_SET_ITEM(tuple, 0, a);
+        PyTuple_SET_ITEM(tuple, 1, b);
+        PyList_SET_ITEM(list, i, tuple);
+      }
+    } else {
+      for (int i = 0; i < size; i++) {
+        PyObject *element = unpack_arg(iterator, array_subtype);
+        PyList_SET_ITEM(list, i, element);
+      }
+    }
+
+    return list;
+  }
+
+  if (_divisor == 1) {
+    switch (type) {
+    case ST_int8:
+      return PyInt_FromLong(iterator.get_int8());
+
+    case ST_int16:
+      return PyInt_FromLong(iterator.get_int16());
+
+    case ST_int32:
+      return PyInt_FromLong(iterator.get_int32());
+
+    case ST_int64:
+      return PyLong_FromLongLong(iterator.get_int64());
+
+    case ST_uint8:
+      return PyInt_FromLong(iterator.get_uint8());
+
+    case ST_uint16:
+      return PyInt_FromLong(iterator.get_uint16());
+
+    case ST_uint32:
+      return PyInt_FromLong(iterator.get_uint32());
+
+    case ST_uint64:
+      return PyLong_FromUnsignedLongLong(iterator.get_uint64());
+
+    case ST_float64:
+      return PyFloat_FromDouble(iterator.get_float64());
+
+    case ST_string:
+    case ST_blob:
+      str = iterator.get_string();
+      return PyString_FromStringAndSize(str.data(), str.size());
+      
+    case ST_blob32:
+      str = iterator.get_string32();
+      return PyString_FromStringAndSize(str.data(), str.size());
+
+    default:
+      return Py_BuildValue("");
+    }
+
+  } else {
+    switch (type) {
+    case ST_int8:
+      return PyFloat_FromDouble(iterator.get_int8() / (double)_divisor);
+
+    case ST_int16:
+      return PyFloat_FromDouble(iterator.get_int16() / (double)_divisor);
+
+    case ST_int32:
+      return PyFloat_FromDouble(iterator.get_int32() / (double)_divisor);
+
+    case ST_int64:
+      return PyFloat_FromDouble(iterator.get_int64() / (double)_divisor);
+
+    case ST_uint8:
+      return PyFloat_FromDouble(iterator.get_uint8() / (double)_divisor);
+
+    case ST_uint16:
+      return PyFloat_FromDouble(iterator.get_uint16() / (double)_divisor);
+
+    case ST_uint32:
+      return PyFloat_FromDouble(iterator.get_uint32() / (double)_divisor);
+
+    case ST_uint64:
+      return PyFloat_FromDouble(iterator.get_uint64() / (double)_divisor);
+
+    case ST_float64:
+      return PyFloat_FromDouble(iterator.get_float64() / (double)_divisor);
+
+    case ST_string:
+    case ST_blob:
+      str = iterator.get_string();
+      return PyString_FromStringAndSize(str.data(), str.size());
+      
+    case ST_blob32:
+      str = iterator.get_string32();
+      return PyString_FromStringAndSize(str.data(), str.size());
+
+    default:
+      return Py_BuildValue("");
+    }
+  }
+}
+#endif  // HAVE_PYTHON
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DCAtomicField::ElementType::format_default_value
 //       Access: Private
@@ -591,8 +951,7 @@ is_airecv() const {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 DCAtomicField::
-DCAtomicField() {
-  _number = 0;
+DCAtomicField(const string &name) : DCField(name) {
   _flags = 0;
 }
 
@@ -673,3 +1032,62 @@ generate_hash(HashGenerator &hashgen) const {
   }
   hashgen.add_int(_flags);
 }
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCAtomicField::do_pack_args
+//       Access: Public, Virtual
+//  Description: Packs the Python arguments beginning from the
+//               indicated index of the indicated tuple into the
+//               datagram, appending to the end of the datagram.
+//               Increments index according to the number of arguments
+//               packed.  Returns true if the tuple contained at least
+//               enough arguments to match the field, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool DCAtomicField::
+do_pack_args(Datagram &datagram, PyObject *tuple, int &index) const {
+  int size = PySequence_Size(tuple);
+
+  Elements::const_iterator ei;
+  for (ei = _elements.begin(); ei != _elements.end(); ++ei) {
+    const ElementType &element = (*ei);
+    if (index >= size) {
+      // Not enough elements in the tuple.
+      return false;
+    }
+    PyObject *item = PySequence_GetItem(tuple, index);
+    index++;
+    element.pack_arg(datagram, item);
+    Py_DECREF(item);
+  }
+
+  return true;
+}
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCAtomicField::do_unpack_args
+//       Access: Public, Virtual
+//  Description: Unpacks the values from the datagram, beginning at
+//               the current point in the interator, into a vector of
+//               Python objects (each with its own reference count).
+//               Returns true if there are enough values in the
+//               datagram, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool DCAtomicField::
+do_unpack_args(pvector<PyObject *> &args, DatagramIterator &iterator) const {
+  Elements::const_iterator ei;
+  for (ei = _elements.begin(); ei != _elements.end(); ++ei) {
+    const ElementType &element = (*ei);
+    PyObject *item = element.unpack_arg(iterator);
+    if (item == (PyObject *)NULL) {
+      // Ran out of datagram bytes.
+      return false;
+    }
+    args.push_back(item);
+  }
+
+  return true;
+}
+#endif  // HAVE_PYTHON

+ 12 - 1
direct/src/dcparser/dcAtomicField.h

@@ -56,10 +56,16 @@ PUBLISHED:
   bool is_airecv() const;
 
 public:
-  DCAtomicField();
+  DCAtomicField(const string &name);
   virtual void write(ostream &out, bool brief, int indent_level) const;
   virtual void generate_hash(HashGenerator &hash) const;
 
+public:
+#ifdef HAVE_PYTHON
+  virtual bool do_pack_args(Datagram &datagram, PyObject *tuple, int &index) const;
+  virtual bool do_unpack_args(pvector<PyObject *> &args, DatagramIterator &iterator) const;
+#endif
+
 public:
   // These members define the primary interface to the atomic field
   // definition as read from the file.
@@ -77,6 +83,11 @@ public:
 
     void output(ostream &out, bool brief) const;
 
+#ifdef HAVE_PYTHON
+    void pack_arg(Datagram &datagram, PyObject *item, DCSubatomicType = ST_invalid) const;
+    PyObject *unpack_arg(DatagramIterator &iterator, DCSubatomicType = ST_invalid) const;
+#endif
+
     DCSubatomicType _type;
     string _name;
     int _divisor;

+ 339 - 17
direct/src/dcparser/dcClass.cxx

@@ -19,10 +19,11 @@
 #include "dcClass.h"
 #include "hashGenerator.h"
 #include "dcindent.h"
+#include "dcmsgtypes.h"
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DCClass::get_number
-//       Access: Public
+//       Access: Published
 //  Description: Returns a unique index number associated with this
 //               class.  This is defined implicitly when the .dc
 //               file(s) are read.
@@ -34,7 +35,7 @@ get_number() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DCClass::get_name
-//       Access: Public
+//       Access: Published
 //  Description: Returns the name of this class.
 ////////////////////////////////////////////////////////////////////
 string DCClass::
@@ -44,7 +45,7 @@ get_name() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DCClass::has_parent
-//       Access: Public
+//       Access: Published
 //  Description: Returns true if this class inherits from some other
 //               class, false if it does not.
 ////////////////////////////////////////////////////////////////////
@@ -55,7 +56,7 @@ has_parent() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DCClass::get_parent
-//       Access: Public
+//       Access: Published
 //  Description: Returns the parent class this class inherits from, if
 //               any.  It is an error to call this unless has_parent()
 //               returned true.
@@ -68,32 +69,32 @@ get_parent() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DCClass::get_num_fields
-//       Access: Public
+//       Access: Published
 //  Description: Returns the number of fields defined directly in this
 //               class, ignoring inheritance.
 ////////////////////////////////////////////////////////////////////
 int DCClass::
-get_num_fields() {
+get_num_fields() const {
   return _fields.size();
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DCClass::get_field
-//       Access: Public
+//       Access: Published
 //  Description: Returns the nth field in the class.  This is not
 //               necessarily the field with index n; this is the nth
 //               field defined in the class directly, ignoring
 //               inheritance.
 ////////////////////////////////////////////////////////////////////
 DCField *DCClass::
-get_field(int n) {
-  nassertr(n >= 0 && n < (int)_fields.size(), NULL);
+get_field(int n) const {
+  nassertr_always(n >= 0 && n < (int)_fields.size(), NULL);
   return _fields[n];
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DCClass::get_field_by_name
-//       Access: Public
+//       Access: Published
 //  Description: Returns a pointer to the DCField that shares the
 //               indicated name.  If the named field is not found in
 //               the current class, the parent classes will be
@@ -102,7 +103,7 @@ get_field(int n) {
 //               such field defined.
 ////////////////////////////////////////////////////////////////////
 DCField *DCClass::
-get_field_by_name(const string &name) {
+get_field_by_name(const string &name) const {
   FieldsByName::const_iterator ni;
   ni = _fields_by_name.find(name);
   if (ni != _fields_by_name.end()) {
@@ -110,7 +111,7 @@ get_field_by_name(const string &name) {
   }
 
   // We didn't have such a field, so check our parents.
-  Parents::iterator pi;
+  Parents::const_iterator pi;
   for (pi = _parents.begin(); pi != _parents.end(); ++pi) {
     DCField *result = (*pi)->get_field_by_name(name);
     if (result != (DCField *)NULL) {
@@ -124,12 +125,12 @@ get_field_by_name(const string &name) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DCClass::get_num_inherited_fields
-//       Access: Public
+//       Access: Published
 //  Description: Returns the total number of field fields defined in
 //               this class and all ancestor classes.
 ////////////////////////////////////////////////////////////////////
 int DCClass::
-get_num_inherited_fields() {
+get_num_inherited_fields() const {
   if (!_parents.empty()) {
     // This won't work for multiple dclass inheritance.
     return _parents.front()->get_num_inherited_fields() + get_num_fields();
@@ -139,7 +140,7 @@ get_num_inherited_fields() {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DCClass::get_inherited_field
-//       Access: Public
+//       Access: Published
 //  Description: Returns the nth field field in the class and all of
 //               its ancestors.  This *is* the field corresponding to
 //               the given index number, since the fields are ordered
@@ -147,7 +148,7 @@ get_num_inherited_fields() {
 //               fields.
 ////////////////////////////////////////////////////////////////////
 DCField *DCClass::
-get_inherited_field(int n) {
+get_inherited_field(int n) const {
   if (!_parents.empty()) {
     // This won't work for multiple dclass inheritance.
     int psize = _parents.front()->get_num_inherited_fields();
@@ -160,13 +161,321 @@ get_inherited_field(int n) {
   return get_field(n);
 }
 
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::set_class_def
+//       Access: Published
+//  Description: Sets the class object associated with this
+//               DistributedClass.  This object will be used to
+//               construct new instances of the class.
+////////////////////////////////////////////////////////////////////
+void DCClass::
+set_class_def(PyObject *class_def) {
+  Py_XINCREF(class_def);
+  Py_XDECREF(_class_def);
+  _class_def = class_def;
+}
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::get_class_def
+//       Access: Published
+//  Description: Returns the class object that was previously
+//               associated with this DistributedClass.  This will
+//               return a new reference to the object.
+////////////////////////////////////////////////////////////////////
+PyObject *DCClass::
+get_class_def() const {
+  if (_class_def == NULL) {
+    return Py_BuildValue("");
+  }
+
+  Py_INCREF(_class_def);
+  return _class_def;
+}
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::receive_update
+//       Access: Published
+//  Description: Extracts the update message out of the datagram and
+//               applies it to the indicated object by calling the
+//               appropriate method.
+////////////////////////////////////////////////////////////////////
+void DCClass::
+receive_update(PyObject *distobj, DatagramIterator &iterator) const {
+  int field_id = iterator.get_uint16();
+  DCField *field = get_inherited_field(field_id);
+  nassertv_always(field != NULL);
+  field->receive_update(distobj, iterator);
+}
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::receive_update_broadcast_required
+//       Access: Published
+//  Description: Processes a big datagram that includes all of the
+//               "required" fields that are sent along with a normal
+//               "generate with required" message.  This is all of the
+//               atomic fields that are marked "broadcast required".
+////////////////////////////////////////////////////////////////////
+void DCClass::
+receive_update_broadcast_required(PyObject *distobj, DatagramIterator &iterator) const {
+  int num_fields = get_num_inherited_fields();
+  for (int i = 0; i < num_fields; i++) {
+    DCField *field = get_inherited_field(i);
+    DCAtomicField *atom = field->as_atomic_field();
+    if (atom != (DCAtomicField *)NULL &&
+        atom->is_required() && atom->is_broadcast()) {
+      atom->receive_update(distobj, iterator);
+    }
+  }
+}
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::receive_update_all_required
+//       Access: Published
+//  Description: Processes a big datagram that includes all of the
+//               "required" fields that are sent when an avatar is
+//               created.  This is all of the atomic fields that are
+//               marked "required", whether they are broadcast or not.
+////////////////////////////////////////////////////////////////////
+void DCClass::
+receive_update_all_required(PyObject *distobj, DatagramIterator &iterator) const {
+  int num_fields = get_num_inherited_fields();
+  for (int i = 0; i < num_fields; i++) {
+    DCField *field = get_inherited_field(i);
+    DCAtomicField *atom = field->as_atomic_field();
+    if (atom != (DCAtomicField *)NULL && atom->is_required()) {
+      atom->receive_update(distobj, iterator);
+    }
+  }
+}
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::receive_update_other
+//       Access: Published
+//  Description: Processes a datagram that lists some additional
+//               fields that are broadcast in one chunk.
+////////////////////////////////////////////////////////////////////
+void DCClass::
+receive_update_other(PyObject *distobj, DatagramIterator &iterator) const {
+  int num_fields = iterator.get_uint16();
+  for (int i = 0; i < num_fields; i++) {
+    receive_update(distobj, iterator);
+  }
+}
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::named_update
+//       Access: Published
+//  Description: Processes an update for a named field.
+////////////////////////////////////////////////////////////////////
+void DCClass::
+named_update(PyObject *distobj, const string &field_name, 
+             const Datagram &datagram) {
+  DCField *field = get_field_by_name(field_name);
+  nassertv_always(field != NULL);
+  DatagramIterator iterator(datagram);
+  field->receive_update(distobj, iterator);
+}
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::pack_required_field
+//       Access: Published
+//  Description: Looks up the current value of the indicated field by
+//               calling the appropriate get*() function, then packs
+//               that value into the datagram.  This field is
+//               presumably either a required field or a specified
+//               optional field, and we are building up a datagram for
+//               the generate-with-required message.
+////////////////////////////////////////////////////////////////////
+void DCClass::
+pack_required_field(Datagram &dg, PyObject *distobj, DCField *field) const {
+  DCAtomicField *atom = field->as_atomic_field();
+  if (atom == (DCAtomicField *)NULL) {
+    cerr << "Cannot pack non-atomic field " << field->get_name()
+         << " for generate\n";
+    nassertv(false);
+  }
+
+  // We need to get the initial value of this field.  There isn't a
+  // good, robust way to get this; presently, we just mangle the
+  // "setFoo()" name of the required field into "getFoo()" and call
+  // that.
+  string set_name = atom->get_name();
+
+  if (atom->get_num_elements() == 0) {
+    // It sure doesn't make sense to have a required field with no
+    // parameters.  What data, exactly, is required?
+    cerr << "Required field " << set_name << " has no parameters!\n";
+    nassertv(false);
+  }
+  
+  if (set_name.substr(0, 3) != "set") {
+    // This is required to suit our set/get mangling convention.
+    cerr << "Required field " << set_name
+         << " does not begin with 'set'\n";
+    nassertv(false);
+  }
+  string get_name = set_name;
+  get_name[0] = 'g';
+  
+  // Now we have to look up the getter on the distributed object
+  // and call it.
+  if (!PyObject_HasAttrString(distobj, (char *)get_name.c_str())) {
+    cerr << "Required field " << set_name
+         << " doesn't have matching field named " << get_name << "\n";
+    nassertv(false);
+  }
+  PyObject *func = 
+    PyObject_GetAttrString(distobj, (char *)get_name.c_str());
+  nassertv(func != (PyObject *)NULL);
+  
+  PyObject *empty_args = PyTuple_New(0);
+  PyObject *result = PyObject_CallObject(func, empty_args);
+  Py_DECREF(empty_args);
+  Py_DECREF(func);
+  if (result == (PyObject *)NULL) {
+    cerr << "Error when calling " << get_name << "\n";
+    return;
+  }
+  
+  if (atom->get_num_elements() == 1) {
+    // In this case, we expect the getter to return one object,
+    // which we wrap up in a tuple.
+    PyObject *tuple = PyTuple_New(1);
+    PyTuple_SET_ITEM(tuple, 0, result);
+    result = tuple;
+  }        
+  
+  // Now pack the arguments into the datagram.
+  atom->pack_args(dg, result);
+  Py_DECREF(result);
+}
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::client_format_update
+//       Access: Published
+//  Description: Generates a datagram containing the message necessary
+//               to send an update for the indicated distributed
+//               object from the client.
+////////////////////////////////////////////////////////////////////
+Datagram DCClass::
+client_format_update(const string &field_name, int do_id, 
+                     PyObject *args) const {
+  DCField *field = get_field_by_name(field_name);
+  nassertr_always(field != NULL, Datagram());
+  return field->client_format_update(do_id, args);
+}
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::al_format_update
+//       Access: Published
+//  Description: Generates a datagram containing the message necessary
+//               to send an update for the indicated distributed
+//               object from the AI.
+////////////////////////////////////////////////////////////////////
+Datagram DCClass::
+ai_format_update(const string &field_name, int do_id, 
+                 int to_id, int from_id, PyObject *args) const {
+  DCField *field = get_field_by_name(field_name);
+  nassertr_always(field != NULL, Datagram());
+  return field->ai_format_update(do_id, to_id, from_id, args);
+}
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::ai_format_generate
+//       Access: Published
+//  Description: Generates a datagram containing the message necessary
+//               to generate a new distributed object from the AI.
+//               This requires querying the object for the initial
+//               value of its required fields.
+//
+//               optional_fields is a list of fieldNames to generate
+//               in addition to the normal required fields.
+////////////////////////////////////////////////////////////////////
+Datagram DCClass::
+ai_format_generate(PyObject *distobj, int do_id, 
+                   int zone_id, int district_id, int from_channel_id,
+                   PyObject *optional_fields) const {
+  Datagram dg;
+  dg.add_uint32(district_id);
+  dg.add_uint32(from_channel_id);
+  dg.add_uint8('A');
+
+  bool has_optional_fields = (PyObject_IsTrue(optional_fields) != 0);
+
+  if (has_optional_fields) {
+    dg.add_uint16(STATESERVER_OBJECT_GENERATE_WITH_REQUIRED_OTHER);
+  } else {
+    dg.add_uint16(STATESERVER_OBJECT_GENERATE_WITH_REQUIRED);
+  }
+
+  dg.add_uint32(zone_id);
+  dg.add_uint16(_number);
+  dg.add_uint32(do_id);
+
+  // Specify all of the required fields.
+  int num_fields = get_num_inherited_fields();
+  for (int i = 0; i < num_fields; i++) {
+    DCField *field = get_inherited_field(i);
+    DCAtomicField *atom = field->as_atomic_field();
+    if (atom != (DCAtomicField *)NULL && atom->is_required()) {
+      pack_required_field(dg, distobj, atom);
+    }
+  }
+
+  // Also specify the optional fields.
+  if (has_optional_fields) {
+    int num_optional_fields = PySequence_Size(optional_fields);
+    dg.add_uint16(num_optional_fields);
+
+    for (int i = 0; i < num_optional_fields; i++) {
+      PyObject *py_field_name = PySequence_GetItem(optional_fields, i);
+      string field_name = PyString_AsString(py_field_name);
+      Py_XDECREF(py_field_name);
+
+      DCField *field = get_field_by_name(field_name);
+      if (field == (DCField *)NULL) {
+        cerr << "No field named " << field_name << " in class " << get_name()
+             << "\n";
+        nassertr(false, Datagram());
+      }
+
+      pack_required_field(dg, distobj, field);
+    }
+  }
+
+  return dg;
+}
+#endif  // HAVE_PYTHON
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DCClass::Constructor
 //       Access: Public
 //  Description:
 ////////////////////////////////////////////////////////////////////
 DCClass::
-DCClass() {
+DCClass(const string &name) : _name(name) {
+  _class_def = NULL;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -180,6 +489,8 @@ DCClass::
   for (fi = _fields.begin(); fi != _fields.end(); ++fi) {
     delete (*fi);
   }
+
+  Py_XDECREF(_class_def);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -262,3 +573,14 @@ add_field(DCField *field) {
   _fields.push_back(field);
   return true;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::add_parent
+//       Access: Public
+//  Description: Adds a new parent to the inheritance hierarchy of the
+//               class.  This is normally called only during parsing.
+////////////////////////////////////////////////////////////////////
+void DCClass::
+add_parent(DCClass *parent) {
+  _parents.push_back(parent);
+}

+ 36 - 7
direct/src/dcparser/dcClass.h

@@ -37,23 +37,48 @@ PUBLISHED:
   bool has_parent() const;
   DCClass *get_parent() const;
 
-  int get_num_fields();
-  DCField *get_field(int n);
-  DCField *get_field_by_name(const string &name);
+  int get_num_fields() const;
+  DCField *get_field(int n) const;
+  DCField *get_field_by_name(const string &name) const;
 
-  int get_num_inherited_fields();
-  DCField *get_inherited_field(int n);
+  int get_num_inherited_fields() const;
+  DCField *get_inherited_field(int n) const;
+
+#ifdef HAVE_PYTHON
+  void set_class_def(PyObject *class_def);
+  PyObject *get_class_def() const;
+
+  void receive_update(PyObject *distobj, DatagramIterator &iterator) const;
+  void receive_update_broadcast_required(PyObject *distobj, DatagramIterator &iterator) const;
+  void receive_update_all_required(PyObject *distobj, DatagramIterator &iterator) const;
+  void receive_update_other(PyObject *distobj, DatagramIterator &iterator) const;
+  void named_update(PyObject *distobj, const string &field_name, 
+                    const Datagram &datagram);
+  void pack_required_field(Datagram &dg, PyObject *distobj, 
+                           DCField *field) const;
+
+
+  Datagram client_format_update(const string &field_name, int do_id, 
+                                PyObject *args) const;
+  Datagram ai_format_update(const string &field_name, int do_id, 
+                            int to_id, int from_id, PyObject *args) const;
+  Datagram ai_format_generate(PyObject *distobj, int do_id, int zone_id,
+                              int district_id, int from_channel_id,
+                              PyObject *optional_fields) const;
+#endif 
 
 public:
-  DCClass();
+  DCClass(const string &name);
   ~DCClass();
 
   void write(ostream &out, bool brief, int indent_level) const;
   void generate_hash(HashGenerator &hash) const;
 
   bool add_field(DCField *field);
+  void add_parent(DCClass *parent);
+
+private:
 
-public:
   // These members define the primary interface to the distributed
   // class as read from the file.
   int _number;
@@ -65,11 +90,15 @@ public:
   typedef pvector<DCField *> Fields;
   Fields _fields;
 
+  PyObject *_class_def;
+
 public:
   // These members are built up during parsing for the convenience of
   // the parser.
   typedef pmap<string, DCField *> FieldsByName;
   FieldsByName _fields_by_name;
+
+  friend class DCFile;
 };
 
 #endif

+ 123 - 4
direct/src/dcparser/dcField.cxx

@@ -18,10 +18,11 @@
 
 #include "dcField.h"
 #include "hashGenerator.h"
+#include "dcmsgtypes.h"
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DCField::get_number
-//       Access: Public
+//       Access: Published
 //  Description: Returns a unique index number associated with this
 //               field.  This is defined implicitly when the .dc
 //               file(s) are read.
@@ -33,7 +34,7 @@ get_number() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DCField::get_name
-//       Access: Public
+//       Access: Published
 //  Description: Returns the name of this field.
 ////////////////////////////////////////////////////////////////////
 const string &DCField::
@@ -43,7 +44,7 @@ get_name() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DCField::as_atomic_field
-//       Access: Public, Virtual
+//       Access: Published, Virtual
 //  Description: Returns the same field pointer converted to an atomic
 //               field pointer, if this is in fact an atomic field;
 //               otherwise, returns NULL.
@@ -55,7 +56,7 @@ as_atomic_field() {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DCField::as_molecular_field
-//       Access: Public, Virtual
+//       Access: Published, Virtual
 //  Description: Returns the same field pointer converted to a
 //               molecular field pointer, if this is in fact a
 //               molecular field; otherwise, returns NULL.
@@ -65,6 +66,124 @@ as_molecular_field() {
   return (DCMolecularField *)NULL;
 }
 
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCField::pack_args
+//       Access: Published
+//  Description: Packs the Python arguments from the indicated tuple
+//               into the datagram, appending to the end of the
+//               datagram.
+////////////////////////////////////////////////////////////////////
+void DCField::
+pack_args(Datagram &datagram, PyObject *tuple) const {
+  nassertv(PySequence_Check(tuple));
+  int index = 0;
+  bool enough_args = do_pack_args(datagram, tuple, index);
+  nassertv(enough_args && index == PySequence_Size(tuple));
+}
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCField::unpack_args
+//       Access: Published
+//  Description: Unpacks the values from the datagram, beginning at
+//               the current point in the interator, into a Python
+//               tuple and returns the tuple.  If there are remaining
+//               bytes in the datagram, they are ignored (but the
+//               iterator is left at the first unread byte).
+////////////////////////////////////////////////////////////////////
+PyObject *DCField::
+unpack_args(DatagramIterator &iterator) const {
+  pvector<PyObject *> args;
+  bool enough_data = do_unpack_args(args, iterator);
+  nassertr(enough_data, NULL);
+
+  PyObject *tuple = PyTuple_New(args.size());
+  for (size_t i = 0; i < args.size(); i++) {
+    PyTuple_SET_ITEM(tuple, i, args[i]);
+  }
+
+  return tuple;
+}
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCField::receive_update
+//       Access: Published
+//  Description: Extracts the update message out of the datagram and
+//               applies it to the indicated object by calling the
+//               appropriate method.
+////////////////////////////////////////////////////////////////////
+void DCField::
+receive_update(PyObject *distobj, DatagramIterator &iterator) const {
+  PyObject *args = unpack_args(iterator);
+
+  if (PyObject_HasAttrString(distobj, (char *)_name.c_str())) {
+    PyObject *func = PyObject_GetAttrString(distobj, (char *)_name.c_str());
+    nassertv(func != (PyObject *)NULL);
+
+    PyObject *result = PyObject_CallObject(func, args);
+    Py_XDECREF(result);
+    Py_DECREF(func);
+  }
+  Py_DECREF(args);
+}
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCField::client_format_update
+//       Access: Published
+//  Description: Generates a datagram containing the message necessary
+//               to send an update for the indicated distributed
+//               object from the client.
+////////////////////////////////////////////////////////////////////
+Datagram DCField::
+client_format_update(int do_id, PyObject *args) const {
+  Datagram dg;
+  dg.add_uint16(CLIENT_OBJECT_UPDATE_FIELD);
+  dg.add_uint32(do_id);
+  dg.add_uint16(_number);
+  pack_args(dg, args);
+  return dg;
+}
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCField::al_format_update
+//       Access: Published
+//  Description: Generates a datagram containing the message necessary
+//               to send an update for the indicated distributed
+//               object from the AI.
+////////////////////////////////////////////////////////////////////
+Datagram DCField::
+ai_format_update(int do_id, int to_id, int from_id, PyObject *args) const {
+  Datagram dg;
+  dg.add_uint32(to_id);
+  dg.add_uint32(from_id);
+  dg.add_uint8('A');
+  dg.add_uint16(STATESERVER_OBJECT_UPDATE_FIELD);
+  dg.add_uint32(do_id);
+  dg.add_uint16(_number);
+  pack_args(dg, args);
+  return dg;
+}
+#endif  // HAVE_PYTHON
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCField::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DCField::
+DCField(const string &name) : _name(name) {
+  _number = 0;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DCField::Destructor
 //       Access: Public, Virtual

+ 31 - 1
direct/src/dcparser/dcField.h

@@ -21,6 +21,17 @@
 
 #include "dcbase.h"
 
+#ifdef HAVE_PYTHON
+
+#undef HAVE_LONG_LONG  // NSPR and Python both define this.
+#include <Python.h>
+
+// We only need these headers if we are also building a Python interface.
+#include "datagram.h"
+#include "datagramIterator.h"
+
+#endif  // HAVE_PYTHON
+
 class DCAtomicField;
 class DCMolecularField;
 class HashGenerator;
@@ -38,14 +49,33 @@ PUBLISHED:
   virtual DCAtomicField *as_atomic_field();
   virtual DCMolecularField *as_molecular_field();
 
+#ifdef HAVE_PYTHON
+  void pack_args(Datagram &datagram, PyObject *tuple) const;
+  PyObject *unpack_args(DatagramIterator &iterator) const;
+
+  void receive_update(PyObject *distobj, DatagramIterator &iterator) const;
+
+  Datagram client_format_update(int do_id, PyObject *args) const;
+  Datagram ai_format_update(int do_id, int to_id, int from_id, PyObject *args) const;
+#endif 
+
 public:
+  DCField(const string &name);
   virtual ~DCField();
   virtual void write(ostream &out, bool brief, int indent_level) const=0;
   virtual void generate_hash(HashGenerator &hash) const;
 
-public:
+protected:
   int _number;
   string _name;
+
+public:
+#ifdef HAVE_PYTHON
+  virtual bool do_pack_args(Datagram &datagram, PyObject *tuple, int &index) const=0;
+  virtual bool do_unpack_args(pvector<PyObject *> &args, DatagramIterator &iterator) const=0;
+#endif
+
+  friend class DCClass;
 };
 
 #endif

+ 99 - 0
direct/src/dcparser/dcFile.cxx

@@ -196,6 +196,25 @@ write(Filename filename, bool brief) const {
 ////////////////////////////////////////////////////////////////////
 bool DCFile::
 write(ostream &out, bool brief) const {
+  Imports::const_iterator ii;
+  for (ii = _imports.begin(); ii != _imports.end(); ++ii) {
+    const Import &import = (*ii);
+    if (import._symbols.empty()) {
+      out << "import " << import._module << "\n";
+    } else {
+      out << "from " << import._module << " import ";
+      ImportSymbols::const_iterator si = import._symbols.begin();
+      out << *si;
+      ++si;
+      while (si != import._symbols.end()) {
+        out << ", " << *si;
+        ++si;
+      }
+      out << "\n";
+    }
+  }
+  out << "\n";
+
   Classes::const_iterator ci;
   for (ci = _classes.begin(); ci != _classes.end(); ++ci) {
     (*ci)->write(out, brief, 0);
@@ -244,6 +263,56 @@ get_class_by_name(const string &name) {
   return (DCClass *)NULL;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: DCFile::get_num_import_modules
+//       Access: Published
+//  Description: Returns the number of import lines read from the .dc
+//               file(s).
+////////////////////////////////////////////////////////////////////
+int DCFile::
+get_num_import_modules() const {
+  return _imports.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCFile::get_import_module
+//       Access: Published
+//  Description: Returns the module named by the nth import line read
+//               from the .dc file(s).
+////////////////////////////////////////////////////////////////////
+string DCFile::
+get_import_module(int n) const {
+  nassertr(n >= 0 && n < (int)_imports.size(), string());
+  return _imports[n]._module;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCFile::get_num_import_symbols
+//       Access: Published
+//  Description: Returns the number of symbols explicitly imported by
+//               the nth import line.  If this is 0, the line is
+//               "import modulename"; if it is more than 0, the line
+//               is "from modulename import symbol, symbol ... ".
+////////////////////////////////////////////////////////////////////
+int DCFile::
+get_num_import_symbols(int n) const {
+  nassertr(n >= 0 && n < (int)_imports.size(), 0);
+  return _imports[n]._symbols.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCFile::get_import_symbol
+//       Access: Published
+//  Description: Returns the ith symbol named by the nth import line
+//               read from the .dc file(s).
+////////////////////////////////////////////////////////////////////
+string DCFile::
+get_import_symbol(int n, int i) const {
+  nassertr(n >= 0 && n < (int)_imports.size(), string());
+  nassertr(i >= 0 && i < (int)_imports[n]._symbols.size(), string());
+  return _imports[n]._symbols[i];
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DCFile::get_hash
 //       Access: Published
@@ -297,3 +366,33 @@ add_class(DCClass *dclass) {
   _classes.push_back(dclass);
   return true;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCFile::add_import_module
+//       Access: Public
+//  Description: Adds a new name to the list of names of Python
+//               modules that are to be imported by the client or AI
+//               to define the code that is associated with the class
+//               interfaces named within the .dc file.
+////////////////////////////////////////////////////////////////////
+void DCFile::
+add_import_module(const string &import_module) {
+  Import import;
+  import._module = import_module;
+  _imports.push_back(import);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DCFile::add_import_symbol
+//       Access: Public
+//  Description: Adds a new name to the list of symbols that are to be
+//               explicitly imported from the most-recently added
+//               module, e.g. "from module_name import symbol".  If
+//               the list of symbols is empty, the syntax is taken to
+//               be "import module_name".
+////////////////////////////////////////////////////////////////////
+void DCFile::
+add_import_symbol(const string &import_symbol) {
+  nassertv(!_imports.empty());
+  _imports.back()._symbols.push_back(import_symbol);
+}

+ 17 - 1
direct/src/dcparser/dcFile.h

@@ -46,14 +46,20 @@ PUBLISHED:
 
   int get_num_classes();
   DCClass *get_class(int n);
-
   DCClass *get_class_by_name(const string &name);
 
+  int get_num_import_modules() const;
+  string get_import_module(int n) const;
+  int get_num_import_symbols(int n) const;
+  string get_import_symbol(int n, int i) const;
+
   unsigned long get_hash() const;
 
 public:
   void generate_hash(HashGenerator &hash) const;
   bool add_class(DCClass *dclass);
+  void add_import_module(const string &import_module);
+  void add_import_symbol(const string &import_symbol);
 
 public:
   // This vector is the primary interface to the distributed classes
@@ -61,6 +67,16 @@ public:
   typedef pvector<DCClass *> Classes;
   Classes _classes;
 
+  typedef pvector<string> ImportSymbols;
+  class Import {
+  public:
+    string _module;
+    ImportSymbols _symbols;
+  };
+
+  typedef pvector<Import> Imports;
+  Imports _imports;
+
 public:
   // This map is built up during parsing for the convenience of the parser.
   typedef pmap<string, DCClass *> ClassesByName;

+ 10 - 0
direct/src/dcparser/dcLexer.lxx

@@ -333,6 +333,16 @@ REALNUM          ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?)
   return KW_DCLASS;
 }
 
+"from" {
+  accept();
+  return KW_FROM;
+}
+
+"import" {
+  accept();
+  return KW_IMPORT;
+}
+
 "int8" {
   accept();
   return KW_INT8;

+ 51 - 4
direct/src/dcparser/dcMolecularField.cxx

@@ -66,8 +66,7 @@ get_atomic(int n) const {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 DCMolecularField::
-DCMolecularField() {
-  _number = 0;
+DCMolecularField(const string &name) : DCField(name) {
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -82,10 +81,10 @@ write(ostream &out, bool brief, int indent_level) const {
 
   if (!_fields.empty()) {
     Fields::const_iterator fi = _fields.begin();
-    out << " : " << (*fi)->_name;
+    out << " : " << (*fi)->get_name();
     ++fi;
     while (fi != _fields.end()) {
-      out << ", " << (*fi)->_name;
+      out << ", " << (*fi)->get_name();
       ++fi;
     }
   }
@@ -114,3 +113,51 @@ generate_hash(HashGenerator &hashgen) const {
     (*fi)->generate_hash(hashgen);
   }
 }
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCMolecularField::do_pack_args
+//       Access: Public, Virtual
+//  Description: Packs the Python arguments beginning from the
+//               indicated index of the indicated tuple into the
+//               datagram, appending to the end of the datagram.
+//               Increments index according to the number of arguments
+//               packed.  Returns true if the tuple contained at least
+//               enough arguments to match the field, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool DCMolecularField::
+do_pack_args(Datagram &datagram, PyObject *tuple, int &index) const {
+  Fields::const_iterator fi;
+  for (fi = _fields.begin(); fi != _fields.end(); ++fi) {
+    DCField *field = (*fi);
+    if (!field->do_pack_args(datagram, tuple, index)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: DCMolecularField::do_unpack_args
+//       Access: Public, Virtual
+//  Description: Unpacks the values from the datagram, beginning at
+//               the current point in the interator, into a vector of
+//               Python objects (each with its own reference count).
+//               Returns true if there are enough values in the
+//               datagram, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool DCMolecularField::
+do_unpack_args(pvector<PyObject *> &args, DatagramIterator &iterator) const {
+  Fields::const_iterator fi;
+  for (fi = _fields.begin(); fi != _fields.end(); ++fi) {
+    if (!(*fi)->do_unpack_args(args, iterator)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+#endif  // HAVE_PYTHON

+ 7 - 1
direct/src/dcparser/dcMolecularField.h

@@ -39,10 +39,16 @@ PUBLISHED:
   DCAtomicField *get_atomic(int n) const;
 
 public:
-  DCMolecularField();
+  DCMolecularField(const string &name);
   virtual void write(ostream &out, bool brief, int indent_level) const;
   virtual void generate_hash(HashGenerator &hash) const;
 
+public:
+#ifdef HAVE_PYTHON
+  virtual bool do_pack_args(Datagram &datagram, PyObject *tuple, int &index) const;
+  virtual bool do_unpack_args(pvector<PyObject *> &args, DatagramIterator &iterator) const;
+#endif
+
 public:
   // These members define the primary interface to the molecular field
   // definition as read from the file.

+ 44 - 13
direct/src/dcparser/dcParser.yxx

@@ -47,6 +47,8 @@ dc_cleanup_parser() {
 %token <str> STRING HEX_STRING IDENTIFIER
 
 %token KW_DCLASS 
+%token KW_FROM 
+%token KW_IMPORT 
 
 %token KW_INT8
 %token KW_INT16
@@ -90,15 +92,15 @@ dc:
         empty
         | dc ';'
         | dc dclass
+        | dc import
         ;
 
 dclass:
         KW_DCLASS IDENTIFIER 
 {
-  current_class = new DCClass;
-  current_class->_name = $2;
+  current_class = new DCClass($2);
   if (!dc_file->add_class(current_class)) {
-    yyerror("Duplicate class name: " + current_class->_name);
+    yyerror("Duplicate class name: " + current_class->get_name());
   }
 }
         dclass_derivation '{' dclass_fields '}'
@@ -118,6 +120,37 @@ dclass_name:
 }
 	;
 
+import:
+	KW_IMPORT IDENTIFIER
+{
+  dc_file->add_import_module($2);
+}
+	| KW_FROM IDENTIFIER KW_IMPORT 
+{
+  dc_file->add_import_module($2);
+}
+	import_symbol_list_or_star
+	;
+
+import_symbol_list_or_star:
+	import_symbol_list
+	| '*'
+{
+  dc_file->add_import_symbol("*");
+}
+	;
+
+import_symbol_list:
+	IDENTIFIER
+{
+  dc_file->add_import_symbol($1);
+}
+	| import_symbol_list ',' IDENTIFIER
+{
+  dc_file->add_import_symbol($3);
+}
+	;
+
 dclass_derivation:
         empty
         | ':' base_list
@@ -127,13 +160,13 @@ base_list:
         dclass_name
 {
   if ($1 != (DCClass *)NULL) {
-    current_class->_parents.push_back($1);
+    current_class->add_parent($1);
   }
 }
         | base_list ',' dclass_name
 {
   if ($3 != (DCClass *)NULL) {
-    current_class->_parents.push_back($3);
+    current_class->add_parent($3);
   }
 }
         ;
@@ -148,10 +181,9 @@ dclass_fields:
 atomic_field:
         IDENTIFIER '('
 {
-  current_atomic = new DCAtomicField;
-  current_atomic->_name = $1;
+  current_atomic = new DCAtomicField($1);
   if (!current_class->add_field(current_atomic)) {
-    yyerror("Duplicate field name: " + current_atomic->_name);
+    yyerror("Duplicate field name: " + current_atomic->get_name());
   }
 }
         parameter_list ')' atomic_flags
@@ -430,10 +462,9 @@ atomic_flags:
 molecular_field:
         IDENTIFIER ':'
 {
-  current_molecular = new DCMolecularField;
-  current_molecular->_name = $1;
+  current_molecular = new DCMolecularField($1);
   if (!current_class->add_field(current_molecular)) {
-    yyerror("Duplicate field name: " + current_molecular->_name);
+    yyerror("Duplicate field name: " + current_molecular->get_name());
   }
 }
         molecular_atom_list
@@ -452,8 +483,8 @@ molecular_atom_list:
     current_molecular->_fields.push_back($3);
     if (current_molecular->_fields[0]->_flags != $3->_flags) {
       yyerror("Mismatched flags in molecule between " + 
-              current_molecular->_fields[0]->_name + " and " +
-              $3->_name);
+              current_molecular->_fields[0]->get_name() + " and " +
+              $3->get_name());
     }
   }
 }

+ 34 - 0
direct/src/dcparser/dcmsgtypes.h

@@ -0,0 +1,34 @@
+// Filename: dcmsgtypes.h
+// Created by:  drose (18May04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef DCMSGTYPES_H
+#define DCMSGTYPES_H
+
+// This file defines the server message types used within this module.
+// It duplicates some symbols defined in MsgTypes.py and
+// AIMsgTypes.py.
+
+#define CLIENT_OBJECT_UPDATE_FIELD                        24
+
+#define STATESERVER_OBJECT_GENERATE_WITH_REQUIRED         2001
+#define STATESERVER_OBJECT_GENERATE_WITH_REQUIRED_OTHER   2003
+#define STATESERVER_OBJECT_UPDATE_FIELD                   2004
+
+
+#endif
+

+ 0 - 117
direct/src/distributed/ClientDistClass.py

@@ -1,117 +0,0 @@
-"""ClientDistClass module: contains the ClientDistClass class"""
-
-from PandaModules import *
-import DirectNotifyGlobal
-import ClientDistUpdate
-import sys
-import ihooks
-
-# These are stored here so that the distributed classes we load on the fly
-# can be exec'ed in the module namespace as if we imported them normally.
-# This is important for redefine to work, and is a good idea anyways.
-moduleGlobals = globals()
-moduleLocals = locals()
-
-class ClientDistClass:
-    notify = DirectNotifyGlobal.directNotify.newCategory("ClientDistClass")
-
-    def __init__(self, dcClass):
-        self.number = dcClass.getNumber()
-        self.name = dcClass.getName()
-
-        # Import the class, and store the constructor
-        try:
-             exec("import " + self.name, moduleGlobals, moduleLocals)
-        except ImportError, e:
-            self.notify.warning("Unable to import %s.py: %s" % (self.name, e))
-            self.constructor = None
-            return
-        self.constructor = eval(self.name + "." + self.name)
-
-        """
-        # This does not seem to work on the client publish
-        stuff = ihooks.current_importer.get_loader().find_module(self.name)
-        if not stuff:
-            self.notify.warning("Unable to import %s.py" % (self.name))
-            self.constructor = None
-            return
-        module = __import__(self.name, moduleGlobals, moduleLocals)
-        # The constructor is really the classObj, which is of course callable
-        self.constructor = getattr(module, self.name, None)
-        """
-
-        self.allFields = self.parseFields(dcClass)
-        self.allCDU = self.createAllCDU(self.allFields, self.constructor)
-        self.number2CDU = self.createNumber2CDUDict(self.allCDU)
-        self.name2CDU = self.createName2CDUDict(self.allCDU)
-        self.broadcastRequiredCDU = self.listBroadcastRequiredCDU(self.allCDU)
-        self.allRequiredCDU = self.listRequiredCDU(self.allCDU)
-
-        # If this assertion fails, you probably had an import error in
-        # a file named in your toon.dc file, or in some file included
-        # in a file named in your toon.dc file.
-        assert(self.constructor != None)
-
-    def parseFields(self, dcClass):
-        fields=[]
-        for i in range(0,dcClass.getNumInheritedFields()):
-            fields.append(dcClass.getInheritedField(i))
-        return fields
-
-    def createAllCDU(self, allFields, classObj):
-        allCDU = []
-        for i in allFields:
-            allCDU.append(ClientDistUpdate.ClientDistUpdate(self, i, classObj))
-        return allCDU
-
-    def createNumber2CDUDict(self, allCDU):
-        dict={}
-        for i in allCDU:
-            dict[i.number] = i
-        return dict
-
-    def createName2CDUDict(self, allCDU):
-        dict={}
-        for i in allCDU:
-            dict[i.name] = i
-        return dict
-
-    def listBroadcastRequiredCDU(self, allCDU):
-        requiredCDU = []
-        for i in allCDU:
-            atom = i.field.asAtomicField()
-            if atom:
-                if (atom.isRequired() and atom.isBroadcast()):
-                    requiredCDU.append(i)
-        return requiredCDU
-
-    def listRequiredCDU(self, allCDU):
-        requiredCDU = []
-        for i in allCDU:
-            atom = i.field.asAtomicField()
-            if atom:
-                if (atom.isRequired()):
-                    requiredCDU.append(i)
-        return requiredCDU
-
-    def updateField(self, do, di):
-        # Get the update field id
-        fieldId = di.getArg(STUint16)
-        # look up the CDU
-        assert(self.number2CDU.has_key(fieldId))
-        cdu = self.number2CDU[fieldId]
-        # Let the cdu finish the job
-        cdu.updateField(self, do, di)
-        return None
-
-    def sendUpdate(self, cr, do, fieldName, args, sendToId = None):
-        # Look up the cdu
-        assert(self.name2CDU.has_key(fieldName))
-        cdu = self.name2CDU[fieldName]
-        # Let the cdu finish the job
-        cdu.sendUpdate(cr, do, args, sendToId)
-        
-
-    
-            
-        

+ 0 - 67
direct/src/distributed/ClientDistUpdate.py

@@ -1,67 +0,0 @@
-"""ClientDistUpdate module: contains the ClientDistUpdate class"""
-
-import DirectNotifyGlobal
-from PyDatagram import PyDatagram
-from MsgTypes import *
-import ihooks
-
-# These are stored here so that the distributed classes we load on the fly
-# can be exec'ed in the module namespace as if we imported them normally.
-# This is important for redefine to work, and is a good idea anyways.
-moduleGlobals = globals()
-moduleLocals = locals()
-
-class ClientDistUpdate:
-    notify = DirectNotifyGlobal.directNotify.newCategory("ClientDistUpdate")
-
-    def __init__(self, cdc, dcField, classObj):
-        self.cdc = cdc
-        self.field = dcField
-        self.number = dcField.getNumber()
-        self.name = dcField.getName()
-        self.types = []
-        self.divisors = []
-        self.deriveTypesFromParticle(dcField)
-        # If there is no func, it will just be None
-        self.func = getattr(classObj, self.name, None)
-
-    def deriveTypesFromParticle(self, dcField):
-        dcFieldAtomic = dcField.asAtomicField()
-        dcFieldMolecular = dcField.asMolecularField()
-        if dcFieldAtomic:
-            for i in range(0, dcFieldAtomic.getNumElements()):
-                self.types.append(dcFieldAtomic.getElementType(i))
-                self.divisors.append(dcFieldAtomic.getElementDivisor(i))
-        elif dcFieldMolecular:
-            for i in range(0, dcFieldMolecular.getNumAtomics()):
-                componentField = dcFieldMolecular.getAtomic(i)
-                for j in range(0, componentField.getNumElements()):
-                    self.types.append(componentField.getElementType(j))
-                    self.divisors.append(componentField.getElementDivisor(j))
-        else:
-            self.notify.error("field is neither atom nor molecule")
-
-    def updateField(self, cdc, do, di):
-        # Get the arguments into a list
-        args = map(lambda type, div: di.getArg(type,div), self.types, self.divisors)
-        assert(self.notify.debug("Received update for %d: %s.%s(%s)" % (do.doId, cdc.name, self.name, args)))
-        # Apply the function to the object with the arguments
-        if self.func != None:
-            apply(self.func, [do] + args)
-
-    def sendUpdate(self, cr, do, args, sendToId = None):
-        if sendToId == None:
-            sendToId = do.doId
-        assert(self.notify.debug("Sending update for %d: %s(%s)" % (sendToId, self.name, args)))
-        datagram = PyDatagram()
-        # Add message type
-        datagram.addUint16(CLIENT_OBJECT_UPDATE_FIELD)
-        # Add the DO id
-        datagram.addUint32(sendToId)
-        # Add the field id
-        datagram.addUint16(self.number)
-        # Add the arguments
-        for arg, type, div in zip(args, self.types, self.divisors):
-            datagram.putArg(arg, type, div)
-        # send the datagram
-        cr.send(datagram)

+ 50 - 93
direct/src/distributed/ClientRepository.py

@@ -4,7 +4,6 @@ from PandaModules import *
 from MsgTypes import *
 import Task
 import DirectNotifyGlobal
-import ClientDistClass
 import CRCache
 import ConnectionRepository
 import PythonUtil
@@ -23,11 +22,8 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
 
         self.recorder = base.recorder
         
-        self.number2cdc={}
-        self.name2cdc={}
         self.doId2do={}
-        self.doId2cdc={}
-        self.parseDcFile()
+        self.readDCFile()
         self.cache=CRCache.CRCache()
         self.serverDelta = 0
 
@@ -97,43 +93,25 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
         """
         return time.time() + self.serverDelta
 
-    def parseDcFile(self):
-        self.dcFile = DCFile()
-        readResult = self.dcFile.readAll()
-        if not readResult:
-            self.notify.error("Could not read dc file.")
-        self.hashVal = self.dcFile.getHash()
-        return self.parseDcClasses(self.dcFile)
-
-    def parseDcClasses(self, dcFile):
-        numClasses = dcFile.getNumClasses()
-        for i in range(0, numClasses):
-            # Create a clientDistClass from the dcClass
-            dcClass = dcFile.getClass(i)
-            clientDistClass = ClientDistClass.ClientDistClass(dcClass)
-            # List the cdc in the number and name dictionaries
-            self.number2cdc[dcClass.getNumber()]=clientDistClass
-            self.name2cdc[dcClass.getName()]=clientDistClass
-
     def handleGenerateWithRequired(self, di):
         # Get the class Id
         classId = di.getArg(STUint16);
         # Get the DO Id
         doId = di.getArg(STUint32)
-        # Look up the cdc
-        cdc = self.number2cdc[classId]
+        # Look up the dclass
+        dclass = self.dclassesByNumber[classId]
         # Create a new distributed object, and put it in the dictionary
-        distObj = self.generateWithRequiredFields(cdc, doId, di)
+        distObj = self.generateWithRequiredFields(dclass, doId, di)
 
     def handleGenerateWithRequiredOther(self, di):
         # Get the class Id
         classId = di.getArg(STUint16);
         # Get the DO Id
         doId = di.getArg(STUint32)
-        # Look up the cdc
-        cdc = self.number2cdc[classId]
+        # Look up the dclass
+        dclass = self.dclassesByNumber[classId]
         # Create a new distributed object, and put it in the dictionary
-        distObj = self.generateWithRequiredOtherFields(cdc, doId, di)
+        distObj = self.generateWithRequiredOtherFields(dclass, doId, di)
 
     def handleQuietZoneGenerateWithRequired(self, di):
         # Special handler for quiet zone generates -- we need to filter
@@ -141,13 +119,13 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
         classId = di.getArg(STUint16);
         # Get the DO Id
         doId = di.getArg(STUint32)
-        # Look up the cdc
-        cdc = self.number2cdc[classId]
+        # Look up the dclass
+        dclass = self.dclassesByNumber[classId]
         # If the class is a neverDisable class (which implies uberzone) we
         # should go ahead and generate it even though we are in the quiet zone
-        if cdc.constructor.neverDisable:
+        if dclass.getClassDef().neverDisable:
             # Create a new distributed object, and put it in the dictionary
-            distObj = self.generateWithRequiredFields(cdc, doId, di)
+            distObj = self.generateWithRequiredFields(dclass, doId, di)
 
     def handleQuietZoneGenerateWithRequiredOther(self, di):
         # Special handler for quiet zone generates -- we need to filter
@@ -155,83 +133,89 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
         classId = di.getArg(STUint16);
         # Get the DO Id
         doId = di.getArg(STUint32)
-        # Look up the cdc
-        cdc = self.number2cdc[classId]
+        # Look up the dclass
+        dclass = self.dclassesByNumber[classId]
         # If the class is a neverDisable class (which implies uberzone) we
         # should go ahead and generate it even though we are in the quiet zone
-        if cdc.constructor.neverDisable:
+        if dclass.getClassDef().neverDisable:
             # Create a new distributed object, and put it in the dictionary
-            distObj = self.generateWithRequiredOtherFields(cdc, doId, di)
+            distObj = self.generateWithRequiredOtherFields(dclass, doId, di)
 
-    def generateWithRequiredFields(self, cdc, doId, di):
+    def generateWithRequiredFields(self, dclass, doId, di):
         if self.doId2do.has_key(doId):
             # ...it is in our dictionary.
             # Just update it.
             distObj = self.doId2do[doId]
+            assert(distObj.dclass == dclass)
             distObj.generate()
-            distObj.updateRequiredFields(cdc, di)
+            distObj.updateRequiredFields(dclass, di)
             # updateRequiredFields calls announceGenerate
         elif self.cache.contains(doId):
             # ...it is in the cache.
             # Pull it out of the cache:
             distObj = self.cache.retrieve(doId)
-            # put it in both dictionaries:
+            assert(distObj.dclass == dclass)
+            # put it in the dictionary:
             self.doId2do[doId] = distObj
-            self.doId2cdc[doId] = cdc
             # and update it.
             distObj.generate()
-            distObj.updateRequiredFields(cdc, di)
+            distObj.updateRequiredFields(dclass, di)
             # updateRequiredFields calls announceGenerate
         else:
             # ...it is not in the dictionary or the cache.
             # Construct a new one
-            distObj = cdc.constructor(self)
+            classDef = dclass.getClassDef()
+            if classDef == None:
+                self.notify.error("Could not create an undefined %s object." % (dclass.getName()))
+            distObj = classDef(self)
+            distObj.dclass = dclass
             # Assign it an Id
             distObj.doId = doId
-            # Put the new do in both dictionaries
+            # Put the new do in the dictionary
             self.doId2do[doId] = distObj
-            self.doId2cdc[doId] = cdc
             # Update the required fields
             distObj.generateInit()  # Only called when constructed
             distObj.generate()
-            distObj.updateRequiredFields(cdc, di)
+            distObj.updateRequiredFields(dclass, di)
             # updateRequiredFields calls announceGenerate
         return distObj
 
-    def generateWithRequiredOtherFields(self, cdc, doId, di):
+    def generateWithRequiredOtherFields(self, dclass, doId, di):
         if self.doId2do.has_key(doId):
             # ...it is in our dictionary.
             # Just update it.
             distObj = self.doId2do[doId]
+            assert(distObj.dclass == dclass)
             distObj.generate()
-            distObj.updateRequiredOtherFields(cdc, di)
+            distObj.updateRequiredOtherFields(dclass, di)
             # updateRequiredOtherFields calls announceGenerate
         elif self.cache.contains(doId):
             # ...it is in the cache.
             # Pull it out of the cache:
             distObj = self.cache.retrieve(doId)
-            # put it in both dictionaries:
+            assert(distObj.dclass == dclass)
+            # put it in the dictionary:
             self.doId2do[doId] = distObj
-            self.doId2cdc[doId] = cdc
             # and update it.
             distObj.generate()
-            distObj.updateRequiredOtherFields(cdc, di)
+            distObj.updateRequiredOtherFields(dclass, di)
             # updateRequiredOtherFields calls announceGenerate
         else:
             # ...it is not in the dictionary or the cache.
             # Construct a new one
-            if cdc.constructor == None:
-                self.notify.error("Could not create an undefined %s object." % (cdc.name))
-            distObj = cdc.constructor(self)
+            classDef = dclass.getClassDef()
+            if classDef == None:
+                self.notify.error("Could not create an undefined %s object." % (dclass.getName()))
+            distObj = classDef(self)
+            distObj.dclass = dclass
             # Assign it an Id
             distObj.doId = doId
-            # Put the new do in both dictionaries
+            # Put the new do in the dictionary
             self.doId2do[doId] = distObj
-            self.doId2cdc[doId] = cdc
             # Update the required fields
             distObj.generateInit()  # Only called when constructed
             distObj.generate()
-            distObj.updateRequiredOtherFields(cdc, di)
+            distObj.updateRequiredOtherFields(dclass, di)
             # updateRequiredOtherFields calls announceGenerate
         return distObj
 
@@ -247,10 +231,8 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
         if self.doId2do.has_key(doId):
             # Look up the object
             distObj = self.doId2do[doId]
-            # remove the object from both dictionaries
+            # remove the object from the dictionary
             del(self.doId2do[doId])
-            del(self.doId2cdc[doId])
-            assert(len(self.doId2do) == len(self.doId2cdc))
 
             # Only cache the object if it is a "cacheable" type
             # object; this way we don't clutter up the caches with
@@ -285,14 +267,11 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
         This is not a distributed message and does not delete the
         object on the server or on any other client.
         """
-        # If it is in the dictionaries, remove it.
+        # If it is in the dictionary, remove it.
         if self.doId2do.has_key(doId):
             obj = self.doId2do[doId]
-            # Remove it from the dictionaries
+            # Remove it from the dictionary
             del(self.doId2do[doId])
-            del(self.doId2cdc[doId])
-            # Sanity check the dictionaries
-            assert(len(self.doId2do) == len(self.doId2cdc))
             # Disable, announce, and delete the object itself...
             # unless delayDelete is on...
             obj.deleteOrDelay()
@@ -313,10 +292,9 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
         # Find the DO
             
         do = self.doId2do.get(doId)
-        cdc = self.doId2cdc.get(doId)
-        if (do != None and cdc != None):
-            # Let the cdc finish the job
-            cdc.updateField(do, di)
+        if (do != None):
+            # Let the dclass finish the job
+            do.dclass.receiveUpdate(do, di)
         else:
             ClientRepository.notify.warning(
                 "Asked to update non-existent DistObj " + str(doId))
@@ -456,32 +434,11 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
                               "heartBeat")        
         
     def sendUpdate(self, do, fieldName, args, sendToId = None):
-        # Get the DO id
-        doId = do.doId
-        # Get the cdc
-        cdc = self.doId2cdc.get(doId, None)
-        if cdc:
-            # Let the cdc finish the job
-            cdc.sendUpdate(self, do, fieldName, args, sendToId)
+        dg = do.dclass.clientFormatUpdate(fieldName, sendToId or do.doId, args)
+        self.send(dg)
 
     def replaceMethod(self, oldMethod, newFunction):
-        foundIt = 0
-        import new
-        # Iterate over the ClientDistClasses
-        for cdc in self.number2cdc.values():
-            # Iterate over the ClientDistUpdates
-            for cdu in cdc.allCDU:
-                method = cdu.func
-                # See if this is a match
-                if (method and (method.im_func == oldMethod)):
-                    # Create a new unbound method out of this new function
-                    newMethod = new.instancemethod(newFunction,
-                                                   method.im_self,
-                                                   method.im_class)
-                    # Set the new method on the cdu
-                    cdu.func = newMethod
-                    foundIt = 1
-        return foundIt
+        return 0
 
     def getAllOfType(self, type):
         # Returns a list of all DistributedObjects in the repository

+ 87 - 0
direct/src/distributed/ConnectionRepository.py

@@ -4,6 +4,8 @@ import DirectNotifyGlobal
 import DirectObject
 from PyDatagram import PyDatagram
 
+import types
+
 class ConnectionRepository(DirectObject.DirectObject):
     """
     This is a base class for things that know how to establish a
@@ -54,6 +56,91 @@ class ConnectionRepository(DirectObject.DirectObject):
         self.rsDoReport = self.config.GetBool('reader-statistics', 0)
         self.rsUpdateInterval = self.config.GetDouble('reader-statistics-interval', 10)
 
+    def readDCFile(self, dcFileNames = None):
+
+        """ Reads in the dc files listed in dcFileNames, or if
+        dcFileNames is None, reads in all of the dc files listed in
+        the Configrc file.
+
+        The resulting DCFile object is stored in self.dcFile. """
+        
+        self.dcFile = DCFile()
+        self.dclassesByName = {}
+        self.dclassesByNumber = {}
+        
+        dcImports = {}
+        if dcFileNames == None:
+            readResult = self.dcFile.readAll()
+            if not readResult:
+                self.notify.error("Could not read dc file.")
+        else:
+            for dcFileName in dcFileNames:
+                readResult = self.dcFile.read(Filename(dcFileName))
+                if not readResult:
+                    self.notify.error("Could not read dc file: %s" % (dcFileName))
+        self.hashVal = self.dcFile.getHash()
+
+        # Now import all of the modules required by the DC file.
+        for n in range(self.dcFile.getNumImportModules()):
+            moduleName = self.dcFile.getImportModule(n)
+            moduleName = self.mangleDCName(moduleName)
+
+            module = __import__(moduleName, globals(), locals())
+
+            if self.dcFile.getNumImportSymbols(n) > 0:
+                # "from moduleName import symbolName, symbolName, ..."
+                # Copy just the named symbols into the dictionary.
+                for i in range(self.dcFile.getNumImportSymbols(n)):
+                    symbolName = self.dcFile.getImportSymbol(n, i)
+                    if symbolName == '*':
+                        # Get all symbols.
+                        dcImports.update(module.__dict__)
+                    else:
+                        if not hasattr(module, symbolName):
+                            self.notify.error("Symbol %s not found in module %s." % (
+                                symbolName, moduleName))
+                        dcImports[symbolName] = getattr(module, symbolName)
+            else:
+                # "import moduleName"
+                # Copy the module itself into the dictionary.
+                dcImports[moduleName] = module
+
+        # Now get the class definition for the classes named in the DC
+        # file.
+        for i in range(self.dcFile.getNumClasses()):
+            dclass = self.dcFile.getClass(i)
+            number = dclass.getNumber()
+            className = dclass.getName()
+            className = self.mangleDCName(className)
+
+            # Does the class have a definition defined in the newly
+            # imported namespace?
+            classDef = dcImports.get(className)
+            if classDef == None:
+                self.notify.info("No class definition for %s." % (className))
+            else:
+                if type(classDef) == types.ModuleType:
+                    if not hasattr(classDef, className):
+                        self.notify.error("Module %s does not define class %s." % (className, className))
+                    classDef = getattr(classDef, className)
+                    
+                if type(classDef) != types.ClassType:
+                    self.notify.error("Symbol %s is not a class name." % (className))
+                else:
+                    dclass.setClassDef(classDef)
+
+            self.dclassesByName[className] = dclass
+            self.dclassesByNumber[number] = dclass
+
+    def mangleDCName(self, name):
+        """ This is provided as a hook so that derived classes
+        (e.g. the AIRepository) can rename symbols from the .dc file
+        according to the conventions associated with this particular
+        repository (e.g., an AIRepository appends 'AI' to class and
+        module names)."""
+        
+        return name
+
     def connect(self, serverList, 
                 successCallback = None, successArgs = [],
                 failureCallback = None, failureArgs = []):

+ 8 - 15
direct/src/distributed/DistributedObject.py

@@ -194,30 +194,23 @@ class DistributedObject(PandaObject):
         """
         return self.doId
     
-    def updateRequiredFields(self, cdc, di):
-        for i in cdc.broadcastRequiredCDU:
-            i.updateField(cdc, self, di)
+    def updateRequiredFields(self, dclass, di):
+        dclass.receiveUpdateBroadcastRequired(self, di)
         self.announceGenerate()
     
-    def updateAllRequiredFields(self, cdc, di):
-        for i in cdc.allRequiredCDU:
-            i.updateField(cdc, self, di)
+    def updateAllRequiredFields(self, dclass, di):
+        dclass.receiveUpdateAllRequired(self, di)
         self.announceGenerate()
 
-    def updateRequiredOtherFields(self, cdc, di):
+    def updateRequiredOtherFields(self, dclass, di):
         # First, update the required fields
-        for i in cdc.broadcastRequiredCDU:
-            i.updateField(cdc, self, di)
+        dclass.receiveUpdateBroadcastRequired(self, di)
 
         # Announce generate after updating all the required fields,
         # but before we update the non-required fields.
         self.announceGenerate()
-        
-        # Determine how many other fields there are
-        numberOfOtherFields = di.getArg(STUint16)
-        # Update each of the other fields
-        for i in range(numberOfOtherFields):
-            cdc.updateField(self, di)
+
+        dclass.receiveUpdateOther(self, di)
 
     def sendUpdate(self, fieldName, args = [], sendToId = None):
         self.cr.sendUpdate(self, fieldName, args, sendToId)