move AIDistClass etc. into C++
This commit is contained in:
parent
c6e0a88590
commit
8625787a5e
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 = []):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue