From 8625787a5ea734d6300108be56667ebcc296ae19 Mon Sep 17 00:00:00 2001 From: David Rose Date: Wed, 19 May 2004 04:49:09 +0000 Subject: [PATCH] move AIDistClass etc. into C++ --- direct/src/dcparser/dcAtomicField.cxx | 422 +++++++++++++++++- direct/src/dcparser/dcAtomicField.h | 13 +- direct/src/dcparser/dcClass.cxx | 356 ++++++++++++++- direct/src/dcparser/dcClass.h | 43 +- direct/src/dcparser/dcField.cxx | 127 +++++- direct/src/dcparser/dcField.h | 32 +- direct/src/dcparser/dcFile.cxx | 99 ++++ direct/src/dcparser/dcFile.h | 18 +- direct/src/dcparser/dcLexer.lxx | 10 + direct/src/dcparser/dcMolecularField.cxx | 55 ++- direct/src/dcparser/dcMolecularField.h | 8 +- direct/src/dcparser/dcParser.yxx | 57 ++- direct/src/dcparser/dcmsgtypes.h | 34 ++ direct/src/distributed/ClientDistClass.py | 117 ----- direct/src/distributed/ClientDistUpdate.py | 67 --- direct/src/distributed/ClientRepository.py | 143 +++--- .../src/distributed/ConnectionRepository.py | 87 ++++ direct/src/distributed/DistributedObject.py | 23 +- 18 files changed, 1368 insertions(+), 343 deletions(-) create mode 100755 direct/src/dcparser/dcmsgtypes.h delete mode 100644 direct/src/distributed/ClientDistClass.py delete mode 100644 direct/src/distributed/ClientDistUpdate.py diff --git a/direct/src/dcparser/dcAtomicField.cxx b/direct/src/dcparser/dcAtomicField.cxx index 704bbef220..90c7252214 100644 --- a/direct/src/dcparser/dcAtomicField.cxx +++ b/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 &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 diff --git a/direct/src/dcparser/dcAtomicField.h b/direct/src/dcparser/dcAtomicField.h index 73f0a4f818..e114ab6fb9 100644 --- a/direct/src/dcparser/dcAtomicField.h +++ b/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 &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; diff --git a/direct/src/dcparser/dcClass.cxx b/direct/src/dcparser/dcClass.cxx index bfb12002c2..19cc83867c 100644 --- a/direct/src/dcparser/dcClass.cxx +++ b/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); +} diff --git a/direct/src/dcparser/dcClass.h b/direct/src/dcparser/dcClass.h index 5df5afae55..ee4b19d5ab 100644 --- a/direct/src/dcparser/dcClass.h +++ b/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 Fields; Fields _fields; + PyObject *_class_def; + public: // These members are built up during parsing for the convenience of // the parser. typedef pmap FieldsByName; FieldsByName _fields_by_name; + + friend class DCFile; }; #endif diff --git a/direct/src/dcparser/dcField.cxx b/direct/src/dcparser/dcField.cxx index 9196dac0a7..a3193ce51b 100644 --- a/direct/src/dcparser/dcField.cxx +++ b/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 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 diff --git a/direct/src/dcparser/dcField.h b/direct/src/dcparser/dcField.h index 41ae10aeaa..c4a416a94d 100644 --- a/direct/src/dcparser/dcField.h +++ b/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 + +// 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 &args, DatagramIterator &iterator) const=0; +#endif + + friend class DCClass; }; #endif diff --git a/direct/src/dcparser/dcFile.cxx b/direct/src/dcparser/dcFile.cxx index 6856c320a1..1f56f9b22b 100644 --- a/direct/src/dcparser/dcFile.cxx +++ b/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); +} diff --git a/direct/src/dcparser/dcFile.h b/direct/src/dcparser/dcFile.h index ff4b39ec55..ed30737dfa 100644 --- a/direct/src/dcparser/dcFile.h +++ b/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 Classes; Classes _classes; + typedef pvector ImportSymbols; + class Import { + public: + string _module; + ImportSymbols _symbols; + }; + + typedef pvector Imports; + Imports _imports; + public: // This map is built up during parsing for the convenience of the parser. typedef pmap ClassesByName; diff --git a/direct/src/dcparser/dcLexer.lxx b/direct/src/dcparser/dcLexer.lxx index 609e90eb8e..d3e6e28983 100644 --- a/direct/src/dcparser/dcLexer.lxx +++ b/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; diff --git a/direct/src/dcparser/dcMolecularField.cxx b/direct/src/dcparser/dcMolecularField.cxx index 0c5a71afa0..23ab09bc9e 100644 --- a/direct/src/dcparser/dcMolecularField.cxx +++ b/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 &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 diff --git a/direct/src/dcparser/dcMolecularField.h b/direct/src/dcparser/dcMolecularField.h index 183c317489..69db0d094f 100644 --- a/direct/src/dcparser/dcMolecularField.h +++ b/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 &args, DatagramIterator &iterator) const; +#endif + public: // These members define the primary interface to the molecular field // definition as read from the file. diff --git a/direct/src/dcparser/dcParser.yxx b/direct/src/dcparser/dcParser.yxx index 67c5a9e703..535426a424 100644 --- a/direct/src/dcparser/dcParser.yxx +++ b/direct/src/dcparser/dcParser.yxx @@ -47,6 +47,8 @@ dc_cleanup_parser() { %token 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()); } } } diff --git a/direct/src/dcparser/dcmsgtypes.h b/direct/src/dcparser/dcmsgtypes.h new file mode 100755 index 0000000000..c40123643c --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 + diff --git a/direct/src/distributed/ClientDistClass.py b/direct/src/distributed/ClientDistClass.py deleted file mode 100644 index 173743cafd..0000000000 --- a/direct/src/distributed/ClientDistClass.py +++ /dev/null @@ -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) - - - - - diff --git a/direct/src/distributed/ClientDistUpdate.py b/direct/src/distributed/ClientDistUpdate.py deleted file mode 100644 index 3782e4099f..0000000000 --- a/direct/src/distributed/ClientDistUpdate.py +++ /dev/null @@ -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) diff --git a/direct/src/distributed/ClientRepository.py b/direct/src/distributed/ClientRepository.py index 8dc6d9a4ba..d80be01607 100644 --- a/direct/src/distributed/ClientRepository.py +++ b/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 diff --git a/direct/src/distributed/ConnectionRepository.py b/direct/src/distributed/ConnectionRepository.py index aa3736ae2d..b11e28ccc7 100644 --- a/direct/src/distributed/ConnectionRepository.py +++ b/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 = []): diff --git a/direct/src/distributed/DistributedObject.py b/direct/src/distributed/DistributedObject.py index 053931c52b..702c87ac0b 100644 --- a/direct/src/distributed/DistributedObject.py +++ b/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)