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