295 lines
8.3 KiB
C++
295 lines
8.3 KiB
C++
/**
|
|
* 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) {
|
|
std::string result = PyUnicode_AsUTF8(str);
|
|
Py_DECREF(str);
|
|
return result;
|
|
}
|
|
|
|
PyObject *repr = PyObject_Repr(value);
|
|
if (repr != nullptr) {
|
|
std::string result = PyUnicode_AsUTF8(repr);
|
|
Py_DECREF(repr);
|
|
return result;
|
|
}
|
|
|
|
PyTypeObject *type = Py_TYPE(value);
|
|
if (type != nullptr) {
|
|
PyObject *typestr = PyObject_Str((PyObject *)type);
|
|
if (typestr != nullptr) {
|
|
std::string result = PyUnicode_AsUTF8(typestr);
|
|
Py_DECREF(typestr);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return "(invalid object)";
|
|
}
|
|
|
|
#endif // HAVE_PYTHON
|