move AIDistClass etc. into C++

This commit is contained in:
David Rose 2004-05-19 04:49:09 +00:00
parent c6e0a88590
commit 8625787a5e
18 changed files with 1368 additions and 343 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 = []):

View File

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