// Filename: dcClass.cxx // Created by: drose (05Oct00) // //////////////////////////////////////////////////////////////////// // // 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 . // //////////////////////////////////////////////////////////////////// #include "dcClass.h" #include "dcFile.h" #include "dcAtomicField.h" #include "hashGenerator.h" #include "dcindent.h" #include "dcmsgtypes.h" #include "dcClassParameter.h" #ifdef WITHIN_PANDA #include "pStatTimer.h" #ifndef CPPPARSER PStatCollector DCClass::_update_pcollector("App:Show code:readerPollTask:Update"); PStatCollector DCClass::_generate_pcollector("App:Show code:readerPollTask:Generate"); #endif // CPPPARSER ConfigVariableBool dc_multiple_inheritance ("dc-multiple-inheritance", true, PRC_DESC("Set this true to support multiple inheritance in the dc file. " "If this is false, the old way, multiple inheritance is not " "supported, but field numbers will be numbered sequentially, " "which may be required to support old code that assumed this.")); ConfigVariableBool dc_virtual_inheritance ("dc-virtual-inheritance", false, PRC_DESC("Set this true to support proper virtual inheritance in the " "dc file, so that diamond-of-death type constructs can be used. " "This also enables shadowing (overloading) of inherited method " "names from a base class.")); #endif // WITHIN_PANDA class SortFieldsByIndex { public: inline bool operator ()(const DCField *a, const DCField *b) const { return a->get_number() < b->get_number(); } }; //////////////////////////////////////////////////////////////////// // Function: DCClass::Constructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// DCClass:: DCClass(DCFile *dc_file, const string &name, bool is_struct, bool bogus_class) : #ifdef WITHIN_PANDA _class_update_pcollector(_update_pcollector, name), _class_generate_pcollector(_generate_pcollector, name), #endif _dc_file(dc_file), _name(name), _is_struct(is_struct), _bogus_class(bogus_class) { _number = -1; _constructor = NULL; #ifdef HAVE_PYTHON _class_def = NULL; _owner_class_def = NULL; #endif } //////////////////////////////////////////////////////////////////// // Function: DCClass::Destructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// DCClass:: ~DCClass() { if (_constructor != (DCField *)NULL) { delete _constructor; } Fields::iterator fi; for (fi = _fields.begin(); fi != _fields.end(); ++fi) { delete (*fi); } #ifdef HAVE_PYTHON Py_XDECREF(_class_def); Py_XDECREF(_owner_class_def); #endif } //////////////////////////////////////////////////////////////////// // Function: DCClass::as_class // Access: Published, Virtual // Description: //////////////////////////////////////////////////////////////////// DCClass *DCClass:: as_class() { return this; } //////////////////////////////////////////////////////////////////// // Function: DCClass::as_class // Access: Published, Virtual // Description: //////////////////////////////////////////////////////////////////// const DCClass *DCClass:: as_class() const { return this; } //////////////////////////////////////////////////////////////////// // Function: DCClass::get_num_parents // Access: Published // Description: Returns the number of base classes this class // inherits from. //////////////////////////////////////////////////////////////////// int DCClass:: get_num_parents() const { return _parents.size(); } //////////////////////////////////////////////////////////////////// // Function: DCClass::get_parent // Access: Published // Description: Returns the nth parent class this class inherits // from. //////////////////////////////////////////////////////////////////// DCClass *DCClass:: get_parent(int n) const { nassertr(n >= 0 && n < (int)_parents.size(), NULL); return _parents[n]; } //////////////////////////////////////////////////////////////////// // Function: DCClass::has_constructor // Access: Published // Description: Returns true if this class has a constructor method, // false if it just uses the default constructor. //////////////////////////////////////////////////////////////////// bool DCClass:: has_constructor() const { return (_constructor != (DCField *)NULL); } //////////////////////////////////////////////////////////////////// // Function: DCClass::get_constructor // Access: Published // Description: Returns the constructor method for this class if it // is defined, or NULL if the class uses the default // constructor. //////////////////////////////////////////////////////////////////// DCField *DCClass:: get_constructor() const { return _constructor; } //////////////////////////////////////////////////////////////////// // Function: DCClass::get_num_fields // Access: Published // Description: Returns the number of fields defined directly in this // class, ignoring inheritance. //////////////////////////////////////////////////////////////////// int DCClass:: get_num_fields() const { return _fields.size(); } //////////////////////////////////////////////////////////////////// // Function: DCClass::get_field // 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) const { #ifndef NDEBUG //[ if (n < 0 || n >= (int)_fields.size()) { cerr << *this << " " << "n:" << n << " _fields.size():" << (int)_fields.size() << endl; // __asm { int 3 } } #endif //] nassertr_always(n >= 0 && n < (int)_fields.size(), NULL); return _fields[n]; } //////////////////////////////////////////////////////////////////// // Function: DCClass::get_field_by_name // 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 // searched, so the value returned may not actually be a // field within this class. Returns NULL if there is no // such field defined. //////////////////////////////////////////////////////////////////// DCField *DCClass:: get_field_by_name(const string &name) const { FieldsByName::const_iterator ni; ni = _fields_by_name.find(name); if (ni != _fields_by_name.end()) { return (*ni).second; } // We didn't have such a field, so check our parents. Parents::const_iterator pi; for (pi = _parents.begin(); pi != _parents.end(); ++pi) { DCField *result = (*pi)->get_field_by_name(name); if (result != (DCField *)NULL) { return result; } } // Nobody knew what this field is. return (DCField *)NULL; } //////////////////////////////////////////////////////////////////// // Function: DCClass::get_field_by_index // Access: Published // Description: Returns a pointer to the DCField that has the // indicated index number. If the numbered field is not // found in the current class, the parent classes will // be searched, so the value returned may not actually // be a field within this class. Returns NULL if there // is no such field defined. //////////////////////////////////////////////////////////////////// DCField *DCClass:: get_field_by_index(int index_number) const { FieldsByIndex::const_iterator ni; ni = _fields_by_index.find(index_number); if (ni != _fields_by_index.end()) { return (*ni).second; } // We didn't have such a field, so check our parents. Parents::const_iterator pi; for (pi = _parents.begin(); pi != _parents.end(); ++pi) { DCField *result = (*pi)->get_field_by_index(index_number); if (result != (DCField *)NULL) { // Cache this result for future lookups. ((DCClass *)this)->_fields_by_index[index_number] = result; return result; } } // Nobody knew what this field is. return (DCField *)NULL; } //////////////////////////////////////////////////////////////////// // Function: DCClass::get_num_inherited_fields // Access: Published // Description: Returns the total number of field fields defined in // this class and all ancestor classes. //////////////////////////////////////////////////////////////////// int DCClass:: get_num_inherited_fields() const { if (dc_virtual_inheritance) { if (_dc_file != (DCFile *)NULL) { _dc_file->check_inherited_fields(); } if (_inherited_fields.empty()) { ((DCClass *)this)->rebuild_inherited_fields(); } return (int)_inherited_fields.size(); } else { int num_fields = get_num_fields(); Parents::const_iterator pi; for (pi = _parents.begin(); pi != _parents.end(); ++pi) { num_fields += (*pi)->get_num_inherited_fields(); } return num_fields; } } //////////////////////////////////////////////////////////////////// // Function: DCClass::get_inherited_field // Access: Published // Description: Returns the nth field field in the class and all of // its ancestors. // // This *used* to be the same thing as // get_field_by_index(), back when the fields were // numbered sequentially within a class's inheritance // hierarchy. Now that fields have a globally unique // index number, this is no longer true. //////////////////////////////////////////////////////////////////// DCField *DCClass:: get_inherited_field(int n) const { if (dc_virtual_inheritance) { if (_dc_file != (DCFile *)NULL) { _dc_file->check_inherited_fields(); } if (_inherited_fields.empty()) { ((DCClass *)this)->rebuild_inherited_fields(); } nassertr(n >= 0 && n < (int)_inherited_fields.size(), NULL); return _inherited_fields[n]; } else { Parents::const_iterator pi; for (pi = _parents.begin(); pi != _parents.end(); ++pi) { int psize = (*pi)->get_num_inherited_fields(); if (n < psize) { return (*pi)->get_inherited_field(n); } n -= psize; } return get_field(n); } } //////////////////////////////////////////////////////////////////// // Function : DCClass::output // Access : Published, Virtual // Description : Write a string representation of this instance to // . //////////////////////////////////////////////////////////////////// void DCClass:: output(ostream &out) const { if (_is_struct) { out << "struct"; } else { out << "dclass"; } if (!_name.empty()) { out << " " << _name; } } #ifdef HAVE_PYTHON //////////////////////////////////////////////////////////////////// // Function: DCClass::has_class_def // Access: Published // Description: Returns true if the DCClass object has an associated // Python class definition, false otherwise. //////////////////////////////////////////////////////////////////// bool DCClass:: has_class_def() const { return (_class_def != NULL); } #endif // HAVE_PYTHON #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_None; } Py_INCREF(_class_def); return _class_def; } #endif // HAVE_PYTHON #ifdef HAVE_PYTHON //////////////////////////////////////////////////////////////////// // Function: DCClass::has_owner_class_def // Access: Published // Description: Returns true if the DCClass object has an associated // Python owner class definition, false otherwise. //////////////////////////////////////////////////////////////////// bool DCClass:: has_owner_class_def() const { return (_owner_class_def != NULL); } #endif // HAVE_PYTHON #ifdef HAVE_PYTHON //////////////////////////////////////////////////////////////////// // Function: DCClass::set_owner_class_def // Access: Published // Description: Sets the owner class object associated with this // DistributedClass. This object will be used to // construct new owner instances of the class. //////////////////////////////////////////////////////////////////// void DCClass:: set_owner_class_def(PyObject *owner_class_def) { Py_XINCREF(owner_class_def); Py_XDECREF(_owner_class_def); _owner_class_def = owner_class_def; } #endif // HAVE_PYTHON #ifdef HAVE_PYTHON //////////////////////////////////////////////////////////////////// // Function: DCClass::get_owner_class_def // Access: Published // Description: Returns the owner class object that was previously // associated with this DistributedClass. This will // return a new reference to the object. //////////////////////////////////////////////////////////////////// PyObject *DCClass:: get_owner_class_def() const { if (_owner_class_def == NULL) { return Py_None; } Py_INCREF(_owner_class_def); return _owner_class_def; } #endif // HAVE_PYTHON #ifdef HAVE_PYTHON //////////////////////////////////////////////////////////////////// // Function: DCClass::receive_update // Access: Published // Description: Extracts the update message out of the packer and // applies it to the indicated object by calling the // appropriate method. //////////////////////////////////////////////////////////////////// void DCClass:: receive_update(PyObject *distobj, DatagramIterator &di) const { #ifdef WITHIN_PANDA PStatTimer timer(((DCClass *)this)->_class_update_pcollector); #endif DCPacker packer; packer.set_unpack_data(di.get_remaining_bytes()); int field_id = packer.raw_unpack_uint16(); DCField *field = get_field_by_index(field_id); if (field == (DCField *)NULL) { ostringstream strm; strm << "Received update for field " << field_id << ", not in class " << get_name(); nassert_raise(strm.str()); return; } packer.begin_unpack(field); field->receive_update(packer, distobj); packer.end_unpack(); di.skip_bytes(packer.get_num_unpacked_bytes()); } #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 &di) const { #ifdef WITHIN_PANDA PStatTimer timer(((DCClass *)this)->_class_update_pcollector); #endif DCPacker packer; packer.set_unpack_data(di.get_remaining_bytes()); int num_fields = get_num_inherited_fields(); for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) { DCField *field = get_inherited_field(i); if (field->as_molecular_field() == (DCMolecularField *)NULL && field->is_required() && field->is_broadcast()) { packer.begin_unpack(field); field->receive_update(packer, distobj); if (!packer.end_unpack()) { break; } } } di.skip_bytes(packer.get_num_unpacked_bytes()); } #endif // HAVE_PYTHON #ifdef HAVE_PYTHON //////////////////////////////////////////////////////////////////// // Function: DCClass::receive_update_broadcast_required_owner // 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 ownrecv". Should // be used for 'owner-view' objects. //////////////////////////////////////////////////////////////////// void DCClass:: receive_update_broadcast_required_owner(PyObject *distobj, DatagramIterator &di) const { #ifdef WITHIN_PANDA PStatTimer timer(((DCClass *)this)->_class_update_pcollector); #endif DCPacker packer; packer.set_unpack_data(di.get_remaining_bytes()); int num_fields = get_num_inherited_fields(); for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) { DCField *field = get_inherited_field(i); if (field->as_molecular_field() == (DCMolecularField *)NULL && field->is_required()) { packer.begin_unpack(field); if (field->is_ownrecv()) { field->receive_update(packer, distobj); } else { // It's not an ownrecv field; skip over it. It's difficult // to filter this on the server, ask Roger for the reason. packer.unpack_skip(); } if (!packer.end_unpack()) { break; } } } di.skip_bytes(packer.get_num_unpacked_bytes()); } #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 &di) const { #ifdef WITHIN_PANDA PStatTimer timer(((DCClass *)this)->_class_update_pcollector); #endif DCPacker packer; packer.set_unpack_data(di.get_remaining_bytes()); int num_fields = get_num_inherited_fields(); for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) { DCField *field = get_inherited_field(i); if (field->as_molecular_field() == (DCMolecularField *)NULL && field->is_required()) { packer.begin_unpack(field); field->receive_update(packer, distobj); if (!packer.end_unpack()) { break; } } } di.skip_bytes(packer.get_num_unpacked_bytes()); } #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 &di) const { #ifdef WITHIN_PANDA PStatTimer timer(((DCClass *)this)->_class_update_pcollector); #endif int num_fields = di.get_uint16(); for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) { receive_update(distobj, di); } } #endif // HAVE_PYTHON #ifdef HAVE_PYTHON //////////////////////////////////////////////////////////////////// // Function: DCClass::direct_update // Access: Published // Description: Processes an update for a named field from a packed // value blob. //////////////////////////////////////////////////////////////////// void DCClass:: direct_update(PyObject *distobj, const string &field_name, const string &value_blob) { DCField *field = get_field_by_name(field_name); nassertv_always(field != NULL); DCPacker packer; packer.set_unpack_data(value_blob); packer.begin_unpack(field); field->receive_update(packer, distobj); packer.end_unpack(); } #endif // HAVE_PYTHON #ifdef HAVE_PYTHON //////////////////////////////////////////////////////////////////// // Function: DCClass::direct_update // Access: Published // Description: Processes an update for a named field from a packed // datagram. //////////////////////////////////////////////////////////////////// void DCClass:: direct_update(PyObject *distobj, const string &field_name, const Datagram &datagram) { direct_update(distobj, field_name, datagram.get_message()); } #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. // // Returns true on success, false on failure. //////////////////////////////////////////////////////////////////// bool DCClass:: pack_required_field(Datagram &datagram, PyObject *distobj, const DCField *field) const { DCPacker packer; packer.begin_pack(field); if (!pack_required_field(packer, distobj, field)) { return false; } if (!packer.end_pack()) { return false; } datagram.append_data(packer.get_data(), packer.get_length()); return true; } #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 packer. 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. // // Returns true on success, false on failure. //////////////////////////////////////////////////////////////////// bool DCClass:: pack_required_field(DCPacker &packer, PyObject *distobj, const DCField *field) const { const DCParameter *parameter = field->as_parameter(); if (parameter != (DCParameter *)NULL) { // This is the easy case: to pack a parameter, we just look on the // class object for the data element. string field_name = field->get_name(); if (!PyObject_HasAttrString(distobj, (char *)field_name.c_str())) { // If the attribute is not defined, but the field has a default // value specified, quietly pack the default value. if (field->has_default_value()) { packer.pack_default_value(); return true; } // If there is no default value specified, it's an error. ostringstream strm; strm << "Data element " << field_name << ", required by dc file for dclass " << get_name() << ", not defined on object"; nassert_raise(strm.str()); return false; } PyObject *result = PyObject_GetAttrString(distobj, (char *)field_name.c_str()); nassertr(result != (PyObject *)NULL, false); // Now pack the value into the datagram. bool pack_ok = parameter->pack_args(packer, result); Py_DECREF(result); return pack_ok; } if (field->as_molecular_field() != (DCMolecularField *)NULL) { ostringstream strm; strm << "Cannot pack molecular field " << field->get_name() << " for generate"; nassert_raise(strm.str()); return false; } const DCAtomicField *atom = field->as_atomic_field(); nassertr(atom != (DCAtomicField *)NULL, 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 setter_name = atom->get_name(); if (setter_name.empty()) { ostringstream strm; strm << "Required field is unnamed!"; nassert_raise(strm.str()); return false; } 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? ostringstream strm; strm << "Required field " << setter_name << " has no parameters!"; nassert_raise(strm.str()); return false; } string getter_name = setter_name; if (setter_name.substr(0, 3) == "set") { // If the original method started with "set", we mangle this // directly to "get". getter_name[0] = 'g'; } else { // Otherwise, we add a "get" prefix, and capitalize the next // letter. getter_name = "get" + setter_name; getter_name[3] = toupper(getter_name[3]); } // Now we have to look up the getter on the distributed object // and call it. if (!PyObject_HasAttrString(distobj, (char *)getter_name.c_str())) { // As above, if there's no getter but the field has a default // value specified, quietly pack the default value. if (field->has_default_value()) { packer.pack_default_value(); return true; } // Otherwise, with no default value it's an error. ostringstream strm; strm << "Distributed class " << get_name() << " doesn't have getter named " << getter_name << " to match required field " << setter_name; nassert_raise(strm.str()); return false; } PyObject *func = PyObject_GetAttrString(distobj, (char *)getter_name.c_str()); nassertr(func != (PyObject *)NULL, false); PyObject *empty_args = PyTuple_New(0); PyObject *result = PyObject_CallObject(func, empty_args); Py_DECREF(empty_args); Py_DECREF(func); if (result == (PyObject *)NULL) { // We don't set this as an exception, since presumably the Python // method itself has already triggered a Python exception. cerr << "Error when calling " << getter_name << "\n"; return false; } 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; } else { // Otherwise, it had better already be a sequence or tuple of some // sort. if (!PySequence_Check(result)) { ostringstream strm; strm << "Since dclass " << get_name() << " method " << setter_name << " is declared to have multiple parameters, Python function " << getter_name << " must return a list or tuple.\n"; nassert_raise(strm.str()); return false; } } // Now pack the arguments into the datagram. bool pack_ok = atom->pack_args(packer, result); Py_DECREF(result); return pack_ok; } #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); if (field == (DCField *)NULL) { ostringstream strm; strm << "No field named " << field_name << " in class " << get_name() << "\n"; nassert_raise(strm.str()); return Datagram(); } return field->client_format_update(do_id, args); } #endif // HAVE_PYTHON #ifdef HAVE_PYTHON //////////////////////////////////////////////////////////////////// // Function: DCClass::ai_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, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const { DCField *field = get_field_by_name(field_name); if (field == (DCField *)NULL) { ostringstream strm; strm << "No field named " << field_name << " in class " << get_name() << "\n"; nassert_raise(strm.str()); return Datagram(); } return field->ai_format_update(do_id, to_id, from_id, args); } #endif // HAVE_PYTHON #ifdef HAVE_PYTHON //////////////////////////////////////////////////////////////////// // Function: DCClass::client_format_generate // Access: Published // Description: Generates a datagram containing the message necessary // to generate a new distributed object from the client. // 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:: client_format_generate(PyObject *distobj, int do_id, int zone_id, PyObject *optional_fields) const { DCPacker packer; //packer.raw_pack_uint8('A'); bool has_optional_fields = (PyObject_IsTrue(optional_fields) != 0); if (has_optional_fields) { packer.raw_pack_uint16(CLIENT_CREATE_OBJECT_REQUIRED_OTHER); } else { packer.raw_pack_uint16(CLIENT_CREATE_OBJECT_REQUIRED); } packer.raw_pack_uint32(zone_id); packer.raw_pack_uint16(_number); packer.raw_pack_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); if (field->is_required() && field->as_molecular_field() == NULL) { packer.begin_pack(field); if (!pack_required_field(packer, distobj, field)) { return Datagram(); } packer.end_pack(); } } // Also specify the optional fields. if (has_optional_fields) { int num_optional_fields = PySequence_Size(optional_fields); packer.raw_pack_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) { ostringstream strm; strm << "No field named " << field_name << " in class " << get_name() << "\n"; nassert_raise(strm.str()); return Datagram(); } packer.raw_pack_uint16(field->get_number()); packer.begin_pack(field); if (!pack_required_field(packer, distobj, field)) { return Datagram(); } packer.end_pack(); } } return Datagram(packer.get_data(), packer.get_length()); } #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 parent_id, int zone_id, CHANNEL_TYPE district_channel_id, CHANNEL_TYPE from_channel_id, PyObject *optional_fields) const { DCPacker packer; packer.raw_pack_uint8(1); packer.RAW_PACK_CHANNEL(district_channel_id); packer.RAW_PACK_CHANNEL(from_channel_id); //packer.raw_pack_uint8('A'); bool has_optional_fields = (PyObject_IsTrue(optional_fields) != 0); if (has_optional_fields) { packer.raw_pack_uint16(STATESERVER_OBJECT_GENERATE_WITH_REQUIRED_OTHER); } else { packer.raw_pack_uint16(STATESERVER_OBJECT_GENERATE_WITH_REQUIRED); } // Parent is a bit overloaded; this parent is not about inheritance, // this one is about the visibility container parent, i.e. the zone // parent: if (parent_id) { packer.raw_pack_uint32(parent_id); } packer.raw_pack_uint32(zone_id); packer.raw_pack_uint16(_number); packer.raw_pack_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); if (field->is_required() && field->as_molecular_field() == NULL) { packer.begin_pack(field); if (!pack_required_field(packer, distobj, field)) { return Datagram(); } packer.end_pack(); } } // Also specify the optional fields. if (has_optional_fields) { int num_optional_fields = PySequence_Size(optional_fields); packer.raw_pack_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) { ostringstream strm; strm << "No field named " << field_name << " in class " << get_name() << "\n"; nassert_raise(strm.str()); return Datagram(); } packer.raw_pack_uint16(field->get_number()); packer.begin_pack(field); if (!pack_required_field(packer, distobj, field)) { return Datagram(); } packer.end_pack(); } } return Datagram(packer.get_data(), packer.get_length()); } #endif // HAVE_PYTHON #ifdef HAVE_PYTHON //////////////////////////////////////////////////////////////////// // Function: DCClass::ai_database_generate_context // Access: Published // Description: Generates a datagram containing the message necessary // to create a new database distributed object from the AI. // // First Pass is to only incldue required values // (with Defaults). //////////////////////////////////////////////////////////////////// Datagram DCClass:: ai_database_generate_context( unsigned int context_id, unsigned int parent_id, unsigned int zone_id, CHANNEL_TYPE owner_channel, CHANNEL_TYPE database_server_id, CHANNEL_TYPE from_channel_id) const { DCPacker packer; packer.raw_pack_uint8(1); packer.RAW_PACK_CHANNEL(database_server_id); packer.RAW_PACK_CHANNEL(from_channel_id); //packer.raw_pack_uint8('A'); packer.raw_pack_uint16(STATESERVER_OBJECT_CREATE_WITH_REQUIRED_CONTEXT); packer.raw_pack_uint32(parent_id); packer.raw_pack_uint32(zone_id); packer.RAW_PACK_CHANNEL(owner_channel); packer.raw_pack_uint16(_number); // DCD class ID packer.raw_pack_uint32(context_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); if (field->is_required() && field->as_molecular_field() == NULL) { packer.begin_pack(field); packer.pack_default_value(); packer.end_pack(); } } return Datagram(packer.get_data(), packer.get_length()); } #endif // HAVE_PYTHON #ifdef HAVE_PYTHON // TODO: remove this once Skyler has things working with the new server //////////////////////////////////////////////////////////////////// // Function: DCClass::ai_database_generate_context_old // Access: Published // Description: Generates a datagram containing the message necessary // to create a new database distributed object from the AI. // // First Pass is to only incldue required values // (with Defaults). //////////////////////////////////////////////////////////////////// Datagram DCClass:: ai_database_generate_context_old( unsigned int context_id, unsigned int parent_id, unsigned int zone_id, CHANNEL_TYPE database_server_id, CHANNEL_TYPE from_channel_id) const { DCPacker packer; packer.raw_pack_uint8(1); packer.RAW_PACK_CHANNEL(database_server_id); packer.RAW_PACK_CHANNEL(from_channel_id); //packer.raw_pack_uint8('A'); packer.raw_pack_uint16(STATESERVER_OBJECT_CREATE_WITH_REQUIRED_CONTEXT); packer.raw_pack_uint32(parent_id); packer.raw_pack_uint32(zone_id); packer.raw_pack_uint16(_number); // DCD class ID packer.raw_pack_uint32(context_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); if (field->is_required() && field->as_molecular_field() == NULL) { packer.begin_pack(field); packer.pack_default_value(); packer.end_pack(); } } return Datagram(packer.get_data(), packer.get_length()); } #endif // HAVE_PYTHON //////////////////////////////////////////////////////////////////// // Function : DCClass::output // Access : Public, Virtual // Description : Write a string representation of this instance to // . //////////////////////////////////////////////////////////////////// void DCClass:: output(ostream &out, bool brief) const { output_instance(out, brief, "", "", ""); } //////////////////////////////////////////////////////////////////// // Function: DCClass::write // Access: Public, Virtual // Description: Generates a parseable description of the object to // the indicated output stream. //////////////////////////////////////////////////////////////////// void DCClass:: write(ostream &out, bool brief, int indent_level) const { indent(out, indent_level); if (_is_struct) { out << "struct"; } else { out << "dclass"; } if (!_name.empty()) { out << " " << _name; } if (!_parents.empty()) { Parents::const_iterator pi = _parents.begin(); out << " : " << (*pi)->_name; ++pi; while (pi != _parents.end()) { out << ", " << (*pi)->_name; ++pi; } } out << " {"; if (!brief && _number >= 0) { out << " // index " << _number; } out << "\n"; if (_constructor != (DCField *)NULL) { _constructor->write(out, brief, indent_level + 2); } Fields::const_iterator fi; for (fi = _fields.begin(); fi != _fields.end(); ++fi) { (*fi)->write(out, brief, indent_level + 2); /* if (true || (*fi)->has_default_value()) { indent(out, indent_level + 2) << "// = "; DCPacker packer; packer.set_unpack_data((*fi)->get_default_value()); packer.begin_unpack(*fi); packer.unpack_and_format(out, false); if (!packer.end_unpack()) { out << ""; } out << "\n"; } */ } indent(out, indent_level) << "};\n"; } //////////////////////////////////////////////////////////////////// // Function: DCClass::output_instance // Access: Public // Description: Generates a parseable description of the object to // the indicated output stream. //////////////////////////////////////////////////////////////////// void DCClass:: output_instance(ostream &out, bool brief, const string &prename, const string &name, const string &postname) const { if (_is_struct) { out << "struct"; } else { out << "dclass"; } if (!_name.empty()) { out << " " << _name; } if (!_parents.empty()) { Parents::const_iterator pi = _parents.begin(); out << " : " << (*pi)->_name; ++pi; while (pi != _parents.end()) { out << ", " << (*pi)->_name; ++pi; } } out << " {"; if (_constructor != (DCField *)NULL) { _constructor->output(out, brief); out << "; "; } Fields::const_iterator fi; for (fi = _fields.begin(); fi != _fields.end(); ++fi) { (*fi)->output(out, brief); out << "; "; } out << "}"; if (!prename.empty() || !name.empty() || !postname.empty()) { out << " " << prename << name << postname; } } //////////////////////////////////////////////////////////////////// // Function: DCClass::generate_hash // Access: Public, Virtual // Description: Accumulates the properties of this class into the // hash. //////////////////////////////////////////////////////////////////// void DCClass:: generate_hash(HashGenerator &hashgen) const { hashgen.add_string(_name); if (is_struct()) { hashgen.add_int(1); } hashgen.add_int(_parents.size()); Parents::const_iterator pi; for (pi = _parents.begin(); pi != _parents.end(); ++pi) { hashgen.add_int((*pi)->get_number()); } if (_constructor != (DCField *)NULL) { _constructor->generate_hash(hashgen); } hashgen.add_int(_fields.size()); Fields::const_iterator fi; for (fi = _fields.begin(); fi != _fields.end(); ++fi) { (*fi)->generate_hash(hashgen); } } //////////////////////////////////////////////////////////////////// // Function: DCClass::clear_inherited_fields // Access: Public // Description: Empties the list of inherited fields for the class, // so that it may be rebuilt. This is normally only // called by DCFile::rebuild_inherited_fields(). //////////////////////////////////////////////////////////////////// void DCClass:: clear_inherited_fields() { _inherited_fields.clear(); } //////////////////////////////////////////////////////////////////// // Function: DCClass::rebuild_inherited_fields // Access: Public // Description: Recomputes the list of inherited fields for the class. //////////////////////////////////////////////////////////////////// void DCClass:: rebuild_inherited_fields() { typedef pset Names; Names names; _inherited_fields.clear(); // First, get a list of all of the inherited field names. Parents::const_iterator pi; for (pi = _parents.begin(); pi != _parents.end(); ++pi) { const DCClass *parent = (*pi); int num_inherited_fields = parent->get_num_inherited_fields(); for (int i = 0; i < num_inherited_fields; ++i) { const DCField *field = parent->get_inherited_field(i); names.insert(field->get_name()); } } // Also get the local field names. Any local unnamed fields are // immediately added to the _inherited_fields list, since the // unnamed fields are not inherited. Fields::const_iterator fi; for (fi = _fields.begin(); fi != _fields.end(); ++fi) { DCField *field = (*fi); if (field->get_name().empty()) { _inherited_fields.push_back(field); } else { names.insert(field->get_name()); } } // And now build up the table. We use get_field_by_name() to // extract each one, to guarantee that the index we build exactly // matches the return value of get_field_by_name(). Names::const_iterator ni; for (ni = names.begin(); ni != names.end(); ++ni) { // Note that we only list the named fields in the inherited field // list. Thus, the unnamed fields, if any, are not inherited. if (!(*ni).empty()) { DCField *field = get_field_by_name(*ni); nassertv(field != (DCField *)NULL); _inherited_fields.push_back(field); } } // Finally, sort the list in global field index order. This will // put the inherited fields at the top of the list. ::sort(_inherited_fields.begin(), _inherited_fields.end(), SortFieldsByIndex()); } //////////////////////////////////////////////////////////////////// // Function: DCClass::add_field // Access: Public // Description: Adds the newly-allocated field to the class. The // class becomes the owner of the pointer and will // delete it when it destructs. Returns true if the // field is successfully added, or false if there was a // name conflict or some other problem. //////////////////////////////////////////////////////////////////// bool DCClass:: add_field(DCField *field) { nassertr(field->get_class() == this || field->get_class() == NULL, false); field->set_class(this); if (_dc_file != (DCFile *)NULL) { _dc_file->mark_inherited_fields_stale(); } if (!field->get_name().empty()) { if (field->get_name() == _name) { // This field is a constructor. if (_constructor != (DCField *)NULL) { // We already have a constructor. return false; } if (field->as_atomic_field() == (DCAtomicField *)NULL) { // The constructor must be an atomic field. return false; } _constructor = field; _fields_by_name.insert (FieldsByName::value_type(field->get_name(), field)); return true; } bool inserted = _fields_by_name.insert (FieldsByName::value_type(field->get_name(), field)).second; if (!inserted) { return false; } } if (!is_struct()) { if (dc_multiple_inheritance) { _dc_file->set_new_index_number(field); } else { field->set_number(get_num_inherited_fields()); } bool inserted = _fields_by_index.insert (FieldsByIndex::value_type(field->get_number(), field)).second; // It shouldn't be possible for that to fail. nassertr(inserted, false); } _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); _dc_file->mark_inherited_fields_stale(); } //////////////////////////////////////////////////////////////////// // Function: DCClass::set_number // Access: Public // Description: Assigns the unique number to this class. This is // normally called only by the DCFile interface as the // class is added. //////////////////////////////////////////////////////////////////// void DCClass:: set_number(int number) { _number = number; }