Merge branch 'release/1.10.x'

This commit is contained in:
rdb 2021-01-02 02:33:35 +01:00
commit e099d9e787
54 changed files with 1260 additions and 44 deletions

View File

@ -21,8 +21,12 @@ shared context between all objects written by that Pickler.
Unfortunately, cPickle cannot be supported, because it does not
support extensions of this nature. """
__all__ = ["PickleError", "PicklingError", "UnpicklingError", "Pickler",
"Unpickler", "dump", "dumps", "load", "loads",
"HIGHEST_PROTOCOL", "DEFAULT_PROTOCOL"]
import sys
from panda3d.core import BamWriter, BamReader
from panda3d.core import BamWriter, BamReader, TypedObject
from copyreg import dispatch_table
@ -30,17 +34,29 @@ from copyreg import dispatch_table
# with the local pickle.py.
pickle = __import__('pickle')
HIGHEST_PROTOCOL = pickle.HIGHEST_PROTOCOL
DEFAULT_PROTOCOL = pickle.DEFAULT_PROTOCOL
PickleError = pickle.PickleError
PicklingError = pickle.PicklingError
UnpicklingError = pickle.UnpicklingError
BasePickler = pickle._Pickler
BaseUnpickler = pickle._Unpickler
class _Pickler(BasePickler):
class Pickler(BasePickler):
def __init__(self, *args, **kw):
self.bamWriter = BamWriter()
self._canonical = {}
BasePickler.__init__(self, *args, **kw)
def clear_memo(self):
BasePickler.clear_memo(self)
self._canonical.clear()
self.bamWriter = BamWriter()
# We have to duplicate most of the save() method, so we can add
# support for __reduce_persist__().
@ -54,6 +70,21 @@ class _Pickler(BasePickler):
self.save_pers(pid)
return
# Check if this is a Panda type that we've already saved; if so, store
# a mapping to the canonical copy, so that Python's memoization system
# works properly. This is needed because Python uses id(obj) for
# memoization, but there may be multiple Python wrappers for the same
# C++ pointer, and we don't want that to result in duplication.
t = type(obj)
if issubclass(t, TypedObject.__base__):
canonical = self._canonical.get(obj.this)
if canonical is not None:
obj = canonical
else:
# First time we're seeing this C++ pointer; save it as the
# "canonical" version.
self._canonical[obj.this] = obj
# Check the memo
x = self.memo.get(id(obj))
if x:
@ -61,7 +92,6 @@ class _Pickler(BasePickler):
return
# Check the type dispatch table
t = type(obj)
f = self.dispatch.get(t)
if f:
f(self, obj) # Call unbound method with explicit self
@ -146,25 +176,6 @@ class Unpickler(BaseUnpickler):
BaseUnpickler.dispatch[pickle.REDUCE[0]] = load_reduce
if sys.version_info >= (3, 8):
# In Python 3.8 and up, we can use the C implementation of Pickler, which
# supports a reducer_override method.
class Pickler(pickle.Pickler):
def __init__(self, *args, **kw):
self.bamWriter = BamWriter()
pickle.Pickler.__init__(self, *args, **kw)
def reducer_override(self, obj):
reduce = getattr(obj, "__reduce_persist__", None)
if reduce:
return reduce(self)
return NotImplemented
else:
# Otherwise, we have to use our custom version that overrides save().
Pickler = _Pickler
# Shorthands
from io import BytesIO

View File

@ -920,6 +920,9 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
|| fname == "__delattr__") {
// Just to prevent these from getting keyword arguments.
} else if (fname == "__setstate__") {
_args_type = InterfaceMaker::AT_single_arg;
} else {
if (_args_type == InterfaceMaker::AT_varargs) {
// Every other method can take keyword arguments, if they take more

View File

@ -3637,18 +3637,23 @@ write_function_for_name(ostream &out, Object *obj,
std::string ClassName = make_safe_name(obj->_itype.get_scoped_name());
std::string cClassName = obj->_itype.get_true_name();
// string class_name = remap->_cpptype->get_simple_name();
CPPStructType *struct_type = obj->_itype._cpptype->as_struct_type();
// If this is a non-static __setstate__, we run the default constructor.
if (remap->_cppfunc->get_local_name() == "__setstate__") {
out << " if (DtoolInstance_VOID_PTR(self) != nullptr) {\n"
<< " Dtool_Raise_TypeError(\"C++ object is already constructed.\");\n";
error_return(out, 4, return_flags);
out << " }\n"
<< " " << cClassName << " *local_this = new " << cClassName << ";\n"
<< " DTool_PyInit_Finalize(self, local_this, &Dtool_" << ClassName
<< ", false, false);\n"
<< " if (local_this == nullptr) {\n"
<< " PyErr_NoMemory();\n";
if (remap->_cppfunc->get_local_name() == "__setstate__" &&
!struct_type->is_abstract()) {
out << " " << cClassName << " *local_this = nullptr;\n"
<< " if (DtoolInstance_VOID_PTR(self) == nullptr) {\n"
<< " local_this = new " << cClassName << ";\n"
<< " DTool_PyInit_Finalize(self, local_this, &Dtool_" << ClassName
<< ", true, false);\n"
<< " if (local_this == nullptr) {\n"
<< " PyErr_NoMemory();\n";
error_return(out, 6, return_flags);
out << " }\n"
<< " } else if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", "
<< "(void **)&local_this, \"" << classNameFromCppName(cClassName, false)
<< "." << methodNameFromCppName(remap, cClassName, false) << "\")) {\n";
}
else if (all_nonconst) {
// All remaps are non-const. Also check that this object isn't const.
@ -3685,6 +3690,14 @@ write_function_for_name(ostream &out, Object *obj,
args_type = AT_varargs;
}
// If this is a __setstate__ taking multiple arguments, and we're given a
// tuple as argument, unpack it.
if (args_type == AT_single_arg && max_required_args > 1 &&
remap->_cppfunc->get_local_name() == "__setstate__") {
out << " PyObject *args = arg;\n";
args_type = AT_varargs;
}
if (args_type == AT_keyword_args || args_type == AT_varargs) {
max_required_args = collapse_default_remaps(map_sets, max_required_args);
}

View File

@ -4397,7 +4397,7 @@ if not PkgSkip("EGG"):
IGATEFILES.remove("parser.h")
TargetAdd('libp3egg.in', opts=OPTS, input=IGATEFILES)
TargetAdd('libp3egg.in', opts=['IMOD:panda3d.egg', 'ILIB:libp3egg', 'SRCDIR:panda/src/egg'])
PyTargetAdd('p3egg_eggGroupNode_ext.obj', opts=OPTS, input='eggGroupNode_ext.cxx')
PyTargetAdd('p3egg_ext_composite.obj', opts=OPTS, input='p3egg_ext_composite.cxx')
#
# DIRECTORY: panda/src/egg2pg/
@ -4486,7 +4486,7 @@ if not PkgSkip("EGG"):
PyTargetAdd('egg_module.obj', opts=['IMOD:panda3d.egg', 'ILIB:egg', 'IMPORT:panda3d.core'])
PyTargetAdd('egg.pyd', input='egg_module.obj')
PyTargetAdd('egg.pyd', input='p3egg_eggGroupNode_ext.obj')
PyTargetAdd('egg.pyd', input='p3egg_ext_composite.obj')
PyTargetAdd('egg.pyd', input='libp3egg_igate.obj')
PyTargetAdd('egg.pyd', input='libp3egg2pg_igate.obj')
PyTargetAdd('egg.pyd', input='libpandaegg.dll')

View File

@ -153,6 +153,52 @@ flush() {
end_group();
}
/**
* Serializes this object, to implement pickle support.
*/
void CollisionHandlerEvent::
write_datagram(Datagram &dg) const {
dg.add_uint32(_in_patterns.size());
for (const std::string &pattern : _in_patterns) {
dg.add_string(pattern);
}
dg.add_uint32(_again_patterns.size());
for (const std::string &pattern : _again_patterns) {
dg.add_string(pattern);
}
dg.add_uint32(_out_patterns.size());
for (const std::string &pattern : _out_patterns) {
dg.add_string(pattern);
}
}
/**
* Restores the object state from the given datagram, previously obtained using
* __getstate__.
*/
void CollisionHandlerEvent::
read_datagram(DatagramIterator &scan) {
_in_patterns.clear();
size_t num_in_patterns = scan.get_uint32();
for (size_t i = 0; i < num_in_patterns; ++i) {
add_in_pattern(scan.get_string());
}
_again_patterns.clear();
size_t num_again_patterns = scan.get_uint32();
for (size_t i = 0; i < num_again_patterns; ++i) {
add_again_pattern(scan.get_string());
}
_out_patterns.clear();
size_t num_out_patterns = scan.get_uint32();
for (size_t i = 0; i < num_out_patterns; ++i) {
add_out_pattern(scan.get_string());
}
}
/**
* Throws whatever events are suggested by the list of patterns.
*/

View File

@ -22,6 +22,7 @@
#include "vector_string.h"
#include "pointerTo.h"
#include "extension.h"
/**
* A specialized kind of CollisionHandler that throws an event for each
@ -61,12 +62,19 @@ PUBLISHED:
MAKE_SEQ(get_out_patterns, get_num_out_patterns, get_out_pattern);
MAKE_SEQ_PROPERTY(in_patterns, get_num_in_patterns, get_in_pattern);
MAKE_SEQ_PROPERTY(again_patterns, get_num_again_patterns, get_out_pattern);
MAKE_SEQ_PROPERTY(again_patterns, get_num_again_patterns, get_again_pattern);
MAKE_SEQ_PROPERTY(out_patterns, get_num_out_patterns, get_out_pattern);
void clear();
void flush();
// These help implement Python pickle support.
EXTENSION(PyObject *__reduce__(PyObject *self) const);
EXTENSION(void __setstate__(PyObject *self, vector_uchar data));
void write_datagram(Datagram &destination) const;
void read_datagram(DatagramIterator &source);
protected:
void throw_event_for(const vector_string &patterns, CollisionEntry *entry);
void throw_event_pattern(const std::string &pattern, CollisionEntry *entry);

View File

@ -0,0 +1,70 @@
/**
* 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 collisionHandlerEvent_ext.cxx
* @author rdb
* @date 2020-12-31
*/
#include "collisionHandlerEvent_ext.h"
#include "collisionHandlerFloor.h"
#include "collisionHandlerGravity.h"
#include "collisionHandlerPusher.h"
#ifdef HAVE_PYTHON
/**
* Implements pickling behavior.
*/
PyObject *Extension<CollisionHandlerEvent>::
__reduce__(PyObject *self) const {
extern struct Dtool_PyTypedObject Dtool_Datagram;
// Call the write_datagram method via Python, since it's not a virtual method
// on the C++ end.
PyObject *method_name = PyUnicode_FromString("write_datagram");
Datagram dg;
PyObject *destination = DTool_CreatePyInstance(&dg, Dtool_Datagram, false, false);
PyObject *retval = PyObject_CallMethodOneArg(self, method_name, destination);
Py_DECREF(method_name);
Py_DECREF(destination);
if (retval == nullptr) {
return nullptr;
}
Py_DECREF(retval);
const char *data = (const char *)dg.get_data();
Py_ssize_t size = dg.get_length();
return Py_BuildValue("O()y#", Py_TYPE(self), data, size);
}
/**
* Takes the value returned by __getstate__ and uses it to freshly initialize
* this CollisionHandlerEvent object.
*/
void Extension<CollisionHandlerEvent>::
__setstate__(PyObject *self, vector_uchar data) {
extern struct Dtool_PyTypedObject Dtool_DatagramIterator;
// Call the read_datagram method via Python, since it's not a virtual method
// on the C++ end.
PyObject *method_name = PyUnicode_FromString("read_datagram");
Datagram dg(std::move(data));
DatagramIterator scan(dg);
PyObject *source = DTool_CreatePyInstance(&scan, Dtool_DatagramIterator, false, false);
PyObject *retval = PyObject_CallMethodOneArg(self, method_name, source);
Py_DECREF(method_name);
Py_DECREF(source);
Py_XDECREF(retval);
}
#endif

View File

@ -0,0 +1,38 @@
/**
* 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 collisionHandlerEvent_ext.h
* @author rdb
* @date 2020-12-31
*/
#ifndef COLLISIONHANDLEREVENT_EXT_H
#define COLLISIONHANDLEREVENT_EXT_H
#include "dtoolbase.h"
#ifdef HAVE_PYTHON
#include "extension.h"
#include "collisionHandlerEvent.h"
#include "py_panda.h"
/**
* This class defines the extension methods for CollisionHandlerEvent, which are
* called instead of any C++ methods with the same prototype.
*/
template<>
class Extension<CollisionHandlerEvent> : public ExtensionBase<CollisionHandlerEvent> {
public:
PyObject *__reduce__(PyObject *self) const;
void __setstate__(PyObject *self, vector_uchar data);
};
#endif // HAVE_PYTHON
#endif // COLLISIONHANDLEREVENT_EXT_H

View File

@ -41,18 +41,31 @@ CollisionHandlerFloor::
}
/**
*
* Serializes this object, to implement pickle support.
*/
void CollisionHandlerFloor::
write_datagram(Datagram &dg) const {
CollisionHandlerPhysical::write_datagram(dg);
*
dg.add_float64(_offset);
dg.add_float64(_reach);
dg.add_float64(_max_velocity);
}
*
/**
* Restores the object state from the given datagram, previously obtained using
* __getstate__.
*/
void CollisionHandlerFloor::
read_datagram(DatagramIterator &scan) {
CollisionHandlerPhysical::read_datagram(scan);
*
*
*
_offset = scan.get_float64();
_reach = scan.get_float64();
_max_velocity = scan.get_float64();
}
/**
*
*/
PN_stdfloat CollisionHandlerFloor::

View File

@ -43,6 +43,9 @@ PUBLISHED:
MAKE_PROPERTY(reach, get_reach, set_reach);
MAKE_PROPERTY(max_velocity, get_max_velocity, set_max_velocity);
void write_datagram(Datagram &destination) const;
void read_datagram(DatagramIterator &source);
protected:
PN_stdfloat set_highest_collision(const NodePath &target_node_path, const NodePath &from_node_path, const Entries &entries);
virtual bool handle_entries();

View File

@ -46,6 +46,35 @@ CollisionHandlerGravity::
~CollisionHandlerGravity() {
}
/**
* Serializes this object, to implement pickle support.
*/
void CollisionHandlerGravity::
write_datagram(Datagram &dg) const {
CollisionHandlerPhysical::write_datagram(dg);
dg.add_float64(_offset);
dg.add_float64(_reach);
dg.add_float64(_max_velocity);
dg.add_float64(_gravity);
dg.add_bool(_legacy_mode);
}
/**
* Restores the object state from the given datagram, previously obtained using
* __getstate__.
*/
void CollisionHandlerGravity::
read_datagram(DatagramIterator &scan) {
CollisionHandlerPhysical::read_datagram(scan);
_offset = scan.get_float64();
_reach = scan.get_float64();
_max_velocity = scan.get_float64();
_gravity = scan.get_float64();
_legacy_mode = scan.get_bool();
}
/**
*
*/

View File

@ -64,6 +64,9 @@ PUBLISHED:
MAKE_PROPERTY(gravity, get_gravity, set_gravity);
MAKE_PROPERTY(max_velocity, get_max_velocity, set_max_velocity);
void write_datagram(Datagram &destination) const;
void read_datagram(DatagramIterator &source);
protected:
PN_stdfloat set_highest_collision(const NodePath &target_node_path, const NodePath &from_node_path, const Entries &entries);
virtual bool handle_entries();

View File

@ -54,6 +54,9 @@ PUBLISHED:
PUBLISHED:
MAKE_PROPERTY2(center, has_center, get_center, set_center, clear_center);
EXTENSION(PyObject *__reduce__(PyObject *self) const);
EXTENSION(void __setstate__(PyObject *self, vector_uchar data, PyObject *nodepaths));
protected:
bool _has_contact; // Are we in contact with anything?
@ -83,6 +86,8 @@ protected:
NodePath _center;
friend class Extension<CollisionHandlerPhysical>;
public:
static TypeHandle get_class_type() {
return _type_handle;

View File

@ -0,0 +1,108 @@
/**
* 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 collisionHandlerPhysical_ext.cxx
* @author rdb
* @date 2020-12-31
*/
#include "collisionHandlerPhysical_ext.h"
#include "collisionHandlerEvent_ext.h"
#ifdef HAVE_PYTHON
/**
* Implements pickling behavior.
*/
PyObject *Extension<CollisionHandlerPhysical>::
__reduce__(PyObject *self) const {
extern struct Dtool_PyTypedObject Dtool_Datagram;
extern struct Dtool_PyTypedObject Dtool_NodePath;
// Create a tuple with all the NodePath pointers.
PyObject *nodepaths = PyTuple_New(_this->_colliders.size() * 2 + 1);
Py_ssize_t i = 0;
if (_this->has_center()) {
const NodePath *center = &(_this->get_center());
PyTuple_SET_ITEM(nodepaths, i++,
DTool_CreatePyInstance((void *)center, Dtool_NodePath, false, true));
} else {
PyTuple_SET_ITEM(nodepaths, i++, Py_None);
Py_INCREF(Py_None);
}
CollisionHandlerPhysical::Colliders::const_iterator it;
for (it = _this->_colliders.begin(); it != _this->_colliders.end(); ++it) {
const NodePath *collider = &(it->first);
const NodePath *target = &(it->second._target);
PyTuple_SET_ITEM(nodepaths, i++,
DTool_CreatePyInstance((void *)collider, Dtool_NodePath, false, true));
PyTuple_SET_ITEM(nodepaths, i++,
DTool_CreatePyInstance((void *)target, Dtool_NodePath, false, true));
}
// Call the write_datagram method via Python, since it's not a virtual method
// on the C++ end.
PyObject *method_name = PyUnicode_FromString("write_datagram");
Datagram dg;
PyObject *destination = DTool_CreatePyInstance(&dg, Dtool_Datagram, false, false);
PyObject *retval = PyObject_CallMethodOneArg(self, method_name, destination);
Py_DECREF(method_name);
Py_DECREF(destination);
if (retval == nullptr) {
return nullptr;
}
Py_DECREF(retval);
const char *data = (const char *)dg.get_data();
Py_ssize_t size = dg.get_length();
return Py_BuildValue("O()(y#N)", Py_TYPE(self), data, size, nodepaths);
}
/**
* Takes the value returned by __getstate__ and uses it to freshly initialize
* this CollisionHandlerPhysical object.
*/
void Extension<CollisionHandlerPhysical>::
__setstate__(PyObject *self, vector_uchar data, PyObject *nodepaths) {
extern struct Dtool_PyTypedObject Dtool_DatagramIterator;
// Call the read_datagram method via Python, since it's not a virtual method
// on the C++ end.
PyObject *method_name = PyUnicode_FromString("read_datagram");
{
Datagram dg(std::move(data));
DatagramIterator scan(dg);
PyObject *source = DTool_CreatePyInstance(&scan, Dtool_DatagramIterator, false, false);
PyObject *retval = PyObject_CallMethodOneArg(self, method_name, source);
Py_DECREF(method_name);
Py_DECREF(source);
Py_XDECREF(retval);
}
PyObject *center = PyTuple_GET_ITEM(nodepaths, 0);
if (center != Py_None) {
_this->set_center(*(NodePath *)DtoolInstance_VOID_PTR(center));
} else {
_this->clear_center();
}
size_t num_nodepaths = Py_SIZE(nodepaths);
for (size_t i = 1; i < num_nodepaths;) {
NodePath *collider = (NodePath *)DtoolInstance_VOID_PTR(PyTuple_GET_ITEM(nodepaths, i++));
NodePath *target = (NodePath *)DtoolInstance_VOID_PTR(PyTuple_GET_ITEM(nodepaths, i++));
_this->add_collider(*collider, *target);
}
}
#endif

View File

@ -0,0 +1,38 @@
/**
* 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 collisionHandlerPhysical_ext.h
* @author rdb
* @date 2020-12-31
*/
#ifndef COLLISIONHANDLERPHYSICAL_EXT_H
#define COLLISIONHANDLERPHYSICAL_EXT_H
#include "dtoolbase.h"
#ifdef HAVE_PYTHON
#include "extension.h"
#include "collisionHandlerPhysical.h"
#include "py_panda.h"
/**
* This class defines the extension methods for CollisionHandlerPhysical, which are
* called instead of any C++ methods with the same prototype.
*/
template<>
class Extension<CollisionHandlerPhysical> : public ExtensionBase<CollisionHandlerPhysical> {
public:
PyObject *__reduce__(PyObject *self) const;
void __setstate__(PyObject *self, vector_uchar data, PyObject *nodepaths);
};
#endif // HAVE_PYTHON
#endif // COLLISIONHANDLERPHYSICAL_EXT_H

View File

@ -49,6 +49,27 @@ CollisionHandlerPusher::
~CollisionHandlerPusher() {
}
/**
* Serializes this object, to implement pickle support.
*/
void CollisionHandlerPusher::
write_datagram(Datagram &dg) const {
CollisionHandlerPhysical::write_datagram(dg);
dg.add_bool(_horizontal);
}
/**
* Restores the object state from the given datagram, previously obtained using
* __getstate__.
*/
void CollisionHandlerPusher::
read_datagram(DatagramIterator &scan) {
CollisionHandlerPhysical::read_datagram(scan);
_horizontal = scan.get_bool();
}
/**
* Called by the parent class after all collisions have been detected, this
* manages the various collisions and moves around the nodes as necessary.

View File

@ -34,6 +34,9 @@ PUBLISHED:
PUBLISHED:
MAKE_PROPERTY(horizontal, get_horizontal, set_horizontal);
void write_datagram(Datagram &destination) const;
void read_datagram(DatagramIterator &source);
protected:
virtual bool handle_entries();
virtual void apply_net_shove(

View File

@ -18,6 +18,7 @@
#include "collisionHandler.h"
#include "collisionEntry.h"
#include "extension.h"
/**
* A special kind of CollisionHandler that does nothing except remember the
@ -45,6 +46,8 @@ PUBLISHED:
void output(std::ostream &out) const;
void write(std::ostream &out, int indent_level = 0) const;
EXTENSION(PyObject *__reduce__(PyObject *self) const);
private:
typedef pvector< PT(CollisionEntry) > Entries;
Entries _entries;

View File

@ -0,0 +1,27 @@
/**
* 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 collisionHandlerQueue_ext.cxx
* @author rdb
* @date 2020-12-31
*/
#include "collisionHandlerQueue_ext.h"
#ifdef HAVE_PYTHON
/**
* Implements pickling behavior.
*/
PyObject *Extension<CollisionHandlerQueue>::
__reduce__(PyObject *self) const {
// CollisionHandlerQueue has no interesting properties.
return Py_BuildValue("(O())", Py_TYPE(self));
}
#endif

View File

@ -0,0 +1,37 @@
/**
* 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 collisionHandler_ext.h
* @author rdb
* @date 2020-12-31
*/
#ifndef COLLISIONHANDLERQUEUE_EXT_H
#define COLLISIONHANDLERQUEUE_EXT_H
#include "dtoolbase.h"
#ifdef HAVE_PYTHON
#include "extension.h"
#include "collisionHandlerQueue.h"
#include "py_panda.h"
/**
* This class defines the extension methods for CollisionHandlerQueue, which are
* called instead of any C++ methods with the same prototype.
*/
template<>
class Extension<CollisionHandlerQueue> : public ExtensionBase<CollisionHandlerQueue> {
public:
PyObject *__reduce__(PyObject *self) const;
};
#endif // HAVE_PYTHON
#endif // COLLISIONHANDLERQUEUE_EXT_H

View File

@ -24,6 +24,7 @@
#include "pset.h"
#include "register_type.h"
#include "extension.h"
class CollisionNode;
class CollisionRecorder;
@ -79,6 +80,9 @@ PUBLISHED:
void output(std::ostream &out) const;
void write(std::ostream &out, int indent_level) const;
EXTENSION(PyObject *__getstate__() const);
EXTENSION(void __setstate__(PyObject *state));
private:
typedef pvector<CollisionLevelStateSingle> LevelStatesSingle;
void prepare_colliders_single(LevelStatesSingle &level_states, const NodePath &root);

View File

@ -0,0 +1,73 @@
/**
* 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 collisionTraverser_ext.cxx
* @author rdb
* @date 2020-12-31
*/
#include "collisionTraverser_ext.h"
#ifdef HAVE_PYTHON
/**
* Implements pickling behavior.
*/
PyObject *Extension<CollisionTraverser>::
__getstate__() const {
extern struct Dtool_PyTypedObject Dtool_CollisionHandler;
extern struct Dtool_PyTypedObject Dtool_CollisionTraverser;
extern struct Dtool_PyTypedObject Dtool_NodePath;
const std::string &name = _this->get_name();
size_t num_colliders = _this->get_num_colliders();
PyObject *state = PyTuple_New(num_colliders * 2 + 3);
PyTuple_SET_ITEM(state, 0, PyUnicode_FromStringAndSize(name.data(), name.size()));
PyTuple_SET_ITEM(state, 1, PyBool_FromLong(_this->get_respect_prev_transform()));
PyTuple_SET_ITEM(state, 2, PyLong_FromLong((long)num_colliders));
for (size_t i = 0; i < num_colliders; ++i) {
NodePath *collider = new NodePath(_this->get_collider(i));
PyTuple_SET_ITEM(state, i * 2 + 3,
DTool_CreatePyInstance((void *)collider, Dtool_NodePath, true, false));
PT(CollisionHandler) handler = _this->get_handler(*collider);
handler->ref();
PyTuple_SET_ITEM(state, i * 2 + 4,
DTool_CreatePyInstanceTyped((void *)handler.p(), Dtool_CollisionHandler, true, false, handler->get_type_index()));
handler.cheat() = nullptr;
}
return state;
}
/**
* Takes the value returned by __getstate__ and uses it to freshly initialize
* this CollisionTraverser object.
*/
void Extension<CollisionTraverser>::
__setstate__(PyObject *state) {
_this->clear_colliders();
Py_ssize_t len = 0;
const char *data = PyUnicode_AsUTF8AndSize(PyTuple_GET_ITEM(state, 0), &len);
_this->set_name(std::string(data, len));
_this->set_respect_prev_transform(PyTuple_GET_ITEM(state, 1) != Py_False);
size_t num_colliders = (ssize_t)PyLong_AsLong(PyTuple_GET_ITEM(state, 2));
for (size_t i = 0; i < num_colliders; ++i) {
NodePath *collider = (NodePath *)DtoolInstance_VOID_PTR(PyTuple_GET_ITEM(state, i * 2 + 3));
CollisionHandler *handler = (CollisionHandler *)DtoolInstance_VOID_PTR(PyTuple_GET_ITEM(state, i * 2 + 4));
_this->add_collider(*collider, handler);
}
}
#endif

View File

@ -0,0 +1,38 @@
/**
* 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 collisionTraverser_ext.h
* @author rdb
* @date 2020-12-31
*/
#ifndef COLLISIONTRAVERSER_EXT_H
#define COLLISIONTRAVERSER_EXT_H
#include "dtoolbase.h"
#ifdef HAVE_PYTHON
#include "extension.h"
#include "collisionTraverser.h"
#include "py_panda.h"
/**
* This class defines the extension methods for CollisionTraverser, which are
* called instead of any C++ methods with the same prototype.
*/
template<>
class Extension<CollisionTraverser> : public ExtensionBase<CollisionTraverser> {
public:
PyObject *__getstate__() const;
void __setstate__(PyObject *state);
};
#endif // HAVE_PYTHON
#endif // COLLISIONTRAVERSER_EXT_H

View File

@ -1 +1,5 @@
#include "collisionHandlerEvent_ext.cxx"
#include "collisionHandlerPhysical_ext.cxx"
#include "collisionHandlerQueue_ext.cxx"
#include "collisionPolygon_ext.cxx"
#include "collisionTraverser_ext.cxx"

View File

@ -25,10 +25,11 @@ static bool traverse_callback(TextureContext *tc, void *data) {
PyObject *element =
DTool_CreatePyInstanceTyped(tex, Dtool_Texture,
true, false, tex->get_type_index());
tex->ref();
tex.cheat() = nullptr;
PyObject *list = (PyObject *) data;
PyList_Append(list, element);
Py_DECREF(element);
return true;
}

View File

@ -37,6 +37,9 @@ PUBLISHED:
INLINE bool has_num_frames() const;
INLINE int get_num_frames() const;
MAKE_PROPERTY2(fps, has_fps, get_fps, set_fps, clear_fps);
MAKE_PROPERTY2(num_frames, has_num_frames, get_num_frames, set_num_frames, clear_num_frames);
virtual void write(std::ostream &out, int indent_level) const;
private:

View File

@ -41,6 +41,8 @@ PUBLISHED:
virtual void write(std::ostream &out, int indent_level) const;
EXTENSION(PyObject *__reduce__() const);
private:
std::string _comment;

View File

@ -0,0 +1,32 @@
/**
* 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 eggComment_ext.cxx
* @author rdb
* @date 2021-01-01
*/
#include "eggComment_ext.h"
#ifdef HAVE_PYTHON
/**
* Implements pickle support.
*/
PyObject *Extension<EggComment>::
__reduce__() const {
extern struct Dtool_PyTypedObject Dtool_EggComment;
std::string node_name = _this->get_name();
std::string comment = _this->get_comment();
return Py_BuildValue("O(s#s#)", (PyObject *)&Dtool_EggComment,
node_name.data(), (Py_ssize_t)node_name.length(),
comment.data(), (Py_ssize_t)comment.length());
}
#endif

View File

@ -0,0 +1,37 @@
/**
* 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 eggComment_ext.h
* @author rdb
* @date 2021-01-01
*/
#ifndef EGGCOMMENT_EXT_H
#define EGGCOMMENT_EXT_H
#include "dtoolbase.h"
#ifdef HAVE_PYTHON
#include "extension.h"
#include "eggComment.h"
#include "py_panda.h"
/**
* This class defines the extension methods for EggComment, which are
* called instead of any C++ methods with the same prototype.
*/
template<>
class Extension<EggComment> : public ExtensionBase<EggComment> {
public:
PyObject *__reduce__() const;
};
#endif // HAVE_PYTHON
#endif // EGGCOMMENT_EXT_H

View File

@ -36,6 +36,8 @@ PUBLISHED:
virtual void write(std::ostream &out, int indent_level) const;
EXTENSION(PyObject *__reduce__() const);
private:
CoordinateSystem _value;

View File

@ -0,0 +1,32 @@
/**
* 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 eggCoordinateSystem_ext.cxx
* @author rdb
* @date 2021-01-01
*/
#include "eggCoordinateSystem_ext.h"
#ifdef HAVE_PYTHON
/**
* Implements pickle support.
*/
PyObject *Extension<EggCoordinateSystem>::
__reduce__() const {
extern struct Dtool_PyTypedObject Dtool_EggCoordinateSystem;
// We can't use the regular EggNode handling for EggCoordinateSystem, because
// the <CoordinateSystem> node is removed from the EggData after reading.
// Oh well, this is more efficient anyway.
int value = _this->get_value();
return Py_BuildValue("O(i)", (PyObject *)&Dtool_EggCoordinateSystem, value);
}
#endif

View File

@ -0,0 +1,37 @@
/**
* 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 eggCoordinateSystem_ext.h
* @author rdb
* @date 2021-01-01
*/
#ifndef EGGCOORDINATESYSTEM_EXT_H
#define EGGCOORDINATESYSTEM_EXT_H
#include "dtoolbase.h"
#ifdef HAVE_PYTHON
#include "extension.h"
#include "eggCoordinateSystem.h"
#include "py_panda.h"
/**
* This class defines the extension methods for EggCoordinateSystem, which are
* called instead of any C++ methods with the same prototype.
*/
template<>
class Extension<EggCoordinateSystem> : public ExtensionBase<EggCoordinateSystem> {
public:
PyObject *__reduce__() const;
};
#endif // HAVE_PYTHON
#endif // EGGCOORDINATESYSTEM_EXT_H

View File

@ -22,6 +22,7 @@
#include "lmatrix.h"
#include "pointerTo.h"
#include "referenceCount.h"
#include "extension.h"
class EggGroupNode;
class EggRenderMode;
@ -90,6 +91,7 @@ PUBLISHED:
void test_under_integrity() const { }
#endif // _DEBUG
EXTENSION(PyObject *__reduce__() const);
protected:
enum UnderFlags {

View 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 eggNode_ext.cxx
* @author rdb
* @date 2021-01-01
*/
#include "eggNode_ext.h"
#include "eggData.h"
#ifdef HAVE_PYTHON
/**
* Implements pickle support.
*/
PyObject *Extension<EggNode>::
__reduce__() const {
extern struct Dtool_PyTypedObject Dtool_EggNode;
// Find the parse_egg_node function in this module.
PyObject *sys_modules = PyImport_GetModuleDict();
nassertr_always(sys_modules != nullptr, nullptr);
PyObject *module_name = PyObject_GetAttrString((PyObject *)&Dtool_EggNode, "__module__");
nassertr_always(module_name != nullptr, nullptr);
PyObject *module = PyDict_GetItem(sys_modules, module_name);
Py_DECREF(module_name);
nassertr_always(module != nullptr, nullptr);
PyObject *func;
if (_this->is_of_type(EggData::get_class_type())) {
func = PyObject_GetAttrString(module, "parse_egg_data");
} else {
func = PyObject_GetAttrString(module, "parse_egg_node");
}
nassertr_always(func != nullptr, nullptr);
// Get the egg syntax to pass to the parse_egg_node function.
std::ostringstream stream;
_this->write(stream, INT_MIN);
std::string data = stream.str();
size_t length = data.size();
// Trim trailing whitespace.
while (length > 0 && isspace(data[length - 1])) {
--length;
}
return Py_BuildValue("N(s#)", func, data.data(), (Py_ssize_t)length);
}
/**
* Parses an EggData from the raw egg syntax.
*/
PT(EggData) parse_egg_data(const std::string &egg_syntax) {
PT(EggData) data = new EggData;
data->set_auto_resolve_externals(false);
std::istringstream in(egg_syntax);
if (!data->read(in)) {
PyErr_Format(PyExc_RuntimeError, "failed to parse egg data");
return nullptr;
}
return data;
}
/**
* Parses a single egg node from the raw egg syntax.
*/
PT(EggNode) parse_egg_node(const std::string &egg_syntax) {
PT(EggData) data = parse_egg_data(egg_syntax);
if (data == nullptr) {
return nullptr;
}
if (data->size() != 1) {
PyErr_Format(PyExc_RuntimeError, "expected exactly one node");
return nullptr;
}
return data->remove_child(data->get_first_child());
}
#endif

View File

@ -0,0 +1,42 @@
/**
* 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 eggNode_ext.h
* @author rdb
* @date 2021-01-01
*/
#ifndef EGGNODE_EXT_H
#define EGGNODE_EXT_H
#include "dtoolbase.h"
#ifdef HAVE_PYTHON
#include "extension.h"
#include "eggNode.h"
#include "py_panda.h"
/**
* This class defines the extension methods for EggNode, which are
* called instead of any C++ methods with the same prototype.
*/
template<>
class Extension<EggNode> : public ExtensionBase<EggNode> {
public:
PyObject *__reduce__() const;
};
BEGIN_PUBLISH
PT(EggData) parse_egg_data(const std::string &egg_syntax);
PT(EggNode) parse_egg_node(const std::string &egg_syntax);
END_PUBLISH
#endif // HAVE_PYTHON
#endif // EGGNODE_EXT_H

View File

@ -971,6 +971,7 @@ extern "C" int eggyywrap(void); // declared below.
static int yyinput(void); // declared by flex.
int eggyylex_destroy(void);
////////////////////////////////////////////////////////////////////
// Static variables
@ -1018,6 +1019,15 @@ egg_init_lexer(istream &in, const string &filename) {
initial_token = START_EGG;
}
void
egg_cleanup_lexer() {
// Reset the lexer state.
eggyylex_destroy();
input_p = nullptr;
egg_filename.clear();
}
void
egg_start_group_body() {
/* Set the initial state to begin within a group_body context,

View File

@ -26,6 +26,7 @@ extern "C" int eggyywrap(void); // declared below.
static int yyinput(void); // declared by flex.
int eggyylex_destroy(void);
////////////////////////////////////////////////////////////////////
// Static variables
@ -73,6 +74,15 @@ egg_init_lexer(istream &in, const string &filename) {
initial_token = START_EGG;
}
void
egg_cleanup_lexer() {
// Reset the lexer state.
yylex_destroy();
input_p = nullptr;
egg_filename.clear();
}
void
egg_start_group_body() {
/* Set the initial state to begin within a group_body context,

View File

@ -21,6 +21,7 @@
#include <string>
void egg_init_lexer(std::istream &in, const std::string &filename);
void egg_cleanup_lexer();
void egg_start_group_body();
void egg_start_texture_body();
void egg_start_primitive_body();

View File

@ -0,0 +1,4 @@
#include "eggComment_ext.cxx"
#include "eggCoordinateSystem_ext.cxx"
#include "eggGroupNode_ext.cxx"
#include "eggNode_ext.cxx"

View File

@ -223,6 +223,8 @@ egg_cleanup_parser() {
textures.clear();
materials.clear();
groups.clear();
egg_cleanup_lexer();
}

View File

@ -153,6 +153,8 @@ egg_cleanup_parser() {
textures.clear();
materials.clear();
groups.clear();
egg_cleanup_lexer();
}
%}

View File

@ -117,6 +117,8 @@ PUBLISHED:
INLINE size_t count(const Element &) const;
#ifdef HAVE_PYTHON
EXTENSION(PyObject *__reduce__(PyObject *self) const);
EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags));
EXTENSION(void __releasebuffer__(PyObject *self, Py_buffer *view) const);
#endif
@ -273,6 +275,8 @@ PUBLISHED:
INLINE size_t count(const Element &) const;
#ifdef HAVE_PYTHON
EXTENSION(PyObject *__reduce__(PyObject *self) const);
EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const);
EXTENSION(void __releasebuffer__(PyObject *self, Py_buffer *view) const);
#endif

View File

@ -77,6 +77,12 @@ protected:
PUBLISHED:
INLINE ~PointerToArrayBase();
#ifdef CPPPARSER
// These are implemented in PointerToVoid, but expose them here.
INLINE bool operator == (const PointerToArrayBase<Element> &other) const;
INLINE bool operator != (const PointerToArrayBase<Element> &other) const;
#endif
};
#include "pointerToArrayBase.I"

View File

@ -219,6 +219,25 @@ get_subdata(size_t n, size_t count) const {
return PyBytes_FromStringAndSize((char *)(this->_this->p() + n), sizeof(Element) * count);
}
/**
* Implements pickle support.
*/
template<class Element>
INLINE PyObject *Extension<PointerToArray<Element> >::
__reduce__(PyObject *self) const {
// This preserves the distinction between a null vs. an empty PTA, though I'm
// not sure that this distinction matters to anyone.
if (this->_this->is_null()) {
return Py_BuildValue("O()", Py_TYPE(self));
}
else if (this->_this->empty()) {
return Py_BuildValue("O(())", Py_TYPE(self));
}
else {
return Py_BuildValue("O(N)", Py_TYPE(self), get_data());
}
}
/**
* Same as get_element(), this returns the nth element of the array.
*/
@ -256,6 +275,22 @@ get_subdata(size_t n, size_t count) const {
return PyBytes_FromStringAndSize((char *)(this->_this->p() + n), sizeof(Element) * count);
}
/**
* Implements pickle support.
*/
template<class Element>
INLINE PyObject *Extension<ConstPointerToArray<Element> >::
__reduce__(PyObject *self) const {
// This preserves the distinction between a null vs. an empty PTA, though I'm
// not sure that this distinction matters to anyone.
if (!this->_this->is_null() && this->_this->empty()) {
return Py_BuildValue("O([])", Py_TYPE(self));
}
else {
return Py_BuildValue("O(N)", Py_TYPE(self), get_data());
}
}
/**
* This is used to implement the buffer protocol, in order to allow efficient
* access to the array data through a Python multiview object.

View File

@ -40,6 +40,8 @@ public:
INLINE void set_data(PyObject *data);
INLINE PyObject *get_subdata(size_t n, size_t count) const;
INLINE PyObject *__reduce__(PyObject *self) const;
INLINE int __getbuffer__(PyObject *self, Py_buffer *view, int flags);
INLINE void __releasebuffer__(PyObject *self, Py_buffer *view) const;
};
@ -75,6 +77,8 @@ public:
INLINE PyObject *get_data() const;
INLINE PyObject *get_subdata(size_t n, size_t count) const;
INLINE PyObject *__reduce__(PyObject *self) const;
INLINE int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
INLINE void __releasebuffer__(PyObject *self, Py_buffer *view) const;
};

View File

@ -53,6 +53,8 @@ PUBLISHED:
static LoaderFileTypeRegistry *get_global_ptr();
EXTENSION(PyObject *__reduce__() const);
private:
void record_extension(const std::string &extension, LoaderFileType *type);

View File

@ -18,6 +18,7 @@
#include "pythonLoaderFileType.h"
extern struct Dtool_PyTypedObject Dtool_LoaderFileType;
extern struct Dtool_PyTypedObject Dtool_LoaderFileTypeRegistry;
/**
* Registers a loader file type that is implemented in Python.
@ -106,4 +107,13 @@ unregister_type(PyObject *type) {
Py_XDECREF(save_func);
}
/**
* Implements pickle support.
*/
PyObject *Extension<LoaderFileTypeRegistry>::
__reduce__() const {
PyObject *func = PyObject_GetAttrString((PyObject *)&Dtool_LoaderFileTypeRegistry, "get_global_ptr");
return Py_BuildValue("N()", func);
}
#endif

View File

@ -33,6 +33,8 @@ public:
void register_deferred_type(PyObject *entry_point);
void unregister_type(PyObject *type);
PyObject *__reduce__() const;
};
#endif // HAVE_PYTHON

View File

@ -0,0 +1,108 @@
from direct.stdpy.pickle import dumps, loads
from panda3d.core import NodePath, CollisionNode
def test_collision_handler_event_pickle():
from panda3d.core import CollisionHandlerEvent
handler = CollisionHandlerEvent()
handler.add_in_pattern("abcdefg")
handler.add_in_pattern("test")
handler.add_again_pattern("again pattern")
handler.add_again_pattern("another again pattern")
handler.add_out_pattern("out pattern")
handler = loads(dumps(handler, -1))
assert tuple(handler.in_patterns) == ("abcdefg", "test")
assert tuple(handler.again_patterns) == ("again pattern", "another again pattern")
assert tuple(handler.out_patterns) == ("out pattern",)
def test_collision_handler_queue_pickle():
from panda3d.core import CollisionHandlerQueue
handler = CollisionHandlerQueue()
handler = loads(dumps(handler, -1))
assert type(handler) == CollisionHandlerQueue
def test_collision_handler_floor_pickle():
from panda3d.core import CollisionHandlerFloor
collider1 = NodePath(CollisionNode("collider1"))
collider2 = NodePath(CollisionNode("collider2"))
target1 = NodePath("target1")
target2 = NodePath("target2")
center = NodePath("center")
handler = CollisionHandlerFloor()
handler.add_out_pattern("out pattern")
handler.add_collider(collider1, target1)
handler.add_collider(collider2, target2)
handler.center = center
handler.offset = 1.0
handler.reach = 2.0
handler.max_velocity = 3.0
handler = loads(dumps(handler, -1))
assert tuple(handler.in_patterns) == ()
assert tuple(handler.again_patterns) == ()
assert tuple(handler.out_patterns) == ("out pattern",)
assert handler.center.name == "center"
assert handler.offset == 1.0
assert handler.reach == 2.0
assert handler.max_velocity == 3.0
def test_collision_handler_gravity_pickle():
from panda3d.core import CollisionHandlerGravity
collider1 = NodePath(CollisionNode("collider1"))
collider2 = NodePath(CollisionNode("collider2"))
target1 = NodePath("target1")
target2 = NodePath("target2")
handler = CollisionHandlerGravity()
handler.add_out_pattern("out pattern")
handler.add_collider(collider1, target1)
handler.add_collider(collider2, target2)
handler.offset = 1.0
handler.reach = 2.0
handler.max_velocity = 3.0
handler.gravity = -4.0
handler = loads(dumps(handler, -1))
assert tuple(handler.in_patterns) == ()
assert tuple(handler.again_patterns) == ()
assert tuple(handler.out_patterns) == ("out pattern",)
assert handler.center == None
assert handler.offset == 1.0
assert handler.reach == 2.0
assert handler.max_velocity == 3.0
assert handler.gravity == -4.0
def test_collision_handler_pusher_pickle():
from panda3d.core import CollisionHandlerPusher
collider1 = NodePath(CollisionNode("collider1"))
collider2 = NodePath(CollisionNode("collider2"))
target1 = NodePath("target1")
target2 = NodePath("target2")
handler = CollisionHandlerPusher()
handler.add_again_pattern("again pattern")
handler.add_collider(collider1, target1)
handler.add_collider(collider2, target2)
handler.horizontal = True
handler = loads(dumps(handler, -1))
assert tuple(handler.in_patterns) == ()
assert tuple(handler.again_patterns) == ("again pattern",)
assert tuple(handler.out_patterns) == ()
assert not handler.has_center()
assert handler.horizontal

View File

@ -0,0 +1,31 @@
from panda3d.core import CollisionTraverser, CollisionHandlerQueue
from panda3d.core import NodePath, CollisionNode
def test_collision_traverser_pickle():
from direct.stdpy.pickle import dumps, loads
handler = CollisionHandlerQueue()
collider1 = NodePath(CollisionNode("collider1"))
collider2 = NodePath(CollisionNode("collider2"))
trav = CollisionTraverser("test123")
trav.respect_prev_transform = True
trav.add_collider(collider1, handler)
trav.add_collider(collider2, handler)
trav = loads(dumps(trav, -1))
assert trav.respect_prev_transform is True
assert trav.name == "test123"
assert trav.get_num_colliders() == 2
collider1 = trav.get_collider(0)
collider2 = trav.get_collider(1)
assert collider1.name == "collider1"
assert collider2.name == "collider2"
# Two colliders must still be the same object; this only works with our own
# version of the pickle module, in direct.stdpy.pickle.
assert trav.get_handler(collider1) == trav.get_handler(collider2)

View File

@ -0,0 +1,71 @@
def test_pta_float_compare():
from panda3d.core import PTA_float, CPTA_float
# Two null PTAs
assert PTA_float() == PTA_float()
assert not (PTA_float() != PTA_float())
# Two non-null PTAs
assert PTA_float([1]) != PTA_float([1])
assert not (PTA_float([1]) == PTA_float([1]))
# A copy of a PTA
pta = PTA_float([1])
assert pta == PTA_float(pta)
assert not (pta != PTA_float(pta))
# A const copy of a PTA
pta = PTA_float([1])
cpta = CPTA_float(pta)
assert pta == cpta
assert not (pta != cpta)
def test_pta_float_pickle():
from panda3d.core import PTA_float
from direct.stdpy.pickle import dumps, loads, HIGHEST_PROTOCOL
null_pta = PTA_float()
empty_pta = PTA_float([])
data_pta = PTA_float([1.0, 2.0, 3.0])
data = data_pta.get_data()
for proto in range(1, HIGHEST_PROTOCOL + 1):
null_pta2 = loads(dumps(null_pta, proto))
assert null_pta2.is_null()
assert len(null_pta2) == 0
empty_pta2 = loads(dumps(empty_pta, proto))
assert not empty_pta2.is_null()
assert len(empty_pta2) == 0
data_pta2 = loads(dumps(data_pta, proto))
assert tuple(data_pta2) == (1.0, 2.0, 3.0)
assert data_pta2.get_data() == data_pta.get_data()
def test_cpta_float_pickle():
from panda3d.core import PTA_float, CPTA_float
from direct.stdpy.pickle import dumps, loads, HIGHEST_PROTOCOL
null_pta = CPTA_float(PTA_float())
empty_pta = CPTA_float([])
data_pta = CPTA_float([1.0, 2.0, 3.0])
data = data_pta.get_data()
for proto in range(1, HIGHEST_PROTOCOL + 1):
null_pta2 = loads(dumps(null_pta, proto))
assert null_pta2.is_null()
assert len(null_pta2) == 0
empty_pta2 = loads(dumps(empty_pta, proto))
assert not empty_pta2.is_null()
assert len(empty_pta2) == 0
data_pta2 = loads(dumps(data_pta, proto))
assert tuple(data_pta2) == (1.0, 2.0, 3.0)
assert data_pta2.get_data() == data_pta.get_data()

View File

@ -3,6 +3,7 @@ import pytest
import tempfile
import os
from contextlib import contextmanager
import sys
@pytest.fixture
@ -218,3 +219,11 @@ def test_loader_ram_cache(test_filename):
assert model1 == model2
ModelPool.release_model(model2)
@pytest.mark.skipif(sys.version_info < (3, 4), reason="Requires Python 3.4")
def test_loader_file_type_registry_pickle():
from direct.stdpy.pickle import dumps, loads
registry = LoaderFileTypeRegistry.get_global_ptr()
assert loads(dumps(registry, -1)) == registry

View File

@ -86,3 +86,13 @@ def test_bitmask_pickle():
data = pickle.dumps(mask1, -1)
mask2 = pickle.loads(data)
assert mask1 == mask2
mask1 = DoubleBitMaskNative(0x7fffffffffffffff)
data = pickle.dumps(mask1, -1)
mask2 = pickle.loads(data)
assert mask1 == mask2
mask1 = DoubleBitMaskNative(1 << (double_num_bits - 1))
data = pickle.dumps(mask1, -1)
mask2 = pickle.loads(data)
assert mask1 == mask2

View File

@ -12,6 +12,20 @@ def test_reduce_persist():
assert tuple(parent2.children) == (child2,)
def test_pickle_copy():
from panda3d.core import PandaNode, NodePath
# Make two Python wrappers pointing to the same node
node1 = PandaNode("node")
node2 = NodePath(node1).node()
assert node1.this == node2.this
assert id(node1) != id(node2)
# Test that pickling and loading still results in the same node object.
node1, node2 = loads(dumps([node1, node2]))
assert node1 == node2
def test_pickle_error():
class ErroneousPickleable(object):
def __reduce__(self):