open_toontown_panda3d/direct/src/dcparser/dcField_ext.cxx

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