diff options
Diffstat (limited to 'modules')
56 files changed, 312 insertions, 102 deletions
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 9c178997c5..6082b468f7 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -2226,7 +2226,7 @@ void CSGPolygon3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "path_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Path3D"), "set_path_node", "get_path_node"); ADD_PROPERTY(PropertyInfo(Variant::INT, "path_interval_type", PROPERTY_HINT_ENUM, "Distance,Subdivide"), "set_path_interval_type", "get_path_interval_type"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_interval", PROPERTY_HINT_RANGE, "0.01,1.0,0.01,exp,or_greater"), "set_path_interval", "get_path_interval"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_simplify_angle", PROPERTY_HINT_RANGE, "0.0,180.0,0.1,exp"), "set_path_simplify_angle", "get_path_simplify_angle"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_simplify_angle", PROPERTY_HINT_RANGE, "0.0,180.0,0.1"), "set_path_simplify_angle", "get_path_simplify_angle"); ADD_PROPERTY(PropertyInfo(Variant::INT, "path_rotation", PROPERTY_HINT_ENUM, "Polygon,Path,PathFollow"), "set_path_rotation", "get_path_rotation"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_local"), "set_path_local", "is_path_local"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_continuous_u"), "set_path_continuous_u", "is_path_continuous_u"); diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 6245cc85a0..ec1682d470 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -992,6 +992,7 @@ void GDScript::set_path(const String &p_path, bool p_take_over) { String old_path = path; path = p_path; + path_valid = true; GDScriptCache::move_script(old_path, p_path); for (KeyValue<StringName, Ref<GDScript>> &kv : subclasses) { @@ -1000,6 +1001,9 @@ void GDScript::set_path(const String &p_path, bool p_take_over) { } String GDScript::get_script_path() const { + if (!path_valid && !get_path().is_empty()) { + return get_path(); + } return path; } @@ -1035,6 +1039,7 @@ Error GDScript::load_source_code(const String &p_path) { source = s; path = p_path; + path_valid = true; #ifdef TOOLS_ENABLED source_changed_cache = true; set_edited(false); @@ -1386,36 +1391,54 @@ String GDScript::debug_get_script_name(const Ref<Script> &p_script) { } #endif -thread_local GDScript::UpdatableFuncPtr GDScript::func_ptrs_to_update_thread_local; +GDScript::UpdatableFuncPtr GDScript::func_ptrs_to_update_main_thread; +thread_local GDScript::UpdatableFuncPtr *GDScript::func_ptrs_to_update_thread_local = nullptr; GDScript::UpdatableFuncPtrElement GDScript::_add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr) { UpdatableFuncPtrElement result = {}; { - MutexLock lock(func_ptrs_to_update_thread_local.mutex); - result.element = func_ptrs_to_update_thread_local.ptrs.push_back(p_func_ptr_ptr); - result.mutex = &func_ptrs_to_update_thread_local.mutex; + MutexLock lock(func_ptrs_to_update_thread_local->mutex); + result.element = func_ptrs_to_update_thread_local->ptrs.push_back(p_func_ptr_ptr); + result.func_ptr = func_ptrs_to_update_thread_local; - if (likely(func_ptrs_to_update_thread_local.initialized)) { + if (likely(func_ptrs_to_update_thread_local->initialized)) { return result; } - func_ptrs_to_update_thread_local.initialized = true; + func_ptrs_to_update_thread_local->initialized = true; } MutexLock lock(func_ptrs_to_update_mutex); - func_ptrs_to_update.push_back(&func_ptrs_to_update_thread_local); + func_ptrs_to_update.push_back(func_ptrs_to_update_thread_local); + func_ptrs_to_update_thread_local->rc++; return result; } -void GDScript::_remove_func_ptr_to_update(const UpdatableFuncPtrElement p_func_ptr_element) { +void GDScript::_remove_func_ptr_to_update(const UpdatableFuncPtrElement &p_func_ptr_element) { ERR_FAIL_NULL(p_func_ptr_element.element); - ERR_FAIL_NULL(p_func_ptr_element.mutex); - MutexLock lock(*p_func_ptr_element.mutex); + ERR_FAIL_NULL(p_func_ptr_element.func_ptr); + MutexLock lock(p_func_ptr_element.func_ptr->mutex); p_func_ptr_element.element->erase(); } +void GDScript::_fixup_thread_function_bookkeeping() { + // Transfer the ownership of these update items to the main thread, + // because the current one is dying, leaving theirs orphan, dangling. + + DEV_ASSERT(!Thread::is_main_thread()); + + MutexLock lock(func_ptrs_to_update_main_thread.mutex); + MutexLock lock2(func_ptrs_to_update_thread_local->mutex); + + while (!func_ptrs_to_update_thread_local->ptrs.is_empty()) { + List<GDScriptFunction **>::Element *E = func_ptrs_to_update_thread_local->ptrs.front(); + E->transfer_to_back(&func_ptrs_to_update_main_thread.ptrs); + func_ptrs_to_update_thread_local->transferred = true; + } +} + void GDScript::clear(ClearData *p_clear_data) { if (clearing) { return; @@ -1436,9 +1459,27 @@ void GDScript::clear(ClearData *p_clear_data) { { MutexLock outer_lock(func_ptrs_to_update_mutex); for (UpdatableFuncPtr *updatable : func_ptrs_to_update) { - MutexLock inner_lock(updatable->mutex); - for (GDScriptFunction **func_ptr_ptr : updatable->ptrs) { - *func_ptr_ptr = nullptr; + bool destroy = false; + { + MutexLock inner_lock(updatable->mutex); + if (updatable->transferred) { + func_ptrs_to_update_main_thread.mutex.lock(); + } + for (GDScriptFunction **func_ptr_ptr : updatable->ptrs) { + *func_ptr_ptr = nullptr; + } + DEV_ASSERT(updatable->rc != 0); + updatable->rc--; + if (updatable->rc == 0) { + destroy = true; + } + if (updatable->transferred) { + func_ptrs_to_update_main_thread.mutex.unlock(); + } + } + if (destroy) { + DEV_ASSERT(updatable != &func_ptrs_to_update_main_thread); + memdelete(updatable); } } } @@ -2060,6 +2101,33 @@ void GDScriptLanguage::remove_named_global_constant(const StringName &p_name) { named_globals.erase(p_name); } +void GDScriptLanguage::thread_enter() { + GDScript::func_ptrs_to_update_thread_local = memnew(GDScript::UpdatableFuncPtr); +} + +void GDScriptLanguage::thread_exit() { + // This thread may have been created before GDScript was up + // (which also means it can't have run any GDScript code at all). + if (!GDScript::func_ptrs_to_update_thread_local) { + return; + } + + GDScript::_fixup_thread_function_bookkeeping(); + + bool destroy = false; + { + MutexLock lock(GDScript::func_ptrs_to_update_thread_local->mutex); + DEV_ASSERT(GDScript::func_ptrs_to_update_thread_local->rc != 0); + GDScript::func_ptrs_to_update_thread_local->rc--; + if (GDScript::func_ptrs_to_update_thread_local->rc == 0) { + destroy = true; + } + } + if (destroy) { + memdelete(GDScript::func_ptrs_to_update_thread_local); + } +} + void GDScriptLanguage::init() { //populate global constants int gcc = CoreConstants::get_global_constant_count(); @@ -2092,6 +2160,8 @@ void GDScriptLanguage::init() { _add_global(E.name, E.ptr); } + GDScript::func_ptrs_to_update_thread_local = &GDScript::func_ptrs_to_update_main_thread; + #ifdef TESTS_ENABLED GDScriptTests::GDScriptTestRunner::handle_cmdline(); #endif @@ -2141,6 +2211,8 @@ void GDScriptLanguage::finish() { } script_list.clear(); function_list.clear(); + + DEV_ASSERT(GDScript::func_ptrs_to_update_main_thread.rc == 1); } void GDScriptLanguage::profiling_start() { @@ -2257,6 +2329,19 @@ void GDScriptLanguage::reload_all_scripts() { } elem = elem->next(); } + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + // Reload all pointers to existing singletons so that tool scripts can work with the reloaded extensions. + List<Engine::Singleton> singletons; + Engine::get_singleton()->get_singletons(&singletons); + for (const Engine::Singleton &E : singletons) { + if (globals.has(E.name)) { + _add_global(E.name, E.ptr); + } + } + } +#endif } //as scripts are going to be reloaded, must proceed without locking here diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 04b0a1d786..aba4d7e721 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -121,18 +121,25 @@ class GDScript : public Script { struct UpdatableFuncPtr { List<GDScriptFunction **> ptrs; Mutex mutex; - bool initialized = false; + bool initialized : 1; + bool transferred : 1; + uint32_t rc = 1; + UpdatableFuncPtr() : + initialized(false), transferred(false) {} }; struct UpdatableFuncPtrElement { List<GDScriptFunction **>::Element *element = nullptr; - Mutex *mutex = nullptr; + UpdatableFuncPtr *func_ptr = nullptr; }; - static thread_local UpdatableFuncPtr func_ptrs_to_update_thread_local; + static UpdatableFuncPtr func_ptrs_to_update_main_thread; + static thread_local UpdatableFuncPtr *func_ptrs_to_update_thread_local; List<UpdatableFuncPtr *> func_ptrs_to_update; Mutex func_ptrs_to_update_mutex; UpdatableFuncPtrElement _add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr); - static void _remove_func_ptr_to_update(const UpdatableFuncPtrElement p_func_ptr_element); + static void _remove_func_ptr_to_update(const UpdatableFuncPtrElement &p_func_ptr_element); + + static void _fixup_thread_function_bookkeeping(); #ifdef TOOLS_ENABLED // For static data storage during hot-reloading. @@ -171,6 +178,7 @@ class GDScript : public Script { //exported members String source; String path; + bool path_valid = false; // False if using default path. StringName local_name; // Inner class identifier or `class_name`. StringName global_name; // `class_name`. String fully_qualified_name; @@ -553,6 +561,11 @@ public: virtual void add_named_global_constant(const StringName &p_name, const Variant &p_value) override; virtual void remove_named_global_constant(const StringName &p_name) override; + /* MULTITHREAD FUNCTIONS */ + + virtual void thread_enter() override; + virtual void thread_exit() override; + /* DEBUGGER FUNCTIONS */ virtual String debug_get_error() const override; diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp index 26f01ec218..76f4e69ab9 100644 --- a/modules/gdscript/gdscript_cache.cpp +++ b/modules/gdscript/gdscript_cache.cpp @@ -364,28 +364,33 @@ void GDScriptCache::remove_static_script(const String &p_fqcn) { Ref<PackedScene> GDScriptCache::get_packed_scene(const String &p_path, Error &r_error, const String &p_owner) { MutexLock lock(singleton->mutex); - if (singleton->packed_scene_cache.has(p_path)) { - singleton->packed_scene_dependencies[p_path].insert(p_owner); - return singleton->packed_scene_cache[p_path]; + String path = p_path; + if (path.begins_with("uid://")) { + path = ResourceUID::get_singleton()->get_id_path(ResourceUID::get_singleton()->text_to_id(path)); } - Ref<PackedScene> scene = ResourceCache::get_ref(p_path); + if (singleton->packed_scene_cache.has(path)) { + singleton->packed_scene_dependencies[path].insert(p_owner); + return singleton->packed_scene_cache[path]; + } + + Ref<PackedScene> scene = ResourceCache::get_ref(path); if (scene.is_valid()) { - singleton->packed_scene_cache[p_path] = scene; - singleton->packed_scene_dependencies[p_path].insert(p_owner); + singleton->packed_scene_cache[path] = scene; + singleton->packed_scene_dependencies[path].insert(p_owner); return scene; } scene.instantiate(); r_error = OK; - if (p_path.is_empty()) { + if (path.is_empty()) { r_error = ERR_FILE_BAD_PATH; return scene; } - scene->set_path(p_path); - singleton->packed_scene_cache[p_path] = scene; - singleton->packed_scene_dependencies[p_path].insert(p_owner); + scene->set_path(path); + singleton->packed_scene_cache[path] = scene; + singleton->packed_scene_dependencies[path].insert(p_owner); scene->reload_from_file(); return scene; diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 724715d9e5..faaff53344 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -1083,6 +1083,12 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base List<PropertyInfo> members; scr->get_script_property_list(&members); for (const PropertyInfo &E : members) { + if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP)) { + continue; + } + if (E.name.contains("/")) { + continue; + } int location = p_recursion_depth + _get_property_location(scr->get_class_name(), E.name); ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location); r_result.insert(option.display, option); @@ -1152,7 +1158,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base List<PropertyInfo> pinfo; ClassDB::get_property_list(type, &pinfo); for (const PropertyInfo &E : pinfo) { - if (E.usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY)) { + if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP)) { continue; } if (E.name.contains("/")) { @@ -1213,6 +1219,9 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base } for (const PropertyInfo &E : members) { + if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP)) { + continue; + } if (!String(E.name).contains("/")) { ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER); if (GDScriptParser::theme_color_names.has(E.name)) { @@ -2667,6 +2676,11 @@ static bool _get_subscript_type(GDScriptParser::CompletionContext &p_context, co if (p_context.base == nullptr) { return false; } + if (p_subscript->base->datatype.type_source == GDScriptParser::DataType::ANNOTATED_EXPLICIT) { + // Annotated type takes precedence. + return false; + } + const GDScriptParser::GetNodeNode *get_node = nullptr; switch (p_subscript->base->type) { @@ -2715,10 +2729,19 @@ static bool _get_subscript_type(GDScriptParser::CompletionContext &p_context, co if (r_base != nullptr) { *r_base = node; } - r_base_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; - r_base_type.kind = GDScriptParser::DataType::NATIVE; + + r_base_type.type_source = GDScriptParser::DataType::INFERRED; r_base_type.builtin_type = Variant::OBJECT; r_base_type.native_type = node->get_class_name(); + + Ref<Script> scr = node->get_script(); + if (scr.is_null()) { + r_base_type.kind = GDScriptParser::DataType::NATIVE; + } else { + r_base_type.kind = GDScriptParser::DataType::SCRIPT; + r_base_type.script_type = scr; + } + return true; } } @@ -2745,8 +2768,6 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_call); GDScriptParser::Node::Type callee_type = call->get_callee_type(); - GDScriptCompletionIdentifier connect_base; - if (callee_type == GDScriptParser::Node::SUBSCRIPT) { const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(call->callee); diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h index 6dd8a98652..a64aaf6820 100644 --- a/modules/gdscript/gdscript_tokenizer.h +++ b/modules/gdscript/gdscript_tokenizer.h @@ -37,6 +37,12 @@ #include "core/templates/vector.h" #include "core/variant/variant.h" +#ifdef MINGW_ENABLED +#undef CONST +#undef IN +#undef VOID +#endif + class GDScriptTokenizer { public: enum CursorPlace { diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp index f91dc83f2c..361ca276bb 100644 --- a/modules/gdscript/tests/gdscript_test_runner.cpp +++ b/modules/gdscript/tests/gdscript_test_runner.cpp @@ -78,31 +78,30 @@ void init_autoloads() { scn.instantiate(); scn->set_path(info.path); scn->reload_from_file(); - ERR_CONTINUE_MSG(!scn.is_valid(), vformat("Can't autoload: %s.", info.path)); + ERR_CONTINUE_MSG(!scn.is_valid(), vformat("Failed to instantiate an autoload, can't load from path: %s.", info.path)); if (scn.is_valid()) { n = scn->instantiate(); } } else { Ref<Resource> res = ResourceLoader::load(info.path); - ERR_CONTINUE_MSG(res.is_null(), vformat("Can't autoload: %s.", info.path)); + ERR_CONTINUE_MSG(res.is_null(), vformat("Failed to instantiate an autoload, can't load from path: %s.", info.path)); Ref<Script> scr = res; if (scr.is_valid()) { StringName ibt = scr->get_instance_base_type(); bool valid_type = ClassDB::is_parent_class(ibt, "Node"); - ERR_CONTINUE_MSG(!valid_type, vformat("Script does not inherit from Node: %s.", info.path)); + ERR_CONTINUE_MSG(!valid_type, vformat("Failed to instantiate an autoload, script '%s' does not inherit from 'Node'.", info.path)); Object *obj = ClassDB::instantiate(ibt); - - ERR_CONTINUE_MSG(!obj, vformat("Cannot instance script for Autoload, expected 'Node' inheritance, got: %s.", ibt)); + ERR_CONTINUE_MSG(!obj, vformat("Failed to instantiate an autoload, cannot instantiate '%s'.", ibt)); n = Object::cast_to<Node>(obj); n->set_script(scr); } } - ERR_CONTINUE_MSG(!n, vformat("Path in autoload not a node or script: %s.", info.path)); + ERR_CONTINUE_MSG(!n, vformat("Failed to instantiate an autoload, path is not pointing to a scene or a script: %s.", info.path)); n->set_name(info.name); for (int i = 0; i < ScriptServer::get_language_count(); i++) { diff --git a/modules/gltf/doc_classes/GLTFAccessor.xml b/modules/gltf/doc_classes/GLTFAccessor.xml index 8e4a72e6ae..f678a11319 100644 --- a/modules/gltf/doc_classes/GLTFAccessor.xml +++ b/modules/gltf/doc_classes/GLTFAccessor.xml @@ -5,6 +5,7 @@ <description> </description> <tutorials> + <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> </tutorials> <members> <member name="buffer_view" type="int" setter="set_buffer_view" getter="get_buffer_view" default="-1"> diff --git a/modules/gltf/doc_classes/GLTFAnimation.xml b/modules/gltf/doc_classes/GLTFAnimation.xml index c1fe85c1c0..65335250aa 100644 --- a/modules/gltf/doc_classes/GLTFAnimation.xml +++ b/modules/gltf/doc_classes/GLTFAnimation.xml @@ -5,6 +5,7 @@ <description> </description> <tutorials> + <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> </tutorials> <members> <member name="loop" type="bool" setter="set_loop" getter="get_loop" default="false"> diff --git a/modules/gltf/doc_classes/GLTFBufferView.xml b/modules/gltf/doc_classes/GLTFBufferView.xml index ce19650b5b..d0f76a9af3 100644 --- a/modules/gltf/doc_classes/GLTFBufferView.xml +++ b/modules/gltf/doc_classes/GLTFBufferView.xml @@ -5,6 +5,7 @@ <description> </description> <tutorials> + <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> </tutorials> <members> <member name="buffer" type="int" setter="set_buffer" getter="get_buffer" default="-1"> diff --git a/modules/gltf/doc_classes/GLTFCamera.xml b/modules/gltf/doc_classes/GLTFCamera.xml index c2ed4d08e3..b334bf2867 100644 --- a/modules/gltf/doc_classes/GLTFCamera.xml +++ b/modules/gltf/doc_classes/GLTFCamera.xml @@ -7,6 +7,7 @@ Represents a camera as defined by the base GLTF spec. </description> <tutorials> + <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> <link title="GLTF camera detailed specification">https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-camera</link> <link title="GLTF camera spec and example file">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_015_SimpleCameras.md</link> </tutorials> diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml index 5c10b76e0a..f5066ba4f1 100644 --- a/modules/gltf/doc_classes/GLTFDocument.xml +++ b/modules/gltf/doc_classes/GLTFDocument.xml @@ -9,6 +9,7 @@ GLTFDocument can be extended with arbitrary functionality by extending the [GLTFDocumentExtension] class and registering it with GLTFDocument via [method register_gltf_document_extension]. This allows for custom data to be imported and exported. </description> <tutorials> + <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> <link title="glTF 'What the duck?' guide">https://www.khronos.org/files/gltf20-reference-guide.pdf</link> <link title="Khronos glTF specification">https://registry.khronos.org/glTF/</link> </tutorials> diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml index eee62845ca..aaa55e772a 100644 --- a/modules/gltf/doc_classes/GLTFDocumentExtension.xml +++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml @@ -9,6 +9,7 @@ [b]Note:[/b] Like GLTFDocument itself, all GLTFDocumentExtension classes must be stateless in order to function properly. If you need to store data, use the [code]set_additional_data[/code] and [code]get_additional_data[/code] methods in [GLTFState] or [GLTFNode]. </description> <tutorials> + <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> </tutorials> <methods> <method name="_convert_scene_node" qualifiers="virtual"> diff --git a/modules/gltf/doc_classes/GLTFDocumentExtensionConvertImporterMesh.xml b/modules/gltf/doc_classes/GLTFDocumentExtensionConvertImporterMesh.xml index 3ca0359311..04075caba5 100644 --- a/modules/gltf/doc_classes/GLTFDocumentExtensionConvertImporterMesh.xml +++ b/modules/gltf/doc_classes/GLTFDocumentExtensionConvertImporterMesh.xml @@ -5,5 +5,6 @@ <description> </description> <tutorials> + <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> </tutorials> </class> diff --git a/modules/gltf/doc_classes/GLTFLight.xml b/modules/gltf/doc_classes/GLTFLight.xml index c55962eeaa..653a394ebb 100644 --- a/modules/gltf/doc_classes/GLTFLight.xml +++ b/modules/gltf/doc_classes/GLTFLight.xml @@ -7,6 +7,7 @@ Represents a light as defined by the [code]KHR_lights_punctual[/code] GLTF extension. </description> <tutorials> + <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> <link title="KHR_lights_punctual GLTF extension spec">https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_lights_punctual</link> </tutorials> <methods> diff --git a/modules/gltf/doc_classes/GLTFMesh.xml b/modules/gltf/doc_classes/GLTFMesh.xml index df4d436f5c..da05474ab9 100644 --- a/modules/gltf/doc_classes/GLTFMesh.xml +++ b/modules/gltf/doc_classes/GLTFMesh.xml @@ -5,6 +5,7 @@ <description> </description> <tutorials> + <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> </tutorials> <members> <member name="blend_weights" type="PackedFloat32Array" setter="set_blend_weights" getter="get_blend_weights" default="PackedFloat32Array()"> diff --git a/modules/gltf/doc_classes/GLTFNode.xml b/modules/gltf/doc_classes/GLTFNode.xml index 2ec39801f3..184ced4312 100644 --- a/modules/gltf/doc_classes/GLTFNode.xml +++ b/modules/gltf/doc_classes/GLTFNode.xml @@ -8,6 +8,7 @@ GLTF nodes generally exist inside of [GLTFState] which represents all data of a GLTF file. Most of GLTFNode's properties are indices of other data in the GLTF file. You can extend a GLTF node with additional properties by using [method get_additional_data] and [method set_additional_data]. </description> <tutorials> + <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> <link title="GLTF scene and node spec">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_004_ScenesNodes.md"</link> </tutorials> <methods> diff --git a/modules/gltf/doc_classes/GLTFPhysicsBody.xml b/modules/gltf/doc_classes/GLTFPhysicsBody.xml index d364069193..cf39721ce8 100644 --- a/modules/gltf/doc_classes/GLTFPhysicsBody.xml +++ b/modules/gltf/doc_classes/GLTFPhysicsBody.xml @@ -7,6 +7,7 @@ Represents a physics body as defined by the [code]OMI_physics_body[/code] GLTF extension. This class is an intermediary between the GLTF data and Godot's nodes, and it's abstracted in a way that allows adding support for different GLTF physics extensions in the future. </description> <tutorials> + <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> <link title="OMI_physics_body GLTF extension">https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/OMI_physics_body</link> </tutorials> <methods> diff --git a/modules/gltf/doc_classes/GLTFPhysicsShape.xml b/modules/gltf/doc_classes/GLTFPhysicsShape.xml index 2891fab115..67382f3295 100644 --- a/modules/gltf/doc_classes/GLTFPhysicsShape.xml +++ b/modules/gltf/doc_classes/GLTFPhysicsShape.xml @@ -7,6 +7,7 @@ Represents a physics shape as defined by the [code]OMI_collider[/code] GLTF extension. This class is an intermediary between the GLTF data and Godot's nodes, and it's abstracted in a way that allows adding support for different GLTF physics extensions in the future. </description> <tutorials> + <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> <link title="OMI_collider GLTF extension">https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/OMI_collider</link> </tutorials> <methods> diff --git a/modules/gltf/doc_classes/GLTFSkeleton.xml b/modules/gltf/doc_classes/GLTFSkeleton.xml index c7b8cb2ac3..ac03a6ee9e 100644 --- a/modules/gltf/doc_classes/GLTFSkeleton.xml +++ b/modules/gltf/doc_classes/GLTFSkeleton.xml @@ -5,6 +5,7 @@ <description> </description> <tutorials> + <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> </tutorials> <methods> <method name="get_bone_attachment"> diff --git a/modules/gltf/doc_classes/GLTFSkin.xml b/modules/gltf/doc_classes/GLTFSkin.xml index 98436ea2e7..0835a2d510 100644 --- a/modules/gltf/doc_classes/GLTFSkin.xml +++ b/modules/gltf/doc_classes/GLTFSkin.xml @@ -5,6 +5,7 @@ <description> </description> <tutorials> + <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> </tutorials> <methods> <method name="get_inverse_binds"> diff --git a/modules/gltf/doc_classes/GLTFSpecGloss.xml b/modules/gltf/doc_classes/GLTFSpecGloss.xml index d64a918920..722fa5e9ae 100644 --- a/modules/gltf/doc_classes/GLTFSpecGloss.xml +++ b/modules/gltf/doc_classes/GLTFSpecGloss.xml @@ -7,6 +7,7 @@ KHR_materials_pbrSpecularGlossiness is an archived GLTF extension. This means that it is deprecated and not recommended for new files. However, it is still supported for loading old files. </description> <tutorials> + <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> <link title="KHR_materials_pbrSpecularGlossiness GLTF extension spec">https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Archived/KHR_materials_pbrSpecularGlossiness</link> </tutorials> <members> diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml index ba1c531283..e256041737 100644 --- a/modules/gltf/doc_classes/GLTFState.xml +++ b/modules/gltf/doc_classes/GLTFState.xml @@ -8,6 +8,7 @@ GLTFState can be populated by [GLTFDocument] reading a file or by converting a Godot scene. Then the data can either be used to create a Godot scene or save to a GLTF file. The code that converts to/from a Godot scene can be intercepted at arbitrary points by [GLTFDocumentExtension] classes. This allows for custom data to be stored in the GLTF file or for custom data to be converted to/from Godot nodes. </description> <tutorials> + <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> <link title="GLTF asset header schema">https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/schema/asset.schema.json"</link> </tutorials> <methods> diff --git a/modules/gltf/doc_classes/GLTFTexture.xml b/modules/gltf/doc_classes/GLTFTexture.xml index 41c20439ea..dd323b753b 100644 --- a/modules/gltf/doc_classes/GLTFTexture.xml +++ b/modules/gltf/doc_classes/GLTFTexture.xml @@ -5,6 +5,7 @@ <description> </description> <tutorials> + <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> </tutorials> <members> <member name="sampler" type="int" setter="set_sampler" getter="get_sampler" default="-1"> diff --git a/modules/gltf/doc_classes/GLTFTextureSampler.xml b/modules/gltf/doc_classes/GLTFTextureSampler.xml index 027d35e9d2..2b5bad6724 100644 --- a/modules/gltf/doc_classes/GLTFTextureSampler.xml +++ b/modules/gltf/doc_classes/GLTFTextureSampler.xml @@ -7,6 +7,7 @@ Represents a texture sampler as defined by the base GLTF spec. Texture samplers in GLTF specify how to sample data from the texture's base image, when rendering the texture on an object. </description> <tutorials> + <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> </tutorials> <members> <member name="mag_filter" type="int" setter="set_mag_filter" getter="get_mag_filter" default="9729"> diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp index cb45a6589e..9587604e56 100644 --- a/modules/gltf/editor/editor_scene_importer_blend.cpp +++ b/modules/gltf/editor/editor_scene_importer_blend.cpp @@ -45,6 +45,11 @@ #include "main/main.h" #include "scene/gui/line_edit.h" +#ifdef MINGW_ENABLED +#define near +#define far +#endif + #ifdef WINDOWS_ENABLED #include <shlwapi.h> #endif @@ -130,8 +135,9 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_ // Get global paths for source and sink. // Escape paths to be valid Python strings to embed in the script. const String source_global = ProjectSettings::get_singleton()->globalize_path(p_path).c_escape(); + const String blend_basename = p_path.get_file().get_basename(); const String sink = ProjectSettings::get_singleton()->get_imported_files_path().path_join( - vformat("%s-%s.gltf", p_path.get_file().get_basename(), p_path.md5_text())); + vformat("%s-%s.gltf", blend_basename, p_path.md5_text())); const String sink_global = ProjectSettings::get_singleton()->globalize_path(sink).c_escape(); // Handle configuration options. @@ -282,6 +288,7 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_ if (p_options.has(SNAME("blender/materials/unpack_enabled")) && p_options[SNAME("blender/materials/unpack_enabled")]) { base_dir = sink.get_base_dir(); } + state->set_scene_name(blend_basename); err = gltf->append_from_file(sink.get_basename() + ".gltf", state, p_flags, base_dir); if (err != OK) { if (r_err) { diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index b5adea7da0..4060f7f626 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -573,7 +573,7 @@ Error GLTFDocument::_parse_scenes(Ref<GLTFState> p_state) { // Determine what to use for the scene name. if (scene_dict.has("name") && !String(scene_dict["name"]).is_empty() && !((String)scene_dict["name"]).begins_with("Scene")) { p_state->scene_name = scene_dict["name"]; - } else { + } else if (p_state->scene_name.is_empty()) { p_state->scene_name = p_state->filename; } if (_naming_version == 0) { diff --git a/modules/jpg/image_loader_jpegd.cpp b/modules/jpg/image_loader_jpegd.cpp index 0b9fcf4455..e7fa909706 100644 --- a/modules/jpg/image_loader_jpegd.cpp +++ b/modules/jpg/image_loader_jpegd.cpp @@ -156,7 +156,11 @@ public: static Error _jpgd_save_to_output_stream(jpge::output_stream *p_output_stream, const Ref<Image> &p_img, float p_quality) { ERR_FAIL_COND_V(p_img.is_null() || p_img->is_empty(), ERR_INVALID_PARAMETER); - Ref<Image> image = p_img; + Ref<Image> image = p_img->duplicate(); + if (image->is_compressed()) { + Error error = image->decompress(); + ERR_FAIL_COND_V_MSG(error != OK, error, "Couldn't decompress image."); + } if (image->get_format() != Image::FORMAT_RGB8) { image->convert(Image::FORMAT_RGB8); } diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp index fe919953c1..a4ecb767a7 100644 --- a/modules/lightmapper_rd/lightmapper_rd.cpp +++ b/modules/lightmapper_rd/lightmapper_rd.cpp @@ -877,7 +877,7 @@ LightmapperRD::BakeError LightmapperRD::_denoise_oidn(RenderingDevice *p_rd, RID if (err != OK || exitcode != 0) { da->remove(fname_out); print_verbose(str); - ERR_FAIL_V_MSG(BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES, vformat(TTR("OIDN denoiser failed, return code: %d"), exitcode)); + ERR_FAIL_V_MSG(BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES, vformat("OIDN denoiser failed, return code: %d", exitcode)); } Ref<Image> img = _read_pfm(fname_out); @@ -988,7 +988,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d oidn_path = oidn_path.path_join("oidnDenoise"); } } - ERR_FAIL_COND_V_MSG(oidn_path.is_empty() || !da->file_exists(oidn_path), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES, TTR("OIDN denoiser is selected in the project settings, but no or invalid OIDN executable path is configured in the editor settings.")); + ERR_FAIL_COND_V_MSG(oidn_path.is_empty() || !da->file_exists(oidn_path), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES, "OIDN denoiser is selected in the project settings, but no or invalid OIDN executable path is configured in the editor settings."); } if (p_step_function) { diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 3cc32bff10..8e1587997b 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -1989,24 +1989,31 @@ const Variant CSharpInstance::get_rpc_config() const { void CSharpInstance::notification(int p_notification, bool p_reversed) { if (p_notification == Object::NOTIFICATION_PREDELETE) { - // When NOTIFICATION_PREDELETE is sent, we also take the chance to call Dispose(). - // It's safe to call Dispose() multiple times and NOTIFICATION_PREDELETE is guaranteed + if (base_ref_counted) { + // At this point, Dispose() was already called (manually or from the finalizer). + // The RefCounted wouldn't have reached 0 otherwise, since the managed side + // references it and Dispose() needs to be called to release it. + // However, this means C# RefCounted scripts can't receive NOTIFICATION_PREDELETE, but + // this is likely the case with GDScript as well: https://github.com/godotengine/godot/issues/6784 + return; + } + } else if (p_notification == Object::NOTIFICATION_PREDELETE_CLEANUP) { + // When NOTIFICATION_PREDELETE_CLEANUP is sent, we also take the chance to call Dispose(). + // It's safe to call Dispose() multiple times and NOTIFICATION_PREDELETE_CLEANUP is guaranteed // to be sent at least once, which happens right before the call to the destructor. predelete_notified = true; if (base_ref_counted) { - // It's not safe to proceed if the owner derives RefCounted and the refcount reached 0. - // At this point, Dispose() was already called (manually or from the finalizer) so - // that's not a problem. The refcount wouldn't have reached 0 otherwise, since the - // managed side references it and Dispose() needs to be called to release it. - // However, this means C# RefCounted scripts can't receive NOTIFICATION_PREDELETE, but - // this is likely the case with GDScript as well: https://github.com/godotengine/godot/issues/6784 + // At this point, Dispose() was already called (manually or from the finalizer). + // The RefCounted wouldn't have reached 0 otherwise, since the managed side + // references it and Dispose() needs to be called to release it. return; } - _call_notification(p_notification, p_reversed); - + // NOTIFICATION_PREDELETE_CLEANUP is not sent to scripts. + // After calling Dispose() the C# instance can no longer be used, + // so it should be the last thing we do. GDMonoCache::managed_callbacks.CSharpInstanceBridge_CallDispose( gchandle.get_intptr(), /* okIfNull */ false); @@ -2613,12 +2620,12 @@ MethodInfo CSharpScript::get_method_info(const StringName &p_method) const { } Variant CSharpScript::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - ERR_FAIL_COND_V(!valid, Variant()); - - Variant ret; - bool ok = GDMonoCache::managed_callbacks.ScriptManagerBridge_CallStatic(this, &p_method, p_args, p_argcount, &r_error, &ret); - if (ok) { - return ret; + if (valid) { + Variant ret; + bool ok = GDMonoCache::managed_callbacks.ScriptManagerBridge_CallStatic(this, &p_method, p_args, p_argcount, &r_error, &ret); + if (ok) { + return ret; + } } return Script::callp(p_method, p_args, p_argcount, r_error); diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj index ad3a10ba49..ccef90c911 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj @@ -7,7 +7,7 @@ <Authors>Godot Engine contributors</Authors> <PackageId>Godot.NET.Sdk</PackageId> - <Version>4.2.0</Version> + <Version>4.3.0</Version> <PackageVersion>$(PackageVersion_Godot_NET_Sdk)</PackageVersion> <RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/editor/Godot.NET.Sdk</RepositoryUrl> <PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl> diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/iOSNativeAOT.targets b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/iOSNativeAOT.targets index d8129a6652..b51ce5cb8c 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/iOSNativeAOT.targets +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/iOSNativeAOT.targets @@ -33,9 +33,9 @@ <Message Importance="normal" Text="Found XCode at $(XcodeSelect)" Condition=" '$(FindXCode)' == 'true' "/> <ItemGroup> - <LinkerArg Include="-isysroot %22$(XCodePath)Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk%22" + <LinkerArg Include="-mios-simulator-version-min=12.0 -isysroot %22$(XCodePath)Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk%22" Condition=" $(RuntimeIdentifier.Contains('simulator')) "/> - <LinkerArg Include="-isysroot %22$(XCodePath)Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk%22" + <LinkerArg Include="-miphoneos-version-min=12.0 -isysroot %22$(XCodePath)Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk%22" Condition=" !$(RuntimeIdentifier.Contains('simulator')) "/> </ItemGroup> diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj index 23879e0e53..7d2395ba61 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj @@ -9,7 +9,7 @@ <Authors>Godot Engine contributors</Authors> <PackageId>Godot.SourceGenerators</PackageId> - <Version>4.2.0</Version> + <Version>4.3.0</Version> <PackageVersion>$(PackageVersion_Godot_SourceGenerators)</PackageVersion> <RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators</RepositoryUrl> <PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl> diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs index b16adb6f55..e516b4dd18 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs @@ -195,7 +195,7 @@ namespace GodotTools.Export bool isSim = arch == "i386" || arch == "x86_64"; // Shouldn't really happen as we don't do AOT for the simulator string versionMinName = isSim ? "iphonesimulator" : "iphoneos"; string iOSPlatformName = isSim ? "iPhoneSimulator" : "iPhoneOS"; - const string versionMin = "10.0"; // TODO: Turn this hard-coded version into an exporter setting + const string versionMin = "12.0"; // TODO: Turn this hard-coded version into an exporter setting string iOSSdkPath = Path.Combine(XcodeHelper.XcodePath, $"Contents/Developer/Platforms/{iOSPlatformName}.platform/Developer/SDKs/{iOSPlatformName}.sdk"); diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs index 91e5118990..c634d9c1ac 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -194,7 +194,7 @@ namespace GodotTools.Export BundleOutputs = false, IncludeDebugSymbols = publishConfig.IncludeDebugSymbols, RidOS = OS.DotNetOS.iOSSimulator, - UseTempDir = true, + UseTempDir = false, }); } @@ -361,7 +361,7 @@ namespace GodotTools.Export } var xcFrameworkPath = Path.Combine(GodotSharpDirs.ProjectBaseOutputPath, publishConfig.BuildConfig, - $"{GodotSharpDirs.ProjectAssemblyName}.xcframework"); + $"{GodotSharpDirs.ProjectAssemblyName}_aot.xcframework"); if (!BuildManager.GenerateXCFrameworkBlocking(outputPaths, Path.Combine(GodotSharpDirs.ProjectBaseOutputPath, publishConfig.BuildConfig, xcFrameworkPath))) { diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index 6bdf207873..650afb4cdf 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -497,7 +497,10 @@ namespace GodotTools AddChild(new HotReloadAssemblyWatcher { Name = "HotReloadAssemblyWatcher" }); - _menuPopup = new PopupMenu(); + _menuPopup = new PopupMenu + { + Name = "CSharpTools", + }; _menuPopup.Hide(); AddToolSubmenuItem("C#", _menuPopup); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index 93a83b701b..0cc89d78af 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -259,7 +259,7 @@ namespace Godot.NativeInterop } return new godot_callable(method /* Takes ownership of disposable */, - p_managed_callable.Target.GetInstanceId()); + p_managed_callable.Target?.GetInstanceId() ?? 0); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs index 4ee452455e..215bb4df8c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs @@ -182,8 +182,8 @@ namespace Godot } // Constants - private static readonly Vector2I _min = new Vector2I(int.MinValue, int.MinValue); - private static readonly Vector2I _max = new Vector2I(int.MaxValue, int.MaxValue); + private static readonly Vector2I _minValue = new Vector2I(int.MinValue, int.MinValue); + private static readonly Vector2I _maxValue = new Vector2I(int.MaxValue, int.MaxValue); private static readonly Vector2I _zero = new Vector2I(0, 0); private static readonly Vector2I _one = new Vector2I(1, 1); @@ -197,12 +197,12 @@ namespace Godot /// Min vector, a vector with all components equal to <see cref="int.MinValue"/>. Can be used as a negative integer equivalent of <see cref="Vector2.Inf"/>. /// </summary> /// <value>Equivalent to <c>new Vector2I(int.MinValue, int.MinValue)</c>.</value> - public static Vector2I Min { get { return _min; } } + public static Vector2I MinValue { get { return _minValue; } } /// <summary> /// Max vector, a vector with all components equal to <see cref="int.MaxValue"/>. Can be used as an integer equivalent of <see cref="Vector2.Inf"/>. /// </summary> /// <value>Equivalent to <c>new Vector2I(int.MaxValue, int.MaxValue)</c>.</value> - public static Vector2I Max { get { return _max; } } + public static Vector2I MaxValue { get { return _maxValue; } } /// <summary> /// Zero vector, a vector with all components set to <c>0</c>. diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs index db8ceb30e9..fe74ec8884 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs @@ -193,8 +193,8 @@ namespace Godot } // Constants - private static readonly Vector3I _min = new Vector3I(int.MinValue, int.MinValue, int.MinValue); - private static readonly Vector3I _max = new Vector3I(int.MaxValue, int.MaxValue, int.MaxValue); + private static readonly Vector3I _minValue = new Vector3I(int.MinValue, int.MinValue, int.MinValue); + private static readonly Vector3I _maxValue = new Vector3I(int.MaxValue, int.MaxValue, int.MaxValue); private static readonly Vector3I _zero = new Vector3I(0, 0, 0); private static readonly Vector3I _one = new Vector3I(1, 1, 1); @@ -210,12 +210,12 @@ namespace Godot /// Min vector, a vector with all components equal to <see cref="int.MinValue"/>. Can be used as a negative integer equivalent of <see cref="Vector3.Inf"/>. /// </summary> /// <value>Equivalent to <c>new Vector3I(int.MinValue, int.MinValue, int.MinValue)</c>.</value> - public static Vector3I Min { get { return _min; } } + public static Vector3I MinValue { get { return _minValue; } } /// <summary> /// Max vector, a vector with all components equal to <see cref="int.MaxValue"/>. Can be used as an integer equivalent of <see cref="Vector3.Inf"/>. /// </summary> /// <value>Equivalent to <c>new Vector3I(int.MaxValue, int.MaxValue, int.MaxValue)</c>.</value> - public static Vector3I Max { get { return _max; } } + public static Vector3I MaxValue { get { return _maxValue; } } /// <summary> /// Zero vector, a vector with all components set to <c>0</c>. diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs index e75e996b04..a0a4393523 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs @@ -228,8 +228,8 @@ namespace Godot } // Constants - private static readonly Vector4I _min = new Vector4I(int.MinValue, int.MinValue, int.MinValue, int.MinValue); - private static readonly Vector4I _max = new Vector4I(int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue); + private static readonly Vector4I _minValue = new Vector4I(int.MinValue, int.MinValue, int.MinValue, int.MinValue); + private static readonly Vector4I _maxValue = new Vector4I(int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue); private static readonly Vector4I _zero = new Vector4I(0, 0, 0, 0); private static readonly Vector4I _one = new Vector4I(1, 1, 1, 1); @@ -238,12 +238,12 @@ namespace Godot /// Min vector, a vector with all components equal to <see cref="int.MinValue"/>. Can be used as a negative integer equivalent of <see cref="Vector4.Inf"/>. /// </summary> /// <value>Equivalent to <c>new Vector4I(int.MinValue, int.MinValue, int.MinValue, int.MinValue)</c>.</value> - public static Vector4I Min { get { return _min; } } + public static Vector4I MinValue { get { return _minValue; } } /// <summary> /// Max vector, a vector with all components equal to <see cref="int.MaxValue"/>. Can be used as an integer equivalent of <see cref="Vector4.Inf"/>. /// </summary> /// <value>Equivalent to <c>new Vector4I(int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue)</c>.</value> - public static Vector4I Max { get { return _max; } } + public static Vector4I MaxValue { get { return _maxValue; } } /// <summary> /// Zero vector, a vector with all components set to <c>0</c>. diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index a55b8d693b..db16b1fe1d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -17,7 +17,7 @@ <Authors>Godot Engine contributors</Authors> <PackageId>GodotSharp</PackageId> - <Version>4.2.0</Version> + <Version>4.3.0</Version> <PackageVersion>$(PackageVersion_GodotSharp)</PackageVersion> <RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/glue/GodotSharp/GodotSharp</RepositoryUrl> <PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl> diff --git a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj index db9337d4eb..31e20e4ecd 100644 --- a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj @@ -15,7 +15,7 @@ <Authors>Godot Engine contributors</Authors> <PackageId>GodotSharpEditor</PackageId> - <Version>4.2.0</Version> + <Version>4.3.0</Version> <PackageVersion>$(PackageVersion_GodotSharp)</PackageVersion> <RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/glue/GodotSharp/GodotSharpEditor</RepositoryUrl> <PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl> diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index 530936cfb4..1b46a619ca 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -68,7 +68,9 @@ class GDMono { String project_assembly_path; uint64_t project_assembly_modified_time = 0; +#ifdef GD_MONO_HOT_RELOAD int project_load_failure_count = 0; +#endif #ifdef TOOLS_ENABLED bool _load_project_assembly(); diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp index 5ef0298eb4..7a0799a735 100644 --- a/modules/navigation/nav_map.cpp +++ b/modules/navigation/nav_map.cpp @@ -1107,8 +1107,14 @@ void NavMap::_update_rvo_obstacles_tree_2d() { obstacle_vertex_count += obstacle->get_vertices().size(); } + // Cleaning old obstacles. + for (size_t i = 0; i < rvo_simulation_2d.obstacles_.size(); ++i) { + delete rvo_simulation_2d.obstacles_[i]; + } + rvo_simulation_2d.obstacles_.clear(); + // Cannot use LocalVector here as RVO library expects std::vector to build KdTree - std::vector<RVO2D::Obstacle2D *> raw_obstacles; + std::vector<RVO2D::Obstacle2D *> &raw_obstacles = rvo_simulation_2d.obstacles_; raw_obstacles.reserve(obstacle_vertex_count); // The following block is modified copy from RVO2D::AddObstacle() @@ -1125,8 +1131,14 @@ void NavMap::_update_rvo_obstacles_tree_2d() { rvo_2d_vertices.reserve(_obstacle_vertices.size()); uint32_t _obstacle_avoidance_layers = obstacle->get_avoidance_layers(); + real_t _obstacle_height = obstacle->get_height(); for (const Vector3 &_obstacle_vertex : _obstacle_vertices) { +#ifdef TOOLS_ENABLED + if (_obstacle_vertex.y != 0) { + WARN_PRINT_ONCE("Y coordinates of static obstacle vertices are ignored. Please use obstacle position Y to change elevation of obstacle."); + } +#endif rvo_2d_vertices.push_back(RVO2D::Vector2(_obstacle_vertex.x + _obstacle_position.x, _obstacle_vertex.z + _obstacle_position.z)); } @@ -1135,6 +1147,9 @@ void NavMap::_update_rvo_obstacles_tree_2d() { for (size_t i = 0; i < rvo_2d_vertices.size(); i++) { RVO2D::Obstacle2D *rvo_2d_obstacle = new RVO2D::Obstacle2D(); rvo_2d_obstacle->point_ = rvo_2d_vertices[i]; + rvo_2d_obstacle->height_ = _obstacle_height; + rvo_2d_obstacle->elevation_ = _obstacle_position.y; + rvo_2d_obstacle->avoidance_layers_ = _obstacle_avoidance_layers; if (i != 0) { diff --git a/modules/openxr/action_map/openxr_action_map.cpp b/modules/openxr/action_map/openxr_action_map.cpp index 72866f1cf7..bbcb63a7e6 100644 --- a/modules/openxr/action_map/openxr_action_map.cpp +++ b/modules/openxr/action_map/openxr_action_map.cpp @@ -177,7 +177,6 @@ void OpenXRActionMap::create_default_action_sets() { Ref<OpenXRAction> trigger_touch = action_set->add_new_action("trigger_touch", "Trigger touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right"); Ref<OpenXRAction> grip = action_set->add_new_action("grip", "Grip", OpenXRAction::OPENXR_ACTION_FLOAT, "/user/hand/left,/user/hand/right"); Ref<OpenXRAction> grip_click = action_set->add_new_action("grip_click", "Grip click", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right"); - Ref<OpenXRAction> grip_touch = action_set->add_new_action("grip_touch", "Grip touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right"); Ref<OpenXRAction> grip_force = action_set->add_new_action("grip_force", "Grip force", OpenXRAction::OPENXR_ACTION_FLOAT, "/user/hand/left,/user/hand/right"); Ref<OpenXRAction> primary = action_set->add_new_action("primary", "Primary joystick/thumbstick/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2, "/user/hand/left,/user/hand/right"); Ref<OpenXRAction> primary_click = action_set->add_new_action("primary_click", "Primary joystick/thumbstick/trackpad click", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right"); diff --git a/modules/openxr/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml index 6d1c215ffc..666d20d7e7 100644 --- a/modules/openxr/doc_classes/OpenXRInterface.xml +++ b/modules/openxr/doc_classes/OpenXRInterface.xml @@ -129,9 +129,11 @@ </member> <member name="foveation_dynamic" type="bool" setter="set_foveation_dynamic" getter="get_foveation_dynamic" default="false"> Enable dynamic foveation adjustment, the interface must be initialized before this is accessible. If enabled foveation will automatically adjusted between low and [member foveation_level]. + [b]Note:[/b] Only works on compatibility renderer. </member> <member name="foveation_level" type="int" setter="set_foveation_level" getter="get_foveation_level" default="0"> Set foveation level from 0 (off) to 3 (high), the interface must be initialized before this is accessible. + [b]Note:[/b] Only works on compatibility renderer. </member> <member name="render_target_size_multiplier" type="float" setter="set_render_target_size_multiplier" getter="get_render_target_size_multiplier" default="1.0"> The render size multiplier for the current HMD. Must be set before the interface has been initialized. diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index dc3ccccd08..3c606de670 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -289,7 +289,7 @@ bool OpenXRAPI::create_instance() { for (KeyValue<String, bool *> &requested_extension : requested_extensions) { if (!is_extension_supported(requested_extension.key)) { if (requested_extension.value == nullptr) { - // Null means this is a manditory extension so we fail. + // Null means this is a mandatory extension so we fail. ERR_FAIL_V_MSG(false, String("OpenXR: OpenXR Runtime does not support ") + requested_extension.key + String(" extension!")); } else { // Set this extension as not supported. diff --git a/modules/text_server_adv/gdextension_build/methods.py b/modules/text_server_adv/gdextension_build/methods.py index 3c5229462c..e58bc3abec 100644 --- a/modules/text_server_adv/gdextension_build/methods.py +++ b/modules/text_server_adv/gdextension_build/methods.py @@ -99,8 +99,8 @@ def make_icu_data(target, source, env): def write_macos_plist(target, binary_name, identifier, name): - os.makedirs(f"{target}/Resourece/", exist_ok=True) - f = open(f"{target}/Resourece/Info.plist", "w") + os.makedirs(f"{target}/Resource/", exist_ok=True) + f = open(f"{target}/Resource/Info.plist", "w") f.write(f'<?xml version="1.0" encoding="UTF-8"?>\n') f.write(f'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n') diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index eaab04b49a..6d0a398218 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -1113,14 +1113,14 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma case FT_PIXEL_MODE_LCD: { int ofs_color = i * bitmap.pitch + (j * 3); if (p_bgra) { - wr[ofs + 0] = bitmap.buffer[ofs_color + 0]; + wr[ofs + 0] = bitmap.buffer[ofs_color + 2]; wr[ofs + 1] = bitmap.buffer[ofs_color + 1]; - wr[ofs + 2] = bitmap.buffer[ofs_color + 2]; + wr[ofs + 2] = bitmap.buffer[ofs_color + 0]; wr[ofs + 3] = 255; } else { - wr[ofs + 0] = bitmap.buffer[ofs_color + 2]; + wr[ofs + 0] = bitmap.buffer[ofs_color + 0]; wr[ofs + 1] = bitmap.buffer[ofs_color + 1]; - wr[ofs + 2] = bitmap.buffer[ofs_color + 0]; + wr[ofs + 2] = bitmap.buffer[ofs_color + 2]; wr[ofs + 3] = 255; } } break; diff --git a/modules/text_server_fb/gdextension_build/methods.py b/modules/text_server_fb/gdextension_build/methods.py index 3c5229462c..e58bc3abec 100644 --- a/modules/text_server_fb/gdextension_build/methods.py +++ b/modules/text_server_fb/gdextension_build/methods.py @@ -99,8 +99,8 @@ def make_icu_data(target, source, env): def write_macos_plist(target, binary_name, identifier, name): - os.makedirs(f"{target}/Resourece/", exist_ok=True) - f = open(f"{target}/Resourece/Info.plist", "w") + os.makedirs(f"{target}/Resource/", exist_ok=True) + f = open(f"{target}/Resource/Info.plist", "w") f.write(f'<?xml version="1.0" encoding="UTF-8"?>\n') f.write(f'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n') diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index eb247cdcbe..f12275d10c 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -548,14 +548,14 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitma case FT_PIXEL_MODE_LCD: { int ofs_color = i * bitmap.pitch + (j * 3); if (p_bgra) { - wr[ofs + 0] = bitmap.buffer[ofs_color + 0]; + wr[ofs + 0] = bitmap.buffer[ofs_color + 2]; wr[ofs + 1] = bitmap.buffer[ofs_color + 1]; - wr[ofs + 2] = bitmap.buffer[ofs_color + 2]; + wr[ofs + 2] = bitmap.buffer[ofs_color + 0]; wr[ofs + 3] = 255; } else { - wr[ofs + 0] = bitmap.buffer[ofs_color + 2]; + wr[ofs + 0] = bitmap.buffer[ofs_color + 0]; wr[ofs + 1] = bitmap.buffer[ofs_color + 1]; - wr[ofs + 2] = bitmap.buffer[ofs_color + 0]; + wr[ofs + 2] = bitmap.buffer[ofs_color + 2]; wr[ofs + 3] = 255; } } break; diff --git a/modules/tinyexr/image_loader_tinyexr.cpp b/modules/tinyexr/image_loader_tinyexr.cpp index f64bb14e4a..8720ca56f6 100644 --- a/modules/tinyexr/image_loader_tinyexr.cpp +++ b/modules/tinyexr/image_loader_tinyexr.cpp @@ -68,6 +68,7 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitF if (ret != TINYEXR_SUCCESS) { if (err) { ERR_PRINT(String(err)); + FreeEXRErrorMessage(err); } return ERR_FILE_CORRUPT; } @@ -86,6 +87,7 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitF if (ret != TINYEXR_SUCCESS) { if (err) { ERR_PRINT(String(err)); + FreeEXRErrorMessage(err); } return ERR_FILE_CORRUPT; } diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp index 8a265ffaf3..7ec0b697bf 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp @@ -144,7 +144,7 @@ int AudioStreamPlaybackOggVorbis::_mix_internal(AudioFrame *p_buffer, int p_fram } int AudioStreamPlaybackOggVorbis::_mix_frames_vorbis(AudioFrame *p_buffer, int p_frames) { - ERR_FAIL_COND_V(!ready, 0); + ERR_FAIL_COND_V(!ready, p_frames); if (!have_samples_left) { ogg_packet *packet = nullptr; int err; @@ -156,10 +156,10 @@ int AudioStreamPlaybackOggVorbis::_mix_frames_vorbis(AudioFrame *p_buffer, int p } err = vorbis_synthesis(&block, packet); - ERR_FAIL_COND_V_MSG(err != 0, 0, "Error during vorbis synthesis " + itos(err)); + ERR_FAIL_COND_V_MSG(err != 0, p_frames, "Error during vorbis synthesis " + itos(err)); err = vorbis_synthesis_blockin(&dsp_state, &block); - ERR_FAIL_COND_V_MSG(err != 0, 0, "Error during vorbis block processing " + itos(err)); + ERR_FAIL_COND_V_MSG(err != 0, p_frames, "Error during vorbis block processing " + itos(err)); have_packets_left = !packet->e_o_s; } diff --git a/modules/vorbis/doc_classes/AudioStreamOggVorbis.xml b/modules/vorbis/doc_classes/AudioStreamOggVorbis.xml index 7e3af6688a..d2f6745e2f 100644 --- a/modules/vorbis/doc_classes/AudioStreamOggVorbis.xml +++ b/modules/vorbis/doc_classes/AudioStreamOggVorbis.xml @@ -7,6 +7,7 @@ The AudioStreamOggVorbis class is a specialized [AudioStream] for handling Ogg Vorbis file formats. It offers functionality for loading and playing back Ogg Vorbis files, as well as managing looping and other playback properties. This class is part of the audio stream system, which also supports WAV files through the [AudioStreamWAV] class. </description> <tutorials> + <link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link> </tutorials> <methods> <method name="load_from_buffer" qualifiers="static"> diff --git a/modules/webp/webp_common.cpp b/modules/webp/webp_common.cpp index bc34a25733..3a2ac5a90e 100644 --- a/modules/webp/webp_common.cpp +++ b/modules/webp/webp_common.cpp @@ -59,6 +59,10 @@ Vector<uint8_t> _webp_packer(const Ref<Image> &p_image, float p_quality, bool p_ compression_method = CLAMP(compression_method, 0, 6); Ref<Image> img = p_image->duplicate(); + if (img->is_compressed()) { + Error error = img->decompress(); + ERR_FAIL_COND_V_MSG(error != OK, Vector<uint8_t>(), "Couldn't decompress image."); + } if (img->detect_alpha()) { img->convert(Image::FORMAT_RGBA8); } else { diff --git a/modules/zip/zip_packer.cpp b/modules/zip/zip_packer.cpp index 5f623476fc..e67c65d4d1 100644 --- a/modules/zip/zip_packer.cpp +++ b/modules/zip/zip_packer.cpp @@ -72,7 +72,24 @@ Error ZIPPacker::start_file(const String &p_path) { zipfi.internal_fa = 0; zipfi.external_fa = 0; - int err = zipOpenNewFileInZip(zf, p_path.utf8().get_data(), &zipfi, nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, Z_DEFAULT_COMPRESSION); + int err = zipOpenNewFileInZip4(zf, + p_path.utf8().get_data(), + &zipfi, + nullptr, + 0, + nullptr, + 0, + nullptr, + Z_DEFLATED, + Z_DEFAULT_COMPRESSION, + 0, + -MAX_WBITS, + DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, + nullptr, + 0, + 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions. + 1 << 11); // Bit 11 is the language encoding flag. When set, filename and comment fields must be encoded using UTF-8. return err == ZIP_OK ? OK : FAILED; } |
