From 0866d485b186bd0a499c283d65530ce9b144c8dc Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 4 Dec 2022 16:57:55 +0100 Subject: [PATCH 1/4] pstats: Set default value for `pstats-max-queue-size` to 4 Otherwise any multithreaded application is dropping frames like mad, since the packets for different frames may be added to the same queue at roughly the same time. --- panda/src/pstatclient/config_pstatclient.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda/src/pstatclient/config_pstatclient.cxx b/panda/src/pstatclient/config_pstatclient.cxx index ecbf6baa79..51ab439e2a 100644 --- a/panda/src/pstatclient/config_pstatclient.cxx +++ b/panda/src/pstatclient/config_pstatclient.cxx @@ -43,7 +43,7 @@ ConfigVariableBool pstats_threaded_write "broken with the threaded network interfaces.")); ConfigVariableInt pstats_max_queue_size -("pstats-max-queue-size", 1, +("pstats-max-queue-size", 4, PRC_DESC("If pstats-threaded-write is true, this specifies the maximum " "number of packets (generally, frames of data) that may be queued " "up for the thread to process. If this is large, the writer " From 8ca804086f07309b411e2f888e9e72a2f7e589b9 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 4 Dec 2022 16:58:49 +0100 Subject: [PATCH 2/4] pgraph: Fix a compiler warning in renderEffects.I Initialization is unnecessary, but harmless --- panda/src/pgraph/renderEffects.I | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda/src/pgraph/renderEffects.I b/panda/src/pgraph/renderEffects.I index a0ae6af279..74f161f05c 100644 --- a/panda/src/pgraph/renderEffects.I +++ b/panda/src/pgraph/renderEffects.I @@ -26,7 +26,7 @@ Effect(const RenderEffect *effect) : * file. At this point, the effect pointer is unknown. */ INLINE RenderEffects::Effect:: -Effect() { +Effect() : _type(TypeHandle::none()) { } /** From d621df47ac24ba1dc09f19e779b95c53854e7f9d Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 4 Dec 2022 17:00:10 +0100 Subject: [PATCH 3/4] prc: Fix bf-cbc encryption/decryption regression with OpenSSL 3.0 Loads the legacy provider to continue supporting this algorithm --- dtool/src/prc/encryptStreamBuf.cxx | 71 ++++++++++++++++++++++++++++++ tests/express/test_encrypt.py | 20 +++++++++ 2 files changed, 91 insertions(+) create mode 100644 tests/express/test_encrypt.py diff --git a/dtool/src/prc/encryptStreamBuf.cxx b/dtool/src/prc/encryptStreamBuf.cxx index a8e7c04967..afe8ec1d61 100644 --- a/dtool/src/prc/encryptStreamBuf.cxx +++ b/dtool/src/prc/encryptStreamBuf.cxx @@ -23,6 +23,32 @@ #include #include +#if OPENSSL_VERSION_MAJOR >= 3 +#include + +/** + * Tries to load the legacy provider in OpenSSL. Returns true if the provider + * was just loaded, false if it was already loaded or couldn't be loaded. + */ +static bool load_legacy_provider() { + static bool tried = false; + if (!tried) { + tried = true; + if (OSSL_PROVIDER_try_load(nullptr, "legacy", 1) != nullptr) { + if (prc_cat.is_debug()) { + prc_cat.debug() + << "Loaded legacy OpenSSL provider.\n"; + } + return true; + } else { + prc_cat.warning() + << "Failed to load legacy OpenSSL provider.\n"; + } + } + return false; +} +#endif // OPENSSL_VERSION_MAJOR + // The iteration count is scaled by this factor for writing to the stream. static const int iteration_count_factor = 1000; @@ -122,7 +148,31 @@ open_read(std::istream *source, bool owns_source, const std::string &password) { int key_length = sr.get_uint16(); int count = sr.get_uint16(); +#if OPENSSL_VERSION_MAJOR >= 3 + // First, convert the cipher's nid to its full name. + const char *cipher_name = OBJ_nid2ln(nid); + + const EVP_CIPHER *cipher = nullptr; + if (cipher_name != nullptr) { + // Now, fetch the cipher known by this name. + cipher = EVP_CIPHER_fetch(nullptr, cipher_name, nullptr); + + if (cipher == nullptr && EVP_get_cipherbynid(nid) != nullptr) { + if (load_legacy_provider()) { + cipher = EVP_CIPHER_fetch(nullptr, cipher_name, nullptr); + } + + if (cipher == nullptr) { + prc_cat.error() + << "No implementation available for encryption algorithm in stream: " + << cipher_name << "\n"; + return; + } + } + } +#else const EVP_CIPHER *cipher = EVP_get_cipherbynid(nid); +#endif if (cipher == nullptr) { prc_cat.error() @@ -219,8 +269,29 @@ open_write(std::ostream *dest, bool owns_dest, const std::string &password) { _dest = dest; _owns_dest = owns_dest; +#if OPENSSL_VERSION_MAJOR >= 3 + // This checks that there is actually an implementation available. + const EVP_CIPHER *cipher = + EVP_CIPHER_fetch(nullptr, _algorithm.c_str(), nullptr); + + if (cipher == nullptr && + EVP_get_cipherbyname(_algorithm.c_str()) != nullptr) { + // The cipher does exist, though, do we need to load the legacy provider? + if (load_legacy_provider()) { + cipher = EVP_CIPHER_fetch(nullptr, _algorithm.c_str(), nullptr); + } + + if (cipher == nullptr) { + prc_cat.error() + << "No implementation available for encryption algorithm: " + << _algorithm << "\n"; + return; + } + } +#else const EVP_CIPHER *cipher = EVP_get_cipherbyname(_algorithm.c_str()); +#endif if (cipher == nullptr) { prc_cat.error() diff --git a/tests/express/test_encrypt.py b/tests/express/test_encrypt.py new file mode 100644 index 0000000000..c42398a9df --- /dev/null +++ b/tests/express/test_encrypt.py @@ -0,0 +1,20 @@ +from panda3d import core +import pytest + + +def test_encrypt_string(): + # Test encrypt and then decrypt cycle + for algorithm in ('', 'bf-cbc', 'aes-256-cbc'): + enc = core.encrypt_string('abcdefg', '12345', algorithm) + assert len(enc) > 0 + + dec = core.decrypt_string(enc, '12345') + assert dec == 'abcdefg' + + # Test pre-encrypted bf-cbc string + enc = b'[\x00\x10\x00d\x00\xb5\x7f\xc44Y\xb7\xd9\x15\xe3\xbd\xcf\xb3yK\xfb\xf6' + assert 'test' == core.decrypt_string(enc, '98765') + + # Test pre-encrypted aes-256-cbc string + enc = b'\xab\x01 \x00d\x00\xf1WP\xb0\x96h\xf8\xc5\xf4\x8d\x0b>q0\xf15\x185\xf8+\x1b\xe4\xae8\x88\xf2\x91\x15\xb8\x8fh\x88' + assert 'test' == core.decrypt_string(enc, '98765') From 41f0c9d48d3cf586ca46c0747a647ad004b87ab8 Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 4 Dec 2022 17:05:29 +0100 Subject: [PATCH 4/4] pgraph: Support None as second arg of replace_texture/replace_material This removes the relevant texture or material --- panda/src/pgraph/nodePath.cxx | 23 ++++++++++++++++++----- panda/src/pgraph/nodePath.h | 6 ++++++ panda/src/pgraph/textureAttrib.cxx | 11 ++++++++++- panda/src/pgraph/textureAttrib.h | 3 +++ tests/pgraph/test_nodepath.py | 21 +++++++++++++++++++++ 5 files changed, 58 insertions(+), 6 deletions(-) diff --git a/panda/src/pgraph/nodePath.cxx b/panda/src/pgraph/nodePath.cxx index 3e2430aa3c..8e0f986436 100644 --- a/panda/src/pgraph/nodePath.cxx +++ b/panda/src/pgraph/nodePath.cxx @@ -3207,13 +3207,14 @@ get_texture(TextureStage *stage) const { * Recursively searches the scene graph for references to the given texture, * and replaces them with the new texture. * + * As of Panda3D 1.10.13, new_tex may be null to remove the texture. + * * @since 1.10.4 */ void NodePath:: replace_texture(Texture *tex, Texture *new_tex) { nassertv_always(!is_empty()); nassertv(tex != nullptr); - nassertv(new_tex != nullptr); r_replace_texture(node(), tex, new_tex); } @@ -4249,15 +4250,19 @@ get_material() const { * Recursively searches the scene graph for references to the given material, * and replaces them with the new material. * + * As of Panda3D 1.10.13, new_mat may be null to remove the material. + * * @since 1.10.0 */ void NodePath:: replace_material(Material *mat, Material *new_mat) { nassertv_always(!is_empty()); nassertv(mat != nullptr); - nassertv(new_mat != nullptr); - CPT(RenderAttrib) new_attrib = MaterialAttrib::make(new_mat); + CPT(RenderAttrib) new_attrib; + if (new_mat != nullptr) { + new_attrib = MaterialAttrib::make(new_mat); + } r_replace_material(node(), mat, (const MaterialAttrib *)new_attrib.p()); } @@ -6841,7 +6846,11 @@ r_replace_material(PandaNode *node, Material *mat, const MaterialAttrib *ma; if (node_state->get_attrib(ma)) { if (mat == ma->get_material()) { - node->set_state(node_state->set_attrib(new_attrib)); + if (new_attrib != nullptr) { + node->set_state(node_state->set_attrib(new_attrib)); + } else { + node->set_state(node_state->remove_attrib(MaterialAttrib::get_class_slot())); + } } } } @@ -6860,7 +6869,11 @@ r_replace_material(PandaNode *node, Material *mat, if (geom_state->get_attrib(ma)) { if (mat == ma->get_material()) { // Replace it - gnode->set_geom_state(i, geom_state->set_attrib(new_attrib)); + if (new_attrib != nullptr) { + gnode->set_geom_state(i, geom_state->set_attrib(new_attrib)); + } else { + gnode->set_geom_state(i, geom_state->remove_attrib(MaterialAttrib::get_class_slot())); + } } } } diff --git a/panda/src/pgraph/nodePath.h b/panda/src/pgraph/nodePath.h index 187129d30c..43dd2ec6f4 100644 --- a/panda/src/pgraph/nodePath.h +++ b/panda/src/pgraph/nodePath.h @@ -619,6 +619,9 @@ PUBLISHED: Texture *get_texture() const; Texture *get_texture(TextureStage *stage) const; void replace_texture(Texture *tex, Texture *new_tex); +#ifdef CPPPARSER // Let interrogate know this also accepts None + void replace_texture(Texture *tex, std::nullptr_t new_tex); +#endif const SamplerState &get_texture_sampler() const; const SamplerState &get_texture_sampler(TextureStage *stage) const; @@ -771,6 +774,9 @@ PUBLISHED: bool has_material() const; PT(Material) get_material() const; void replace_material(Material *mat, Material *new_mat); +#ifdef CPPPARSER // Let interrogate know this also accepts None + void replace_material(Material *mat, std::nullptr_t new_mat); +#endif void set_fog(Fog *fog, int priority = 0); void set_fog_off(int priority = 0); diff --git a/panda/src/pgraph/textureAttrib.cxx b/panda/src/pgraph/textureAttrib.cxx index d08344fc4d..7303ee9245 100644 --- a/panda/src/pgraph/textureAttrib.cxx +++ b/panda/src/pgraph/textureAttrib.cxx @@ -253,12 +253,15 @@ unify_texture_stages(TextureStage *stage) const { * Returns a new TextureAttrib, just like this one, but with all references to * the given texture replaced with the new texture. * + * As of Panda3D 1.10.13, new_tex may be null to remove the texture. + * * @since 1.10.4 */ CPT(RenderAttrib) TextureAttrib:: replace_texture(Texture *tex, Texture *new_tex) const { TextureAttrib *attrib = nullptr; + size_t j = 0; for (size_t i = 0; i < _on_stages.size(); ++i) { const StageNode &sn = _on_stages[i]; if (sn._texture == tex) { @@ -266,8 +269,14 @@ replace_texture(Texture *tex, Texture *new_tex) const { attrib = new TextureAttrib(*this); } - attrib->_on_stages[i]._texture = new_tex; + if (new_tex != nullptr) { + attrib->_on_stages[j]._texture = new_tex; + } else { + attrib->_on_stages.erase(attrib->_on_stages.begin() + j); + continue; + } } + ++j; } if (attrib != nullptr) { diff --git a/panda/src/pgraph/textureAttrib.h b/panda/src/pgraph/textureAttrib.h index 0858cc68a7..ba0eae0d19 100644 --- a/panda/src/pgraph/textureAttrib.h +++ b/panda/src/pgraph/textureAttrib.h @@ -90,6 +90,9 @@ PUBLISHED: CPT(RenderAttrib) remove_off_stage(TextureStage *stage) const; CPT(RenderAttrib) unify_texture_stages(TextureStage *stage) const; CPT(RenderAttrib) replace_texture(Texture *tex, Texture *new_tex) const; +#ifdef CPPPARSER // Let interrogate know this also accepts None + CPT(RenderAttrib) replace_texture(Texture *tex, std::nullptr_t new_tex) const; +#endif public: CPT(TextureAttrib) filter_to_max(int max_texture_stages) const; diff --git a/tests/pgraph/test_nodepath.py b/tests/pgraph/test_nodepath.py index 6a7d0990b4..1ef0c15bef 100644 --- a/tests/pgraph/test_nodepath.py +++ b/tests/pgraph/test_nodepath.py @@ -248,3 +248,24 @@ def test_nodepath_replace_texture(): path1.replace_texture(tex1, tex2) assert not path1.has_texture() assert path2.get_texture() == tex2 + + +def test_nodepath_replace_texture_none(): + from panda3d.core import NodePath, Texture + + tex1 = Texture("tex1") + + path1 = NodePath("node1") + assert path1.get_texture() is None + path1.set_texture(tex1) + assert path1.get_texture() == tex1 + path1.replace_texture(tex1, None) + assert path1.get_texture() is None + + path1 = NodePath("node1") + path2 = path1.attach_new_node("node2") + assert path2.get_texture() is None + path2.set_texture(tex1) + assert path2.get_texture() == tex1 + path1.replace_texture(tex1, None) + assert path2.get_texture() is None