mirror of
https://github.com/panda3d/panda3d.git
synced 2026-02-17 20:59:30 -06:00
dcparser: move Python-specific code to extension files
We can now build dcparser without linking to Python and dcparse.exe's dependency on pystub is removed. Co-authored-by: rdb <git@rdb.name>
This commit is contained in:
@@ -16,25 +16,13 @@
|
||||
#include "dcAtomicField.h"
|
||||
#include "hashGenerator.h"
|
||||
#include "dcindent.h"
|
||||
#include "dcmsgtypes.h"
|
||||
|
||||
#include "datagram.h"
|
||||
#include "datagramIterator.h"
|
||||
|
||||
#include "dcClassParameter.h"
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
#include "py_panda.h"
|
||||
#endif
|
||||
|
||||
using std::ostream;
|
||||
using std::ostringstream;
|
||||
using std::string;
|
||||
|
||||
#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");
|
||||
@@ -89,10 +77,7 @@ DCClass(DCFile *dc_file, const string &name, bool is_struct, bool bogus_class) :
|
||||
_number = -1;
|
||||
_constructor = nullptr;
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
_class_def = nullptr;
|
||||
_owner_class_def = nullptr;
|
||||
#endif
|
||||
_python_class_defs = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,11 +93,6 @@ DCClass::
|
||||
for (fi = _fields.begin(); fi != _fields.end(); ++fi) {
|
||||
delete (*fi);
|
||||
}
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
Py_XDECREF(_class_def);
|
||||
Py_XDECREF(_owner_class_def);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -338,7 +318,7 @@ inherits_from_bogus_class() const {
|
||||
* Write a string representation of this instance to <out>.
|
||||
*/
|
||||
void DCClass::
|
||||
output(ostream &out) const {
|
||||
output(std::ostream &out) const {
|
||||
if (_is_struct) {
|
||||
out << "struct";
|
||||
} else {
|
||||
@@ -349,678 +329,11 @@ output(ostream &out) const {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
/**
|
||||
* Returns true if the DCClass object has an associated Python class
|
||||
* definition, false otherwise.
|
||||
*/
|
||||
bool DCClass::
|
||||
has_class_def() const {
|
||||
return (_class_def != nullptr);
|
||||
}
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
/**
|
||||
* 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
|
||||
/**
|
||||
* 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 == nullptr) {
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
Py_INCREF(_class_def);
|
||||
return _class_def;
|
||||
}
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
/**
|
||||
* 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 != nullptr);
|
||||
}
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
/**
|
||||
* 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
|
||||
/**
|
||||
* 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 == nullptr) {
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
Py_INCREF(_owner_class_def);
|
||||
return _owner_class_def;
|
||||
}
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
/**
|
||||
* 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;
|
||||
const char *data = (const char *)di.get_datagram().get_data();
|
||||
packer.set_unpack_data(data + di.get_current_index(),
|
||||
di.get_remaining_size(), false);
|
||||
|
||||
int field_id = packer.raw_unpack_uint16();
|
||||
DCField *field = get_field_by_index(field_id);
|
||||
if (field == nullptr) {
|
||||
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
|
||||
/**
|
||||
* 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;
|
||||
const char *data = (const char *)di.get_datagram().get_data();
|
||||
packer.set_unpack_data(data + di.get_current_index(),
|
||||
di.get_remaining_size(), false);
|
||||
|
||||
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() == nullptr &&
|
||||
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
|
||||
/**
|
||||
* 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;
|
||||
const char *data = (const char *)di.get_datagram().get_data();
|
||||
packer.set_unpack_data(data + di.get_current_index(),
|
||||
di.get_remaining_size(), false);
|
||||
|
||||
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() == nullptr &&
|
||||
field->is_required() && (field->is_ownrecv() || 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
|
||||
/**
|
||||
* 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;
|
||||
const char *data = (const char *)di.get_datagram().get_data();
|
||||
packer.set_unpack_data(data + di.get_current_index(),
|
||||
di.get_remaining_size(), false);
|
||||
|
||||
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() == nullptr &&
|
||||
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
|
||||
/**
|
||||
* 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
|
||||
/**
|
||||
* Processes an update for a named field from a packed value blob.
|
||||
*/
|
||||
void DCClass::
|
||||
direct_update(PyObject *distobj, const string &field_name,
|
||||
const vector_uchar &value_blob) {
|
||||
DCField *field = get_field_by_name(field_name);
|
||||
nassertv_always(field != nullptr);
|
||||
|
||||
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
|
||||
/**
|
||||
* Processes an update for a named field from a packed datagram.
|
||||
*/
|
||||
void DCClass::
|
||||
direct_update(PyObject *distobj, const string &field_name,
|
||||
const Datagram &datagram) {
|
||||
DCField *field = get_field_by_name(field_name);
|
||||
nassertv_always(field != nullptr);
|
||||
|
||||
DCPacker packer;
|
||||
packer.set_unpack_data((const char *)datagram.get_data(), datagram.get_length(), false);
|
||||
packer.begin_unpack(field);
|
||||
field->receive_update(packer, distobj);
|
||||
packer.end_unpack();
|
||||
}
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
/**
|
||||
* 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
|
||||
/**
|
||||
* 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 != nullptr) {
|
||||
// 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 != nullptr, 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() != nullptr) {
|
||||
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 != nullptr, 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 != nullptr, false);
|
||||
|
||||
PyObject *empty_args = PyTuple_New(0);
|
||||
PyObject *result = PyObject_CallObject(func, empty_args);
|
||||
Py_DECREF(empty_args);
|
||||
Py_DECREF(func);
|
||||
if (result == nullptr) {
|
||||
// We don't set this as an exception, since presumably the Python method
|
||||
// itself has already triggered a Python exception.
|
||||
std::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
|
||||
/**
|
||||
* 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, DOID_TYPE do_id,
|
||||
PyObject *args) const {
|
||||
DCField *field = get_field_by_name(field_name);
|
||||
if (field == nullptr) {
|
||||
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
|
||||
/**
|
||||
* 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, DOID_TYPE do_id,
|
||||
CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const {
|
||||
DCField *field = get_field_by_name(field_name);
|
||||
if (field == nullptr) {
|
||||
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
|
||||
/**
|
||||
* Generates a datagram containing the message necessary to send an update,
|
||||
* using the indicated msg type for the indicated distributed object from the
|
||||
* AI.
|
||||
*/
|
||||
Datagram DCClass::
|
||||
ai_format_update_msg_type(const string &field_name, DOID_TYPE do_id,
|
||||
CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const {
|
||||
DCField *field = get_field_by_name(field_name);
|
||||
if (field == nullptr) {
|
||||
ostringstream strm;
|
||||
strm << "No field named " << field_name << " in class " << get_name()
|
||||
<< "\n";
|
||||
nassert_raise(strm.str());
|
||||
return Datagram();
|
||||
}
|
||||
|
||||
return field->ai_format_update_msg_type(do_id, to_id, from_id, msg_type, args);
|
||||
}
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* This method is only called by the CMU implementation.
|
||||
*/
|
||||
Datagram DCClass::
|
||||
client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id,
|
||||
ZONEID_TYPE zone_id,
|
||||
PyObject *optional_fields) const {
|
||||
DCPacker packer;
|
||||
|
||||
packer.raw_pack_uint16(CLIENT_OBJECT_GENERATE_CMU);
|
||||
|
||||
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() == nullptr) {
|
||||
packer.begin_pack(field);
|
||||
if (!pack_required_field(packer, distobj, field)) {
|
||||
return Datagram();
|
||||
}
|
||||
packer.end_pack();
|
||||
}
|
||||
}
|
||||
|
||||
// Also specify the optional fields.
|
||||
int num_optional_fields = 0;
|
||||
if (PyObject_IsTrue(optional_fields)) {
|
||||
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);
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
string field_name = PyUnicode_AsUTF8(py_field_name);
|
||||
#else
|
||||
string field_name = PyString_AsString(py_field_name);
|
||||
#endif
|
||||
Py_XDECREF(py_field_name);
|
||||
|
||||
DCField *field = get_field_by_name(field_name);
|
||||
if (field == nullptr) {
|
||||
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
|
||||
/**
|
||||
* 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, DOID_TYPE do_id,
|
||||
DOID_TYPE parent_id, ZONEID_TYPE 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_CREATE_OBJECT_WITH_REQUIRED_OTHER);
|
||||
} else {
|
||||
packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED);
|
||||
}
|
||||
|
||||
packer.raw_pack_uint32(do_id);
|
||||
// Parent is a bit overloaded; this parent is not about inheritance, this
|
||||
// one is about the visibility container parent, i.e. the zone parent:
|
||||
packer.raw_pack_uint32(parent_id);
|
||||
packer.raw_pack_uint32(zone_id);
|
||||
packer.raw_pack_uint16(_number);
|
||||
|
||||
// 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() == nullptr) {
|
||||
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);
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
string field_name = PyUnicode_AsUTF8(py_field_name);
|
||||
#else
|
||||
string field_name = PyString_AsString(py_field_name);
|
||||
#endif
|
||||
Py_XDECREF(py_field_name);
|
||||
|
||||
DCField *field = get_field_by_name(field_name);
|
||||
if (field == nullptr) {
|
||||
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
|
||||
|
||||
/**
|
||||
* Write a string representation of this instance to <out>.
|
||||
*/
|
||||
void DCClass::
|
||||
output(ostream &out, bool brief) const {
|
||||
output(std::ostream &out, bool brief) const {
|
||||
output_instance(out, brief, "", "", "");
|
||||
}
|
||||
|
||||
@@ -1029,7 +342,7 @@ output(ostream &out, bool brief) const {
|
||||
* stream.
|
||||
*/
|
||||
void DCClass::
|
||||
write(ostream &out, bool brief, int indent_level) const {
|
||||
write(std::ostream &out, bool brief, int indent_level) const {
|
||||
indent(out, indent_level);
|
||||
if (_is_struct) {
|
||||
out << "struct";
|
||||
@@ -1089,7 +402,7 @@ write(ostream &out, bool brief, int indent_level) const {
|
||||
* stream.
|
||||
*/
|
||||
void DCClass::
|
||||
output_instance(ostream &out, bool brief, const string &prename,
|
||||
output_instance(std::ostream &out, bool brief, const string &prename,
|
||||
const string &name, const string &postname) const {
|
||||
if (_is_struct) {
|
||||
out << "struct";
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#ifdef WITHIN_PANDA
|
||||
#include "pStatCollector.h"
|
||||
#include "configVariableBool.h"
|
||||
#include "extension.h"
|
||||
#include "datagramIterator.h"
|
||||
|
||||
extern ConfigVariableBool dc_multiple_inheritance;
|
||||
extern ConfigVariableBool dc_virtual_inheritance;
|
||||
@@ -79,44 +81,52 @@ PUBLISHED:
|
||||
|
||||
virtual void output(std::ostream &out) const;
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
bool has_class_def() const;
|
||||
void set_class_def(PyObject *class_def);
|
||||
PyObject *get_class_def() const;
|
||||
bool has_owner_class_def() const;
|
||||
void set_owner_class_def(PyObject *owner_class_def);
|
||||
PyObject *get_owner_class_def() const;
|
||||
EXTENSION(bool has_class_def() const);
|
||||
EXTENSION(void set_class_def(PyObject *class_def));
|
||||
EXTENSION(PyObject *get_class_def() const);
|
||||
EXTENSION(bool has_owner_class_def() const);
|
||||
EXTENSION(void set_owner_class_def(PyObject *owner_class_def));
|
||||
EXTENSION(PyObject *get_owner_class_def() const);
|
||||
|
||||
void receive_update(PyObject *distobj, DatagramIterator &di) const;
|
||||
void receive_update_broadcast_required(PyObject *distobj, DatagramIterator &di) const;
|
||||
void receive_update_broadcast_required_owner(PyObject *distobj, DatagramIterator &di) const;
|
||||
void receive_update_all_required(PyObject *distobj, DatagramIterator &di) const;
|
||||
void receive_update_other(PyObject *distobj, DatagramIterator &di) const;
|
||||
EXTENSION(void receive_update(PyObject *distobj, DatagramIterator &di) const);
|
||||
EXTENSION(void receive_update_broadcast_required(PyObject *distobj, DatagramIterator &di) const);
|
||||
EXTENSION(void receive_update_broadcast_required_owner(PyObject *distobj, DatagramIterator &di) const);
|
||||
EXTENSION(void receive_update_all_required(PyObject *distobj, DatagramIterator &di) const);
|
||||
EXTENSION(void receive_update_other(PyObject *distobj, DatagramIterator &di) const);
|
||||
|
||||
void direct_update(PyObject *distobj, const std::string &field_name,
|
||||
const vector_uchar &value_blob);
|
||||
void direct_update(PyObject *distobj, const std::string &field_name,
|
||||
const Datagram &datagram);
|
||||
bool pack_required_field(Datagram &datagram, PyObject *distobj,
|
||||
const DCField *field) const;
|
||||
bool pack_required_field(DCPacker &packer, PyObject *distobj,
|
||||
const DCField *field) const;
|
||||
EXTENSION(void direct_update(PyObject *distobj, const std::string &field_name,
|
||||
const vector_uchar &value_blob));
|
||||
EXTENSION(void direct_update(PyObject *distobj, const std::string &field_name,
|
||||
const Datagram &datagram));
|
||||
EXTENSION(bool pack_required_field(Datagram &datagram, PyObject *distobj,
|
||||
const DCField *field) const);
|
||||
EXTENSION(bool pack_required_field(DCPacker &packer, PyObject *distobj,
|
||||
const DCField *field) const);
|
||||
|
||||
|
||||
|
||||
Datagram client_format_update(const std::string &field_name,
|
||||
DOID_TYPE do_id, PyObject *args) const;
|
||||
Datagram ai_format_update(const std::string &field_name, DOID_TYPE do_id,
|
||||
CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const;
|
||||
Datagram ai_format_update_msg_type(const std::string &field_name, DOID_TYPE do_id,
|
||||
CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const;
|
||||
Datagram ai_format_generate(PyObject *distobj, DOID_TYPE do_id, ZONEID_TYPE parent_id, ZONEID_TYPE zone_id,
|
||||
CHANNEL_TYPE district_channel_id, CHANNEL_TYPE from_channel_id,
|
||||
PyObject *optional_fields) const;
|
||||
Datagram client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id,
|
||||
ZONEID_TYPE zone_id, PyObject *optional_fields) const;
|
||||
EXTENSION(Datagram client_format_update(const std::string &field_name,
|
||||
DOID_TYPE do_id, PyObject *args) const);
|
||||
EXTENSION(Datagram ai_format_update(const std::string &field_name,
|
||||
DOID_TYPE do_id,
|
||||
CHANNEL_TYPE to_id, CHANNEL_TYPE from_id,
|
||||
PyObject *args) const);
|
||||
EXTENSION(Datagram ai_format_update_msg_type(const std::string &field_name,
|
||||
DOID_TYPE do_id,
|
||||
CHANNEL_TYPE to_id,
|
||||
CHANNEL_TYPE from_id,
|
||||
int msg_type,
|
||||
PyObject *args) const);
|
||||
EXTENSION(Datagram ai_format_generate(PyObject *distobj, DOID_TYPE do_id,
|
||||
ZONEID_TYPE parent_id,
|
||||
ZONEID_TYPE zone_id,
|
||||
CHANNEL_TYPE district_channel_id,
|
||||
CHANNEL_TYPE from_channel_id,
|
||||
PyObject *optional_fields) const);
|
||||
EXTENSION(Datagram client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id,
|
||||
ZONEID_TYPE zone_id,
|
||||
PyObject *optional_fields) const);
|
||||
|
||||
#endif
|
||||
|
||||
public:
|
||||
virtual void output(std::ostream &out, bool brief) const;
|
||||
@@ -135,8 +145,8 @@ private:
|
||||
void shadow_inherited_field(const std::string &name);
|
||||
|
||||
#ifdef WITHIN_PANDA
|
||||
PStatCollector _class_update_pcollector;
|
||||
PStatCollector _class_generate_pcollector;
|
||||
mutable PStatCollector _class_update_pcollector;
|
||||
mutable PStatCollector _class_generate_pcollector;
|
||||
static PStatCollector _update_pcollector;
|
||||
static PStatCollector _generate_pcollector;
|
||||
#endif
|
||||
@@ -162,12 +172,17 @@ private:
|
||||
typedef pmap<int, DCField *> FieldsByIndex;
|
||||
FieldsByIndex _fields_by_index;
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
PyObject *_class_def;
|
||||
PyObject *_owner_class_def;
|
||||
#endif
|
||||
// See pandaNode.h for an explanation of this trick
|
||||
class PythonClassDefs : public ReferenceCount {
|
||||
public:
|
||||
virtual ~PythonClassDefs() {};
|
||||
};
|
||||
PT(PythonClassDefs) _python_class_defs;
|
||||
|
||||
friend class DCField;
|
||||
#ifdef WITHIN_PANDA
|
||||
friend class Extension<DCClass>;
|
||||
#endif
|
||||
};
|
||||
|
||||
#include "dcClass.I"
|
||||
|
||||
665
direct/src/dcparser/dcClass_ext.cxx
Normal file
665
direct/src/dcparser/dcClass_ext.cxx
Normal file
@@ -0,0 +1,665 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file dcClass_ext.cxx
|
||||
* @author CFSworks
|
||||
* @date 2019-07-03
|
||||
*/
|
||||
|
||||
#include "dcClass_ext.h"
|
||||
#include "dcField_ext.h"
|
||||
#include "dcAtomicField.h"
|
||||
#include "dcPacker.h"
|
||||
#include "dcmsgtypes.h"
|
||||
|
||||
#include "datagram.h"
|
||||
#include "datagramIterator.h"
|
||||
#include "pStatTimer.h"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
/**
|
||||
* Returns true if the DCClass object has an associated Python class
|
||||
* definition, false otherwise.
|
||||
*/
|
||||
bool Extension<DCClass>::
|
||||
has_class_def() const {
|
||||
return _this->_python_class_defs != nullptr
|
||||
&& ((PythonClassDefsImpl *)_this->_python_class_defs.p())->_class_def != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the class object associated with this DistributedClass. This object
|
||||
* will be used to construct new instances of the class.
|
||||
*/
|
||||
void Extension<DCClass>::
|
||||
set_class_def(PyObject *class_def) {
|
||||
PythonClassDefsImpl *defs = do_get_defs();
|
||||
|
||||
Py_XINCREF(class_def);
|
||||
Py_XDECREF(defs->_class_def);
|
||||
defs->_class_def = class_def;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the class object that was previously associated with this
|
||||
* DistributedClass. This will return a new reference to the object.
|
||||
*/
|
||||
PyObject *Extension<DCClass>::
|
||||
get_class_def() const {
|
||||
if (!has_class_def()) {
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
PythonClassDefsImpl *defs = do_get_defs();
|
||||
Py_INCREF(defs->_class_def);
|
||||
return defs->_class_def;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the DCClass object has an associated Python owner class
|
||||
* definition, false otherwise.
|
||||
*/
|
||||
bool Extension<DCClass>::
|
||||
has_owner_class_def() const {
|
||||
return _this->_python_class_defs != nullptr
|
||||
&& ((PythonClassDefsImpl *)_this->_python_class_defs.p())->_owner_class_def != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the owner class object associated with this DistributedClass. This
|
||||
* object will be used to construct new owner instances of the class.
|
||||
*/
|
||||
void Extension<DCClass>::
|
||||
set_owner_class_def(PyObject *owner_class_def) {
|
||||
PythonClassDefsImpl *defs = do_get_defs();
|
||||
|
||||
Py_XINCREF(owner_class_def);
|
||||
Py_XDECREF(defs->_owner_class_def);
|
||||
defs->_owner_class_def = owner_class_def;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the owner class object that was previously associated with this
|
||||
* DistributedClass. This will return a new reference to the object.
|
||||
*/
|
||||
PyObject *Extension<DCClass>::
|
||||
get_owner_class_def() const {
|
||||
if (!has_owner_class_def()) {
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
PythonClassDefsImpl *defs = do_get_defs();
|
||||
Py_INCREF(defs->_owner_class_def);
|
||||
return defs->_owner_class_def;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the update message out of the packer and applies it to the
|
||||
* indicated object by calling the appropriate method.
|
||||
*/
|
||||
void Extension<DCClass>::
|
||||
receive_update(PyObject *distobj, DatagramIterator &di) const {
|
||||
PStatTimer timer(_this->_class_update_pcollector);
|
||||
DCPacker packer;
|
||||
const char *data = (const char *)di.get_datagram().get_data();
|
||||
packer.set_unpack_data(data + di.get_current_index(),
|
||||
di.get_remaining_size(), false);
|
||||
|
||||
int field_id = packer.raw_unpack_uint16();
|
||||
DCField *field = _this->get_field_by_index(field_id);
|
||||
if (field == nullptr) {
|
||||
ostringstream strm;
|
||||
strm
|
||||
<< "Received update for field " << field_id << ", not in class "
|
||||
<< _this->get_name();
|
||||
nassert_raise(strm.str());
|
||||
return;
|
||||
}
|
||||
|
||||
packer.begin_unpack(field);
|
||||
invoke_extension(field).receive_update(packer, distobj);
|
||||
packer.end_unpack();
|
||||
|
||||
di.skip_bytes(packer.get_num_unpacked_bytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Extension<DCClass>::
|
||||
receive_update_broadcast_required(PyObject *distobj, DatagramIterator &di) const {
|
||||
PStatTimer timer(_this->_class_update_pcollector);
|
||||
DCPacker packer;
|
||||
const char *data = (const char *)di.get_datagram().get_data();
|
||||
packer.set_unpack_data(data + di.get_current_index(),
|
||||
di.get_remaining_size(), false);
|
||||
|
||||
int num_fields = _this->get_num_inherited_fields();
|
||||
for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
|
||||
DCField *field = _this->get_inherited_field(i);
|
||||
if (field->as_molecular_field() == nullptr &&
|
||||
field->is_required() && field->is_broadcast()) {
|
||||
packer.begin_unpack(field);
|
||||
invoke_extension(field).receive_update(packer, distobj);
|
||||
if (!packer.end_unpack()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
di.skip_bytes(packer.get_num_unpacked_bytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Extension<DCClass>::
|
||||
receive_update_broadcast_required_owner(PyObject *distobj,
|
||||
DatagramIterator &di) const {
|
||||
PStatTimer timer(_this->_class_update_pcollector);
|
||||
DCPacker packer;
|
||||
const char *data = (const char *)di.get_datagram().get_data();
|
||||
packer.set_unpack_data(data + di.get_current_index(),
|
||||
di.get_remaining_size(), false);
|
||||
|
||||
int num_fields = _this->get_num_inherited_fields();
|
||||
for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
|
||||
DCField *field = _this->get_inherited_field(i);
|
||||
if (field->as_molecular_field() == nullptr &&
|
||||
field->is_required() && (field->is_ownrecv() || field->is_broadcast())) {
|
||||
packer.begin_unpack(field);
|
||||
invoke_extension(field).receive_update(packer, distobj);
|
||||
if (!packer.end_unpack()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
di.skip_bytes(packer.get_num_unpacked_bytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Extension<DCClass>::
|
||||
receive_update_all_required(PyObject *distobj, DatagramIterator &di) const {
|
||||
PStatTimer timer(_this->_class_update_pcollector);
|
||||
DCPacker packer;
|
||||
const char *data = (const char *)di.get_datagram().get_data();
|
||||
packer.set_unpack_data(data + di.get_current_index(),
|
||||
di.get_remaining_size(), false);
|
||||
|
||||
int num_fields = _this->get_num_inherited_fields();
|
||||
for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
|
||||
DCField *field = _this->get_inherited_field(i);
|
||||
if (field->as_molecular_field() == nullptr &&
|
||||
field->is_required()) {
|
||||
packer.begin_unpack(field);
|
||||
invoke_extension(field).receive_update(packer, distobj);
|
||||
if (!packer.end_unpack()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
di.skip_bytes(packer.get_num_unpacked_bytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a datagram that lists some additional fields that are broadcast
|
||||
* in one chunk.
|
||||
*/
|
||||
void Extension<DCClass>::
|
||||
receive_update_other(PyObject *distobj, DatagramIterator &di) const {
|
||||
PStatTimer timer(_this->_class_update_pcollector);
|
||||
int num_fields = di.get_uint16();
|
||||
for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
|
||||
receive_update(distobj, di);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes an update for a named field from a packed value blob.
|
||||
*/
|
||||
void Extension<DCClass>::
|
||||
direct_update(PyObject *distobj, const std::string &field_name,
|
||||
const vector_uchar &value_blob) {
|
||||
DCField *field = _this->get_field_by_name(field_name);
|
||||
nassertv_always(field != nullptr);
|
||||
|
||||
DCPacker packer;
|
||||
packer.set_unpack_data(value_blob);
|
||||
packer.begin_unpack(field);
|
||||
invoke_extension(field).receive_update(packer, distobj);
|
||||
packer.end_unpack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes an update for a named field from a packed datagram.
|
||||
*/
|
||||
void Extension<DCClass>::
|
||||
direct_update(PyObject *distobj, const std::string &field_name,
|
||||
const Datagram &datagram) {
|
||||
DCField *field = _this->get_field_by_name(field_name);
|
||||
nassertv_always(field != nullptr);
|
||||
|
||||
DCPacker packer;
|
||||
packer.set_unpack_data((const char *)datagram.get_data(), datagram.get_length(), false);
|
||||
packer.begin_unpack(field);
|
||||
invoke_extension(field).receive_update(packer, distobj);
|
||||
packer.end_unpack();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Extension<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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Extension<DCClass>::
|
||||
pack_required_field(DCPacker &packer, PyObject *distobj,
|
||||
const DCField *field) const {
|
||||
using std::ostringstream;
|
||||
|
||||
const DCParameter *parameter = field->as_parameter();
|
||||
if (parameter != nullptr) {
|
||||
// This is the easy case: to pack a parameter, we just look on the class
|
||||
// object for the data element.
|
||||
std::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 " << _this->get_name()
|
||||
<< ", not defined on object";
|
||||
nassert_raise(strm.str());
|
||||
return false;
|
||||
}
|
||||
PyObject *result =
|
||||
PyObject_GetAttrString(distobj, (char *)field_name.c_str());
|
||||
nassertr(result != nullptr, false);
|
||||
|
||||
// Now pack the value into the datagram.
|
||||
bool pack_ok = invoke_extension((DCField *)parameter).pack_args(packer, result);
|
||||
Py_DECREF(result);
|
||||
|
||||
return pack_ok;
|
||||
}
|
||||
|
||||
if (field->as_molecular_field() != nullptr) {
|
||||
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 != nullptr, 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.
|
||||
std::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;
|
||||
}
|
||||
|
||||
std::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 " << _this->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 != nullptr, false);
|
||||
|
||||
PyObject *empty_args = PyTuple_New(0);
|
||||
PyObject *result = PyObject_CallObject(func, empty_args);
|
||||
Py_DECREF(empty_args);
|
||||
Py_DECREF(func);
|
||||
if (result == nullptr) {
|
||||
// We don't set this as an exception, since presumably the Python method
|
||||
// itself has already triggered a Python exception.
|
||||
std::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 " << _this->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 = invoke_extension((DCField *)atom).pack_args(packer, result);
|
||||
Py_DECREF(result);
|
||||
|
||||
return pack_ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a datagram containing the message necessary to send an update for
|
||||
* the indicated distributed object from the client.
|
||||
*/
|
||||
Datagram Extension<DCClass>::
|
||||
client_format_update(const std::string &field_name, DOID_TYPE do_id,
|
||||
PyObject *args) const {
|
||||
DCField *field = _this->get_field_by_name(field_name);
|
||||
if (field == nullptr) {
|
||||
std::ostringstream strm;
|
||||
strm << "No field named " << field_name << " in class " << _this->get_name()
|
||||
<< "\n";
|
||||
nassert_raise(strm.str());
|
||||
return Datagram();
|
||||
}
|
||||
|
||||
return invoke_extension(field).client_format_update(do_id, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a datagram containing the message necessary to send an update for
|
||||
* the indicated distributed object from the AI.
|
||||
*/
|
||||
Datagram Extension<DCClass>::
|
||||
ai_format_update(const std::string &field_name, DOID_TYPE do_id,
|
||||
CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const {
|
||||
DCField *field = _this->get_field_by_name(field_name);
|
||||
if (field == nullptr) {
|
||||
std::ostringstream strm;
|
||||
strm << "No field named " << field_name << " in class " << _this->get_name()
|
||||
<< "\n";
|
||||
nassert_raise(strm.str());
|
||||
return Datagram();
|
||||
}
|
||||
|
||||
return invoke_extension(field).ai_format_update(do_id, to_id, from_id, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a datagram containing the message necessary to send an update,
|
||||
* using the indicated msg type for the indicated distributed object from the
|
||||
* AI.
|
||||
*/
|
||||
Datagram Extension<DCClass>::
|
||||
ai_format_update_msg_type(const std::string &field_name, DOID_TYPE do_id,
|
||||
CHANNEL_TYPE to_id, CHANNEL_TYPE from_id,
|
||||
int msg_type, PyObject *args) const {
|
||||
DCField *field = _this->get_field_by_name(field_name);
|
||||
if (field == nullptr) {
|
||||
std::ostringstream strm;
|
||||
strm << "No field named " << field_name << " in class " << _this->get_name()
|
||||
<< "\n";
|
||||
nassert_raise(strm.str());
|
||||
return Datagram();
|
||||
}
|
||||
|
||||
return invoke_extension(field).ai_format_update_msg_type(do_id, to_id, from_id, msg_type, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* This method is only called by the CMU implementation.
|
||||
*/
|
||||
Datagram Extension<DCClass>::
|
||||
client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id,
|
||||
ZONEID_TYPE zone_id,
|
||||
PyObject *optional_fields) const {
|
||||
DCPacker packer;
|
||||
|
||||
packer.raw_pack_uint16(CLIENT_OBJECT_GENERATE_CMU);
|
||||
|
||||
packer.raw_pack_uint32(zone_id);
|
||||
packer.raw_pack_uint16(_this->_number);
|
||||
packer.raw_pack_uint32(do_id);
|
||||
|
||||
// Specify all of the required fields.
|
||||
int num_fields = _this->get_num_inherited_fields();
|
||||
for (int i = 0; i < num_fields; ++i) {
|
||||
DCField *field = _this->get_inherited_field(i);
|
||||
if (field->is_required() && field->as_molecular_field() == nullptr) {
|
||||
packer.begin_pack(field);
|
||||
if (!pack_required_field(packer, distobj, field)) {
|
||||
return Datagram();
|
||||
}
|
||||
packer.end_pack();
|
||||
}
|
||||
}
|
||||
|
||||
// Also specify the optional fields.
|
||||
int num_optional_fields = 0;
|
||||
if (PyObject_IsTrue(optional_fields)) {
|
||||
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);
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
std::string field_name = PyUnicode_AsUTF8(py_field_name);
|
||||
#else
|
||||
std::string field_name = PyString_AsString(py_field_name);
|
||||
#endif
|
||||
Py_XDECREF(py_field_name);
|
||||
|
||||
DCField *field = _this->get_field_by_name(field_name);
|
||||
if (field == nullptr) {
|
||||
std::ostringstream strm;
|
||||
strm << "No field named " << field_name << " in class " << _this->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());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Extension<DCClass>::
|
||||
ai_format_generate(PyObject *distobj, DOID_TYPE do_id,
|
||||
DOID_TYPE parent_id, ZONEID_TYPE 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_CREATE_OBJECT_WITH_REQUIRED_OTHER);
|
||||
} else {
|
||||
packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED);
|
||||
}
|
||||
|
||||
packer.raw_pack_uint32(do_id);
|
||||
// Parent is a bit overloaded; this parent is not about inheritance, this
|
||||
// one is about the visibility container parent, i.e. the zone parent:
|
||||
packer.raw_pack_uint32(parent_id);
|
||||
packer.raw_pack_uint32(zone_id);
|
||||
packer.raw_pack_uint16(_this->_number);
|
||||
|
||||
// Specify all of the required fields.
|
||||
int num_fields = _this->get_num_inherited_fields();
|
||||
for (int i = 0; i < num_fields; ++i) {
|
||||
DCField *field = _this->get_inherited_field(i);
|
||||
if (field->is_required() && field->as_molecular_field() == nullptr) {
|
||||
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);
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
std::string field_name = PyUnicode_AsUTF8(py_field_name);
|
||||
#else
|
||||
std::string field_name = PyString_AsString(py_field_name);
|
||||
#endif
|
||||
Py_XDECREF(py_field_name);
|
||||
|
||||
DCField *field = _this->get_field_by_name(field_name);
|
||||
if (field == nullptr) {
|
||||
std::ostringstream strm;
|
||||
strm << "No field named " << field_name << " in class "
|
||||
<< _this->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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the PythonClassDefsImpl object stored on the DCClass object,
|
||||
* creating it if it didn't yet exist.
|
||||
*/
|
||||
Extension<DCClass>::PythonClassDefsImpl *Extension<DCClass>::
|
||||
do_get_defs() const {
|
||||
if (!_this->_python_class_defs) {
|
||||
_this->_python_class_defs = new PythonClassDefsImpl();
|
||||
}
|
||||
return (PythonClassDefsImpl *)_this->_python_class_defs.p();
|
||||
}
|
||||
|
||||
#endif // HAVE_PYTHON
|
||||
93
direct/src/dcparser/dcClass_ext.h
Normal file
93
direct/src/dcparser/dcClass_ext.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file dcClass_ext.h
|
||||
* @author CFSworks
|
||||
* @date 2019-07-03
|
||||
*/
|
||||
|
||||
#ifndef DCCLASS_EXT_H
|
||||
#define DCCLASS_EXT_H
|
||||
|
||||
#include "dtoolbase.h"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
#include "extension.h"
|
||||
#include "dcClass.h"
|
||||
#include "py_panda.h"
|
||||
|
||||
/**
|
||||
* This class defines the extension methods for DCClass, which are called
|
||||
* instead of any C++ methods with the same prototype.
|
||||
*/
|
||||
template<>
|
||||
class Extension<DCClass> : public ExtensionBase<DCClass> {
|
||||
public:
|
||||
bool has_class_def() const;
|
||||
void set_class_def(PyObject *class_def);
|
||||
PyObject *get_class_def() const;
|
||||
bool has_owner_class_def() const;
|
||||
void set_owner_class_def(PyObject *owner_class_def);
|
||||
PyObject *get_owner_class_def() const;
|
||||
|
||||
void receive_update(PyObject *distobj, DatagramIterator &di) const;
|
||||
void receive_update_broadcast_required(PyObject *distobj, DatagramIterator &di) const;
|
||||
void receive_update_broadcast_required_owner(PyObject *distobj, DatagramIterator &di) const;
|
||||
void receive_update_all_required(PyObject *distobj, DatagramIterator &di) const;
|
||||
void receive_update_other(PyObject *distobj, DatagramIterator &di) const;
|
||||
|
||||
void direct_update(PyObject *distobj, const std::string &field_name,
|
||||
const vector_uchar &value_blob);
|
||||
void direct_update(PyObject *distobj, const std::string &field_name,
|
||||
const Datagram &datagram);
|
||||
bool pack_required_field(Datagram &datagram, PyObject *distobj,
|
||||
const DCField *field) const;
|
||||
bool pack_required_field(DCPacker &packer, PyObject *distobj,
|
||||
const DCField *field) const;
|
||||
|
||||
|
||||
|
||||
Datagram client_format_update(const std::string &field_name,
|
||||
DOID_TYPE do_id, PyObject *args) const;
|
||||
Datagram ai_format_update(const std::string &field_name, DOID_TYPE do_id,
|
||||
CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const;
|
||||
Datagram ai_format_update_msg_type(const std::string &field_name, DOID_TYPE do_id,
|
||||
CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const;
|
||||
Datagram ai_format_generate(PyObject *distobj, DOID_TYPE do_id,
|
||||
ZONEID_TYPE parent_id, ZONEID_TYPE zone_id,
|
||||
CHANNEL_TYPE district_channel_id,
|
||||
CHANNEL_TYPE from_channel_id,
|
||||
PyObject *optional_fields) const;
|
||||
Datagram client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id,
|
||||
ZONEID_TYPE zone_id,
|
||||
PyObject *optional_fields) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Implementation of DCClass::PythonClassDefs which actually stores the
|
||||
* Python pointers. This needs to be defined here rather than on DCClass
|
||||
* itself, since DCClass cannot include Python.h or call Python functions.
|
||||
*/
|
||||
class PythonClassDefsImpl : public DCClass::PythonClassDefs {
|
||||
public:
|
||||
virtual ~PythonClassDefsImpl() {
|
||||
Py_XDECREF(_class_def);
|
||||
Py_XDECREF(_owner_class_def);
|
||||
}
|
||||
|
||||
PyObject *_class_def = nullptr;
|
||||
PyObject *_owner_class_def = nullptr;
|
||||
};
|
||||
|
||||
PythonClassDefsImpl *do_get_defs() const;
|
||||
};
|
||||
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
#endif // DCCLASS_EXT_H
|
||||
@@ -18,19 +18,6 @@
|
||||
#include "hashGenerator.h"
|
||||
#include "dcmsgtypes.h"
|
||||
|
||||
#include "datagram.h"
|
||||
#include "datagramIterator.h"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
#include "py_panda.h"
|
||||
#endif
|
||||
|
||||
#ifdef WITHIN_PANDA
|
||||
#include "pStatTimer.h"
|
||||
#endif
|
||||
|
||||
using std::string;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -61,7 +48,7 @@ DCField() :
|
||||
*
|
||||
*/
|
||||
DCField::
|
||||
DCField(const string &name, DCClass *dclass) :
|
||||
DCField(const std::string &name, DCClass *dclass) :
|
||||
DCPackerInterface(name),
|
||||
_dclass(dclass)
|
||||
#ifdef WITHIN_PANDA
|
||||
@@ -164,14 +151,14 @@ as_parameter() const {
|
||||
* string formatting it for human consumption. Returns empty string if there
|
||||
* is an error.
|
||||
*/
|
||||
string DCField::
|
||||
std::string DCField::
|
||||
format_data(const vector_uchar &packed_data, bool show_field_names) {
|
||||
DCPacker packer;
|
||||
packer.set_unpack_data(packed_data);
|
||||
packer.begin_unpack(this);
|
||||
string result = packer.unpack_and_format(show_field_names);
|
||||
std::string result = packer.unpack_and_format(show_field_names);
|
||||
if (!packer.end_unpack()) {
|
||||
return string();
|
||||
return std::string();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -182,7 +169,7 @@ format_data(const vector_uchar &packed_data, bool show_field_names) {
|
||||
* the corresponding packed data. Returns empty string if there is an error.
|
||||
*/
|
||||
vector_uchar DCField::
|
||||
parse_string(const string &formatted_string) {
|
||||
parse_string(const std::string &formatted_string) {
|
||||
DCPacker packer;
|
||||
packer.begin_pack(this);
|
||||
if (!packer.parse_and_pack(formatted_string)) {
|
||||
@@ -215,254 +202,6 @@ validate_ranges(const vector_uchar &packed_data) const {
|
||||
return (packer.get_num_unpacked_bytes() == packed_data.size());
|
||||
}
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
/**
|
||||
* Packs the Python arguments from the indicated tuple into the packer.
|
||||
* Returns true on success, false on failure.
|
||||
*
|
||||
* It is assumed that the packer is currently positioned on this field.
|
||||
*/
|
||||
bool DCField::
|
||||
pack_args(DCPacker &packer, PyObject *sequence) const {
|
||||
nassertr(!packer.had_error(), false);
|
||||
nassertr(packer.get_current_field() == this, false);
|
||||
|
||||
packer.pack_object(sequence);
|
||||
if (!packer.had_error()) {
|
||||
/*
|
||||
cerr << "pack " << get_name() << get_pystr(sequence) << "\n";
|
||||
*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Notify::ptr()->has_assert_failed()) {
|
||||
std::ostringstream strm;
|
||||
PyObject *exc_type = PyExc_Exception;
|
||||
|
||||
if (as_parameter() != nullptr) {
|
||||
// If it's a parameter-type field, the value may or may not be a
|
||||
// sequence.
|
||||
if (packer.had_pack_error()) {
|
||||
strm << "Incorrect arguments to field: " << get_name()
|
||||
<< " = " << get_pystr(sequence);
|
||||
exc_type = PyExc_TypeError;
|
||||
} else {
|
||||
strm << "Value out of range on field: " << get_name()
|
||||
<< " = " << get_pystr(sequence);
|
||||
exc_type = PyExc_ValueError;
|
||||
}
|
||||
|
||||
} else {
|
||||
// If it's a molecular or atomic field, the value should be a sequence.
|
||||
PyObject *tuple = PySequence_Tuple(sequence);
|
||||
if (tuple == nullptr) {
|
||||
strm << "Value for " << get_name() << " not a sequence: " \
|
||||
<< get_pystr(sequence);
|
||||
exc_type = PyExc_TypeError;
|
||||
|
||||
} else {
|
||||
if (packer.had_pack_error()) {
|
||||
strm << "Incorrect arguments to field: " << get_name()
|
||||
<< get_pystr(sequence);
|
||||
exc_type = PyExc_TypeError;
|
||||
} else {
|
||||
strm << "Value out of range on field: " << get_name()
|
||||
<< get_pystr(sequence);
|
||||
exc_type = PyExc_ValueError;
|
||||
}
|
||||
|
||||
Py_DECREF(tuple);
|
||||
}
|
||||
}
|
||||
|
||||
string message = strm.str();
|
||||
PyErr_SetString(exc_type, message.c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
/**
|
||||
* Unpacks the values from the packer, beginning at the current point in the
|
||||
* unpack_buffer, into a Python tuple and returns the tuple.
|
||||
*
|
||||
* It is assumed that the packer is currently positioned on this field.
|
||||
*/
|
||||
PyObject *DCField::
|
||||
unpack_args(DCPacker &packer) const {
|
||||
nassertr(!packer.had_error(), nullptr);
|
||||
nassertr(packer.get_current_field() == this, nullptr);
|
||||
|
||||
size_t start_byte = packer.get_num_unpacked_bytes();
|
||||
PyObject *object = packer.unpack_object();
|
||||
|
||||
if (!packer.had_error()) {
|
||||
// Successfully unpacked.
|
||||
/*
|
||||
cerr << "recv " << get_name() << get_pystr(object) << "\n";
|
||||
*/
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
if (!Notify::ptr()->has_assert_failed()) {
|
||||
std::ostringstream strm;
|
||||
PyObject *exc_type = PyExc_Exception;
|
||||
|
||||
if (packer.had_pack_error()) {
|
||||
strm << "Data error unpacking field ";
|
||||
output(strm, true);
|
||||
size_t length = packer.get_unpack_length() - start_byte;
|
||||
strm << "\nGot data (" << (int)length << " bytes):\n";
|
||||
Datagram dg(packer.get_unpack_data() + start_byte, length);
|
||||
dg.dump_hex(strm);
|
||||
size_t error_byte = packer.get_num_unpacked_bytes() - start_byte;
|
||||
strm << "Error detected on byte " << error_byte
|
||||
<< " (" << std::hex << error_byte << std::dec << " hex)";
|
||||
|
||||
exc_type = PyExc_RuntimeError;
|
||||
} else {
|
||||
strm << "Value outside specified range when unpacking field "
|
||||
<< get_name() << ": " << get_pystr(object);
|
||||
exc_type = PyExc_ValueError;
|
||||
}
|
||||
|
||||
string message = strm.str();
|
||||
PyErr_SetString(exc_type, message.c_str());
|
||||
}
|
||||
|
||||
Py_XDECREF(object);
|
||||
return nullptr;
|
||||
}
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
/**
|
||||
* Extracts the update message out of the datagram and applies it to the
|
||||
* indicated object by calling the appropriate method.
|
||||
*/
|
||||
void DCField::
|
||||
receive_update(DCPacker &packer, PyObject *distobj) const {
|
||||
if (as_parameter() != nullptr) {
|
||||
// If it's a parameter-type field, just store a new value on the object.
|
||||
PyObject *value = unpack_args(packer);
|
||||
if (value != nullptr) {
|
||||
PyObject_SetAttrString(distobj, (char *)_name.c_str(), value);
|
||||
}
|
||||
Py_DECREF(value);
|
||||
|
||||
} else {
|
||||
// Otherwise, it must be an atomic or molecular field, so call the
|
||||
// corresponding method.
|
||||
|
||||
if (!PyObject_HasAttrString(distobj, (char *)_name.c_str())) {
|
||||
// If there's no Python method to receive this message, don't bother
|
||||
// unpacking it to a Python tuple--just skip past the message.
|
||||
packer.unpack_skip();
|
||||
|
||||
} else {
|
||||
// Otherwise, get a Python tuple from the args and call the Python
|
||||
// method.
|
||||
PyObject *args = unpack_args(packer);
|
||||
|
||||
if (args != nullptr) {
|
||||
PyObject *func = PyObject_GetAttrString(distobj, (char *)_name.c_str());
|
||||
nassertv(func != nullptr);
|
||||
|
||||
PyObject *result;
|
||||
{
|
||||
#ifdef WITHIN_PANDA
|
||||
PStatTimer timer(((DCField *)this)->_field_update_pcollector);
|
||||
#endif
|
||||
result = PyObject_CallObject(func, args);
|
||||
}
|
||||
Py_XDECREF(result);
|
||||
Py_DECREF(func);
|
||||
Py_DECREF(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
/**
|
||||
* Generates a datagram containing the message necessary to send an update for
|
||||
* the indicated distributed object from the client.
|
||||
*/
|
||||
Datagram DCField::
|
||||
client_format_update(DOID_TYPE do_id, PyObject *args) const {
|
||||
DCPacker packer;
|
||||
|
||||
packer.raw_pack_uint16(CLIENT_OBJECT_SET_FIELD);
|
||||
packer.raw_pack_uint32(do_id);
|
||||
packer.raw_pack_uint16(_number);
|
||||
|
||||
packer.begin_pack(this);
|
||||
pack_args(packer, args);
|
||||
if (!packer.end_pack()) {
|
||||
return Datagram();
|
||||
}
|
||||
|
||||
return Datagram(packer.get_data(), packer.get_length());
|
||||
}
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
/**
|
||||
* Generates a datagram containing the message necessary to send an update for
|
||||
* the indicated distributed object from the AI.
|
||||
*/
|
||||
Datagram DCField::
|
||||
ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const {
|
||||
DCPacker packer;
|
||||
|
||||
packer.raw_pack_uint8(1);
|
||||
packer.RAW_PACK_CHANNEL(to_id);
|
||||
packer.RAW_PACK_CHANNEL(from_id);
|
||||
packer.raw_pack_uint16(STATESERVER_OBJECT_SET_FIELD);
|
||||
packer.raw_pack_uint32(do_id);
|
||||
packer.raw_pack_uint16(_number);
|
||||
|
||||
packer.begin_pack(this);
|
||||
pack_args(packer, args);
|
||||
if (!packer.end_pack()) {
|
||||
return Datagram();
|
||||
}
|
||||
|
||||
return Datagram(packer.get_data(), packer.get_length());
|
||||
}
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
/**
|
||||
* Generates a datagram containing the message necessary to send an update,
|
||||
* with the msg type, for the indicated distributed object from the AI.
|
||||
*/
|
||||
Datagram DCField::
|
||||
ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const {
|
||||
DCPacker packer;
|
||||
|
||||
packer.raw_pack_uint8(1);
|
||||
packer.RAW_PACK_CHANNEL(to_id);
|
||||
packer.RAW_PACK_CHANNEL(from_id);
|
||||
packer.raw_pack_uint16(msg_type);
|
||||
packer.raw_pack_uint32(do_id);
|
||||
packer.raw_pack_uint16(_number);
|
||||
|
||||
packer.begin_pack(this);
|
||||
pack_args(packer, args);
|
||||
if (!packer.end_pack()) {
|
||||
return Datagram();
|
||||
}
|
||||
|
||||
return Datagram(packer.get_data(), packer.get_length());
|
||||
}
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
|
||||
/**
|
||||
* Accumulates the properties of this field into the hash.
|
||||
*/
|
||||
@@ -502,62 +241,13 @@ pack_default_value(DCPackData &pack_data, bool &) const {
|
||||
* Sets the name of this field.
|
||||
*/
|
||||
void DCField::
|
||||
set_name(const string &name) {
|
||||
set_name(const std::string &name) {
|
||||
DCPackerInterface::set_name(name);
|
||||
if (_dclass != nullptr) {
|
||||
_dclass->_dc_file->mark_inherited_fields_stale();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
/**
|
||||
* Returns the string representation of the indicated Python object.
|
||||
*/
|
||||
string DCField::
|
||||
get_pystr(PyObject *value) {
|
||||
if (value == nullptr) {
|
||||
return "(null)";
|
||||
}
|
||||
|
||||
PyObject *str = PyObject_Str(value);
|
||||
if (str != nullptr) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
string result = PyUnicode_AsUTF8(str);
|
||||
#else
|
||||
string result = PyString_AsString(str);
|
||||
#endif
|
||||
Py_DECREF(str);
|
||||
return result;
|
||||
}
|
||||
|
||||
PyObject *repr = PyObject_Repr(value);
|
||||
if (repr != nullptr) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
string result = PyUnicode_AsUTF8(repr);
|
||||
#else
|
||||
string result = PyString_AsString(repr);
|
||||
#endif
|
||||
Py_DECREF(repr);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (value->ob_type != nullptr) {
|
||||
PyObject *typestr = PyObject_Str((PyObject *)(value->ob_type));
|
||||
if (typestr != nullptr) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
string result = PyUnicode_AsUTF8(typestr);
|
||||
#else
|
||||
string result = PyString_AsString(typestr);
|
||||
#endif
|
||||
Py_DECREF(typestr);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return "(invalid object)";
|
||||
}
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
/**
|
||||
* Recomputes the default value of the field by repacking it.
|
||||
*/
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
#ifdef WITHIN_PANDA
|
||||
#include "pStatCollector.h"
|
||||
#include "extension.h"
|
||||
#include "datagram.h"
|
||||
#endif
|
||||
|
||||
class DCPacker;
|
||||
@@ -75,18 +77,17 @@ PUBLISHED:
|
||||
INLINE void output(std::ostream &out) const;
|
||||
INLINE void write(std::ostream &out, int indent_level) const;
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
bool pack_args(DCPacker &packer, PyObject *sequence) const;
|
||||
PyObject *unpack_args(DCPacker &packer) const;
|
||||
EXTENSION(bool pack_args(DCPacker &packer, PyObject *sequence) const);
|
||||
EXTENSION(PyObject *unpack_args(DCPacker &packer) const);
|
||||
|
||||
void receive_update(DCPacker &packer, PyObject *distobj) const;
|
||||
EXTENSION(void receive_update(DCPacker &packer, PyObject *distobj) const);
|
||||
|
||||
Datagram client_format_update(DOID_TYPE do_id, PyObject *args) const;
|
||||
Datagram ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id,
|
||||
PyObject *args) const;
|
||||
Datagram ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id,
|
||||
int msg_type, PyObject *args) const;
|
||||
#endif
|
||||
EXTENSION(Datagram client_format_update(DOID_TYPE do_id, PyObject *args) const);
|
||||
EXTENSION(Datagram ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id,
|
||||
CHANNEL_TYPE from_id, PyObject *args) const);
|
||||
EXTENSION(Datagram ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id,
|
||||
CHANNEL_TYPE from_id, int msg_type,
|
||||
PyObject *args) const);
|
||||
|
||||
public:
|
||||
virtual void output(std::ostream &out, bool brief) const=0;
|
||||
@@ -99,10 +100,6 @@ public:
|
||||
INLINE void set_class(DCClass *dclass);
|
||||
INLINE void set_default_value(vector_uchar default_value);
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
static std::string get_pystr(PyObject *value);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void refresh_default_value();
|
||||
|
||||
@@ -118,6 +115,8 @@ private:
|
||||
|
||||
#ifdef WITHIN_PANDA
|
||||
PStatCollector _field_update_pcollector;
|
||||
|
||||
friend class Extension<DCField>;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
305
direct/src/dcparser/dcField_ext.cxx
Normal file
305
direct/src/dcparser/dcField_ext.cxx
Normal file
@@ -0,0 +1,305 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file dcField_ext.cxx
|
||||
* @author CFSworks
|
||||
* @date 2019-07-03
|
||||
*/
|
||||
|
||||
#include "dcField_ext.h"
|
||||
#include "dcPacker_ext.h"
|
||||
#include "dcmsgtypes.h"
|
||||
|
||||
#include "datagram.h"
|
||||
#include "pStatTimer.h"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
/**
|
||||
* Packs the Python arguments from the indicated tuple into the packer.
|
||||
* Returns true on success, false on failure.
|
||||
*
|
||||
* It is assumed that the packer is currently positioned on this field.
|
||||
*/
|
||||
bool Extension<DCField>::
|
||||
pack_args(DCPacker &packer, PyObject *sequence) const {
|
||||
nassertr(!packer.had_error(), false);
|
||||
nassertr(packer.get_current_field() == _this, false);
|
||||
|
||||
invoke_extension(&packer).pack_object(sequence);
|
||||
if (!packer.had_error()) {
|
||||
/*
|
||||
cerr << "pack " << _this->get_name() << get_pystr(sequence) << "\n";
|
||||
*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Notify::ptr()->has_assert_failed()) {
|
||||
std::ostringstream strm;
|
||||
PyObject *exc_type = PyExc_Exception;
|
||||
|
||||
if (_this->as_parameter() != nullptr) {
|
||||
// If it's a parameter-type field, the value may or may not be a
|
||||
// sequence.
|
||||
if (packer.had_pack_error()) {
|
||||
strm << "Incorrect arguments to field: " << _this->get_name()
|
||||
<< " = " << get_pystr(sequence);
|
||||
exc_type = PyExc_TypeError;
|
||||
} else {
|
||||
strm << "Value out of range on field: " << _this->get_name()
|
||||
<< " = " << get_pystr(sequence);
|
||||
exc_type = PyExc_ValueError;
|
||||
}
|
||||
|
||||
} else {
|
||||
// If it's a molecular or atomic field, the value should be a sequence.
|
||||
PyObject *tuple = PySequence_Tuple(sequence);
|
||||
if (tuple == nullptr) {
|
||||
strm << "Value for " << _this->get_name() << " not a sequence: " \
|
||||
<< get_pystr(sequence);
|
||||
exc_type = PyExc_TypeError;
|
||||
|
||||
} else {
|
||||
if (packer.had_pack_error()) {
|
||||
strm << "Incorrect arguments to field: " << _this->get_name()
|
||||
<< get_pystr(sequence);
|
||||
exc_type = PyExc_TypeError;
|
||||
} else {
|
||||
strm << "Value out of range on field: " << _this->get_name()
|
||||
<< get_pystr(sequence);
|
||||
exc_type = PyExc_ValueError;
|
||||
}
|
||||
|
||||
Py_DECREF(tuple);
|
||||
}
|
||||
}
|
||||
|
||||
std::string message = strm.str();
|
||||
PyErr_SetString(exc_type, message.c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpacks the values from the packer, beginning at the current point in the
|
||||
* unpack_buffer, into a Python tuple and returns the tuple.
|
||||
*
|
||||
* It is assumed that the packer is currently positioned on this field.
|
||||
*/
|
||||
PyObject *Extension<DCField>::
|
||||
unpack_args(DCPacker &packer) const {
|
||||
nassertr(!packer.had_error(), nullptr);
|
||||
nassertr(packer.get_current_field() == _this, nullptr);
|
||||
|
||||
size_t start_byte = packer.get_num_unpacked_bytes();
|
||||
PyObject *object = invoke_extension(&packer).unpack_object();
|
||||
|
||||
if (!packer.had_error()) {
|
||||
// Successfully unpacked.
|
||||
/*
|
||||
cerr << "recv " << _this->get_name() << get_pystr(object) << "\n";
|
||||
*/
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
if (!Notify::ptr()->has_assert_failed()) {
|
||||
std::ostringstream strm;
|
||||
PyObject *exc_type = PyExc_Exception;
|
||||
|
||||
if (packer.had_pack_error()) {
|
||||
strm << "Data error unpacking field ";
|
||||
_this->output(strm, true);
|
||||
size_t length = packer.get_unpack_length() - start_byte;
|
||||
strm << "\nGot data (" << (int)length << " bytes):\n";
|
||||
Datagram dg(packer.get_unpack_data() + start_byte, length);
|
||||
dg.dump_hex(strm);
|
||||
size_t error_byte = packer.get_num_unpacked_bytes() - start_byte;
|
||||
strm << "Error detected on byte " << error_byte
|
||||
<< " (" << std::hex << error_byte << std::dec << " hex)";
|
||||
|
||||
exc_type = PyExc_RuntimeError;
|
||||
} else {
|
||||
strm << "Value outside specified range when unpacking field "
|
||||
<< _this->get_name() << ": " << get_pystr(object);
|
||||
exc_type = PyExc_ValueError;
|
||||
}
|
||||
|
||||
std::string message = strm.str();
|
||||
PyErr_SetString(exc_type, message.c_str());
|
||||
}
|
||||
|
||||
Py_XDECREF(object);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the update message out of the datagram and applies it to the
|
||||
* indicated object by calling the appropriate method.
|
||||
*/
|
||||
void Extension<DCField>::
|
||||
receive_update(DCPacker &packer, PyObject *distobj) const {
|
||||
if (_this->as_parameter() != nullptr) {
|
||||
// If it's a parameter-type field, just store a new value on the object.
|
||||
PyObject *value = unpack_args(packer);
|
||||
if (value != nullptr) {
|
||||
PyObject_SetAttrString(distobj, (char *)_this->_name.c_str(), value);
|
||||
}
|
||||
Py_DECREF(value);
|
||||
|
||||
} else {
|
||||
// Otherwise, it must be an atomic or molecular field, so call the
|
||||
// corresponding method.
|
||||
|
||||
if (!PyObject_HasAttrString(distobj, (char *)_this->_name.c_str())) {
|
||||
// If there's no Python method to receive this message, don't bother
|
||||
// unpacking it to a Python tuple--just skip past the message.
|
||||
packer.unpack_skip();
|
||||
|
||||
} else {
|
||||
// Otherwise, get a Python tuple from the args and call the Python
|
||||
// method.
|
||||
PyObject *args = unpack_args(packer);
|
||||
|
||||
if (args != nullptr) {
|
||||
PyObject *func = PyObject_GetAttrString(distobj, (char *)_this->_name.c_str());
|
||||
nassertv(func != nullptr);
|
||||
|
||||
PyObject *result;
|
||||
{
|
||||
#ifdef WITHIN_PANDA
|
||||
PStatTimer timer(_this->_field_update_pcollector);
|
||||
#endif
|
||||
result = PyObject_CallObject(func, args);
|
||||
}
|
||||
Py_XDECREF(result);
|
||||
Py_DECREF(func);
|
||||
Py_DECREF(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a datagram containing the message necessary to send an update for
|
||||
* the indicated distributed object from the client.
|
||||
*/
|
||||
Datagram Extension<DCField>::
|
||||
client_format_update(DOID_TYPE do_id, PyObject *args) const {
|
||||
DCPacker packer;
|
||||
|
||||
packer.raw_pack_uint16(CLIENT_OBJECT_SET_FIELD);
|
||||
packer.raw_pack_uint32(do_id);
|
||||
packer.raw_pack_uint16(_this->_number);
|
||||
|
||||
packer.begin_pack(_this);
|
||||
pack_args(packer, args);
|
||||
if (!packer.end_pack()) {
|
||||
return Datagram();
|
||||
}
|
||||
|
||||
return Datagram(packer.get_data(), packer.get_length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a datagram containing the message necessary to send an update for
|
||||
* the indicated distributed object from the AI.
|
||||
*/
|
||||
Datagram Extension<DCField>::
|
||||
ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const {
|
||||
DCPacker packer;
|
||||
|
||||
packer.raw_pack_uint8(1);
|
||||
packer.RAW_PACK_CHANNEL(to_id);
|
||||
packer.RAW_PACK_CHANNEL(from_id);
|
||||
packer.raw_pack_uint16(STATESERVER_OBJECT_SET_FIELD);
|
||||
packer.raw_pack_uint32(do_id);
|
||||
packer.raw_pack_uint16(_this->_number);
|
||||
|
||||
packer.begin_pack(_this);
|
||||
pack_args(packer, args);
|
||||
if (!packer.end_pack()) {
|
||||
return Datagram();
|
||||
}
|
||||
|
||||
return Datagram(packer.get_data(), packer.get_length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a datagram containing the message necessary to send an update,
|
||||
* with the msg type, for the indicated distributed object from the AI.
|
||||
*/
|
||||
Datagram Extension<DCField>::
|
||||
ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const {
|
||||
DCPacker packer;
|
||||
|
||||
packer.raw_pack_uint8(1);
|
||||
packer.RAW_PACK_CHANNEL(to_id);
|
||||
packer.RAW_PACK_CHANNEL(from_id);
|
||||
packer.raw_pack_uint16(msg_type);
|
||||
packer.raw_pack_uint32(do_id);
|
||||
packer.raw_pack_uint16(_this->_number);
|
||||
|
||||
packer.begin_pack(_this);
|
||||
pack_args(packer, args);
|
||||
if (!packer.end_pack()) {
|
||||
return Datagram();
|
||||
}
|
||||
|
||||
return Datagram(packer.get_data(), packer.get_length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of the indicated Python object.
|
||||
*/
|
||||
std::string Extension<DCField>::
|
||||
get_pystr(PyObject *value) {
|
||||
if (value == nullptr) {
|
||||
return "(null)";
|
||||
}
|
||||
|
||||
PyObject *str = PyObject_Str(value);
|
||||
if (str != nullptr) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
std::string result = PyUnicode_AsUTF8(str);
|
||||
#else
|
||||
std::string result = PyString_AsString(str);
|
||||
#endif
|
||||
Py_DECREF(str);
|
||||
return result;
|
||||
}
|
||||
|
||||
PyObject *repr = PyObject_Repr(value);
|
||||
if (repr != nullptr) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
std::string result = PyUnicode_AsUTF8(repr);
|
||||
#else
|
||||
std::string result = PyString_AsString(repr);
|
||||
#endif
|
||||
Py_DECREF(repr);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (value->ob_type != nullptr) {
|
||||
PyObject *typestr = PyObject_Str((PyObject *)(value->ob_type));
|
||||
if (typestr != nullptr) {
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
std::string result = PyUnicode_AsUTF8(typestr);
|
||||
#else
|
||||
std::string result = PyString_AsString(typestr);
|
||||
#endif
|
||||
Py_DECREF(typestr);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return "(invalid object)";
|
||||
}
|
||||
|
||||
#endif // HAVE_PYTHON
|
||||
48
direct/src/dcparser/dcField_ext.h
Normal file
48
direct/src/dcparser/dcField_ext.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file dcField_ext.h
|
||||
* @author CFSworks
|
||||
* @date 2019-07-03
|
||||
*/
|
||||
|
||||
#ifndef DCFIELD_EXT_H
|
||||
#define DCFIELD_EXT_H
|
||||
|
||||
#include "dtoolbase.h"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
#include "extension.h"
|
||||
#include "dcField.h"
|
||||
#include "py_panda.h"
|
||||
|
||||
/**
|
||||
* This class defines the extension methods for DCField, which are called
|
||||
* instead of any C++ methods with the same prototype.
|
||||
*/
|
||||
template<>
|
||||
class Extension<DCField> : public ExtensionBase<DCField> {
|
||||
public:
|
||||
bool pack_args(DCPacker &packer, PyObject *sequence) const;
|
||||
PyObject *unpack_args(DCPacker &packer) const;
|
||||
|
||||
void receive_update(DCPacker &packer, PyObject *distobj) const;
|
||||
|
||||
Datagram client_format_update(DOID_TYPE do_id, PyObject *args) const;
|
||||
Datagram ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id,
|
||||
PyObject *args) const;
|
||||
Datagram ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id,
|
||||
int msg_type, PyObject *args) const;
|
||||
|
||||
static std::string get_pystr(PyObject *value);
|
||||
};
|
||||
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
#endif // DCFIELD_EXT_H
|
||||
@@ -19,10 +19,6 @@
|
||||
#include "dcSwitchParameter.h"
|
||||
#include "dcClass.h"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
#include "py_panda.h"
|
||||
#endif
|
||||
|
||||
using std::istream;
|
||||
using std::istringstream;
|
||||
using std::ostream;
|
||||
@@ -622,335 +618,6 @@ unpack_skip() {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
/**
|
||||
* Packs the Python object of whatever type into the packer. Each numeric
|
||||
* object and string object maps to the corresponding pack_value() call; a
|
||||
* tuple or sequence maps to a push() followed by all of the tuple's contents
|
||||
* followed by a pop().
|
||||
*/
|
||||
void DCPacker::
|
||||
pack_object(PyObject *object) {
|
||||
nassertv(_mode == M_pack || _mode == M_repack);
|
||||
DCPackType pack_type = get_pack_type();
|
||||
|
||||
// had to add this for basic 64 and unsigned data to get packed right .. Not
|
||||
// sure if we can just do the rest this way..
|
||||
|
||||
switch(pack_type)
|
||||
{
|
||||
case PT_int64:
|
||||
if(PyLong_Check(object))
|
||||
{
|
||||
pack_int64(PyLong_AsLongLong(object));
|
||||
return;
|
||||
}
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
else if (PyInt_Check(object))
|
||||
{
|
||||
pack_int64(PyInt_AsLong(object));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case PT_uint64:
|
||||
if(PyLong_Check(object))
|
||||
{
|
||||
pack_uint64(PyLong_AsUnsignedLongLong(object));
|
||||
return;
|
||||
}
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
else if(PyInt_Check(object))
|
||||
{
|
||||
PyObject *obj1 = PyNumber_Long(object);
|
||||
pack_int(PyLong_AsUnsignedLongLong(obj1));
|
||||
Py_DECREF(obj1);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case PT_int:
|
||||
if(PyLong_Check(object))
|
||||
{
|
||||
pack_int(PyLong_AsLong(object));
|
||||
return;
|
||||
}
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
else if (PyInt_Check(object))
|
||||
{
|
||||
pack_int(PyInt_AsLong(object));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case PT_uint:
|
||||
if(PyLong_Check(object))
|
||||
{
|
||||
pack_uint(PyLong_AsUnsignedLong(object));
|
||||
return;
|
||||
}
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
else if (PyInt_Check(object))
|
||||
{
|
||||
PyObject *obj1 = PyNumber_Long(object);
|
||||
pack_uint(PyLong_AsUnsignedLong(obj1));
|
||||
Py_DECREF(obj1);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (PyLong_Check(object)) {
|
||||
pack_int(PyLong_AsLong(object));
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
} else if (PyInt_Check(object)) {
|
||||
pack_int(PyInt_AS_LONG(object));
|
||||
#endif
|
||||
} else if (PyFloat_Check(object)) {
|
||||
pack_double(PyFloat_AS_DOUBLE(object));
|
||||
} else if (PyLong_Check(object)) {
|
||||
pack_int64(PyLong_AsLongLong(object));
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
} else if (PyUnicode_Check(object)) {
|
||||
const char *buffer;
|
||||
Py_ssize_t length;
|
||||
buffer = PyUnicode_AsUTF8AndSize(object, &length);
|
||||
if (buffer) {
|
||||
pack_string(string(buffer, length));
|
||||
}
|
||||
} else if (PyBytes_Check(object)) {
|
||||
const unsigned char *buffer;
|
||||
Py_ssize_t length;
|
||||
PyBytes_AsStringAndSize(object, (char **)&buffer, &length);
|
||||
if (buffer) {
|
||||
pack_blob(vector_uchar(buffer, buffer + length));
|
||||
}
|
||||
#else
|
||||
} else if (PyString_Check(object) || PyUnicode_Check(object)) {
|
||||
char *buffer;
|
||||
Py_ssize_t length;
|
||||
PyString_AsStringAndSize(object, &buffer, &length);
|
||||
if (buffer) {
|
||||
pack_string(string(buffer, length));
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
// For some reason, PySequence_Check() is incorrectly reporting that a
|
||||
// class instance is a sequence, even if it doesn't provide __len__, so we
|
||||
// double-check by testing for __len__ explicitly.
|
||||
bool is_sequence =
|
||||
(PySequence_Check(object) != 0) &&
|
||||
(PyObject_HasAttrString(object, "__len__") != 0);
|
||||
bool is_instance = false;
|
||||
|
||||
const DCClass *dclass = nullptr;
|
||||
const DCPackerInterface *current_field = get_current_field();
|
||||
if (current_field != nullptr) {
|
||||
const DCClassParameter *class_param = get_current_field()->as_class_parameter();
|
||||
if (class_param != nullptr) {
|
||||
dclass = class_param->get_class();
|
||||
|
||||
if (dclass->has_class_def()) {
|
||||
PyObject *class_def = dclass->get_class_def();
|
||||
is_instance = (PyObject_IsInstance(object, dclass->get_class_def()) != 0);
|
||||
Py_DECREF(class_def);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If dclass is not NULL, the packer is expecting a class object. There
|
||||
// are then two cases: (1) the user has supplied a matching class object,
|
||||
// or (2) the user has supplied a sequence object. Unfortunately, it may
|
||||
// be difficult to differentiate these two cases, since a class object may
|
||||
// also be a sequence object.
|
||||
|
||||
// The rule to differentiate them is:
|
||||
|
||||
// (1) If the supplied class object is an instance of the expected class
|
||||
// object, it is considered to be a class object.
|
||||
|
||||
// (2) Otherwise, if the supplied class object has a __len__() method
|
||||
// (i.e. PySequence_Check() returns true), then it is considered to be a
|
||||
// sequence.
|
||||
|
||||
// (3) Otherwise, it is considered to be a class object.
|
||||
|
||||
if (dclass != nullptr && (is_instance || !is_sequence)) {
|
||||
// The supplied object is either an instance of the expected class
|
||||
// object, or it is not a sequence--this is case (1) or (3).
|
||||
pack_class_object(dclass, object);
|
||||
} else if (is_sequence) {
|
||||
// The supplied object is not an instance of the expected class object,
|
||||
// but it is a sequence. This is case (2).
|
||||
push();
|
||||
int size = PySequence_Size(object);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
PyObject *element = PySequence_GetItem(object, i);
|
||||
if (element != nullptr) {
|
||||
pack_object(element);
|
||||
Py_DECREF(element);
|
||||
} else {
|
||||
std::cerr << "Unable to extract item " << i << " from sequence.\n";
|
||||
}
|
||||
}
|
||||
pop();
|
||||
} else {
|
||||
// The supplied object is not a sequence, and we weren't expecting a
|
||||
// class parameter. This is none of the above, an error.
|
||||
ostringstream strm;
|
||||
strm << "Don't know how to pack object: "
|
||||
<< DCField::get_pystr(object);
|
||||
nassert_raise(strm.str());
|
||||
_pack_error = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
/**
|
||||
* Unpacks a Python object of the appropriate type from the stream for the
|
||||
* current field. This may be an integer or a string for a simple field
|
||||
* object; if the current field represents a list of fields it will be a
|
||||
* tuple.
|
||||
*/
|
||||
PyObject *DCPacker::
|
||||
unpack_object() {
|
||||
PyObject *object = nullptr;
|
||||
|
||||
DCPackType pack_type = get_pack_type();
|
||||
|
||||
switch (pack_type) {
|
||||
case PT_invalid:
|
||||
object = Py_None;
|
||||
Py_INCREF(object);
|
||||
unpack_skip();
|
||||
break;
|
||||
|
||||
case PT_double:
|
||||
{
|
||||
double value = unpack_double();
|
||||
object = PyFloat_FromDouble(value);
|
||||
}
|
||||
break;
|
||||
|
||||
case PT_int:
|
||||
{
|
||||
int value = unpack_int();
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
object = PyLong_FromLong(value);
|
||||
#else
|
||||
object = PyInt_FromLong(value);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case PT_uint:
|
||||
{
|
||||
unsigned int value = unpack_uint();
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
object = PyLong_FromLong(value);
|
||||
#else
|
||||
if (value & 0x80000000) {
|
||||
object = PyLong_FromUnsignedLong(value);
|
||||
} else {
|
||||
object = PyInt_FromLong(value);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case PT_int64:
|
||||
{
|
||||
int64_t value = unpack_int64();
|
||||
object = PyLong_FromLongLong(value);
|
||||
}
|
||||
break;
|
||||
|
||||
case PT_uint64:
|
||||
{
|
||||
uint64_t value = unpack_uint64();
|
||||
object = PyLong_FromUnsignedLongLong(value);
|
||||
}
|
||||
break;
|
||||
|
||||
case PT_blob:
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
{
|
||||
string str;
|
||||
unpack_string(str);
|
||||
object = PyBytes_FromStringAndSize(str.data(), str.size());
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
// On Python 2, fall through to below.
|
||||
|
||||
case PT_string:
|
||||
{
|
||||
string str;
|
||||
unpack_string(str);
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
object = PyUnicode_FromStringAndSize(str.data(), str.size());
|
||||
#else
|
||||
object = PyString_FromStringAndSize(str.data(), str.size());
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case PT_class:
|
||||
{
|
||||
const DCClassParameter *class_param = get_current_field()->as_class_parameter();
|
||||
if (class_param != nullptr) {
|
||||
const DCClass *dclass = class_param->get_class();
|
||||
if (dclass->has_class_def()) {
|
||||
// If we know what kind of class object this is and it has a valid
|
||||
// constructor, create the class object instead of just a tuple.
|
||||
object = unpack_class_object(dclass);
|
||||
if (object == nullptr) {
|
||||
std::cerr << "Unable to construct object of class "
|
||||
<< dclass->get_name() << "\n";
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fall through (if no constructor)
|
||||
|
||||
// If we don't know what kind of class object it is, or it doesn't have a
|
||||
// constructor, fall through and make a tuple.
|
||||
default:
|
||||
{
|
||||
// First, build up a list from the nested objects.
|
||||
object = PyList_New(0);
|
||||
|
||||
push();
|
||||
while (more_nested_fields()) {
|
||||
PyObject *element = unpack_object();
|
||||
PyList_Append(object, element);
|
||||
Py_DECREF(element);
|
||||
}
|
||||
pop();
|
||||
|
||||
if (pack_type != PT_array) {
|
||||
// For these other kinds of objects, we'll convert the list into a
|
||||
// tuple.
|
||||
PyObject *tuple = PyList_AsTuple(object);
|
||||
Py_DECREF(object);
|
||||
object = tuple;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
nassertr(object != nullptr, nullptr);
|
||||
return object;
|
||||
}
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
|
||||
/**
|
||||
* Parses an object's value according to the DC file syntax (e.g. as a
|
||||
* default value string) and packs it. Returns true on success, false on a
|
||||
@@ -1206,178 +873,3 @@ clear_stack() {
|
||||
_stack = next;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
/**
|
||||
* Given that the current element is a ClassParameter for a Python class
|
||||
* object, try to extract the appropriate values from the class object and
|
||||
* pack in.
|
||||
*/
|
||||
void DCPacker::
|
||||
pack_class_object(const DCClass *dclass, PyObject *object) {
|
||||
push();
|
||||
while (more_nested_fields() && !_pack_error) {
|
||||
const DCField *field = get_current_field()->as_field();
|
||||
nassertv(field != nullptr);
|
||||
get_class_element(dclass, object, field);
|
||||
}
|
||||
pop();
|
||||
}
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
/**
|
||||
* Given that the current element is a ClassParameter for a Python class for
|
||||
* which we have a valid constructor, unpack it and fill in its values.
|
||||
*/
|
||||
PyObject *DCPacker::
|
||||
unpack_class_object(const DCClass *dclass) {
|
||||
PyObject *class_def = dclass->get_class_def();
|
||||
nassertr(class_def != nullptr, nullptr);
|
||||
|
||||
PyObject *object = nullptr;
|
||||
|
||||
if (!dclass->has_constructor()) {
|
||||
// If the class uses a default constructor, go ahead and create the Python
|
||||
// object for it now.
|
||||
object = PyObject_CallObject(class_def, nullptr);
|
||||
if (object == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
push();
|
||||
if (object == nullptr && more_nested_fields()) {
|
||||
// The first nested field will be the constructor.
|
||||
const DCField *field = get_current_field()->as_field();
|
||||
nassertr(field != nullptr, object);
|
||||
nassertr(field == dclass->get_constructor(), object);
|
||||
|
||||
set_class_element(class_def, object, field);
|
||||
|
||||
// By now, the object should have been constructed.
|
||||
if (object == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
while (more_nested_fields()) {
|
||||
const DCField *field = get_current_field()->as_field();
|
||||
nassertr(field != nullptr, object);
|
||||
|
||||
set_class_element(class_def, object, field);
|
||||
}
|
||||
pop();
|
||||
|
||||
return object;
|
||||
}
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
/**
|
||||
* Unpacks the current element and stuffs it on the Python class object in
|
||||
* whatever way is appropriate.
|
||||
*/
|
||||
void DCPacker::
|
||||
set_class_element(PyObject *class_def, PyObject *&object,
|
||||
const DCField *field) {
|
||||
string field_name = field->get_name();
|
||||
DCPackType pack_type = get_pack_type();
|
||||
|
||||
if (field_name.empty()) {
|
||||
switch (pack_type) {
|
||||
case PT_class:
|
||||
case PT_switch:
|
||||
// If the field has no name, but it is one of these container objects,
|
||||
// we want to unpack its nested objects directly into the class.
|
||||
push();
|
||||
while (more_nested_fields()) {
|
||||
const DCField *field = get_current_field()->as_field();
|
||||
nassertv(field != nullptr);
|
||||
nassertv(object != nullptr);
|
||||
set_class_element(class_def, object, field);
|
||||
}
|
||||
pop();
|
||||
break;
|
||||
|
||||
default:
|
||||
// Otherwise, we just skip over the field.
|
||||
unpack_skip();
|
||||
}
|
||||
|
||||
} else {
|
||||
// If the field does have a name, we will want to store it on the class,
|
||||
// either by calling a method (for a PT_field pack_type) or by setting a
|
||||
// value (for any other kind of pack_type).
|
||||
|
||||
PyObject *element = unpack_object();
|
||||
|
||||
if (pack_type == PT_field) {
|
||||
if (object == nullptr) {
|
||||
// If the object hasn't been constructed yet, assume this is the
|
||||
// constructor.
|
||||
object = PyObject_CallObject(class_def, element);
|
||||
|
||||
} else {
|
||||
if (PyObject_HasAttrString(object, (char *)field_name.c_str())) {
|
||||
PyObject *func = PyObject_GetAttrString(object, (char *)field_name.c_str());
|
||||
if (func != nullptr) {
|
||||
PyObject *result = PyObject_CallObject(func, element);
|
||||
Py_XDECREF(result);
|
||||
Py_DECREF(func);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
nassertv(object != nullptr);
|
||||
PyObject_SetAttrString(object, (char *)field_name.c_str(), element);
|
||||
}
|
||||
|
||||
Py_DECREF(element);
|
||||
}
|
||||
}
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
/**
|
||||
* Gets the current element from the Python object and packs it.
|
||||
*/
|
||||
void DCPacker::
|
||||
get_class_element(const DCClass *dclass, PyObject *object,
|
||||
const DCField *field) {
|
||||
string field_name = field->get_name();
|
||||
DCPackType pack_type = get_pack_type();
|
||||
|
||||
if (field_name.empty()) {
|
||||
switch (pack_type) {
|
||||
case PT_class:
|
||||
case PT_switch:
|
||||
// If the field has no name, but it is one of these container objects,
|
||||
// we want to get its nested objects directly from the class.
|
||||
push();
|
||||
while (more_nested_fields() && !_pack_error) {
|
||||
const DCField *field = get_current_field()->as_field();
|
||||
nassertv(field != nullptr);
|
||||
get_class_element(dclass, object, field);
|
||||
}
|
||||
pop();
|
||||
break;
|
||||
|
||||
default:
|
||||
// Otherwise, we just pack the default value.
|
||||
pack_default_value();
|
||||
}
|
||||
|
||||
} else {
|
||||
// If the field does have a name, we will want to get it from the class
|
||||
// and pack it. It just so happens that there's already a method that
|
||||
// does this on DCClass.
|
||||
|
||||
if (!dclass->pack_required_field(*this, object, field)) {
|
||||
_pack_error = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
#include "dcPackData.h"
|
||||
#include "dcPackerCatalog.h"
|
||||
|
||||
#ifdef WITHIN_PANDA
|
||||
#include "extension.h"
|
||||
#endif
|
||||
|
||||
class DCClass;
|
||||
class DCSwitchParameter;
|
||||
|
||||
@@ -103,10 +107,8 @@ public:
|
||||
|
||||
PUBLISHED:
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
void pack_object(PyObject *object);
|
||||
PyObject *unpack_object();
|
||||
#endif
|
||||
EXTENSION(void pack_object(PyObject *object));
|
||||
EXTENSION(PyObject *unpack_object());
|
||||
|
||||
bool parse_and_pack(const std::string &formatted_object);
|
||||
bool parse_and_pack(std::istream &in);
|
||||
@@ -194,14 +196,12 @@ private:
|
||||
void clear();
|
||||
void clear_stack();
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
void pack_class_object(const DCClass *dclass, PyObject *object);
|
||||
PyObject *unpack_class_object(const DCClass *dclass);
|
||||
void set_class_element(PyObject *class_def, PyObject *&object,
|
||||
const DCField *field);
|
||||
void get_class_element(const DCClass *dclass, PyObject *object,
|
||||
const DCField *field);
|
||||
#endif
|
||||
EXTENSION(void pack_class_object(const DCClass *dclass, PyObject *object));
|
||||
EXTENSION(PyObject *unpack_class_object(const DCClass *dclass));
|
||||
EXTENSION(void set_class_element(PyObject *class_def, PyObject *&object,
|
||||
const DCField *field));
|
||||
EXTENSION(void get_class_element(const DCClass *dclass, PyObject *object,
|
||||
const DCField *field));
|
||||
|
||||
private:
|
||||
enum Mode {
|
||||
@@ -222,7 +222,7 @@ private:
|
||||
const DCPackerCatalog *_catalog;
|
||||
const DCPackerCatalog::LiveCatalog *_live_catalog;
|
||||
|
||||
class StackElement {
|
||||
class EXPCL_DIRECT_DCPARSER StackElement {
|
||||
public:
|
||||
// As an optimization, we implement operator new and delete here to
|
||||
// minimize allocation overhead during push() and pop().
|
||||
@@ -257,6 +257,10 @@ private:
|
||||
bool _parse_error;
|
||||
bool _pack_error;
|
||||
bool _range_error;
|
||||
|
||||
#ifdef WITHIN_PANDA
|
||||
friend class Extension<DCPacker>;
|
||||
#endif
|
||||
};
|
||||
|
||||
#include "dcPacker.I"
|
||||
|
||||
508
direct/src/dcparser/dcPacker_ext.cxx
Normal file
508
direct/src/dcparser/dcPacker_ext.cxx
Normal file
@@ -0,0 +1,508 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file dcPacker_ext.cxx
|
||||
* @author CFSworks
|
||||
* @date 2019-07-03
|
||||
*/
|
||||
|
||||
#include "dcPacker_ext.h"
|
||||
#include "dcClass_ext.h"
|
||||
#include "dcField_ext.h"
|
||||
|
||||
#include "dcClassParameter.h"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
/**
|
||||
* Packs the Python object of whatever type into the packer. Each numeric
|
||||
* object and string object maps to the corresponding pack_value() call; a
|
||||
* tuple or sequence maps to a push() followed by all of the tuple's contents
|
||||
* followed by a pop().
|
||||
*/
|
||||
void Extension<DCPacker>::
|
||||
pack_object(PyObject *object) {
|
||||
nassertv(_this->_mode == DCPacker::Mode::M_pack ||
|
||||
_this->_mode == DCPacker::Mode::M_repack);
|
||||
DCPackType pack_type = _this->get_pack_type();
|
||||
|
||||
// had to add this for basic 64 and unsigned data to get packed right .. Not
|
||||
// sure if we can just do the rest this way..
|
||||
|
||||
switch(pack_type) {
|
||||
case PT_int64:
|
||||
if (PyLong_Check(object)) {
|
||||
_this->pack_int64(PyLong_AsLongLong(object));
|
||||
return;
|
||||
}
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
else if (PyInt_Check(object)) {
|
||||
_this->pack_int64(PyInt_AsLong(object));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case PT_uint64:
|
||||
if (PyLong_Check(object)) {
|
||||
_this->pack_uint64(PyLong_AsUnsignedLongLong(object));
|
||||
return;
|
||||
}
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
else if (PyInt_Check(object)) {
|
||||
PyObject *obj1 = PyNumber_Long(object);
|
||||
_this->pack_int(PyLong_AsUnsignedLongLong(obj1));
|
||||
Py_DECREF(obj1);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case PT_int:
|
||||
if (PyLong_Check(object)) {
|
||||
_this->pack_int(PyLong_AsLong(object));
|
||||
return;
|
||||
}
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
else if (PyInt_Check(object)) {
|
||||
_this->pack_int(PyInt_AsLong(object));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case PT_uint:
|
||||
if (PyLong_Check(object)) {
|
||||
_this->pack_uint(PyLong_AsUnsignedLong(object));
|
||||
return;
|
||||
}
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
else if (PyInt_Check(object)) {
|
||||
PyObject *obj1 = PyNumber_Long(object);
|
||||
_this->pack_uint(PyLong_AsUnsignedLong(obj1));
|
||||
Py_DECREF(obj1);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (PyLong_Check(object)) {
|
||||
_this->pack_int(PyLong_AsLong(object));
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
} else if (PyInt_Check(object)) {
|
||||
_this->pack_int(PyInt_AS_LONG(object));
|
||||
#endif
|
||||
} else if (PyFloat_Check(object)) {
|
||||
_this->pack_double(PyFloat_AS_DOUBLE(object));
|
||||
} else if (PyLong_Check(object)) {
|
||||
_this->pack_int64(PyLong_AsLongLong(object));
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
} else if (PyUnicode_Check(object)) {
|
||||
const char *buffer;
|
||||
Py_ssize_t length;
|
||||
buffer = PyUnicode_AsUTF8AndSize(object, &length);
|
||||
if (buffer) {
|
||||
_this->pack_string(std::string(buffer, length));
|
||||
}
|
||||
} else if (PyBytes_Check(object)) {
|
||||
const unsigned char *buffer;
|
||||
Py_ssize_t length;
|
||||
PyBytes_AsStringAndSize(object, (char **)&buffer, &length);
|
||||
if (buffer) {
|
||||
_this->pack_blob(vector_uchar(buffer, buffer + length));
|
||||
}
|
||||
#else
|
||||
} else if (PyString_Check(object) || PyUnicode_Check(object)) {
|
||||
char *buffer;
|
||||
Py_ssize_t length;
|
||||
PyString_AsStringAndSize(object, &buffer, &length);
|
||||
if (buffer) {
|
||||
_this->pack_string(std::string(buffer, length));
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
// For some reason, PySequence_Check() is incorrectly reporting that a
|
||||
// class instance is a sequence, even if it doesn't provide __len__, so we
|
||||
// double-check by testing for __len__ explicitly.
|
||||
bool is_sequence =
|
||||
(PySequence_Check(object) != 0) &&
|
||||
(PyObject_HasAttrString(object, "__len__") != 0);
|
||||
bool is_instance = false;
|
||||
|
||||
const DCClass *dclass = nullptr;
|
||||
const DCPackerInterface *current_field = _this->get_current_field();
|
||||
if (current_field != nullptr) {
|
||||
const DCClassParameter *class_param = _this->get_current_field()->as_class_parameter();
|
||||
if (class_param != nullptr) {
|
||||
dclass = class_param->get_class();
|
||||
|
||||
if (invoke_extension(dclass).has_class_def()) {
|
||||
PyObject *class_def = invoke_extension(dclass).get_class_def();
|
||||
is_instance = (PyObject_IsInstance(object, invoke_extension(dclass).get_class_def()) != 0);
|
||||
Py_DECREF(class_def);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If dclass is not NULL, the packer is expecting a class object. There
|
||||
// are then two cases: (1) the user has supplied a matching class object,
|
||||
// or (2) the user has supplied a sequence object. Unfortunately, it may
|
||||
// be difficult to differentiate these two cases, since a class object may
|
||||
// also be a sequence object.
|
||||
|
||||
// The rule to differentiate them is:
|
||||
|
||||
// (1) If the supplied class object is an instance of the expected class
|
||||
// object, it is considered to be a class object.
|
||||
|
||||
// (2) Otherwise, if the supplied class object has a __len__() method
|
||||
// (i.e. PySequence_Check() returns true), then it is considered to be a
|
||||
// sequence.
|
||||
|
||||
// (3) Otherwise, it is considered to be a class object.
|
||||
|
||||
if (dclass != nullptr && (is_instance || !is_sequence)) {
|
||||
// The supplied object is either an instance of the expected class
|
||||
// object, or it is not a sequence--this is case (1) or (3).
|
||||
pack_class_object(dclass, object);
|
||||
} else if (is_sequence) {
|
||||
// The supplied object is not an instance of the expected class object,
|
||||
// but it is a sequence. This is case (2).
|
||||
_this->push();
|
||||
int size = PySequence_Size(object);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
PyObject *element = PySequence_GetItem(object, i);
|
||||
if (element != nullptr) {
|
||||
pack_object(element);
|
||||
Py_DECREF(element);
|
||||
} else {
|
||||
std::cerr << "Unable to extract item " << i << " from sequence.\n";
|
||||
}
|
||||
}
|
||||
_this->pop();
|
||||
} else {
|
||||
// The supplied object is not a sequence, and we weren't expecting a
|
||||
// class parameter. This is none of the above, an error.
|
||||
std::ostringstream strm;
|
||||
strm << "Don't know how to pack object: "
|
||||
<< Extension<DCField>::get_pystr(object);
|
||||
nassert_raise(strm.str());
|
||||
_this->_pack_error = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpacks a Python object of the appropriate type from the stream for the
|
||||
* current field. This may be an integer or a string for a simple field
|
||||
* object; if the current field represents a list of fields it will be a
|
||||
* tuple.
|
||||
*/
|
||||
PyObject *Extension<DCPacker>::
|
||||
unpack_object() {
|
||||
PyObject *object = nullptr;
|
||||
|
||||
DCPackType pack_type = _this->get_pack_type();
|
||||
|
||||
switch (pack_type) {
|
||||
case PT_invalid:
|
||||
object = Py_None;
|
||||
Py_INCREF(object);
|
||||
_this->unpack_skip();
|
||||
break;
|
||||
|
||||
case PT_double:
|
||||
{
|
||||
double value = _this->unpack_double();
|
||||
object = PyFloat_FromDouble(value);
|
||||
}
|
||||
break;
|
||||
|
||||
case PT_int:
|
||||
{
|
||||
int value = _this->unpack_int();
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
object = PyLong_FromLong(value);
|
||||
#else
|
||||
object = PyInt_FromLong(value);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case PT_uint:
|
||||
{
|
||||
unsigned int value = _this->unpack_uint();
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
object = PyLong_FromLong(value);
|
||||
#else
|
||||
if (value & 0x80000000) {
|
||||
object = PyLong_FromUnsignedLong(value);
|
||||
} else {
|
||||
object = PyInt_FromLong(value);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case PT_int64:
|
||||
{
|
||||
int64_t value = _this->unpack_int64();
|
||||
object = PyLong_FromLongLong(value);
|
||||
}
|
||||
break;
|
||||
|
||||
case PT_uint64:
|
||||
{
|
||||
uint64_t value = _this->unpack_uint64();
|
||||
object = PyLong_FromUnsignedLongLong(value);
|
||||
}
|
||||
break;
|
||||
|
||||
case PT_blob:
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
{
|
||||
std::string str;
|
||||
_this->unpack_string(str);
|
||||
object = PyBytes_FromStringAndSize(str.data(), str.size());
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
// On Python 2, fall through to below.
|
||||
|
||||
case PT_string:
|
||||
{
|
||||
std::string str;
|
||||
_this->unpack_string(str);
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
object = PyUnicode_FromStringAndSize(str.data(), str.size());
|
||||
#else
|
||||
object = PyString_FromStringAndSize(str.data(), str.size());
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case PT_class:
|
||||
{
|
||||
const DCClassParameter *class_param = _this->get_current_field()->as_class_parameter();
|
||||
if (class_param != nullptr) {
|
||||
const DCClass *dclass = class_param->get_class();
|
||||
if (invoke_extension(dclass).has_class_def()) {
|
||||
// If we know what kind of class object this is and it has a valid
|
||||
// constructor, create the class object instead of just a tuple.
|
||||
object = unpack_class_object(dclass);
|
||||
if (object == nullptr) {
|
||||
std::cerr << "Unable to construct object of class "
|
||||
<< dclass->get_name() << "\n";
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fall through (if no constructor)
|
||||
|
||||
// If we don't know what kind of class object it is, or it doesn't have a
|
||||
// constructor, fall through and make a tuple.
|
||||
default:
|
||||
{
|
||||
// First, build up a list from the nested objects.
|
||||
object = PyList_New(0);
|
||||
|
||||
_this->push();
|
||||
while (_this->more_nested_fields()) {
|
||||
PyObject *element = unpack_object();
|
||||
PyList_Append(object, element);
|
||||
Py_DECREF(element);
|
||||
}
|
||||
_this->pop();
|
||||
|
||||
if (pack_type != PT_array) {
|
||||
// For these other kinds of objects, we'll convert the list into a
|
||||
// tuple.
|
||||
PyObject *tuple = PyList_AsTuple(object);
|
||||
Py_DECREF(object);
|
||||
object = tuple;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
nassertr(object != nullptr, nullptr);
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that the current element is a ClassParameter for a Python class
|
||||
* object, try to extract the appropriate values from the class object and
|
||||
* pack in.
|
||||
*/
|
||||
void Extension<DCPacker>::
|
||||
pack_class_object(const DCClass *dclass, PyObject *object) {
|
||||
_this->push();
|
||||
while (_this->more_nested_fields() && !_this->_pack_error) {
|
||||
const DCField *field = _this->get_current_field()->as_field();
|
||||
nassertv(field != nullptr);
|
||||
get_class_element(dclass, object, field);
|
||||
}
|
||||
_this->pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that the current element is a ClassParameter for a Python class for
|
||||
* which we have a valid constructor, unpack it and fill in its values.
|
||||
*/
|
||||
PyObject *Extension<DCPacker>::
|
||||
unpack_class_object(const DCClass *dclass) {
|
||||
PyObject *class_def = invoke_extension(dclass).get_class_def();
|
||||
nassertr(class_def != nullptr, nullptr);
|
||||
|
||||
PyObject *object = nullptr;
|
||||
|
||||
if (!dclass->has_constructor()) {
|
||||
// If the class uses a default constructor, go ahead and create the Python
|
||||
// object for it now.
|
||||
object = PyObject_CallObject(class_def, nullptr);
|
||||
if (object == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
_this->push();
|
||||
if (object == nullptr && _this->more_nested_fields()) {
|
||||
// The first nested field will be the constructor.
|
||||
const DCField *field = _this->get_current_field()->as_field();
|
||||
nassertr(field != nullptr, object);
|
||||
nassertr(field == dclass->get_constructor(), object);
|
||||
|
||||
set_class_element(class_def, object, field);
|
||||
|
||||
// By now, the object should have been constructed.
|
||||
if (object == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
while (_this->more_nested_fields()) {
|
||||
const DCField *field = _this->get_current_field()->as_field();
|
||||
nassertr(field != nullptr, object);
|
||||
|
||||
set_class_element(class_def, object, field);
|
||||
}
|
||||
_this->pop();
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpacks the current element and stuffs it on the Python class object in
|
||||
* whatever way is appropriate.
|
||||
*/
|
||||
void Extension<DCPacker>::
|
||||
set_class_element(PyObject *class_def, PyObject *&object,
|
||||
const DCField *field) {
|
||||
std::string field_name = field->get_name();
|
||||
DCPackType pack_type = _this->get_pack_type();
|
||||
|
||||
if (field_name.empty()) {
|
||||
switch (pack_type) {
|
||||
case PT_class:
|
||||
case PT_switch:
|
||||
// If the field has no name, but it is one of these container objects,
|
||||
// we want to unpack its nested objects directly into the class.
|
||||
_this->push();
|
||||
while (_this->more_nested_fields()) {
|
||||
const DCField *field = _this->get_current_field()->as_field();
|
||||
nassertv(field != nullptr);
|
||||
nassertv(object != nullptr);
|
||||
set_class_element(class_def, object, field);
|
||||
}
|
||||
_this->pop();
|
||||
break;
|
||||
|
||||
default:
|
||||
// Otherwise, we just skip over the field.
|
||||
_this->unpack_skip();
|
||||
}
|
||||
|
||||
} else {
|
||||
// If the field does have a name, we will want to store it on the class,
|
||||
// either by calling a method (for a PT_field pack_type) or by setting a
|
||||
// value (for any other kind of pack_type).
|
||||
|
||||
PyObject *element = unpack_object();
|
||||
|
||||
if (pack_type == PT_field) {
|
||||
if (object == nullptr) {
|
||||
// If the object hasn't been constructed yet, assume this is the
|
||||
// constructor.
|
||||
object = PyObject_CallObject(class_def, element);
|
||||
|
||||
} else {
|
||||
if (PyObject_HasAttrString(object, (char *)field_name.c_str())) {
|
||||
PyObject *func = PyObject_GetAttrString(object, (char *)field_name.c_str());
|
||||
if (func != nullptr) {
|
||||
PyObject *result = PyObject_CallObject(func, element);
|
||||
Py_XDECREF(result);
|
||||
Py_DECREF(func);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
nassertv(object != nullptr);
|
||||
PyObject_SetAttrString(object, (char *)field_name.c_str(), element);
|
||||
}
|
||||
|
||||
Py_DECREF(element);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current element from the Python object and packs it.
|
||||
*/
|
||||
void Extension<DCPacker>::
|
||||
get_class_element(const DCClass *dclass, PyObject *object,
|
||||
const DCField *field) {
|
||||
std::string field_name = field->get_name();
|
||||
DCPackType pack_type = _this->get_pack_type();
|
||||
|
||||
if (field_name.empty()) {
|
||||
switch (pack_type) {
|
||||
case PT_class:
|
||||
case PT_switch:
|
||||
// If the field has no name, but it is one of these container objects,
|
||||
// we want to get its nested objects directly from the class.
|
||||
_this->push();
|
||||
while (_this->more_nested_fields() && !_this->_pack_error) {
|
||||
const DCField *field = _this->get_current_field()->as_field();
|
||||
nassertv(field != nullptr);
|
||||
get_class_element(dclass, object, field);
|
||||
}
|
||||
_this->pop();
|
||||
break;
|
||||
|
||||
default:
|
||||
// Otherwise, we just pack the default value.
|
||||
_this->pack_default_value();
|
||||
}
|
||||
|
||||
} else {
|
||||
// If the field does have a name, we will want to get it from the class
|
||||
// and pack it. It just so happens that there's already a method that
|
||||
// does this on DCClass.
|
||||
|
||||
if (!invoke_extension(dclass).pack_required_field(*_this, object, field)) {
|
||||
_this->_pack_error = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HAVE_PYTHON
|
||||
45
direct/src/dcparser/dcPacker_ext.h
Normal file
45
direct/src/dcparser/dcPacker_ext.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file dcPacker_ext.h
|
||||
* @author CFSworks
|
||||
* @date 2019-07-03
|
||||
*/
|
||||
|
||||
#ifndef DCPACKER_EXT_H
|
||||
#define DCPACKER_EXT_H
|
||||
|
||||
#include "dtoolbase.h"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
#include "extension.h"
|
||||
#include "dcPacker.h"
|
||||
#include "py_panda.h"
|
||||
|
||||
/**
|
||||
* This class defines the extension methods for DCPacker, which are called
|
||||
* instead of any C++ methods with the same prototype.
|
||||
*/
|
||||
template<>
|
||||
class Extension<DCPacker> : public ExtensionBase<DCPacker> {
|
||||
public:
|
||||
void pack_object(PyObject *object);
|
||||
PyObject *unpack_object();
|
||||
|
||||
void pack_class_object(const DCClass *dclass, PyObject *object);
|
||||
PyObject *unpack_class_object(const DCClass *dclass);
|
||||
void set_class_element(PyObject *class_def, PyObject *&object,
|
||||
const DCField *field);
|
||||
void get_class_element(const DCClass *dclass, PyObject *object,
|
||||
const DCField *field);
|
||||
};
|
||||
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
#endif // DCPACKER_EXT_H
|
||||
3
direct/src/dcparser/p3dcparser_ext_composite.cxx
Normal file
3
direct/src/dcparser/p3dcparser_ext_composite.cxx
Normal file
@@ -0,0 +1,3 @@
|
||||
#include "dcClass_ext.cxx"
|
||||
#include "dcField_ext.cxx"
|
||||
#include "dcPacker_ext.cxx"
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
#include "py_panda.h"
|
||||
#include "dcClass_ext.h"
|
||||
#endif
|
||||
|
||||
using std::endl;
|
||||
@@ -736,7 +737,7 @@ handle_update_field() {
|
||||
// get into trouble if it tried to delete the object from the doId2do
|
||||
// map.
|
||||
Py_INCREF(distobj);
|
||||
dclass->receive_update(distobj, _di);
|
||||
invoke_extension(dclass).receive_update(distobj, _di);
|
||||
Py_DECREF(distobj);
|
||||
|
||||
if (PyErr_Occurred()) {
|
||||
@@ -820,7 +821,7 @@ handle_update_field_owner() {
|
||||
// make a copy of the datagram iterator so that we can use the main
|
||||
// iterator for the non-owner update
|
||||
DatagramIterator _odi(_di);
|
||||
dclass->receive_update(distobjOV, _odi);
|
||||
invoke_extension(dclass).receive_update(distobjOV, _odi);
|
||||
Py_DECREF(distobjOV);
|
||||
|
||||
if (PyErr_Occurred()) {
|
||||
@@ -861,7 +862,7 @@ handle_update_field_owner() {
|
||||
// get into trouble if it tried to delete the object from the doId2do
|
||||
// map.
|
||||
Py_INCREF(distobj);
|
||||
dclass->receive_update(distobj, _di);
|
||||
invoke_extension(dclass).receive_update(distobj, _di);
|
||||
Py_DECREF(distobj);
|
||||
|
||||
if (PyErr_Occurred()) {
|
||||
|
||||
@@ -53,7 +53,7 @@ class SocketStream;
|
||||
* the C++ layer, while server messages that are not understood by the C++
|
||||
* layer are returned up to the Python layer for processing.
|
||||
*/
|
||||
class EXPCL_DIRECT_DISTRIBUTED CConnectionRepository {
|
||||
class CConnectionRepository {
|
||||
PUBLISHED:
|
||||
explicit CConnectionRepository(bool has_owner_view = false,
|
||||
bool threaded_net = false);
|
||||
|
||||
@@ -27,7 +27,7 @@ class CConnectionRepository;
|
||||
* This class defines some basic methods of DistributedSmoothNodeBase which
|
||||
* have been moved into C++ as a performance optimization.
|
||||
*/
|
||||
class EXPCL_DIRECT_DISTRIBUTED CDistributedSmoothNodeBase {
|
||||
class CDistributedSmoothNodeBase {
|
||||
PUBLISHED:
|
||||
CDistributedSmoothNodeBase();
|
||||
~CDistributedSmoothNodeBase();
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
|
||||
NotifyCategoryDecl(distributed, EXPCL_DIRECT_DISTRIBUTED, EXPTP_DIRECT_DISTRIBUTED);
|
||||
|
||||
extern ConfigVariableInt game_server_timeout_ms;
|
||||
extern ConfigVariableDouble min_lag;
|
||||
extern ConfigVariableDouble max_lag;
|
||||
extern ConfigVariableBool handle_datagrams_internally;
|
||||
extern EXPCL_DIRECT_DISTRIBUTED ConfigVariableInt game_server_timeout_ms;
|
||||
extern EXPCL_DIRECT_DISTRIBUTED ConfigVariableDouble min_lag;
|
||||
extern EXPCL_DIRECT_DISTRIBUTED ConfigVariableDouble max_lag;
|
||||
extern EXPCL_DIRECT_DISTRIBUTED ConfigVariableBool handle_datagrams_internally;
|
||||
|
||||
extern EXPCL_DIRECT_DISTRIBUTED void init_libdistributed();
|
||||
|
||||
|
||||
@@ -5256,11 +5256,11 @@ if (PkgSkip("DIRECT")==0):
|
||||
if (PkgSkip("DIRECT")==0):
|
||||
OPTS=['DIR:direct/src/dcparser', 'BUILDING:DIRECT_DCPARSER', 'WITHINPANDA', 'BISONPREFIX_dcyy']
|
||||
CreateFile(GetOutputDir()+"/include/dcParser.h")
|
||||
PyTargetAdd('p3dcparser_dcParser.obj', opts=OPTS, input='dcParser.yxx')
|
||||
TargetAdd('p3dcparser_dcParser.obj', opts=OPTS, input='dcParser.yxx')
|
||||
#TargetAdd('dcParser.h', input='p3dcparser_dcParser.obj', opts=['DEPENDENCYONLY'])
|
||||
PyTargetAdd('p3dcparser_dcLexer.obj', opts=OPTS, input='dcLexer.lxx')
|
||||
PyTargetAdd('p3dcparser_composite1.obj', opts=OPTS, input='p3dcparser_composite1.cxx')
|
||||
PyTargetAdd('p3dcparser_composite2.obj', opts=OPTS, input='p3dcparser_composite2.cxx')
|
||||
TargetAdd('p3dcparser_dcLexer.obj', opts=OPTS, input='dcLexer.lxx')
|
||||
TargetAdd('p3dcparser_composite1.obj', opts=OPTS, input='p3dcparser_composite1.cxx')
|
||||
TargetAdd('p3dcparser_composite2.obj', opts=OPTS, input='p3dcparser_composite2.cxx')
|
||||
|
||||
OPTS=['DIR:direct/src/dcparser', 'WITHINPANDA']
|
||||
IGATEFILES=GetDirectoryContents('direct/src/dcparser', ["*.h", "*_composite*.cxx"])
|
||||
@@ -5268,6 +5268,7 @@ if (PkgSkip("DIRECT")==0):
|
||||
if "dcmsgtypes.h" in IGATEFILES: IGATEFILES.remove('dcmsgtypes.h')
|
||||
TargetAdd('libp3dcparser.in', opts=OPTS, input=IGATEFILES)
|
||||
TargetAdd('libp3dcparser.in', opts=['IMOD:panda3d.direct', 'ILIB:libp3dcparser', 'SRCDIR:direct/src/dcparser'])
|
||||
PyTargetAdd('p3dcparser_ext_composite.obj', opts=OPTS, input='p3dcparser_ext_composite.cxx')
|
||||
|
||||
#
|
||||
# DIRECTORY: direct/src/deadrec/
|
||||
@@ -5289,13 +5290,13 @@ if (PkgSkip("DIRECT")==0):
|
||||
if (PkgSkip("DIRECT")==0):
|
||||
OPTS=['DIR:direct/src/distributed', 'DIR:direct/src/dcparser', 'WITHINPANDA', 'BUILDING:DIRECT', 'OPENSSL']
|
||||
TargetAdd('p3distributed_config_distributed.obj', opts=OPTS, input='config_distributed.cxx')
|
||||
PyTargetAdd('p3distributed_cConnectionRepository.obj', opts=OPTS, input='cConnectionRepository.cxx')
|
||||
PyTargetAdd('p3distributed_cDistributedSmoothNodeBase.obj', opts=OPTS, input='cDistributedSmoothNodeBase.cxx')
|
||||
|
||||
OPTS=['DIR:direct/src/distributed', 'WITHINPANDA', 'OPENSSL']
|
||||
IGATEFILES=GetDirectoryContents('direct/src/distributed', ["*.h", "*.cxx"])
|
||||
TargetAdd('libp3distributed.in', opts=OPTS, input=IGATEFILES)
|
||||
TargetAdd('libp3distributed.in', opts=['IMOD:panda3d.direct', 'ILIB:libp3distributed', 'SRCDIR:direct/src/distributed'])
|
||||
PyTargetAdd('p3distributed_cConnectionRepository.obj', opts=OPTS, input='cConnectionRepository.cxx')
|
||||
PyTargetAdd('p3distributed_cDistributedSmoothNodeBase.obj', opts=OPTS, input='cDistributedSmoothNodeBase.cxx')
|
||||
|
||||
#
|
||||
# DIRECTORY: direct/src/interval/
|
||||
@@ -5345,10 +5346,15 @@ if (PkgSkip("DIRECT")==0):
|
||||
|
||||
if (PkgSkip("DIRECT")==0):
|
||||
TargetAdd('libp3direct.dll', input='p3directbase_directbase.obj')
|
||||
TargetAdd('libp3direct.dll', input='p3dcparser_composite1.obj')
|
||||
TargetAdd('libp3direct.dll', input='p3dcparser_composite2.obj')
|
||||
TargetAdd('libp3direct.dll', input='p3dcparser_dcParser.obj')
|
||||
TargetAdd('libp3direct.dll', input='p3dcparser_dcLexer.obj')
|
||||
TargetAdd('libp3direct.dll', input='p3showbase_showBase.obj')
|
||||
if GetTarget() == 'darwin':
|
||||
TargetAdd('libp3direct.dll', input='p3showbase_showBase_assist.obj')
|
||||
TargetAdd('libp3direct.dll', input='p3deadrec_composite1.obj')
|
||||
TargetAdd('libp3direct.dll', input='p3distributed_config_distributed.obj')
|
||||
TargetAdd('libp3direct.dll', input='p3interval_composite1.obj')
|
||||
TargetAdd('libp3direct.dll', input='p3motiontrail_config_motiontrail.obj')
|
||||
TargetAdd('libp3direct.dll', input='p3motiontrail_cMotionTrail.obj')
|
||||
@@ -5373,11 +5379,7 @@ if (PkgSkip("DIRECT")==0):
|
||||
# These are part of direct.pyd, not libp3direct.dll, because they rely on
|
||||
# the Python libraries. If a C++ user needs these modules, we can move them
|
||||
# back and filter out the Python-specific code.
|
||||
PyTargetAdd('direct.pyd', input='p3dcparser_composite1.obj')
|
||||
PyTargetAdd('direct.pyd', input='p3dcparser_composite2.obj')
|
||||
PyTargetAdd('direct.pyd', input='p3dcparser_dcParser.obj')
|
||||
PyTargetAdd('direct.pyd', input='p3dcparser_dcLexer.obj')
|
||||
PyTargetAdd('direct.pyd', input='p3distributed_config_distributed.obj')
|
||||
PyTargetAdd('direct.pyd', input='p3dcparser_ext_composite.obj')
|
||||
PyTargetAdd('direct.pyd', input='p3distributed_cConnectionRepository.obj')
|
||||
PyTargetAdd('direct.pyd', input='p3distributed_cDistributedSmoothNodeBase.obj')
|
||||
|
||||
@@ -5391,18 +5393,13 @@ if (PkgSkip("DIRECT")==0):
|
||||
# DIRECTORY: direct/src/dcparse/
|
||||
#
|
||||
|
||||
if (PkgSkip("PYTHON")==0 and PkgSkip("DIRECT")==0 and not RTDIST and not RUNTIME):
|
||||
if (PkgSkip("DIRECT")==0 and not RTDIST and not RUNTIME):
|
||||
OPTS=['DIR:direct/src/dcparse', 'DIR:direct/src/dcparser', 'WITHINPANDA', 'ADVAPI']
|
||||
PyTargetAdd('dcparse_dcparse.obj', opts=OPTS, input='dcparse.cxx')
|
||||
PyTargetAdd('p3dcparse.exe', input='p3dcparser_composite1.obj')
|
||||
PyTargetAdd('p3dcparse.exe', input='p3dcparser_composite2.obj')
|
||||
PyTargetAdd('p3dcparse.exe', input='p3dcparser_dcParser.obj')
|
||||
PyTargetAdd('p3dcparse.exe', input='p3dcparser_dcLexer.obj')
|
||||
PyTargetAdd('p3dcparse.exe', input='dcparse_dcparse.obj')
|
||||
PyTargetAdd('p3dcparse.exe', input='libp3direct.dll')
|
||||
PyTargetAdd('p3dcparse.exe', input=COMMON_PANDA_LIBS)
|
||||
PyTargetAdd('p3dcparse.exe', input='libp3pystub.lib')
|
||||
PyTargetAdd('p3dcparse.exe', opts=['ADVAPI'])
|
||||
TargetAdd('dcparse_dcparse.obj', opts=OPTS, input='dcparse.cxx')
|
||||
TargetAdd('p3dcparse.exe', input='dcparse_dcparse.obj')
|
||||
TargetAdd('p3dcparse.exe', input='libp3direct.dll')
|
||||
TargetAdd('p3dcparse.exe', input=COMMON_PANDA_LIBS)
|
||||
TargetAdd('p3dcparse.exe', opts=['ADVAPI'])
|
||||
|
||||
#
|
||||
# DIRECTORY: direct/src/plugin/
|
||||
|
||||
Reference in New Issue
Block a user