summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/extension/gdextension.cpp8
-rw-r--r--core/extension/gdextension_manager.cpp93
-rw-r--r--core/extension/gdextension_manager.h3
-rw-r--r--core/io/json.cpp748
-rw-r--r--core/io/json.h3
-rw-r--r--core/object/class_db.cpp16
-rw-r--r--core/object/class_db.h1
-rw-r--r--core/object/object.cpp8
-rw-r--r--core/object/object.h1
-rw-r--r--doc/classes/EditorExportPlatform.xml206
-rw-r--r--doc/classes/EditorExportPlatformExtension.xml282
-rw-r--r--doc/classes/EditorExportPlugin.xml12
-rw-r--r--doc/classes/EditorExportPreset.xml186
-rw-r--r--doc/classes/EditorPlugin.xml14
-rw-r--r--doc/classes/GDExtensionManager.xml14
-rw-r--r--doc/classes/JSON.xml20
-rw-r--r--doc/classes/ScriptEditor.xml6
-rw-r--r--drivers/windows/dir_access_windows.cpp4
-rw-r--r--editor/animation_track_editor.cpp132
-rw-r--r--editor/animation_track_editor.h4
-rw-r--r--editor/editor_file_system.cpp69
-rw-r--r--editor/editor_file_system.h2
-rw-r--r--editor/editor_node.cpp15
-rw-r--r--editor/editor_run_native.cpp10
-rw-r--r--editor/export/editor_export.cpp18
-rw-r--r--editor/export/editor_export.h2
-rw-r--r--editor/export/editor_export_platform.cpp264
-rw-r--r--editor/export/editor_export_platform.h105
-rw-r--r--editor/export/editor_export_platform_extension.cpp317
-rw-r--r--editor/export/editor_export_platform_extension.h149
-rw-r--r--editor/export/editor_export_platform_pc.cpp6
-rw-r--r--editor/export/editor_export_platform_pc.h8
-rw-r--r--editor/export/editor_export_plugin.cpp11
-rw-r--r--editor/export/editor_export_plugin.h1
-rw-r--r--editor/export/editor_export_preset.cpp42
-rw-r--r--editor/export/editor_export_preset.h7
-rw-r--r--editor/import/3d/resource_importer_scene.cpp10
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp18
-rw-r--r--editor/plugins/editor_plugin.cpp13
-rw-r--r--editor/plugins/editor_plugin.h4
-rw-r--r--editor/plugins/script_editor_plugin.cpp71
-rw-r--r--editor/plugins/script_editor_plugin.h1
-rw-r--r--editor/register_editor_types.cpp3
-rw-r--r--modules/gdscript/gdscript.cpp53
-rw-r--r--modules/gdscript/gdscript.h7
-rw-r--r--modules/gltf/gltf_document.cpp39
-rw-r--r--modules/gltf/tests/test_gltf_extras.h165
-rw-r--r--modules/minimp3/audio_stream_mp3.cpp14
-rw-r--r--modules/minimp3/audio_stream_mp3.h2
-rw-r--r--platform/android/export/export_plugin.cpp24
-rw-r--r--platform/android/export/export_plugin.h8
-rw-r--r--platform/ios/export/export_plugin.cpp16
-rw-r--r--platform/ios/export/export_plugin.h6
-rw-r--r--platform/linuxbsd/export/export_plugin.cpp9
-rw-r--r--platform/linuxbsd/export/export_plugin.h4
-rw-r--r--platform/macos/export/export_plugin.cpp9
-rw-r--r--platform/macos/export/export_plugin.h4
-rw-r--r--platform/web/export/export_plugin.cpp9
-rw-r--r--platform/web/export/export_plugin.h6
-rw-r--r--platform/windows/export/export_plugin.cpp11
-rw-r--r--platform/windows/export/export_plugin.h6
-rw-r--r--scene/resources/audio_stream_wav.cpp23
-rw-r--r--scene/resources/audio_stream_wav.h2
-rw-r--r--tests/core/io/test_json_native.h160
-rw-r--r--tests/core/object/test_object.h25
-rw-r--r--tests/test_main.cpp1
67 files changed, 3116 insertions, 396 deletions
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp
index c9e609cddc..e764b9c112 100644
--- a/core/extension/gdextension.cpp
+++ b/core/extension/gdextension.cpp
@@ -678,12 +678,10 @@ Error GDExtension::open_library(const String &p_path, const Ref<GDExtensionLoade
ERR_FAIL_NULL_V_MSG(p_loader, FAILED, "Can't open GDExtension without a loader.");
loader = p_loader;
- String abs_path = ProjectSettings::get_singleton()->globalize_path(p_path);
+ Error err = loader->open_library(p_path);
- Error err = loader->open_library(abs_path);
-
- ERR_FAIL_COND_V_MSG(err == ERR_FILE_NOT_FOUND, err, "GDExtension dynamic library not found: " + abs_path);
- ERR_FAIL_COND_V_MSG(err != OK, err, "Can't open GDExtension dynamic library: " + abs_path);
+ ERR_FAIL_COND_V_MSG(err == ERR_FILE_NOT_FOUND, err, "GDExtension dynamic library not found: " + p_path);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Can't open GDExtension dynamic library: " + p_path);
err = loader->initialize(&gdextension_get_proc_address, this, &initialization);
diff --git a/core/extension/gdextension_manager.cpp b/core/extension/gdextension_manager.cpp
index eeae6b1996..01efe0d96e 100644
--- a/core/extension/gdextension_manager.cpp
+++ b/core/extension/gdextension_manager.cpp
@@ -32,14 +32,18 @@
#include "core/extension/gdextension_compat_hashes.h"
#include "core/extension/gdextension_library_loader.h"
+#include "core/io/dir_access.h"
#include "core/io/file_access.h"
#include "core/object/script_language.h"
-GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(const Ref<GDExtension> &p_extension) {
+GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(const Ref<GDExtension> &p_extension, bool p_first_load) {
if (level >= 0) { // Already initialized up to some level.
- int32_t minimum_level = p_extension->get_minimum_library_initialization_level();
- if (minimum_level < MIN(level, GDExtension::INITIALIZATION_LEVEL_SCENE)) {
- return LOAD_STATUS_NEEDS_RESTART;
+ int32_t minimum_level = 0;
+ if (!p_first_load) {
+ minimum_level = p_extension->get_minimum_library_initialization_level();
+ if (minimum_level < MIN(level, GDExtension::INITIALIZATION_LEVEL_SCENE)) {
+ return LOAD_STATUS_NEEDS_RESTART;
+ }
}
// Initialize up to current level.
for (int32_t i = minimum_level; i <= level; i++) {
@@ -51,10 +55,20 @@ GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(cons
gdextension_class_icon_paths[kv.key] = kv.value;
}
+#ifdef TOOLS_ENABLED
+ // Signals that a new extension is loaded so GDScript can register new class names.
+ emit_signal("extension_loaded", p_extension);
+#endif
+
return LOAD_STATUS_OK;
}
GDExtensionManager::LoadStatus GDExtensionManager::_unload_extension_internal(const Ref<GDExtension> &p_extension) {
+#ifdef TOOLS_ENABLED
+ // Signals that a new extension is unloading so GDScript can unregister class names.
+ emit_signal("extension_unloading", p_extension);
+#endif
+
if (level >= 0) { // Already initialized up to some level.
// Deinitialize down from current level.
for (int32_t i = level; i >= GDExtension::INITIALIZATION_LEVEL_CORE; i--) {
@@ -89,7 +103,7 @@ GDExtensionManager::LoadStatus GDExtensionManager::load_extension_with_loader(co
return LOAD_STATUS_FAILED;
}
- LoadStatus status = _load_extension_internal(extension);
+ LoadStatus status = _load_extension_internal(extension, true);
if (status != LOAD_STATUS_OK) {
return status;
}
@@ -135,7 +149,7 @@ GDExtensionManager::LoadStatus GDExtensionManager::reload_extension(const String
return LOAD_STATUS_FAILED;
}
- status = _load_extension_internal(extension);
+ status = _load_extension_internal(extension, false);
if (status != LOAD_STATUS_OK) {
return status;
}
@@ -274,6 +288,71 @@ void GDExtensionManager::reload_extensions() {
#endif
}
+bool GDExtensionManager::ensure_extensions_loaded(const HashSet<String> &p_extensions) {
+ Vector<String> extensions_added;
+ Vector<String> extensions_removed;
+
+ for (const String &E : p_extensions) {
+ if (!is_extension_loaded(E)) {
+ extensions_added.push_back(E);
+ }
+ }
+
+ Vector<String> loaded_extensions = get_loaded_extensions();
+ for (const String &loaded_extension : loaded_extensions) {
+ if (!p_extensions.has(loaded_extension)) {
+ // The extension may not have a .gdextension file.
+ if (!FileAccess::exists(loaded_extension)) {
+ extensions_removed.push_back(loaded_extension);
+ }
+ }
+ }
+
+ String extension_list_config_file = GDExtension::get_extension_list_config_file();
+ if (p_extensions.size()) {
+ if (extensions_added.size() || extensions_removed.size()) {
+ // Extensions were added or removed.
+ Ref<FileAccess> f = FileAccess::open(extension_list_config_file, FileAccess::WRITE);
+ for (const String &E : p_extensions) {
+ f->store_line(E);
+ }
+ }
+ } else {
+ if (loaded_extensions.size() || FileAccess::exists(extension_list_config_file)) {
+ // Extensions were removed.
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ da->remove(extension_list_config_file);
+ }
+ }
+
+ bool needs_restart = false;
+ for (const String &extension : extensions_added) {
+ GDExtensionManager::LoadStatus st = GDExtensionManager::get_singleton()->load_extension(extension);
+ if (st == GDExtensionManager::LOAD_STATUS_NEEDS_RESTART) {
+ needs_restart = true;
+ }
+ }
+
+ for (const String &extension : extensions_removed) {
+ GDExtensionManager::LoadStatus st = GDExtensionManager::get_singleton()->unload_extension(extension);
+ if (st == GDExtensionManager::LOAD_STATUS_NEEDS_RESTART) {
+ needs_restart = true;
+ }
+ }
+
+#ifdef TOOLS_ENABLED
+ if (extensions_added.size() || extensions_removed.size()) {
+ // Emitting extensions_reloaded so EditorNode can reload Inspector and regenerate documentation.
+ emit_signal("extensions_reloaded");
+
+ // Reload all scripts to clear out old references.
+ callable_mp_static(&GDExtensionManager::_reload_all_scripts).call_deferred();
+ }
+#endif
+
+ return needs_restart;
+}
+
GDExtensionManager *GDExtensionManager::get_singleton() {
return singleton;
}
@@ -294,6 +373,8 @@ void GDExtensionManager::_bind_methods() {
BIND_ENUM_CONSTANT(LOAD_STATUS_NEEDS_RESTART);
ADD_SIGNAL(MethodInfo("extensions_reloaded"));
+ ADD_SIGNAL(MethodInfo("extension_loaded", PropertyInfo(Variant::OBJECT, "extension", PROPERTY_HINT_RESOURCE_TYPE, "GDExtension")));
+ ADD_SIGNAL(MethodInfo("extension_unloading", PropertyInfo(Variant::OBJECT, "extension", PROPERTY_HINT_RESOURCE_TYPE, "GDExtension")));
}
GDExtensionManager *GDExtensionManager::singleton = nullptr;
diff --git a/core/extension/gdextension_manager.h b/core/extension/gdextension_manager.h
index b488189604..39a600474c 100644
--- a/core/extension/gdextension_manager.h
+++ b/core/extension/gdextension_manager.h
@@ -54,7 +54,7 @@ public:
};
private:
- LoadStatus _load_extension_internal(const Ref<GDExtension> &p_extension);
+ LoadStatus _load_extension_internal(const Ref<GDExtension> &p_extension, bool p_first_load);
LoadStatus _unload_extension_internal(const Ref<GDExtension> &p_extension);
#ifdef TOOLS_ENABLED
@@ -85,6 +85,7 @@ public:
void load_extensions();
void reload_extensions();
+ bool ensure_extensions_loaded(const HashSet<String> &p_extensions);
GDExtensionManager();
~GDExtensionManager();
diff --git a/core/io/json.cpp b/core/io/json.cpp
index 61051727c1..664ff7857b 100644
--- a/core/io/json.cpp
+++ b/core/io/json.cpp
@@ -588,10 +588,756 @@ void JSON::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_error_line"), &JSON::get_error_line);
ClassDB::bind_method(D_METHOD("get_error_message"), &JSON::get_error_message);
+ ClassDB::bind_static_method("JSON", D_METHOD("to_native", "json", "allow_classes", "allow_scripts"), &JSON::to_native, DEFVAL(false), DEFVAL(false));
+ ClassDB::bind_static_method("JSON", D_METHOD("from_native", "variant", "allow_classes", "allow_scripts"), &JSON::from_native, DEFVAL(false), DEFVAL(false));
+
ADD_PROPERTY(PropertyInfo(Variant::NIL, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), "set_data", "get_data"); // Ensures that it can be serialized as binary.
}
-////
+#define GDTYPE "__gdtype"
+#define VALUES "values"
+#define PASS_ARG p_allow_classes, p_allow_scripts
+
+Variant JSON::from_native(const Variant &p_variant, bool p_allow_classes, bool p_allow_scripts) {
+ switch (p_variant.get_type()) {
+ case Variant::NIL: {
+ Dictionary nil;
+ nil[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return nil;
+ } break;
+ case Variant::BOOL: {
+ return p_variant;
+ } break;
+ case Variant::INT: {
+ return p_variant;
+ } break;
+ case Variant::FLOAT: {
+ return p_variant;
+ } break;
+ case Variant::STRING: {
+ return p_variant;
+ } break;
+ case Variant::VECTOR2: {
+ Dictionary d;
+ Vector2 v = p_variant;
+ Array values;
+ values.push_back(v.x);
+ values.push_back(v.y);
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::VECTOR2I: {
+ Dictionary d;
+ Vector2i v = p_variant;
+ Array values;
+ values.push_back(v.x);
+ values.push_back(v.y);
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::RECT2: {
+ Dictionary d;
+ Rect2 r = p_variant;
+ d["position"] = from_native(r.position);
+ d["size"] = from_native(r.size);
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::RECT2I: {
+ Dictionary d;
+ Rect2i r = p_variant;
+ d["position"] = from_native(r.position);
+ d["size"] = from_native(r.size);
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::VECTOR3: {
+ Dictionary d;
+ Vector3 v = p_variant;
+ Array values;
+ values.push_back(v.x);
+ values.push_back(v.y);
+ values.push_back(v.z);
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::VECTOR3I: {
+ Dictionary d;
+ Vector3i v = p_variant;
+ Array values;
+ values.push_back(v.x);
+ values.push_back(v.y);
+ values.push_back(v.z);
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::TRANSFORM2D: {
+ Dictionary d;
+ Transform2D t = p_variant;
+ d["x"] = from_native(t[0]);
+ d["y"] = from_native(t[1]);
+ d["origin"] = from_native(t[2]);
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::VECTOR4: {
+ Dictionary d;
+ Vector4 v = p_variant;
+ Array values;
+ values.push_back(v.x);
+ values.push_back(v.y);
+ values.push_back(v.z);
+ values.push_back(v.w);
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::VECTOR4I: {
+ Dictionary d;
+ Vector4i v = p_variant;
+ Array values;
+ values.push_back(v.x);
+ values.push_back(v.y);
+ values.push_back(v.z);
+ values.push_back(v.w);
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::PLANE: {
+ Dictionary d;
+ Plane p = p_variant;
+ d["normal"] = from_native(p.normal);
+ d["d"] = p.d;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::QUATERNION: {
+ Dictionary d;
+ Quaternion q = p_variant;
+ Array values;
+ values.push_back(q.x);
+ values.push_back(q.y);
+ values.push_back(q.z);
+ values.push_back(q.w);
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::AABB: {
+ Dictionary d;
+ AABB aabb = p_variant;
+ d["position"] = from_native(aabb.position);
+ d["size"] = from_native(aabb.size);
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::BASIS: {
+ Dictionary d;
+ Basis t = p_variant;
+ d["x"] = from_native(t.get_column(0));
+ d["y"] = from_native(t.get_column(1));
+ d["z"] = from_native(t.get_column(2));
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::TRANSFORM3D: {
+ Dictionary d;
+ Transform3D t = p_variant;
+ d["basis"] = from_native(t.basis);
+ d["origin"] = from_native(t.origin);
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::PROJECTION: {
+ Dictionary d;
+ Projection t = p_variant;
+ d["x"] = from_native(t[0]);
+ d["y"] = from_native(t[1]);
+ d["z"] = from_native(t[2]);
+ d["w"] = from_native(t[3]);
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::COLOR: {
+ Dictionary d;
+ Color c = p_variant;
+ Array values;
+ values.push_back(c.r);
+ values.push_back(c.g);
+ values.push_back(c.b);
+ values.push_back(c.a);
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::STRING_NAME: {
+ Dictionary d;
+ d["name"] = String(p_variant);
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::NODE_PATH: {
+ Dictionary d;
+ d["path"] = String(p_variant);
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::RID: {
+ Dictionary d;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::OBJECT: {
+ Object *obj = p_variant.get_validated_object();
+
+ if (p_allow_classes && obj) {
+ Dictionary d;
+ List<PropertyInfo> property_list;
+ obj->get_property_list(&property_list);
+
+ d["type"] = obj->get_class();
+ Dictionary p;
+ for (const PropertyInfo &P : property_list) {
+ if (P.usage & PROPERTY_USAGE_STORAGE) {
+ if (P.name == "script" && !p_allow_scripts) {
+ continue;
+ }
+ p[P.name] = from_native(obj->get(P.name), PASS_ARG);
+ }
+ }
+ d["properties"] = p;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } else {
+ Dictionary nil;
+ nil[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return nil;
+ }
+ } break;
+ case Variant::CALLABLE:
+ case Variant::SIGNAL: {
+ Dictionary nil;
+ nil[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return nil;
+ } break;
+ case Variant::DICTIONARY: {
+ Dictionary d = p_variant;
+ List<Variant> keys;
+ d.get_key_list(&keys);
+ bool all_strings = true;
+ for (const Variant &K : keys) {
+ if (K.get_type() != Variant::STRING) {
+ all_strings = false;
+ break;
+ }
+ }
+
+ if (all_strings) {
+ Dictionary ret_dict;
+ for (const Variant &K : keys) {
+ ret_dict[K] = from_native(d[K], PASS_ARG);
+ }
+ return ret_dict;
+ } else {
+ Dictionary ret;
+ Array pairs;
+ for (const Variant &K : keys) {
+ Dictionary pair;
+ pair["key"] = from_native(K, PASS_ARG);
+ pair["value"] = from_native(d[K], PASS_ARG);
+ pairs.push_back(pair);
+ }
+ ret["pairs"] = pairs;
+ ret[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return ret;
+ }
+ } break;
+ case Variant::ARRAY: {
+ Array arr = p_variant;
+ Array ret;
+ for (int i = 0; i < arr.size(); i++) {
+ ret.push_back(from_native(arr[i], PASS_ARG));
+ }
+ return ret;
+ } break;
+ case Variant::PACKED_BYTE_ARRAY: {
+ Dictionary d;
+ PackedByteArray arr = p_variant;
+ Array values;
+ for (int i = 0; i < arr.size(); i++) {
+ values.push_back(arr[i]);
+ }
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::PACKED_INT32_ARRAY: {
+ Dictionary d;
+ PackedInt32Array arr = p_variant;
+ Array values;
+ for (int i = 0; i < arr.size(); i++) {
+ values.push_back(arr[i]);
+ }
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+
+ } break;
+ case Variant::PACKED_INT64_ARRAY: {
+ Dictionary d;
+ PackedInt64Array arr = p_variant;
+ Array values;
+ for (int i = 0; i < arr.size(); i++) {
+ values.push_back(arr[i]);
+ }
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::PACKED_FLOAT32_ARRAY: {
+ Dictionary d;
+ PackedFloat32Array arr = p_variant;
+ Array values;
+ for (int i = 0; i < arr.size(); i++) {
+ values.push_back(arr[i]);
+ }
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::PACKED_FLOAT64_ARRAY: {
+ Dictionary d;
+ PackedFloat64Array arr = p_variant;
+ Array values;
+ for (int i = 0; i < arr.size(); i++) {
+ values.push_back(arr[i]);
+ }
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::PACKED_STRING_ARRAY: {
+ Dictionary d;
+ PackedStringArray arr = p_variant;
+ Array values;
+ for (int i = 0; i < arr.size(); i++) {
+ values.push_back(arr[i]);
+ }
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::PACKED_VECTOR2_ARRAY: {
+ Dictionary d;
+ PackedVector2Array arr = p_variant;
+ Array values;
+ for (int i = 0; i < arr.size(); i++) {
+ Vector2 v = arr[i];
+ values.push_back(v.x);
+ values.push_back(v.y);
+ }
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::PACKED_VECTOR3_ARRAY: {
+ Dictionary d;
+ PackedVector3Array arr = p_variant;
+ Array values;
+ for (int i = 0; i < arr.size(); i++) {
+ Vector3 v = arr[i];
+ values.push_back(v.x);
+ values.push_back(v.y);
+ values.push_back(v.z);
+ }
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::PACKED_COLOR_ARRAY: {
+ Dictionary d;
+ PackedColorArray arr = p_variant;
+ Array values;
+ for (int i = 0; i < arr.size(); i++) {
+ Color v = arr[i];
+ values.push_back(v.r);
+ values.push_back(v.g);
+ values.push_back(v.b);
+ values.push_back(v.a);
+ }
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ case Variant::PACKED_VECTOR4_ARRAY: {
+ Dictionary d;
+ PackedVector4Array arr = p_variant;
+ Array values;
+ for (int i = 0; i < arr.size(); i++) {
+ Vector4 v = arr[i];
+ values.push_back(v.x);
+ values.push_back(v.y);
+ values.push_back(v.z);
+ values.push_back(v.w);
+ }
+ d[VALUES] = values;
+ d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return d;
+ } break;
+ default: {
+ ERR_PRINT(vformat("Unhandled conversion from native Variant type '%s' to JSON.", Variant::get_type_name(p_variant.get_type())));
+ } break;
+ }
+
+ Dictionary nil;
+ nil[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+ return nil;
+}
+
+Variant JSON::to_native(const Variant &p_json, bool p_allow_classes, bool p_allow_scripts) {
+ switch (p_json.get_type()) {
+ case Variant::BOOL: {
+ return p_json;
+ } break;
+ case Variant::INT: {
+ return p_json;
+ } break;
+ case Variant::FLOAT: {
+ return p_json;
+ } break;
+ case Variant::STRING: {
+ return p_json;
+ } break;
+ case Variant::STRING_NAME: {
+ return p_json;
+ } break;
+ case Variant::CALLABLE: {
+ return p_json;
+ } break;
+ case Variant::DICTIONARY: {
+ Dictionary d = p_json;
+ if (d.has(GDTYPE)) {
+ // Specific Godot Variant types serialized to JSON.
+ String type = d[GDTYPE];
+ if (type == Variant::get_type_name(Variant::VECTOR2)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ ERR_FAIL_COND_V(values.size() != 2, Variant());
+ Vector2 v;
+ v.x = values[0];
+ v.y = values[1];
+ return v;
+ } else if (type == Variant::get_type_name(Variant::VECTOR2I)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ ERR_FAIL_COND_V(values.size() != 2, Variant());
+ Vector2i v;
+ v.x = values[0];
+ v.y = values[1];
+ return v;
+ } else if (type == Variant::get_type_name(Variant::RECT2)) {
+ ERR_FAIL_COND_V(!d.has("position"), Variant());
+ ERR_FAIL_COND_V(!d.has("size"), Variant());
+ Rect2 r;
+ r.position = to_native(d["position"]);
+ r.size = to_native(d["size"]);
+ return r;
+ } else if (type == Variant::get_type_name(Variant::RECT2I)) {
+ ERR_FAIL_COND_V(!d.has("position"), Variant());
+ ERR_FAIL_COND_V(!d.has("size"), Variant());
+ Rect2i r;
+ r.position = to_native(d["position"]);
+ r.size = to_native(d["size"]);
+ return r;
+ } else if (type == Variant::get_type_name(Variant::VECTOR3)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ ERR_FAIL_COND_V(values.size() != 3, Variant());
+ Vector3 v;
+ v.x = values[0];
+ v.y = values[1];
+ v.z = values[2];
+ return v;
+ } else if (type == Variant::get_type_name(Variant::VECTOR3I)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ ERR_FAIL_COND_V(values.size() != 3, Variant());
+ Vector3i v;
+ v.x = values[0];
+ v.y = values[1];
+ v.z = values[2];
+ return v;
+ } else if (type == Variant::get_type_name(Variant::TRANSFORM2D)) {
+ ERR_FAIL_COND_V(!d.has("x"), Variant());
+ ERR_FAIL_COND_V(!d.has("y"), Variant());
+ ERR_FAIL_COND_V(!d.has("origin"), Variant());
+ Transform2D t;
+ t[0] = to_native(d["x"]);
+ t[1] = to_native(d["y"]);
+ t[2] = to_native(d["origin"]);
+ return t;
+ } else if (type == Variant::get_type_name(Variant::VECTOR4)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ ERR_FAIL_COND_V(values.size() != 4, Variant());
+ Vector4 v;
+ v.x = values[0];
+ v.y = values[1];
+ v.z = values[2];
+ v.w = values[3];
+ return v;
+ } else if (type == Variant::get_type_name(Variant::VECTOR4I)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ ERR_FAIL_COND_V(values.size() != 4, Variant());
+ Vector4i v;
+ v.x = values[0];
+ v.y = values[1];
+ v.z = values[2];
+ v.w = values[3];
+ return v;
+ } else if (type == Variant::get_type_name(Variant::PLANE)) {
+ ERR_FAIL_COND_V(!d.has("normal"), Variant());
+ ERR_FAIL_COND_V(!d.has("d"), Variant());
+ Plane p;
+ p.normal = to_native(d["normal"]);
+ p.d = d["d"];
+ return p;
+ } else if (type == Variant::get_type_name(Variant::QUATERNION)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ ERR_FAIL_COND_V(values.size() != 4, Variant());
+ Quaternion v;
+ v.x = values[0];
+ v.y = values[1];
+ v.z = values[2];
+ v.w = values[3];
+ return v;
+ } else if (type == Variant::get_type_name(Variant::AABB)) {
+ ERR_FAIL_COND_V(!d.has("position"), Variant());
+ ERR_FAIL_COND_V(!d.has("size"), Variant());
+ AABB r;
+ r.position = to_native(d["position"]);
+ r.size = to_native(d["size"]);
+ return r;
+ } else if (type == Variant::get_type_name(Variant::BASIS)) {
+ ERR_FAIL_COND_V(!d.has("x"), Variant());
+ ERR_FAIL_COND_V(!d.has("y"), Variant());
+ ERR_FAIL_COND_V(!d.has("z"), Variant());
+ Basis b;
+ b.set_column(0, to_native(d["x"]));
+ b.set_column(1, to_native(d["y"]));
+ b.set_column(2, to_native(d["z"]));
+ return b;
+ } else if (type == Variant::get_type_name(Variant::TRANSFORM3D)) {
+ ERR_FAIL_COND_V(!d.has("basis"), Variant());
+ ERR_FAIL_COND_V(!d.has("origin"), Variant());
+ Transform3D t;
+ t.basis = to_native(d["basis"]);
+ t.origin = to_native(d["origin"]);
+ return t;
+ } else if (type == Variant::get_type_name(Variant::PROJECTION)) {
+ ERR_FAIL_COND_V(!d.has("x"), Variant());
+ ERR_FAIL_COND_V(!d.has("y"), Variant());
+ ERR_FAIL_COND_V(!d.has("z"), Variant());
+ ERR_FAIL_COND_V(!d.has("w"), Variant());
+ Projection p;
+ p[0] = to_native(d["x"]);
+ p[1] = to_native(d["y"]);
+ p[2] = to_native(d["z"]);
+ p[3] = to_native(d["w"]);
+ return p;
+ } else if (type == Variant::get_type_name(Variant::COLOR)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ ERR_FAIL_COND_V(values.size() != 4, Variant());
+ Color c;
+ c.r = values[0];
+ c.g = values[1];
+ c.b = values[2];
+ c.a = values[3];
+ return c;
+ } else if (type == Variant::get_type_name(Variant::NODE_PATH)) {
+ ERR_FAIL_COND_V(!d.has("path"), Variant());
+ NodePath np = d["path"];
+ return np;
+ } else if (type == Variant::get_type_name(Variant::STRING_NAME)) {
+ ERR_FAIL_COND_V(!d.has("name"), Variant());
+ StringName s = d["name"];
+ return s;
+ } else if (type == Variant::get_type_name(Variant::OBJECT)) {
+ ERR_FAIL_COND_V(!d.has("type"), Variant());
+ ERR_FAIL_COND_V(!d.has("properties"), Variant());
+
+ ERR_FAIL_COND_V(!p_allow_classes, Variant());
+
+ String obj_type = d["type"];
+ bool is_script = obj_type == "Script" || ClassDB::is_parent_class(obj_type, "Script");
+ ERR_FAIL_COND_V(!p_allow_scripts && is_script, Variant());
+ Object *obj = ClassDB::instantiate(obj_type);
+ ERR_FAIL_NULL_V(obj, Variant());
+
+ Dictionary p = d["properties"];
+
+ List<Variant> keys;
+ p.get_key_list(&keys);
+
+ for (const Variant &K : keys) {
+ String property = K;
+ Variant value = to_native(p[K], PASS_ARG);
+ obj->set(property, value);
+ }
+
+ Variant v(obj);
+
+ return v;
+ } else if (type == Variant::get_type_name(Variant::DICTIONARY)) {
+ ERR_FAIL_COND_V(!d.has("pairs"), Variant());
+ Array pairs = d["pairs"];
+ Dictionary r;
+ for (int i = 0; i < pairs.size(); i++) {
+ Dictionary p = pairs[i];
+ ERR_CONTINUE(!p.has("key"));
+ ERR_CONTINUE(!p.has("value"));
+ r[to_native(p["key"], PASS_ARG)] = to_native(p["value"]);
+ }
+ return r;
+ } else if (type == Variant::get_type_name(Variant::ARRAY)) {
+ ERR_PRINT(vformat("Unexpected Array with '%s' key. Arrays are supported natively.", GDTYPE));
+ } else if (type == Variant::get_type_name(Variant::PACKED_BYTE_ARRAY)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ PackedByteArray pbarr;
+ pbarr.resize(values.size());
+ for (int i = 0; i < pbarr.size(); i++) {
+ pbarr.write[i] = values[i];
+ }
+ return pbarr;
+ } else if (type == Variant::get_type_name(Variant::PACKED_INT32_ARRAY)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ PackedInt32Array arr;
+ arr.resize(values.size());
+ for (int i = 0; i < arr.size(); i++) {
+ arr.write[i] = values[i];
+ }
+ return arr;
+ } else if (type == Variant::get_type_name(Variant::PACKED_INT64_ARRAY)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ PackedInt64Array arr;
+ arr.resize(values.size());
+ for (int i = 0; i < arr.size(); i++) {
+ arr.write[i] = values[i];
+ }
+ return arr;
+ } else if (type == Variant::get_type_name(Variant::PACKED_FLOAT32_ARRAY)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ PackedFloat32Array arr;
+ arr.resize(values.size());
+ for (int i = 0; i < arr.size(); i++) {
+ arr.write[i] = values[i];
+ }
+ return arr;
+ } else if (type == Variant::get_type_name(Variant::PACKED_FLOAT64_ARRAY)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ PackedFloat64Array arr;
+ arr.resize(values.size());
+ for (int i = 0; i < arr.size(); i++) {
+ arr.write[i] = values[i];
+ }
+ return arr;
+ } else if (type == Variant::get_type_name(Variant::PACKED_STRING_ARRAY)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ PackedStringArray arr;
+ arr.resize(values.size());
+ for (int i = 0; i < arr.size(); i++) {
+ arr.write[i] = values[i];
+ }
+ return arr;
+ } else if (type == Variant::get_type_name(Variant::PACKED_VECTOR2_ARRAY)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ ERR_FAIL_COND_V(values.size() % 2 != 0, Variant());
+ PackedVector2Array arr;
+ arr.resize(values.size() / 2);
+ for (int i = 0; i < arr.size(); i++) {
+ arr.write[i] = Vector2(values[i * 2 + 0], values[i * 2 + 1]);
+ }
+ return arr;
+ } else if (type == Variant::get_type_name(Variant::PACKED_VECTOR3_ARRAY)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ ERR_FAIL_COND_V(values.size() % 3 != 0, Variant());
+ PackedVector3Array arr;
+ arr.resize(values.size() / 3);
+ for (int i = 0; i < arr.size(); i++) {
+ arr.write[i] = Vector3(values[i * 3 + 0], values[i * 3 + 1], values[i * 3 + 2]);
+ }
+ return arr;
+ } else if (type == Variant::get_type_name(Variant::PACKED_COLOR_ARRAY)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ ERR_FAIL_COND_V(values.size() % 4 != 0, Variant());
+ PackedColorArray arr;
+ arr.resize(values.size() / 4);
+ for (int i = 0; i < arr.size(); i++) {
+ arr.write[i] = Color(values[i * 4 + 0], values[i * 4 + 1], values[i * 4 + 2], values[i * 4 + 3]);
+ }
+ return arr;
+ } else if (type == Variant::get_type_name(Variant::PACKED_VECTOR4_ARRAY)) {
+ ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+ Array values = d[VALUES];
+ ERR_FAIL_COND_V(values.size() % 4 != 0, Variant());
+ PackedVector4Array arr;
+ arr.resize(values.size() / 4);
+ for (int i = 0; i < arr.size(); i++) {
+ arr.write[i] = Vector4(values[i * 4 + 0], values[i * 4 + 1], values[i * 4 + 2], values[i * 4 + 3]);
+ }
+ return arr;
+ } else {
+ return Variant();
+ }
+ } else {
+ // Regular dictionary with string keys.
+ List<Variant> keys;
+ d.get_key_list(&keys);
+ Dictionary r;
+ for (const Variant &K : keys) {
+ r[K] = to_native(d[K], PASS_ARG);
+ }
+ return r;
+ }
+ } break;
+ case Variant::ARRAY: {
+ Array arr = p_json;
+ Array ret;
+ ret.resize(arr.size());
+ for (int i = 0; i < arr.size(); i++) {
+ ret[i] = to_native(arr[i], PASS_ARG);
+ }
+ return ret;
+ } break;
+ default: {
+ ERR_PRINT(vformat("Unhandled conversion from JSON type '%s' to native Variant type.", Variant::get_type_name(p_json.get_type())));
+ return Variant();
+ }
+ }
+
+ return Variant();
+}
+
+#undef GDTYPE
+#undef VALUES
+#undef PASS_ARG
////////////
diff --git a/core/io/json.h b/core/io/json.h
index 801fa29b4b..67b5e09afa 100644
--- a/core/io/json.h
+++ b/core/io/json.h
@@ -94,6 +94,9 @@ public:
void set_data(const Variant &p_data);
inline int get_error_line() const { return err_line; }
inline String get_error_message() const { return err_str; }
+
+ static Variant from_native(const Variant &p_variant, bool p_allow_classes = false, bool p_allow_scripts = false);
+ static Variant to_native(const Variant &p_json, bool p_allow_classes = false, bool p_allow_scripts = false);
};
class ResourceFormatLoaderJSON : public ResourceFormatLoader {
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index 5c793a676f..c929b29ee9 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -271,6 +271,22 @@ void ClassDB::get_extensions_class_list(List<StringName> *p_classes) {
p_classes->sort_custom<StringName::AlphCompare>();
}
+
+void ClassDB::get_extension_class_list(const Ref<GDExtension> &p_extension, List<StringName> *p_classes) {
+ OBJTYPE_RLOCK;
+
+ for (const KeyValue<StringName, ClassInfo> &E : classes) {
+ if (E.value.api != API_EXTENSION && E.value.api != API_EDITOR_EXTENSION) {
+ continue;
+ }
+ if (!E.value.gdextension || E.value.gdextension->library != p_extension.ptr()) {
+ continue;
+ }
+ p_classes->push_back(E.key);
+ }
+
+ p_classes->sort_custom<StringName::AlphCompare>();
+}
#endif
void ClassDB::get_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes) {
diff --git a/core/object/class_db.h b/core/object/class_db.h
index d6a95b58e2..620092a6c4 100644
--- a/core/object/class_db.h
+++ b/core/object/class_db.h
@@ -285,6 +285,7 @@ public:
static void get_class_list(List<StringName> *p_classes);
#ifdef TOOLS_ENABLED
static void get_extensions_class_list(List<StringName> *p_classes);
+ static void get_extension_class_list(const Ref<GDExtension> &p_extension, List<StringName> *p_classes);
static ObjectGDExtension *get_placeholder_extension(const StringName &p_class);
#endif
static void get_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes);
diff --git a/core/object/object.cpp b/core/object/object.cpp
index 340ba95436..651f4e77a4 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -1023,6 +1023,14 @@ void Object::remove_meta(const StringName &p_name) {
set_meta(p_name, Variant());
}
+void Object::merge_meta_from(const Object *p_src) {
+ List<StringName> meta_keys;
+ p_src->get_meta_list(&meta_keys);
+ for (const StringName &key : meta_keys) {
+ set_meta(key, p_src->get_meta(key));
+ }
+}
+
TypedArray<Dictionary> Object::_get_property_list_bind() const {
List<PropertyInfo> lpi;
get_property_list(&lpi);
diff --git a/core/object/object.h b/core/object/object.h
index a96598d12a..4752e72d67 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -894,6 +894,7 @@ public:
MTVIRTUAL void remove_meta(const StringName &p_name);
MTVIRTUAL Variant get_meta(const StringName &p_name, const Variant &p_default = Variant()) const;
MTVIRTUAL void get_meta_list(List<StringName> *p_list) const;
+ MTVIRTUAL void merge_meta_from(const Object *p_src);
#ifdef TOOLS_ENABLED
void set_edited(bool p_edited);
diff --git a/doc/classes/EditorExportPlatform.xml b/doc/classes/EditorExportPlatform.xml
index 0e5de79b25..d4084e84a0 100644
--- a/doc/classes/EditorExportPlatform.xml
+++ b/doc/classes/EditorExportPlatform.xml
@@ -11,11 +11,217 @@
<link title="Console support in Godot">$DOCS_URL/tutorials/platform/consoles.html</link>
</tutorials>
<methods>
+ <method name="add_message">
+ <return type="void" />
+ <param index="0" name="type" type="int" enum="EditorExportPlatform.ExportMessageType" />
+ <param index="1" name="category" type="String" />
+ <param index="2" name="message" type="String" />
+ <description>
+ Adds a message to the export log that will be displayed when exporting ends.
+ </description>
+ </method>
+ <method name="clear_messages">
+ <return type="void" />
+ <description>
+ Clears the export log.
+ </description>
+ </method>
+ <method name="create_preset">
+ <return type="EditorExportPreset" />
+ <description>
+ Create a new preset for this platform.
+ </description>
+ </method>
+ <method name="export_pack">
+ <return type="int" enum="Error" />
+ <param index="0" name="preset" type="EditorExportPreset" />
+ <param index="1" name="debug" type="bool" />
+ <param index="2" name="path" type="String" />
+ <param index="3" name="flags" type="int" enum="EditorExportPlatform.DebugFlags" is_bitfield="true" default="0" />
+ <description>
+ Creates a PCK archive at [param path] for the specified [param preset].
+ </description>
+ </method>
+ <method name="export_project">
+ <return type="int" enum="Error" />
+ <param index="0" name="preset" type="EditorExportPreset" />
+ <param index="1" name="debug" type="bool" />
+ <param index="2" name="path" type="String" />
+ <param index="3" name="flags" type="int" enum="EditorExportPlatform.DebugFlags" is_bitfield="true" default="0" />
+ <description>
+ Creates a full project at [param path] for the specified [param preset].
+ </description>
+ </method>
+ <method name="export_project_files">
+ <return type="int" enum="Error" />
+ <param index="0" name="preset" type="EditorExportPreset" />
+ <param index="1" name="debug" type="bool" />
+ <param index="2" name="save_cb" type="Callable" />
+ <param index="3" name="shared_cb" type="Callable" default="Callable()" />
+ <description>
+ Exports project files for the specified preset. This method can be used to implement custom export format, other than PCK and ZIP. One of the callbacks is called for each exported file.
+ [param save_cb] is called for all exported files and have the following arguments: [code]file_path: String[/code], [code]file_data: PackedByteArray[/code], [code]file_index: int[/code], [code]file_count: int[/code], [code]encryption_include_filters: PackedStringArray[/code], [code]encryption_exclude_filters: PackedStringArray[/code], [code]encryption_key: PackedByteArray[/code].
+ [param shared_cb] is called for exported native shared/static libraries and have the following arguments: [code]file_path: String[/code], [code]tags: PackedStringArray[/code], [code]target_folder: String[/code].
+ [b]Note:[/b] [code]file_index[/code] and [code]file_count[/code] are intended for progress tracking only and aren't necesserely unique and precise.
+ </description>
+ </method>
+ <method name="export_zip">
+ <return type="int" enum="Error" />
+ <param index="0" name="preset" type="EditorExportPreset" />
+ <param index="1" name="debug" type="bool" />
+ <param index="2" name="path" type="String" />
+ <param index="3" name="flags" type="int" enum="EditorExportPlatform.DebugFlags" is_bitfield="true" default="0" />
+ <description>
+ Create a ZIP archive at [param path] for the specified [param preset].
+ </description>
+ </method>
+ <method name="find_export_template" qualifiers="const">
+ <return type="Dictionary" />
+ <param index="0" name="template_file_name" type="String" />
+ <description>
+ Locates export template for the platform, and returns [Dictionary] with the following keys: [code]path: String[/code] and [code]error: String[/code]. This method is provided for convenience and custom export platforms aren't required to use it or keep export templates stored in the same way official templates are.
+ </description>
+ </method>
+ <method name="gen_export_flags">
+ <return type="PackedStringArray" />
+ <param index="0" name="flags" type="int" enum="EditorExportPlatform.DebugFlags" is_bitfield="true" />
+ <description>
+ Generates array of command line arguments for the default export templates for the debug flags and editor settings.
+ </description>
+ </method>
+ <method name="get_current_presets" qualifiers="const">
+ <return type="Array" />
+ <description>
+ Returns array of [EditorExportPreset]s for this platform.
+ </description>
+ </method>
+ <method name="get_forced_export_files" qualifiers="static">
+ <return type="PackedStringArray" />
+ <description>
+ Returns array of core file names that always should be exported regardless of preset config.
+ </description>
+ </method>
+ <method name="get_message_category" qualifiers="const">
+ <return type="String" />
+ <param index="0" name="index" type="int" />
+ <description>
+ Returns message category, for the message with [param index].
+ </description>
+ </method>
+ <method name="get_message_count" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns number of messages in the export log.
+ </description>
+ </method>
+ <method name="get_message_text" qualifiers="const">
+ <return type="String" />
+ <param index="0" name="index" type="int" />
+ <description>
+ Returns message text, for the message with [param index].
+ </description>
+ </method>
+ <method name="get_message_type" qualifiers="const">
+ <return type="int" enum="EditorExportPlatform.ExportMessageType" />
+ <param index="0" name="index" type="int" />
+ <description>
+ Returns message type, for the message with [param index].
+ </description>
+ </method>
<method name="get_os_name" qualifiers="const">
<return type="String" />
<description>
Returns the name of the export operating system handled by this [EditorExportPlatform] class, as a friendly string. Possible return values are [code]Windows[/code], [code]Linux[/code], [code]macOS[/code], [code]Android[/code], [code]iOS[/code], and [code]Web[/code].
</description>
</method>
+ <method name="get_worst_message_type" qualifiers="const">
+ <return type="int" enum="EditorExportPlatform.ExportMessageType" />
+ <description>
+ Returns most severe message type currently present in the export log.
+ </description>
+ </method>
+ <method name="save_pack">
+ <return type="Dictionary" />
+ <param index="0" name="preset" type="EditorExportPreset" />
+ <param index="1" name="debug" type="bool" />
+ <param index="2" name="path" type="String" />
+ <param index="3" name="embed" type="bool" default="false" />
+ <description>
+ Saves PCK archive and returns [Dictionary] with the following keys: [code]result: Error[/code], [code]so_files: Array[/code] (array of the shared/static objects which contains dictionaries with the following keys: [code]path: String[/code], [code]tags: PackedStringArray[/code], and [code]target_folder: String[/code]).
+ If [param embed] is [code]true[/code], PCK content is appended to the end of [param path] file and return [Dictionary] additionally include following keys: [code]embedded_start: int[/code] (embedded PCK offset) and [code]embedded_size: int[/code] (embedded PCK size).
+ </description>
+ </method>
+ <method name="save_zip">
+ <return type="Dictionary" />
+ <param index="0" name="preset" type="EditorExportPreset" />
+ <param index="1" name="debug" type="bool" />
+ <param index="2" name="path" type="String" />
+ <description>
+ Saves ZIP archive and returns [Dictionary] with the following keys: [code]result: Error[/code], [code]so_files: Array[/code] (array of the shared/static objects which contains dictionaries with the following keys: [code]path: String[/code], [code]tags: PackedStringArray[/code], and [code]target_folder: String[/code]).
+ </description>
+ </method>
+ <method name="ssh_push_to_remote" qualifiers="const">
+ <return type="int" enum="Error" />
+ <param index="0" name="host" type="String" />
+ <param index="1" name="port" type="String" />
+ <param index="2" name="scp_args" type="PackedStringArray" />
+ <param index="3" name="src_file" type="String" />
+ <param index="4" name="dst_file" type="String" />
+ <description>
+ Uploads specified file over SCP protocol to the remote host.
+ </description>
+ </method>
+ <method name="ssh_run_on_remote" qualifiers="const">
+ <return type="int" enum="Error" />
+ <param index="0" name="host" type="String" />
+ <param index="1" name="port" type="String" />
+ <param index="2" name="ssh_arg" type="PackedStringArray" />
+ <param index="3" name="cmd_args" type="String" />
+ <param index="4" name="output" type="Array" default="[]" />
+ <param index="5" name="port_fwd" type="int" default="-1" />
+ <description>
+ Executes specified command on the remote host via SSH protocol and returns command output in the [param output].
+ </description>
+ </method>
+ <method name="ssh_run_on_remote_no_wait" qualifiers="const">
+ <return type="int" />
+ <param index="0" name="host" type="String" />
+ <param index="1" name="port" type="String" />
+ <param index="2" name="ssh_args" type="PackedStringArray" />
+ <param index="3" name="cmd_args" type="String" />
+ <param index="4" name="port_fwd" type="int" default="-1" />
+ <description>
+ Executes specified command on the remote host via SSH protocol and returns process ID (on the remote host) without waiting for command to finish.
+ </description>
+ </method>
</methods>
+ <constants>
+ <constant name="EXPORT_MESSAGE_NONE" value="0" enum="ExportMessageType">
+ Invalid message type used as the default value when no type is specified.
+ </constant>
+ <constant name="EXPORT_MESSAGE_INFO" value="1" enum="ExportMessageType">
+ Message type for informational messages that have no effect on the export.
+ </constant>
+ <constant name="EXPORT_MESSAGE_WARNING" value="2" enum="ExportMessageType">
+ Message type for warning messages that should be addressed but still allow to complete the export.
+ </constant>
+ <constant name="EXPORT_MESSAGE_ERROR" value="3" enum="ExportMessageType">
+ Message type for error messages that must be addressed and fail the export.
+ </constant>
+ <constant name="DEBUG_FLAG_DUMB_CLIENT" value="1" enum="DebugFlags" is_bitfield="true">
+ Flag is set if remotely debugged project is expected to use remote file system. If set, [method gen_export_flags] will add [code]--remove-fs[/code] and [code]--remote-fs-password[/code] (if password is set in the editor settings) command line arguments to the list.
+ </constant>
+ <constant name="DEBUG_FLAG_REMOTE_DEBUG" value="2" enum="DebugFlags" is_bitfield="true">
+ Flag is set if remote debug is enabled. If set, [method gen_export_flags] will add [code]--remote-debug[/code] and [code]--breakpoints[/code] (if breakpoints are selected in the script editor or added by the plugin) command line arguments to the list.
+ </constant>
+ <constant name="DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST" value="4" enum="DebugFlags" is_bitfield="true">
+ Flag is set if remotely debugged project is running on the localhost. If set, [method gen_export_flags] will use [code]localhost[/code] instead of [member EditorSettings.network/debug/remote_host] as remote debugger host.
+ </constant>
+ <constant name="DEBUG_FLAG_VIEW_COLLISIONS" value="8" enum="DebugFlags" is_bitfield="true">
+ Flag is set if "Visible Collision Shapes" remote debug option is enabled. If set, [method gen_export_flags] will add [code]--debug-collisions[/code] command line arguments to the list.
+ </constant>
+ <constant name="DEBUG_FLAG_VIEW_NAVIGATION" value="16" enum="DebugFlags" is_bitfield="true">
+ Flag is set if Visible Navigation" remote debug option is enabled. If set, [method gen_export_flags] will add [code]--debug-navigation[/code] command line arguments to the list.
+ </constant>
+ </constants>
</class>
diff --git a/doc/classes/EditorExportPlatformExtension.xml b/doc/classes/EditorExportPlatformExtension.xml
new file mode 100644
index 0000000000..ef589e2f58
--- /dev/null
+++ b/doc/classes/EditorExportPlatformExtension.xml
@@ -0,0 +1,282 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="EditorExportPlatformExtension" inherits="EditorExportPlatform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Base class for custom [EditorExportPlatform] implementations (plugins).
+ </brief_description>
+ <description>
+ External [EditorExportPlatform] implementations should inherit from this class.
+ To use [EditorExportPlatform], register it using the [method EditorPlugin.add_export_platform] method first.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="_can_export" qualifiers="virtual const">
+ <return type="bool" />
+ <param index="0" name="preset" type="EditorExportPreset" />
+ <param index="1" name="debug" type="bool" />
+ <description>
+ [b]Optional.[/b]
+ Returns [code]true[/code], if specified [param preset] is valid and can be exported. Use [method set_config_error] and [method set_config_missing_templates] to set error details.
+ Usual implementation can call [method _has_valid_export_configuration] and [method _has_valid_project_configuration] to determine if export is possible.
+ </description>
+ </method>
+ <method name="_cleanup" qualifiers="virtual">
+ <return type="void" />
+ <description>
+ [b]Optional.[/b]
+ Called by the editor before platform is unregistered.
+ </description>
+ </method>
+ <method name="_export_pack" qualifiers="virtual">
+ <return type="int" enum="Error" />
+ <param index="0" name="preset" type="EditorExportPreset" />
+ <param index="1" name="debug" type="bool" />
+ <param index="2" name="path" type="String" />
+ <param index="3" name="flags" type="int" enum="EditorExportPlatform.DebugFlags" is_bitfield="true" />
+ <description>
+ [b]Optional.[/b]
+ Creates a PCK archive at [param path] for the specified [param preset].
+ This method is called when "Export PCK/ZIP" button is pressed in the export dialog, and PCK is selected as a file type.
+ </description>
+ </method>
+ <method name="_export_project" qualifiers="virtual">
+ <return type="int" enum="Error" />
+ <param index="0" name="preset" type="EditorExportPreset" />
+ <param index="1" name="debug" type="bool" />
+ <param index="2" name="path" type="String" />
+ <param index="3" name="flags" type="int" enum="EditorExportPlatform.DebugFlags" is_bitfield="true" />
+ <description>
+ [b]Required.[/b]
+ Creates a full project at [param path] for the specified [param preset].
+ This method is called when "Export" button is pressed in the export dialog.
+ This method implementation can call [method EditorExportPlatform.save_pack] or [method EditorExportPlatform.save_zip] to use default PCK/ZIP export process, or calls [method EditorExportPlatform.export_project_files] and implement custom callback for processing each exported file.
+ </description>
+ </method>
+ <method name="_export_zip" qualifiers="virtual">
+ <return type="int" enum="Error" />
+ <param index="0" name="preset" type="EditorExportPreset" />
+ <param index="1" name="debug" type="bool" />
+ <param index="2" name="path" type="String" />
+ <param index="3" name="flags" type="int" enum="EditorExportPlatform.DebugFlags" is_bitfield="true" />
+ <description>
+ [b]Optional.[/b]
+ Create a ZIP archive at [param path] for the specified [param preset].
+ This method is called when "Export PCK/ZIP" button is pressed in the export dialog, and ZIP is selected as a file type.
+ </description>
+ </method>
+ <method name="_get_binary_extensions" qualifiers="virtual const">
+ <return type="PackedStringArray" />
+ <param index="0" name="preset" type="EditorExportPreset" />
+ <description>
+ [b]Required.[/b]
+ Returns array of supported binary extensions for the full project export.
+ </description>
+ </method>
+ <method name="_get_debug_protocol" qualifiers="virtual const">
+ <return type="String" />
+ <description>
+ [b]Optional.[/b]
+ Returns protocol used for remote debugging. Default implementation return [code]tcp://[/code].
+ </description>
+ </method>
+ <method name="_get_device_architecture" qualifiers="virtual const">
+ <return type="String" />
+ <param index="0" name="device" type="int" />
+ <description>
+ [b]Optional.[/b]
+ Returns device architecture for one-click deploy.
+ </description>
+ </method>
+ <method name="_get_export_option_visibility" qualifiers="virtual const">
+ <return type="bool" />
+ <param index="0" name="preset" type="EditorExportPreset" />
+ <param index="1" name="option" type="String" />
+ <description>
+ [b]Optional.[/b]
+ Validates [param option] and returns visibility for the specified [param preset]. Default implementation return [code]true[/code] for all options.
+ </description>
+ </method>
+ <method name="_get_export_option_warning" qualifiers="virtual const">
+ <return type="String" />
+ <param index="0" name="preset" type="EditorExportPreset" />
+ <param index="1" name="option" type="StringName" />
+ <description>
+ [b]Optional.[/b]
+ Validates [param option] and returns warning message for the specified [param preset]. Default implementation return empty string for all options.
+ </description>
+ </method>
+ <method name="_get_export_options" qualifiers="virtual const">
+ <return type="Dictionary[]" />
+ <description>
+ [b]Optional.[/b]
+ Returns a property list, as an [Array] of dictionaries. Each [Dictionary] must at least contain the [code]name: StringName[/code] and [code]type: Variant.Type[/code] entries.
+ Additionally, the following keys are supported:
+ - [code]hint: PropertyHint[/code]
+ - [code]hint_string: String[/code]
+ - [code]usage: PropertyUsageFlags[/code]
+ - [code]class_name: StringName[/code]
+ - [code]default_value: Variant[/code], default value of the property.
+ - [code]update_visibility: bool[/code], if set to [code]true[/code], [method _get_export_option_visibility] is called for each property when this property is changed.
+ - [code]required: bool[/code], if set to [code]true[/code], this property warnings are critical, and should be resolved to make export possible. This value is a hint for the [method _has_valid_export_configuration] implementation, and not used by the engine directly.
+ See also [method Object._get_property_list].
+ </description>
+ </method>
+ <method name="_get_logo" qualifiers="virtual const">
+ <return type="Texture2D" />
+ <description>
+ [b]Required.[/b]
+ Returns platform logo displayed in the export dialog, logo should be 32x32 adjusted to the current editor scale, see [method EditorInterface.get_editor_scale].
+ </description>
+ </method>
+ <method name="_get_name" qualifiers="virtual const">
+ <return type="String" />
+ <description>
+ [b]Required.[/b]
+ Returns export platform name.
+ </description>
+ </method>
+ <method name="_get_option_icon" qualifiers="virtual const">
+ <return type="ImageTexture" />
+ <param index="0" name="device" type="int" />
+ <description>
+ [b]Optional.[/b]
+ Returns one-click deploy menu item icon for the specified [param device], icon should be 16x16 adjusted to the current editor scale, see [method EditorInterface.get_editor_scale].
+ </description>
+ </method>
+ <method name="_get_option_label" qualifiers="virtual const">
+ <return type="String" />
+ <param index="0" name="device" type="int" />
+ <description>
+ [b]Optional.[/b]
+ Returns one-click deploy menu item label for the specified [param device].
+ </description>
+ </method>
+ <method name="_get_option_tooltip" qualifiers="virtual const">
+ <return type="String" />
+ <param index="0" name="device" type="int" />
+ <description>
+ [b]Optional.[/b]
+ Returns one-click deploy menu item tooltip for the specified [param device].
+ </description>
+ </method>
+ <method name="_get_options_count" qualifiers="virtual const">
+ <return type="int" />
+ <description>
+ [b]Optional.[/b]
+ Returns number one-click deploy devices (or other one-click option displayed in the menu).
+ </description>
+ </method>
+ <method name="_get_options_tooltip" qualifiers="virtual const">
+ <return type="String" />
+ <description>
+ [b]Optional.[/b]
+ Returns tooltip of the one-click deploy menu button.
+ </description>
+ </method>
+ <method name="_get_os_name" qualifiers="virtual const">
+ <return type="String" />
+ <description>
+ [b]Required.[/b]
+ Returns target OS name.
+ </description>
+ </method>
+ <method name="_get_platform_features" qualifiers="virtual const">
+ <return type="PackedStringArray" />
+ <description>
+ [b]Required.[/b]
+ Returns array of platform specific features.
+ </description>
+ </method>
+ <method name="_get_preset_features" qualifiers="virtual const">
+ <return type="PackedStringArray" />
+ <param index="0" name="preset" type="EditorExportPreset" />
+ <description>
+ [b]Required.[/b]
+ Returns array of platform specific features for the specified [param preset].
+ </description>
+ </method>
+ <method name="_get_run_icon" qualifiers="virtual const">
+ <return type="Texture2D" />
+ <description>
+ [b]Optional.[/b]
+ Returns icon of the one-click deploy menu button, icon should be 16x16 adjusted to the current editor scale, see [method EditorInterface.get_editor_scale].
+ </description>
+ </method>
+ <method name="_has_valid_export_configuration" qualifiers="virtual const">
+ <return type="bool" />
+ <param index="0" name="preset" type="EditorExportPreset" />
+ <param index="1" name="debug" type="bool" />
+ <description>
+ [b]Required.[/b]
+ Returns [code]true[/code] if export configuration is valid.
+ </description>
+ </method>
+ <method name="_has_valid_project_configuration" qualifiers="virtual const">
+ <return type="bool" />
+ <param index="0" name="preset" type="EditorExportPreset" />
+ <description>
+ [b]Required.[/b]
+ Returns [code]true[/code] if project configuration is valid.
+ </description>
+ </method>
+ <method name="_is_executable" qualifiers="virtual const">
+ <return type="bool" />
+ <param index="0" name="path" type="String" />
+ <description>
+ [b]Optional.[/b]
+ Returns [code]true[/code] if specified file is a valid executable (native executable or script) for the target platform.
+ </description>
+ </method>
+ <method name="_poll_export" qualifiers="virtual">
+ <return type="bool" />
+ <description>
+ [b]Optional.[/b]
+ Returns [code]true[/code] if one-click deploy options are changed and editor interface should be updated.
+ </description>
+ </method>
+ <method name="_run" qualifiers="virtual">
+ <return type="int" enum="Error" />
+ <param index="0" name="preset" type="EditorExportPreset" />
+ <param index="1" name="device" type="int" />
+ <param index="2" name="debug_flags" type="int" enum="EditorExportPlatform.DebugFlags" is_bitfield="true" />
+ <description>
+ [b]Optional.[/b]
+ This method is called when [param device] one-click deploy menu option is selected.
+ Implementation should export project to a temporary location, upload and run it on the specific [param device], or perform another action associated with the menu item.
+ </description>
+ </method>
+ <method name="_should_update_export_options" qualifiers="virtual">
+ <return type="bool" />
+ <description>
+ [b]Optional.[/b]
+ Returns [code]true[/code] if export options list is changed and presets should be updated.
+ </description>
+ </method>
+ <method name="get_config_error" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns current configuration error message text. This method should be called only from the [method _can_export], [method _has_valid_export_configuration], or [method _has_valid_project_configuration] implementations.
+ </description>
+ </method>
+ <method name="get_config_missing_templates" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] is export templates are missing from the current configuration. This method should be called only from the [method _can_export], [method _has_valid_export_configuration], or [method _has_valid_project_configuration] implementations.
+ </description>
+ </method>
+ <method name="set_config_error" qualifiers="const">
+ <return type="void" />
+ <param index="0" name="error_text" type="String" />
+ <description>
+ Sets current configuration error message text. This method should be called only from the [method _can_export], [method _has_valid_export_configuration], or [method _has_valid_project_configuration] implementations.
+ </description>
+ </method>
+ <method name="set_config_missing_templates" qualifiers="const">
+ <return type="void" />
+ <param index="0" name="missing_templates" type="bool" />
+ <description>
+ Set to [code]true[/code] is export templates are missing from the current configuration. This method should be called only from the [method _can_export], [method _has_valid_export_configuration], or [method _has_valid_project_configuration] implementations.
+ </description>
+ </method>
+ </methods>
+</class>
diff --git a/doc/classes/EditorExportPlugin.xml b/doc/classes/EditorExportPlugin.xml
index 9ef911a68d..42e1968eb0 100644
--- a/doc/classes/EditorExportPlugin.xml
+++ b/doc/classes/EditorExportPlugin.xml
@@ -305,6 +305,18 @@
In case of a directory code-sign will error if you place non code object in directory.
</description>
</method>
+ <method name="get_export_platform" qualifiers="const">
+ <return type="EditorExportPlatform" />
+ <description>
+ Returns currently used export platform.
+ </description>
+ </method>
+ <method name="get_export_preset" qualifiers="const">
+ <return type="EditorExportPreset" />
+ <description>
+ Returns currently used export preset.
+ </description>
+ </method>
<method name="get_option" qualifiers="const">
<return type="Variant" />
<param index="0" name="name" type="StringName" />
diff --git a/doc/classes/EditorExportPreset.xml b/doc/classes/EditorExportPreset.xml
new file mode 100644
index 0000000000..bba79364e4
--- /dev/null
+++ b/doc/classes/EditorExportPreset.xml
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="EditorExportPreset" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Export preset configuration.
+ </brief_description>
+ <description>
+ Export preset configuration. Instances of [EditorExportPreset] by editor UI and intended to be used a read-only configuration passed to the [EditorExportPlatform] methods when exporting the project.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="are_advanced_options_enabled" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code], is "Advanced" toggle is enabled in the export dialog.
+ </description>
+ </method>
+ <method name="get_custom_features" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns string with a comma separated list of custom features.
+ </description>
+ </method>
+ <method name="get_customized_files" qualifiers="const">
+ <return type="Dictionary" />
+ <description>
+ Returns [Dictionary] of files selected in the "Resources" tab of the export dialog. Dictionary keys are file names and values are export mode - [code]"strip[/code], [code]"keep"[/code], or [code]"remove"[/code]. See also [method get_file_export_mode].
+ </description>
+ </method>
+ <method name="get_customized_files_count" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns number of files selected in the "Resources" tab of the export dialog.
+ </description>
+ </method>
+ <method name="get_encrypt_directory" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code], PCK directory encryption is enabled in the export dialog.
+ </description>
+ </method>
+ <method name="get_encrypt_pck" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code], PCK encryption is enabled in the export dialog.
+ </description>
+ </method>
+ <method name="get_encryption_ex_filter" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns file filters to exclude during PCK encryption.
+ </description>
+ </method>
+ <method name="get_encryption_in_filter" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns file filters to include during PCK encryption.
+ </description>
+ </method>
+ <method name="get_encryption_key" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns PCK encryption key.
+ </description>
+ </method>
+ <method name="get_exclude_filter" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns file filters to exclude during export.
+ </description>
+ </method>
+ <method name="get_export_filter" qualifiers="const">
+ <return type="int" enum="EditorExportPreset.ExportFilter" />
+ <description>
+ Returns export file filter mode selected in the "Resources" tab of the export dialog.
+ </description>
+ </method>
+ <method name="get_export_path" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns export target path.
+ </description>
+ </method>
+ <method name="get_file_export_mode" qualifiers="const">
+ <return type="int" enum="EditorExportPreset.FileExportMode" />
+ <param index="0" name="path" type="String" />
+ <param index="1" name="default" type="int" enum="EditorExportPreset.FileExportMode" default="0" />
+ <description>
+ Returns file export mode for the specified file.
+ </description>
+ </method>
+ <method name="get_files_to_export" qualifiers="const">
+ <return type="PackedStringArray" />
+ <description>
+ Returns array of files to export.
+ </description>
+ </method>
+ <method name="get_include_filter" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns file filters to include during export.
+ </description>
+ </method>
+ <method name="get_or_env" qualifiers="const">
+ <return type="Variant" />
+ <param index="0" name="name" type="StringName" />
+ <param index="1" name="env_var" type="String" />
+ <description>
+ Returns export option value or value of environment variable if it is set.
+ </description>
+ </method>
+ <method name="get_preset_name" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns export preset name.
+ </description>
+ </method>
+ <method name="get_script_export_mode" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns script export mode.
+ </description>
+ </method>
+ <method name="get_version" qualifiers="const">
+ <return type="String" />
+ <param index="0" name="name" type="StringName" />
+ <param index="1" name="windows_version" type="bool" />
+ <description>
+ Returns the preset's version number, or fall back to the [member ProjectSettings.application/config/version] project setting if set to an empty string.
+ If [param windows_version] is [code]true[/code], formats the returned version number to be compatible with Windows executable metadata.
+ </description>
+ </method>
+ <method name="has" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="property" type="StringName" />
+ <description>
+ Returns [code]true[/code] if preset has specified property.
+ </description>
+ </method>
+ <method name="has_export_file">
+ <return type="bool" />
+ <param index="0" name="path" type="String" />
+ <description>
+ Returns [code]true[/code] if specified file is exported.
+ </description>
+ </method>
+ <method name="is_dedicated_server" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if dedicated server export mode is selected in the export dialog.
+ </description>
+ </method>
+ <method name="is_runnable" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if "Runnable" toggle is enabled in the export dialog.
+ </description>
+ </method>
+ </methods>
+ <constants>
+ <constant name="EXPORT_ALL_RESOURCES" value="0" enum="ExportFilter">
+ </constant>
+ <constant name="EXPORT_SELECTED_SCENES" value="1" enum="ExportFilter">
+ </constant>
+ <constant name="EXPORT_SELECTED_RESOURCES" value="2" enum="ExportFilter">
+ </constant>
+ <constant name="EXCLUDE_SELECTED_RESOURCES" value="3" enum="ExportFilter">
+ </constant>
+ <constant name="EXPORT_CUSTOMIZED" value="4" enum="ExportFilter">
+ </constant>
+ <constant name="MODE_FILE_NOT_CUSTOMIZED" value="0" enum="FileExportMode">
+ </constant>
+ <constant name="MODE_FILE_STRIP" value="1" enum="FileExportMode">
+ </constant>
+ <constant name="MODE_FILE_KEEP" value="2" enum="FileExportMode">
+ </constant>
+ <constant name="MODE_FILE_REMOVE" value="3" enum="FileExportMode">
+ </constant>
+ <constant name="MODE_SCRIPT_TEXT" value="0" enum="ScriptExportMode">
+ </constant>
+ <constant name="MODE_SCRIPT_BINARY_TOKENS" value="1" enum="ScriptExportMode">
+ </constant>
+ <constant name="MODE_SCRIPT_BINARY_TOKENS_COMPRESSED" value="2" enum="ScriptExportMode">
+ </constant>
+ </constants>
+</class>
diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml
index ed3233b1ae..37f8b2213b 100644
--- a/doc/classes/EditorPlugin.xml
+++ b/doc/classes/EditorPlugin.xml
@@ -455,6 +455,13 @@
Adds a [Script] as debugger plugin to the Debugger. The script must extend [EditorDebuggerPlugin].
</description>
</method>
+ <method name="add_export_platform">
+ <return type="void" />
+ <param index="0" name="platform" type="EditorExportPlatform" />
+ <description>
+ Registers a new [EditorExportPlatform]. Export platforms provides functionality of exporting to the specific platform.
+ </description>
+ </method>
<method name="add_export_plugin">
<return type="void" />
<param index="0" name="plugin" type="EditorExportPlugin" />
@@ -653,6 +660,13 @@
Removes the debugger plugin with given script from the Debugger.
</description>
</method>
+ <method name="remove_export_platform">
+ <return type="void" />
+ <param index="0" name="platform" type="EditorExportPlatform" />
+ <description>
+ Removes an export platform registered by [method add_export_platform].
+ </description>
+ </method>
<method name="remove_export_plugin">
<return type="void" />
<param index="0" name="plugin" type="EditorExportPlugin" />
diff --git a/doc/classes/GDExtensionManager.xml b/doc/classes/GDExtensionManager.xml
index 211bc023c0..97d2d08752 100644
--- a/doc/classes/GDExtensionManager.xml
+++ b/doc/classes/GDExtensionManager.xml
@@ -56,6 +56,20 @@
</method>
</methods>
<signals>
+ <signal name="extension_loaded">
+ <param index="0" name="extension" type="GDExtension" />
+ <description>
+ Emitted after the editor has finished loading a new extension.
+ [b]Note:[/b] This signal is only emitted in editor builds.
+ </description>
+ </signal>
+ <signal name="extension_unloading">
+ <param index="0" name="extension" type="GDExtension" />
+ <description>
+ Emitted before the editor starts unloading an extension.
+ [b]Note:[/b] This signal is only emitted in editor builds.
+ </description>
+ </signal>
<signal name="extensions_reloaded">
<description>
Emitted after the editor has finished reloading one or more extensions.
diff --git a/doc/classes/JSON.xml b/doc/classes/JSON.xml
index 8a19aa39bf..fe5fdfa89a 100644
--- a/doc/classes/JSON.xml
+++ b/doc/classes/JSON.xml
@@ -37,6 +37,16 @@
<tutorials>
</tutorials>
<methods>
+ <method name="from_native" qualifiers="static">
+ <return type="Variant" />
+ <param index="0" name="variant" type="Variant" />
+ <param index="1" name="allow_classes" type="bool" default="false" />
+ <param index="2" name="allow_scripts" type="bool" default="false" />
+ <description>
+ Converts a native engine type to a JSON-compliant dictionary.
+ By default, classes and scripts are ignored for security reasons, unless [param allow_classes] or [param allow_scripts] are specified.
+ </description>
+ </method>
<method name="get_error_line" qualifiers="const">
<return type="int" />
<description>
@@ -123,6 +133,16 @@
[/codeblock]
</description>
</method>
+ <method name="to_native" qualifiers="static">
+ <return type="Variant" />
+ <param index="0" name="json" type="Variant" />
+ <param index="1" name="allow_classes" type="bool" default="false" />
+ <param index="2" name="allow_scripts" type="bool" default="false" />
+ <description>
+ Converts a JSON-compliant dictionary that was created with [method from_native] back to native engine types.
+ By default, classes and scripts are ignored for security reasons, unless [param allow_classes] or [param allow_scripts] are specified.
+ </description>
+ </method>
</methods>
<members>
<member name="data" type="Variant" setter="set_data" getter="get_data" default="null">
diff --git a/doc/classes/ScriptEditor.xml b/doc/classes/ScriptEditor.xml
index 50adecccf5..5cf077c266 100644
--- a/doc/classes/ScriptEditor.xml
+++ b/doc/classes/ScriptEditor.xml
@@ -10,6 +10,12 @@
<tutorials>
</tutorials>
<methods>
+ <method name="get_breakpoints">
+ <return type="PackedStringArray" />
+ <description>
+ Returns array of breakpoints.
+ </description>
+ </method>
<method name="get_current_editor" qualifiers="const">
<return type="ScriptEditorBase" />
<description>
diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp
index b8677fe404..f7632842ed 100644
--- a/drivers/windows/dir_access_windows.cpp
+++ b/drivers/windows/dir_access_windows.cpp
@@ -71,7 +71,9 @@ struct DirAccessWindowsPrivate {
String DirAccessWindows::fix_path(const String &p_path) const {
String r_path = DirAccess::fix_path(p_path.trim_prefix(R"(\\?\)").replace("\\", "/"));
-
+ if (r_path.ends_with(":")) {
+ r_path += "/";
+ }
if (r_path.is_relative_path()) {
r_path = current_dir.trim_prefix(R"(\\?\)").replace("\\", "/").path_join(r_path);
} else if (r_path == ".") {
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index f4403780c2..69b130a0f1 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -41,6 +41,7 @@
#include "editor/gui/editor_spin_slider.h"
#include "editor/gui/scene_tree_editor.h"
#include "editor/inspector_dock.h"
+#include "editor/multi_node_edit.h"
#include "editor/plugins/animation_player_editor_plugin.h"
#include "editor/themes/editor_scale.h"
#include "scene/3d/mesh_instance_3d.h"
@@ -3661,7 +3662,7 @@ void AnimationTrackEditor::update_keying() {
EditorSelectionHistory *editor_history = EditorNode::get_singleton()->get_editor_selection_history();
if (is_visible_in_tree() && animation.is_valid() && editor_history->get_path_size() > 0) {
Object *obj = ObjectDB::get_instance(editor_history->get_path_object(0));
- keying_enabled = Object::cast_to<Node>(obj) != nullptr;
+ keying_enabled = Object::cast_to<Node>(obj) != nullptr || Object::cast_to<MultiNodeEdit>(obj) != nullptr;
}
if (keying_enabled == keying) {
@@ -4078,19 +4079,20 @@ void AnimationTrackEditor::_insert_animation_key(NodePath p_path, const Variant
_query_insert(id);
}
-void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_property, const Variant &p_value, bool p_only_if_exists) {
+void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_property, bool p_only_if_exists, bool p_advance) {
ERR_FAIL_NULL(root);
// Let's build a node path.
- Node *node = p_node;
- String path = root->get_path_to(node, true);
+ String path = root->get_path_to(p_node, true);
+
+ Variant value = p_node->get(p_property);
- if (Object::cast_to<AnimationPlayer>(node) && p_property == "current_animation") {
- if (node == AnimationPlayerEditor::get_singleton()->get_player()) {
+ if (Object::cast_to<AnimationPlayer>(p_node) && p_property == "current_animation") {
+ if (p_node == AnimationPlayerEditor::get_singleton()->get_player()) {
EditorNode::get_singleton()->show_warning(TTR("AnimationPlayer can't animate itself, only other players."));
return;
}
- _insert_animation_key(path, p_value);
+ _insert_animation_key(path, value);
return;
}
@@ -4118,26 +4120,26 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p
InsertData id;
id.path = np;
id.track_idx = i;
- id.value = p_value;
+ id.value = value;
id.type = Animation::TYPE_VALUE;
// TRANSLATORS: This describes the target of new animation track, will be inserted into another string.
id.query = vformat(TTR("property '%s'"), p_property);
- id.advance = false;
+ id.advance = p_advance;
// Dialog insert.
_query_insert(id);
inserted = true;
} else if (animation->track_get_type(i) == Animation::TYPE_BEZIER) {
- Variant value;
+ Variant actual_value;
String track_path = animation->track_get_path(i);
if (track_path == np) {
- value = p_value; // All good.
+ actual_value = value; // All good.
} else {
int sep = track_path.rfind(":");
if (sep != -1) {
String base_path = track_path.substr(0, sep);
if (base_path == np) {
String value_name = track_path.substr(sep + 1);
- value = p_value.get(value_name);
+ actual_value = value.get(value_name);
} else {
continue;
}
@@ -4149,10 +4151,10 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p
InsertData id;
id.path = animation->track_get_path(i);
id.track_idx = i;
- id.value = value;
+ id.value = actual_value;
id.type = Animation::TYPE_BEZIER;
id.query = vformat(TTR("property '%s'"), p_property);
- id.advance = false;
+ id.advance = p_advance;
// Dialog insert.
_query_insert(id);
inserted = true;
@@ -4165,105 +4167,41 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p
InsertData id;
id.path = np;
id.track_idx = -1;
- id.value = p_value;
+ id.value = value;
id.type = Animation::TYPE_VALUE;
id.query = vformat(TTR("property '%s'"), p_property);
- id.advance = false;
+ id.advance = p_advance;
// Dialog insert.
_query_insert(id);
}
-void AnimationTrackEditor::insert_value_key(const String &p_property, const Variant &p_value, bool p_advance) {
+void AnimationTrackEditor::insert_value_key(const String &p_property, bool p_advance) {
EditorSelectionHistory *history = EditorNode::get_singleton()->get_editor_selection_history();
ERR_FAIL_NULL(root);
ERR_FAIL_COND(history->get_path_size() == 0);
Object *obj = ObjectDB::get_instance(history->get_path_object(0));
- ERR_FAIL_NULL(Object::cast_to<Node>(obj));
-
- // Let's build a node path.
- Node *node = Object::cast_to<Node>(obj);
- String path = root->get_path_to(node, true);
-
- if (Object::cast_to<AnimationPlayer>(node) && p_property == "current_animation") {
- if (node == AnimationPlayerEditor::get_singleton()->get_player()) {
- EditorNode::get_singleton()->show_warning(TTR("AnimationPlayer can't animate itself, only other players."));
- return;
- }
- _insert_animation_key(path, p_value);
- return;
- }
-
- for (int i = 1; i < history->get_path_size(); i++) {
- String prop = history->get_path_property(i);
- ERR_FAIL_COND(prop.is_empty());
- path += ":" + prop;
- }
-
- path += ":" + p_property;
- NodePath np = path;
+ Ref<MultiNodeEdit> multi_node_edit(obj);
+ if (multi_node_edit.is_valid()) {
+ Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
+ ERR_FAIL_NULL(edited_scene);
- // Locate track.
-
- bool inserted = false;
+ make_insert_queue();
- make_insert_queue();
- for (int i = 0; i < animation->get_track_count(); i++) {
- if (animation->track_get_type(i) == Animation::TYPE_VALUE) {
- if (animation->track_get_path(i) != np) {
- continue;
- }
-
- InsertData id;
- id.path = np;
- id.track_idx = i;
- id.value = p_value;
- id.type = Animation::TYPE_VALUE;
- id.query = vformat(TTR("property '%s'"), p_property);
- id.advance = p_advance;
- // Dialog insert.
- _query_insert(id);
- inserted = true;
- } else if (animation->track_get_type(i) == Animation::TYPE_BEZIER) {
- Variant value;
- if (animation->track_get_path(i) == np) {
- value = p_value; // All good.
- } else {
- String tpath = animation->track_get_path(i);
- int index = tpath.rfind(":");
- if (NodePath(tpath.substr(0, index + 1)) == np) {
- String subindex = tpath.substr(index + 1, tpath.length() - index);
- value = p_value.get(subindex);
- } else {
- continue;
- }
- }
-
- InsertData id;
- id.path = animation->track_get_path(i);
- id.track_idx = i;
- id.value = value;
- id.type = Animation::TYPE_BEZIER;
- id.query = vformat(TTR("property '%s'"), p_property);
- id.advance = p_advance;
- // Dialog insert.
- _query_insert(id);
- inserted = true;
+ for (int i = 0; i < multi_node_edit->get_node_count(); ++i) {
+ Node *node = edited_scene->get_node(multi_node_edit->get_node(i));
+ insert_node_value_key(node, p_property, false, p_advance);
}
- }
- commit_insert_queue();
- if (!inserted) {
- InsertData id;
- id.path = np;
- id.track_idx = -1;
- id.value = p_value;
- id.type = Animation::TYPE_VALUE;
- id.query = vformat(TTR("property '%s'"), p_property);
- id.advance = p_advance;
- // Dialog insert.
- _query_insert(id);
+ commit_insert_queue();
+ } else {
+ Node *node = Object::cast_to<Node>(obj);
+ ERR_FAIL_NULL(node);
+
+ make_insert_queue();
+ insert_node_value_key(node, p_property, false, p_advance);
+ commit_insert_queue();
}
}
diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h
index 59ee6535ac..a517ba8b43 100644
--- a/editor/animation_track_editor.h
+++ b/editor/animation_track_editor.h
@@ -713,8 +713,8 @@ public:
void cleanup();
void set_anim_pos(float p_pos);
- void insert_node_value_key(Node *p_node, const String &p_property, const Variant &p_value, bool p_only_if_exists = false);
- void insert_value_key(const String &p_property, const Variant &p_value, bool p_advance);
+ void insert_node_value_key(Node *p_node, const String &p_property, bool p_only_if_exists = false, bool p_advance = false);
+ void insert_value_key(const String &p_property, bool p_advance);
void insert_transform_key(Node3D *p_node, const String &p_sub, const Animation::TrackType p_type, const Variant &p_value);
bool has_track(Node3D *p_node, const String &p_sub, const Animation::TrackType p_type);
void make_insert_queue();
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index 903d4d9559..bae9062ff1 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -243,18 +243,27 @@ void EditorFileSystem::_first_scan_filesystem() {
first_scan_root_dir = memnew(ScannedDirectory);
first_scan_root_dir->full_path = "res://";
HashSet<String> existing_class_names;
+ HashSet<String> extensions;
ep.step(TTR("Scanning file structure..."), 0, true);
nb_files_total = _scan_new_dir(first_scan_root_dir, d);
// This loads the global class names from the scripts and ensures that even if the
// global_script_class_cache.cfg was missing or invalid, the global class names are valid in ScriptServer.
+ // At the same time, to prevent looping multiple times in all files, it looks for extensions.
ep.step(TTR("Loading global class names..."), 1, true);
- _first_scan_process_scripts(first_scan_root_dir, existing_class_names);
+ _first_scan_process_scripts(first_scan_root_dir, existing_class_names, extensions);
// Removing invalid global class to prevent having invalid paths in ScriptServer.
_remove_invalid_global_class_names(existing_class_names);
+ // Processing extensions to add new extensions or remove invalid ones.
+ // Important to do it in the first scan so custom types, new class names, custom importers, etc...
+ // from extensions are ready to go before plugins, autoloads and resources validation/importation.
+ // At this point, a restart of the editor should not be needed so we don't use the return value.
+ ep.step(TTR("Verifying GDExtensions..."), 2, true);
+ GDExtensionManager::get_singleton()->ensure_extensions_loaded(extensions);
+
// Now that all the global class names should be loaded, create autoloads and plugins.
// This is done after loading the global class names because autoloads and plugins can use
// global class names.
@@ -267,9 +276,9 @@ void EditorFileSystem::_first_scan_filesystem() {
ep.step(TTR("Starting file scan..."), 5, true);
}
-void EditorFileSystem::_first_scan_process_scripts(const ScannedDirectory *p_scan_dir, HashSet<String> &p_existing_class_names) {
+void EditorFileSystem::_first_scan_process_scripts(const ScannedDirectory *p_scan_dir, HashSet<String> &p_existing_class_names, HashSet<String> &p_extensions) {
for (ScannedDirectory *scan_sub_dir : p_scan_dir->subdirs) {
- _first_scan_process_scripts(scan_sub_dir, p_existing_class_names);
+ _first_scan_process_scripts(scan_sub_dir, p_existing_class_names, p_extensions);
}
for (const String &scan_file : p_scan_dir->files) {
@@ -285,6 +294,8 @@ void EditorFileSystem::_first_scan_process_scripts(const ScannedDirectory *p_sca
if (!script_class_name.is_empty()) {
p_existing_class_names.insert(script_class_name);
}
+ } else if (type == SNAME("GDExtension")) {
+ p_extensions.insert(path);
}
}
}
@@ -3014,57 +3025,7 @@ bool EditorFileSystem::_scan_extensions() {
_scan_extensions_dir(d, extensions);
- //verify against loaded extensions
-
- Vector<String> extensions_added;
- Vector<String> extensions_removed;
-
- for (const String &E : extensions) {
- if (!GDExtensionManager::get_singleton()->is_extension_loaded(E)) {
- extensions_added.push_back(E);
- }
- }
-
- Vector<String> loaded_extensions = GDExtensionManager::get_singleton()->get_loaded_extensions();
- for (int i = 0; i < loaded_extensions.size(); i++) {
- if (!extensions.has(loaded_extensions[i])) {
- // The extension may not have a .gdextension file.
- if (!FileAccess::exists(loaded_extensions[i])) {
- extensions_removed.push_back(loaded_extensions[i]);
- }
- }
- }
-
- String extension_list_config_file = GDExtension::get_extension_list_config_file();
- if (extensions.size()) {
- if (extensions_added.size() || extensions_removed.size()) { //extensions were added or removed
- Ref<FileAccess> f = FileAccess::open(extension_list_config_file, FileAccess::WRITE);
- for (const String &E : extensions) {
- f->store_line(E);
- }
- }
- } else {
- if (loaded_extensions.size() || FileAccess::exists(extension_list_config_file)) { //extensions were removed
- Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
- da->remove(extension_list_config_file);
- }
- }
-
- bool needs_restart = false;
- for (int i = 0; i < extensions_added.size(); i++) {
- GDExtensionManager::LoadStatus st = GDExtensionManager::get_singleton()->load_extension(extensions_added[i]);
- if (st == GDExtensionManager::LOAD_STATUS_NEEDS_RESTART) {
- needs_restart = true;
- }
- }
- for (int i = 0; i < extensions_removed.size(); i++) {
- GDExtensionManager::LoadStatus st = GDExtensionManager::get_singleton()->unload_extension(extensions_removed[i]);
- if (st == GDExtensionManager::LOAD_STATUS_NEEDS_RESTART) {
- needs_restart = true;
- }
- }
-
- return needs_restart;
+ return GDExtensionManager::get_singleton()->ensure_extensions_loaded(extensions);
}
void EditorFileSystem::_bind_methods() {
diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h
index 1bc24416eb..ca4a64bfac 100644
--- a/editor/editor_file_system.h
+++ b/editor/editor_file_system.h
@@ -187,7 +187,7 @@ class EditorFileSystem : public Node {
void _scan_filesystem();
void _first_scan_filesystem();
- void _first_scan_process_scripts(const ScannedDirectory *p_scan_dir, HashSet<String> &p_existing_class_names);
+ void _first_scan_process_scripts(const ScannedDirectory *p_scan_dir, HashSet<String> &p_existing_class_names, HashSet<String> &p_extensions);
HashSet<String> late_update_files;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index c6144a34cb..c0aa1f6546 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -481,6 +481,9 @@ void EditorNode::_gdextensions_reloaded() {
// In case the developer is inspecting an object that will be changed by the reload.
InspectorDock::get_inspector_singleton()->update_tree();
+ // Reload script editor to revalidate GDScript if classes are added or removed.
+ ScriptEditor::get_singleton()->reload_scripts(true);
+
// Regenerate documentation.
EditorHelp::generate_doc();
}
@@ -4949,7 +4952,9 @@ bool EditorNode::is_object_of_custom_type(const Object *p_object, const StringNa
}
void EditorNode::progress_add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel) {
- if (singleton->cmdline_export_mode) {
+ if (!singleton) {
+ return;
+ } else if (singleton->cmdline_export_mode) {
print_line(p_task + ": begin: " + p_label + " steps: " + itos(p_steps));
} else if (singleton->progress_dialog) {
singleton->progress_dialog->add_task(p_task, p_label, p_steps, p_can_cancel);
@@ -4957,7 +4962,9 @@ void EditorNode::progress_add_task(const String &p_task, const String &p_label,
}
bool EditorNode::progress_task_step(const String &p_task, const String &p_state, int p_step, bool p_force_refresh) {
- if (singleton->cmdline_export_mode) {
+ if (!singleton) {
+ return false;
+ } else if (singleton->cmdline_export_mode) {
print_line("\t" + p_task + ": step " + itos(p_step) + ": " + p_state);
return false;
} else if (singleton->progress_dialog) {
@@ -4968,7 +4975,9 @@ bool EditorNode::progress_task_step(const String &p_task, const String &p_state,
}
void EditorNode::progress_end_task(const String &p_task) {
- if (singleton->cmdline_export_mode) {
+ if (!singleton) {
+ return;
+ } else if (singleton->cmdline_export_mode) {
print_line(p_task + ": end");
} else if (singleton->progress_dialog) {
singleton->progress_dialog->end_task(p_task);
diff --git a/editor/editor_run_native.cpp b/editor/editor_run_native.cpp
index 5d378820ae..e0e1ef6d19 100644
--- a/editor/editor_run_native.cpp
+++ b/editor/editor_run_native.cpp
@@ -141,7 +141,7 @@ Error EditorRunNative::start_run_native(int p_id) {
emit_signal(SNAME("native_run"), preset);
- int flags = 0;
+ BitField<EditorExportPlatform::DebugFlags> flags = 0;
bool deploy_debug_remote = is_deploy_debug_remote_enabled();
bool deploy_dumb = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_file_server", false);
@@ -149,16 +149,16 @@ Error EditorRunNative::start_run_native(int p_id) {
bool debug_navigation = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_navigation", false);
if (deploy_debug_remote) {
- flags |= EditorExportPlatform::DEBUG_FLAG_REMOTE_DEBUG;
+ flags.set_flag(EditorExportPlatform::DEBUG_FLAG_REMOTE_DEBUG);
}
if (deploy_dumb) {
- flags |= EditorExportPlatform::DEBUG_FLAG_DUMB_CLIENT;
+ flags.set_flag(EditorExportPlatform::DEBUG_FLAG_DUMB_CLIENT);
}
if (debug_collisions) {
- flags |= EditorExportPlatform::DEBUG_FLAG_VIEW_COLLISIONS;
+ flags.set_flag(EditorExportPlatform::DEBUG_FLAG_VIEW_COLLISIONS);
}
if (debug_navigation) {
- flags |= EditorExportPlatform::DEBUG_FLAG_VIEW_NAVIGATION;
+ flags.set_flag(EditorExportPlatform::DEBUG_FLAG_VIEW_NAVIGATION);
}
eep->clear_messages();
diff --git a/editor/export/editor_export.cpp b/editor/export/editor_export.cpp
index 72ab186036..975a601ae1 100644
--- a/editor/export/editor_export.cpp
+++ b/editor/export/editor_export.cpp
@@ -124,7 +124,17 @@ void EditorExport::_bind_methods() {
void EditorExport::add_export_platform(const Ref<EditorExportPlatform> &p_platform) {
export_platforms.push_back(p_platform);
+
should_update_presets = true;
+ should_reload_presets = true;
+}
+
+void EditorExport::remove_export_platform(const Ref<EditorExportPlatform> &p_platform) {
+ export_platforms.erase(p_platform);
+ p_platform->cleanup();
+
+ should_update_presets = true;
+ should_reload_presets = true;
}
int EditorExport::get_export_platform_count() {
@@ -244,7 +254,7 @@ void EditorExport::load_config() {
if (!preset.is_valid()) {
index++;
- ERR_CONTINUE(!preset.is_valid());
+ continue; // Unknown platform, skip without error (platform might be loaded later).
}
preset->set_name(config->get_value(section, "name"));
@@ -343,6 +353,12 @@ void EditorExport::load_config() {
void EditorExport::update_export_presets() {
HashMap<StringName, List<EditorExportPlatform::ExportOption>> platform_options;
+ if (should_reload_presets) {
+ should_reload_presets = false;
+ export_presets.clear();
+ load_config();
+ }
+
for (int i = 0; i < export_platforms.size(); i++) {
Ref<EditorExportPlatform> platform = export_platforms[i];
diff --git a/editor/export/editor_export.h b/editor/export/editor_export.h
index f8cb90dc39..ebb2038f53 100644
--- a/editor/export/editor_export.h
+++ b/editor/export/editor_export.h
@@ -47,6 +47,7 @@ class EditorExport : public Node {
Timer *save_timer = nullptr;
bool block_save = false;
bool should_update_presets = false;
+ bool should_reload_presets = false;
static EditorExport *singleton;
@@ -66,6 +67,7 @@ public:
void add_export_platform(const Ref<EditorExportPlatform> &p_platform);
int get_export_platform_count();
Ref<EditorExportPlatform> get_export_platform(int p_idx);
+ void remove_export_platform(const Ref<EditorExportPlatform> &p_platform);
void add_export_preset(const Ref<EditorExportPreset> &p_preset, int p_at_pos = -1);
int get_export_preset_count() const;
diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp
index 8b31eda3bc..7ad589a58d 100644
--- a/editor/export/editor_export_platform.cpp
+++ b/editor/export/editor_export_platform.cpp
@@ -71,7 +71,7 @@ bool EditorExportPlatform::fill_log_messages(RichTextLabel *p_log, Error p_err)
p_log->add_text(" ");
p_log->add_text(get_name());
p_log->add_text(" - ");
- if (p_err == OK) {
+ if (p_err == OK && get_worst_message_type() < EditorExportPlatform::EXPORT_MESSAGE_ERROR) {
if (get_worst_message_type() >= EditorExportPlatform::EXPORT_MESSAGE_WARNING) {
p_log->add_image(p_log->get_editor_theme_icon(SNAME("StatusWarning")), 16 * EDSCALE, 16 * EDSCALE, Color(1.0, 1.0, 1.0), INLINE_ALIGNMENT_CENTER);
p_log->add_text(" ");
@@ -167,58 +167,6 @@ bool EditorExportPlatform::fill_log_messages(RichTextLabel *p_log, Error p_err)
return has_messages;
}
-void EditorExportPlatform::gen_debug_flags(Vector<String> &r_flags, int p_flags) {
- String host = EDITOR_GET("network/debug/remote_host");
- int remote_port = (int)EDITOR_GET("network/debug/remote_port");
-
- if (EditorSettings::get_singleton()->has_setting("export/android/use_wifi_for_remote_debug") && EDITOR_GET("export/android/use_wifi_for_remote_debug")) {
- host = EDITOR_GET("export/android/wifi_remote_debug_host");
- } else if (p_flags & DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST) {
- host = "localhost";
- }
-
- if (p_flags & DEBUG_FLAG_DUMB_CLIENT) {
- int port = EDITOR_GET("filesystem/file_server/port");
- String passwd = EDITOR_GET("filesystem/file_server/password");
- r_flags.push_back("--remote-fs");
- r_flags.push_back(host + ":" + itos(port));
- if (!passwd.is_empty()) {
- r_flags.push_back("--remote-fs-password");
- r_flags.push_back(passwd);
- }
- }
-
- if (p_flags & DEBUG_FLAG_REMOTE_DEBUG) {
- r_flags.push_back("--remote-debug");
-
- r_flags.push_back(get_debug_protocol() + host + ":" + String::num(remote_port));
-
- List<String> breakpoints;
- ScriptEditor::get_singleton()->get_breakpoints(&breakpoints);
-
- if (breakpoints.size()) {
- r_flags.push_back("--breakpoints");
- String bpoints;
- for (const List<String>::Element *E = breakpoints.front(); E; E = E->next()) {
- bpoints += E->get().replace(" ", "%20");
- if (E->next()) {
- bpoints += ",";
- }
- }
-
- r_flags.push_back(bpoints);
- }
- }
-
- if (p_flags & DEBUG_FLAG_VIEW_COLLISIONS) {
- r_flags.push_back("--debug-collisions");
- }
-
- if (p_flags & DEBUG_FLAG_VIEW_NAVIGATION) {
- r_flags.push_back("--debug-navigation");
- }
-}
-
Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) {
ERR_FAIL_COND_V_MSG(p_total < 1, ERR_PARAMETER_RANGE_ERROR, "Must select at least one file to export.");
@@ -530,7 +478,7 @@ HashSet<String> EditorExportPlatform::get_features(const Ref<EditorExportPreset>
return result;
}
-EditorExportPlatform::ExportNotifier::ExportNotifier(EditorExportPlatform &p_platform, const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
+EditorExportPlatform::ExportNotifier::ExportNotifier(EditorExportPlatform &p_platform, const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
HashSet<String> features = p_platform.get_features(p_preset, p_debug);
Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
//initial export plugin callback
@@ -919,6 +867,55 @@ Vector<String> EditorExportPlatform::get_forced_export_files() {
return files;
}
+Error EditorExportPlatform::_script_save_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) {
+ Callable cb = ((ScriptCallbackData *)p_userdata)->file_cb;
+ ERR_FAIL_COND_V(!cb.is_valid(), FAILED);
+
+ Variant path = p_path;
+ Variant data = p_data;
+ Variant file = p_file;
+ Variant total = p_total;
+ Variant enc_in = p_enc_in_filters;
+ Variant enc_ex = p_enc_ex_filters;
+ Variant enc_key = p_key;
+
+ Variant ret;
+ Callable::CallError ce;
+ const Variant *args[7] = { &path, &data, &file, &total, &enc_in, &enc_ex, &enc_key };
+
+ cb.callp(args, 7, ret, ce);
+ ERR_FAIL_COND_V_MSG(ce.error != Callable::CallError::CALL_OK, FAILED, vformat("Failed to execute file save callback: %s.", Variant::get_callable_error_text(cb, args, 7, ce)));
+
+ return (Error)ret.operator int();
+}
+
+Error EditorExportPlatform::_script_add_shared_object(void *p_userdata, const SharedObject &p_so) {
+ Callable cb = ((ScriptCallbackData *)p_userdata)->so_cb;
+ if (!cb.is_valid()) {
+ return OK; // Optional.
+ }
+
+ Variant path = p_so.path;
+ Variant tags = p_so.tags;
+ Variant target = p_so.target;
+
+ Variant ret;
+ Callable::CallError ce;
+ const Variant *args[3] = { &path, &tags, &target };
+
+ cb.callp(args, 3, ret, ce);
+ ERR_FAIL_COND_V_MSG(ce.error != Callable::CallError::CALL_OK, FAILED, vformat("Failed to execute shared object save callback: %s.", Variant::get_callable_error_text(cb, args, 3, ce)));
+
+ return (Error)ret.operator int();
+}
+
+Error EditorExportPlatform::_export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, const Callable &p_save_func, const Callable &p_so_func) {
+ ScriptCallbackData data;
+ data.file_cb = p_save_func;
+ data.so_cb = p_so_func;
+ return export_project_files(p_preset, p_debug, _script_save_file, &data, _script_add_shared_object);
+}
+
Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, EditorExportSaveFunction p_func, void *p_udata, EditorExportSaveSharedObject p_so_func) {
//figure out paths of files that will be exported
HashSet<String> paths;
@@ -1425,7 +1422,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
return p_func(p_udata, "res://" + config_file, data, idx, total, enc_in_filters, enc_ex_filters, key);
}
-Error EditorExportPlatform::_add_shared_object(void *p_userdata, const SharedObject &p_so) {
+Error EditorExportPlatform::_pack_add_shared_object(void *p_userdata, const SharedObject &p_so) {
PackData *pack_data = (PackData *)p_userdata;
if (pack_data->so_files) {
pack_data->so_files->push_back(p_so);
@@ -1434,6 +1431,15 @@ Error EditorExportPlatform::_add_shared_object(void *p_userdata, const SharedObj
return OK;
}
+Error EditorExportPlatform::_zip_add_shared_object(void *p_userdata, const SharedObject &p_so) {
+ ZipData *zip_data = (ZipData *)p_userdata;
+ if (zip_data->so_files) {
+ zip_data->so_files->push_back(p_so);
+ }
+
+ return OK;
+}
+
void EditorExportPlatform::zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name) {
String dir = p_folder.is_empty() ? p_root_path : p_root_path.path_join(p_folder);
@@ -1551,6 +1557,54 @@ void EditorExportPlatform::zip_folder_recursive(zipFile &p_zip, const String &p_
da->list_dir_end();
}
+Dictionary EditorExportPlatform::_save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, bool p_embed) {
+ Vector<SharedObject> so_files;
+ int64_t embedded_start = 0;
+ int64_t embedded_size = 0;
+ Error err_code = save_pack(p_preset, p_debug, p_path, &so_files, p_embed, &embedded_start, &embedded_size);
+
+ Dictionary ret;
+ ret["result"] = err_code;
+ if (err_code == OK) {
+ Array arr;
+ for (const SharedObject &E : so_files) {
+ Dictionary so;
+ so["path"] = E.path;
+ so["tags"] = E.tags;
+ so["target_folder"] = E.target;
+ arr.push_back(so);
+ }
+ ret["so_files"] = arr;
+ if (p_embed) {
+ ret["embedded_start"] = embedded_start;
+ ret["embedded_size"] = embedded_size;
+ }
+ }
+
+ return ret;
+}
+
+Dictionary EditorExportPlatform::_save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) {
+ Vector<SharedObject> so_files;
+ Error err_code = save_zip(p_preset, p_debug, p_path, &so_files);
+
+ Dictionary ret;
+ ret["result"] = err_code;
+ if (err_code == OK) {
+ Array arr;
+ for (const SharedObject &E : so_files) {
+ Dictionary so;
+ so["path"] = E.path;
+ so["tags"] = E.tags;
+ so["target_folder"] = E.target;
+ arr.push_back(so);
+ }
+ ret["so_files"] = arr;
+ }
+
+ return ret;
+}
+
Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files, bool p_embed, int64_t *r_embedded_start, int64_t *r_embedded_size) {
EditorProgress ep("savepack", TTR("Packing"), 102, true);
@@ -1570,7 +1624,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
pd.f = ftmp;
pd.so_files = p_so_files;
- Error err = export_project_files(p_preset, p_debug, _save_pack_file, &pd, _add_shared_object);
+ Error err = export_project_files(p_preset, p_debug, _save_pack_file, &pd, _pack_add_shared_object);
// Close temp file.
pd.f.unref();
@@ -1777,7 +1831,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
return OK;
}
-Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) {
+Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files) {
EditorProgress ep("savezip", TTR("Packing"), 102, true);
Ref<FileAccess> io_fa;
@@ -1787,8 +1841,9 @@ Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, bo
ZipData zd;
zd.ep = &ep;
zd.zip = zip;
+ zd.so_files = p_so_files;
- Error err = export_project_files(p_preset, p_debug, _save_zip_file, &zd);
+ Error err = export_project_files(p_preset, p_debug, _save_zip_file, &zd, _zip_add_shared_object);
if (err != OK && err != ERR_SKIP) {
add_message(EXPORT_MESSAGE_ERROR, TTR("Save ZIP"), TTR("Failed to export project files."));
}
@@ -1798,45 +1853,48 @@ Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, bo
return OK;
}
-Error EditorExportPlatform::export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
+Error EditorExportPlatform::export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
return save_pack(p_preset, p_debug, p_path);
}
-Error EditorExportPlatform::export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
+Error EditorExportPlatform::export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
return save_zip(p_preset, p_debug, p_path);
}
-void EditorExportPlatform::gen_export_flags(Vector<String> &r_flags, int p_flags) {
+Vector<String> EditorExportPlatform::gen_export_flags(BitField<EditorExportPlatform::DebugFlags> p_flags) {
+ Vector<String> ret;
String host = EDITOR_GET("network/debug/remote_host");
int remote_port = (int)EDITOR_GET("network/debug/remote_port");
- if (p_flags & DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST) {
+ if (get_name() == "Android" && EditorSettings::get_singleton()->has_setting("export/android/use_wifi_for_remote_debug") && EDITOR_GET("export/android/use_wifi_for_remote_debug")) {
+ host = EDITOR_GET("export/android/wifi_remote_debug_host");
+ } else if (p_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST)) {
host = "localhost";
}
- if (p_flags & DEBUG_FLAG_DUMB_CLIENT) {
+ if (p_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT)) {
int port = EDITOR_GET("filesystem/file_server/port");
String passwd = EDITOR_GET("filesystem/file_server/password");
- r_flags.push_back("--remote-fs");
- r_flags.push_back(host + ":" + itos(port));
+ ret.push_back("--remote-fs");
+ ret.push_back(host + ":" + itos(port));
if (!passwd.is_empty()) {
- r_flags.push_back("--remote-fs-password");
- r_flags.push_back(passwd);
+ ret.push_back("--remote-fs-password");
+ ret.push_back(passwd);
}
}
- if (p_flags & DEBUG_FLAG_REMOTE_DEBUG) {
- r_flags.push_back("--remote-debug");
+ if (p_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG)) {
+ ret.push_back("--remote-debug");
- r_flags.push_back(get_debug_protocol() + host + ":" + String::num(remote_port));
+ ret.push_back(get_debug_protocol() + host + ":" + String::num(remote_port));
List<String> breakpoints;
ScriptEditor::get_singleton()->get_breakpoints(&breakpoints);
if (breakpoints.size()) {
- r_flags.push_back("--breakpoints");
+ ret.push_back("--breakpoints");
String bpoints;
for (List<String>::Element *E = breakpoints.front(); E; E = E->next()) {
bpoints += E->get().replace(" ", "%20");
@@ -1845,17 +1903,18 @@ void EditorExportPlatform::gen_export_flags(Vector<String> &r_flags, int p_flags
}
}
- r_flags.push_back(bpoints);
+ ret.push_back(bpoints);
}
}
- if (p_flags & DEBUG_FLAG_VIEW_COLLISIONS) {
- r_flags.push_back("--debug-collisions");
+ if (p_flags.has_flag(DEBUG_FLAG_VIEW_COLLISIONS)) {
+ ret.push_back("--debug-collisions");
}
- if (p_flags & DEBUG_FLAG_VIEW_NAVIGATION) {
- r_flags.push_back("--debug-navigation");
+ if (p_flags.has_flag(DEBUG_FLAG_VIEW_NAVIGATION)) {
+ ret.push_back("--debug-navigation");
}
+ return ret;
}
bool EditorExportPlatform::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const {
@@ -2035,8 +2094,61 @@ Error EditorExportPlatform::ssh_push_to_remote(const String &p_host, const Strin
return OK;
}
+Array EditorExportPlatform::get_current_presets() const {
+ Array ret;
+ for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
+ Ref<EditorExportPreset> ep = EditorExport::get_singleton()->get_export_preset(i);
+ if (ep->get_platform() == this) {
+ ret.push_back(ep);
+ }
+ }
+ return ret;
+}
+
void EditorExportPlatform::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_os_name"), &EditorExportPlatform::get_os_name);
+
+ ClassDB::bind_method(D_METHOD("create_preset"), &EditorExportPlatform::create_preset);
+
+ ClassDB::bind_method(D_METHOD("find_export_template", "template_file_name"), &EditorExportPlatform::_find_export_template);
+ ClassDB::bind_method(D_METHOD("get_current_presets"), &EditorExportPlatform::get_current_presets);
+
+ ClassDB::bind_method(D_METHOD("save_pack", "preset", "debug", "path", "embed"), &EditorExportPlatform::_save_pack, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("save_zip", "preset", "debug", "path"), &EditorExportPlatform::_save_zip);
+
+ ClassDB::bind_method(D_METHOD("gen_export_flags", "flags"), &EditorExportPlatform::gen_export_flags);
+
+ ClassDB::bind_method(D_METHOD("export_project_files", "preset", "debug", "save_cb", "shared_cb"), &EditorExportPlatform::_export_project_files, DEFVAL(Callable()));
+
+ ClassDB::bind_method(D_METHOD("export_project", "preset", "debug", "path", "flags"), &EditorExportPlatform::export_project, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("export_pack", "preset", "debug", "path", "flags"), &EditorExportPlatform::export_pack, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("export_zip", "preset", "debug", "path", "flags"), &EditorExportPlatform::export_zip, DEFVAL(0));
+
+ ClassDB::bind_method(D_METHOD("clear_messages"), &EditorExportPlatform::clear_messages);
+ ClassDB::bind_method(D_METHOD("add_message", "type", "category", "message"), &EditorExportPlatform::add_message);
+ ClassDB::bind_method(D_METHOD("get_message_count"), &EditorExportPlatform::get_message_count);
+
+ ClassDB::bind_method(D_METHOD("get_message_type", "index"), &EditorExportPlatform::_get_message_type);
+ ClassDB::bind_method(D_METHOD("get_message_category", "index"), &EditorExportPlatform::_get_message_category);
+ ClassDB::bind_method(D_METHOD("get_message_text", "index"), &EditorExportPlatform::_get_message_text);
+ ClassDB::bind_method(D_METHOD("get_worst_message_type"), &EditorExportPlatform::get_worst_message_type);
+
+ ClassDB::bind_method(D_METHOD("ssh_run_on_remote", "host", "port", "ssh_arg", "cmd_args", "output", "port_fwd"), &EditorExportPlatform::_ssh_run_on_remote, DEFVAL(Array()), DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("ssh_run_on_remote_no_wait", "host", "port", "ssh_args", "cmd_args", "port_fwd"), &EditorExportPlatform::_ssh_run_on_remote_no_wait, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("ssh_push_to_remote", "host", "port", "scp_args", "src_file", "dst_file"), &EditorExportPlatform::ssh_push_to_remote);
+
+ ClassDB::bind_static_method("EditorExportPlatform", D_METHOD("get_forced_export_files"), &EditorExportPlatform::get_forced_export_files);
+
+ BIND_ENUM_CONSTANT(EXPORT_MESSAGE_NONE);
+ BIND_ENUM_CONSTANT(EXPORT_MESSAGE_INFO);
+ BIND_ENUM_CONSTANT(EXPORT_MESSAGE_WARNING);
+ BIND_ENUM_CONSTANT(EXPORT_MESSAGE_ERROR);
+
+ BIND_BITFIELD_FLAG(DEBUG_FLAG_DUMB_CLIENT);
+ BIND_BITFIELD_FLAG(DEBUG_FLAG_REMOTE_DEBUG);
+ BIND_BITFIELD_FLAG(DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST);
+ BIND_BITFIELD_FLAG(DEBUG_FLAG_VIEW_COLLISIONS);
+ BIND_BITFIELD_FLAG(DEBUG_FLAG_VIEW_NAVIGATION);
}
EditorExportPlatform::EditorExportPlatform() {
diff --git a/editor/export/editor_export_platform.h b/editor/export/editor_export_platform.h
index 3fd75ff67f..a800bb95e6 100644
--- a/editor/export/editor_export_platform.h
+++ b/editor/export/editor_export_platform.h
@@ -56,6 +56,14 @@ public:
typedef Error (*EditorExportSaveFunction)(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key);
typedef Error (*EditorExportSaveSharedObject)(void *p_userdata, const SharedObject &p_so);
+ enum DebugFlags {
+ DEBUG_FLAG_DUMB_CLIENT = 1,
+ DEBUG_FLAG_REMOTE_DEBUG = 2,
+ DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST = 4,
+ DEBUG_FLAG_VIEW_COLLISIONS = 8,
+ DEBUG_FLAG_VIEW_NAVIGATION = 16,
+ };
+
enum ExportMessageType {
EXPORT_MESSAGE_NONE,
EXPORT_MESSAGE_INFO,
@@ -92,6 +100,7 @@ private:
struct ZipData {
void *zip = nullptr;
EditorProgress *ep = nullptr;
+ Vector<SharedObject> *so_files = nullptr;
};
Vector<ExportMessage> messages;
@@ -101,13 +110,22 @@ private:
void _export_find_dependencies(const String &p_path, HashSet<String> &p_paths);
static Error _save_pack_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key);
+ static Error _pack_add_shared_object(void *p_userdata, const SharedObject &p_so);
+
static Error _save_zip_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key);
+ static Error _zip_add_shared_object(void *p_userdata, const SharedObject &p_so);
+
+ struct ScriptCallbackData {
+ Callable file_cb;
+ Callable so_cb;
+ };
+
+ static Error _script_save_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key);
+ static Error _script_add_shared_object(void *p_userdata, const SharedObject &p_so);
void _edit_files_with_filter(Ref<DirAccess> &da, const Vector<String> &p_filters, HashSet<String> &r_list, bool exclude);
void _edit_filter_list(HashSet<String> &r_list, const String &p_filter, bool exclude);
- static Error _add_shared_object(void *p_userdata, const SharedObject &p_so);
-
struct FileExportCache {
uint64_t source_modified_time = 0;
String source_md5;
@@ -126,19 +144,46 @@ private:
protected:
struct ExportNotifier {
- ExportNotifier(EditorExportPlatform &p_platform, const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags);
+ ExportNotifier(EditorExportPlatform &p_platform, const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags);
~ExportNotifier();
};
HashSet<String> get_features(const Ref<EditorExportPreset> &p_preset, bool p_debug) const;
- bool exists_export_template(const String &template_file_name, String *err) const;
- String find_export_template(const String &template_file_name, String *err = nullptr) const;
- void gen_export_flags(Vector<String> &r_flags, int p_flags);
- void gen_debug_flags(Vector<String> &r_flags, int p_flags);
+ Dictionary _find_export_template(const String &p_template_file_name) const {
+ Dictionary ret;
+ String err;
+
+ String path = find_export_template(p_template_file_name, &err);
+ ret["result"] = (err.is_empty() && !path.is_empty()) ? OK : FAILED;
+ ret["path"] = path;
+ ret["error_string"] = err;
+
+ return ret;
+ }
+
+ bool exists_export_template(const String &p_template_file_name, String *r_err) const;
+ String find_export_template(const String &p_template_file_name, String *r_err = nullptr) const;
+ Vector<String> gen_export_flags(BitField<EditorExportPlatform::DebugFlags> p_flags);
virtual void zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name);
+ Error _ssh_run_on_remote(const String &p_host, const String &p_port, const Vector<String> &p_ssh_args, const String &p_cmd_args, Array r_output = Array(), int p_port_fwd = -1) const {
+ String pipe;
+ Error err = ssh_run_on_remote(p_host, p_port, p_ssh_args, p_cmd_args, &pipe, p_port_fwd);
+ r_output.push_back(pipe);
+ return err;
+ }
+ OS::ProcessID _ssh_run_on_remote_no_wait(const String &p_host, const String &p_port, const Vector<String> &p_ssh_args, const String &p_cmd_args, int p_port_fwd = -1) const {
+ OS::ProcessID pid = 0;
+ Error err = ssh_run_on_remote_no_wait(p_host, p_port, p_ssh_args, p_cmd_args, &pid, p_port_fwd);
+ if (err != OK) {
+ return -1;
+ } else {
+ return pid;
+ }
+ }
+
Error ssh_run_on_remote(const String &p_host, const String &p_port, const Vector<String> &p_ssh_args, const String &p_cmd_args, String *r_out = nullptr, int p_port_fwd = -1) const;
Error ssh_run_on_remote_no_wait(const String &p_host, const String &p_port, const Vector<String> &p_ssh_args, const String &p_cmd_args, OS::ProcessID *r_pid = nullptr, int p_port_fwd = -1) const;
Error ssh_push_to_remote(const String &p_host, const String &p_port, const Vector<String> &p_scp_args, const String &p_src_file, const String &p_dst_file) const;
@@ -195,6 +240,21 @@ public:
return messages[p_index];
}
+ virtual ExportMessageType _get_message_type(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, messages.size(), EXPORT_MESSAGE_NONE);
+ return messages[p_index].msg_type;
+ }
+
+ virtual String _get_message_category(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, messages.size(), String());
+ return messages[p_index].category;
+ }
+
+ virtual String _get_message_text(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, messages.size(), String());
+ return messages[p_index].text;
+ }
+
virtual ExportMessageType get_worst_message_type() const {
ExportMessageType worst_type = EXPORT_MESSAGE_NONE;
for (int i = 0; i < messages.size(); i++) {
@@ -216,10 +276,16 @@ public:
virtual String get_name() const = 0;
virtual Ref<Texture2D> get_logo() const = 0;
+ Array get_current_presets() const;
+
+ Error _export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, const Callable &p_save_func, const Callable &p_so_func);
Error export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, EditorExportSaveFunction p_func, void *p_udata, EditorExportSaveSharedObject p_so_func = nullptr);
+ Dictionary _save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, bool p_embed = false);
+ Dictionary _save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
+
Error save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr, bool p_embed = false, int64_t *r_embedded_start = nullptr, int64_t *r_embedded_size = nullptr);
- Error save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
+ Error save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr);
virtual bool poll_export() { return false; }
virtual int get_options_count() const { return 0; }
@@ -229,31 +295,26 @@ public:
virtual String get_option_tooltip(int p_device) const { return ""; }
virtual String get_device_architecture(int p_device) const { return ""; }
- enum DebugFlags {
- DEBUG_FLAG_DUMB_CLIENT = 1,
- DEBUG_FLAG_REMOTE_DEBUG = 2,
- DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST = 4,
- DEBUG_FLAG_VIEW_COLLISIONS = 8,
- DEBUG_FLAG_VIEW_NAVIGATION = 16,
- };
-
virtual void cleanup() {}
- virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { return OK; }
+ virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) { return OK; }
virtual Ref<Texture2D> get_run_icon() const { return get_logo(); }
- bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const;
+ virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const;
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const = 0;
virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const = 0;
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const = 0;
- virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) = 0;
- virtual Error export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
- virtual Error export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
+ virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) = 0;
+ virtual Error export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0);
+ virtual Error export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0);
virtual void get_platform_features(List<String> *r_features) const = 0;
- virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) = 0;
+ virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features){};
virtual String get_debug_protocol() const { return "tcp://"; }
EditorExportPlatform();
};
+VARIANT_ENUM_CAST(EditorExportPlatform::ExportMessageType)
+VARIANT_BITFIELD_CAST(EditorExportPlatform::DebugFlags);
+
#endif // EDITOR_EXPORT_PLATFORM_H
diff --git a/editor/export/editor_export_platform_extension.cpp b/editor/export/editor_export_platform_extension.cpp
new file mode 100644
index 0000000000..808a2076e2
--- /dev/null
+++ b/editor/export/editor_export_platform_extension.cpp
@@ -0,0 +1,317 @@
+/**************************************************************************/
+/* editor_export_platform_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "editor_export_platform_extension.h"
+
+void EditorExportPlatformExtension::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_config_error", "error_text"), &EditorExportPlatformExtension::set_config_error);
+ ClassDB::bind_method(D_METHOD("get_config_error"), &EditorExportPlatformExtension::get_config_error);
+
+ ClassDB::bind_method(D_METHOD("set_config_missing_templates", "missing_templates"), &EditorExportPlatformExtension::set_config_missing_templates);
+ ClassDB::bind_method(D_METHOD("get_config_missing_templates"), &EditorExportPlatformExtension::get_config_missing_templates);
+
+ GDVIRTUAL_BIND(_get_preset_features, "preset");
+ GDVIRTUAL_BIND(_is_executable, "path");
+ GDVIRTUAL_BIND(_get_export_options);
+ GDVIRTUAL_BIND(_should_update_export_options);
+ GDVIRTUAL_BIND(_get_export_option_visibility, "preset", "option");
+ GDVIRTUAL_BIND(_get_export_option_warning, "preset", "option");
+
+ GDVIRTUAL_BIND(_get_os_name);
+ GDVIRTUAL_BIND(_get_name);
+ GDVIRTUAL_BIND(_get_logo);
+
+ GDVIRTUAL_BIND(_poll_export);
+ GDVIRTUAL_BIND(_get_options_count);
+ GDVIRTUAL_BIND(_get_options_tooltip);
+
+ GDVIRTUAL_BIND(_get_option_icon, "device");
+ GDVIRTUAL_BIND(_get_option_label, "device");
+ GDVIRTUAL_BIND(_get_option_tooltip, "device");
+ GDVIRTUAL_BIND(_get_device_architecture, "device");
+
+ GDVIRTUAL_BIND(_cleanup);
+
+ GDVIRTUAL_BIND(_run, "preset", "device", "debug_flags");
+ GDVIRTUAL_BIND(_get_run_icon);
+
+ GDVIRTUAL_BIND(_can_export, "preset", "debug");
+ GDVIRTUAL_BIND(_has_valid_export_configuration, "preset", "debug");
+ GDVIRTUAL_BIND(_has_valid_project_configuration, "preset");
+
+ GDVIRTUAL_BIND(_get_binary_extensions, "preset");
+
+ GDVIRTUAL_BIND(_export_project, "preset", "debug", "path", "flags");
+ GDVIRTUAL_BIND(_export_pack, "preset", "debug", "path", "flags");
+ GDVIRTUAL_BIND(_export_zip, "preset", "debug", "path", "flags");
+
+ GDVIRTUAL_BIND(_get_platform_features);
+
+ GDVIRTUAL_BIND(_get_debug_protocol);
+}
+
+void EditorExportPlatformExtension::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const {
+ Vector<String> ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_preset_features, p_preset, ret) && r_features) {
+ for (const String &E : ret) {
+ r_features->push_back(E);
+ }
+ }
+}
+
+bool EditorExportPlatformExtension::is_executable(const String &p_path) const {
+ bool ret = false;
+ GDVIRTUAL_CALL(_is_executable, p_path, ret);
+ return ret;
+}
+
+void EditorExportPlatformExtension::get_export_options(List<ExportOption> *r_options) const {
+ TypedArray<Dictionary> ret;
+ if (GDVIRTUAL_CALL(_get_export_options, ret) && r_options) {
+ for (const Variant &var : ret) {
+ const Dictionary &d = var;
+ ERR_CONTINUE(!d.has("name"));
+ ERR_CONTINUE(!d.has("type"));
+
+ PropertyInfo pinfo = PropertyInfo::from_dict(d);
+ ERR_CONTINUE(pinfo.name.is_empty() && (pinfo.usage & PROPERTY_USAGE_STORAGE));
+ ERR_CONTINUE(pinfo.type < 0 || pinfo.type >= Variant::VARIANT_MAX);
+
+ Variant default_value;
+ if (d.has("default_value")) {
+ default_value = d["default_value"];
+ }
+ bool update_visibility = false;
+ if (d.has("update_visibility")) {
+ update_visibility = d["update_visibility"];
+ }
+ bool required = false;
+ if (d.has("required")) {
+ required = d["required"];
+ }
+
+ r_options->push_back(ExportOption(pinfo, default_value, update_visibility, required));
+ }
+ }
+}
+
+bool EditorExportPlatformExtension::should_update_export_options() {
+ bool ret = false;
+ GDVIRTUAL_CALL(_should_update_export_options, ret);
+ return ret;
+}
+
+bool EditorExportPlatformExtension::get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const {
+ bool ret = true;
+ GDVIRTUAL_CALL(_get_export_option_visibility, Ref<EditorExportPreset>(p_preset), p_option, ret);
+ return ret;
+}
+
+String EditorExportPlatformExtension::get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const {
+ String ret;
+ GDVIRTUAL_CALL(_get_export_option_warning, Ref<EditorExportPreset>(p_preset), p_name, ret);
+ return ret;
+}
+
+String EditorExportPlatformExtension::get_os_name() const {
+ String ret;
+ GDVIRTUAL_REQUIRED_CALL(_get_os_name, ret);
+ return ret;
+}
+
+String EditorExportPlatformExtension::get_name() const {
+ String ret;
+ GDVIRTUAL_REQUIRED_CALL(_get_name, ret);
+ return ret;
+}
+
+Ref<Texture2D> EditorExportPlatformExtension::get_logo() const {
+ Ref<Texture2D> ret;
+ GDVIRTUAL_REQUIRED_CALL(_get_logo, ret);
+ return ret;
+}
+
+bool EditorExportPlatformExtension::poll_export() {
+ bool ret = false;
+ GDVIRTUAL_CALL(_poll_export, ret);
+ return ret;
+}
+
+int EditorExportPlatformExtension::get_options_count() const {
+ int ret = 0;
+ GDVIRTUAL_CALL(_get_options_count, ret);
+ return ret;
+}
+
+String EditorExportPlatformExtension::get_options_tooltip() const {
+ String ret;
+ GDVIRTUAL_CALL(_get_options_tooltip, ret);
+ return ret;
+}
+
+Ref<ImageTexture> EditorExportPlatformExtension::get_option_icon(int p_index) const {
+ Ref<ImageTexture> ret;
+ if (GDVIRTUAL_CALL(_get_option_icon, p_index, ret)) {
+ return ret;
+ }
+ return EditorExportPlatform::get_option_icon(p_index);
+}
+
+String EditorExportPlatformExtension::get_option_label(int p_device) const {
+ String ret;
+ GDVIRTUAL_CALL(_get_option_label, p_device, ret);
+ return ret;
+}
+
+String EditorExportPlatformExtension::get_option_tooltip(int p_device) const {
+ String ret;
+ GDVIRTUAL_CALL(_get_option_tooltip, p_device, ret);
+ return ret;
+}
+
+String EditorExportPlatformExtension::get_device_architecture(int p_device) const {
+ String ret;
+ GDVIRTUAL_CALL(_get_device_architecture, p_device, ret);
+ return ret;
+}
+
+void EditorExportPlatformExtension::cleanup() {
+ GDVIRTUAL_CALL(_cleanup);
+}
+
+Error EditorExportPlatformExtension::run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) {
+ Error ret = OK;
+ GDVIRTUAL_CALL(_run, p_preset, p_device, p_debug_flags, ret);
+ return ret;
+}
+
+Ref<Texture2D> EditorExportPlatformExtension::get_run_icon() const {
+ Ref<Texture2D> ret;
+ if (GDVIRTUAL_CALL(_get_run_icon, ret)) {
+ return ret;
+ }
+ return EditorExportPlatform::get_run_icon();
+}
+
+bool EditorExportPlatformExtension::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const {
+ bool ret = false;
+ config_error = r_error;
+ config_missing_templates = r_missing_templates;
+ if (GDVIRTUAL_CALL(_can_export, p_preset, p_debug, ret)) {
+ r_error = config_error;
+ r_missing_templates = config_missing_templates;
+ return ret;
+ }
+ return EditorExportPlatform::can_export(p_preset, r_error, r_missing_templates, p_debug);
+}
+
+bool EditorExportPlatformExtension::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const {
+ bool ret = false;
+ config_error = r_error;
+ config_missing_templates = r_missing_templates;
+ if (GDVIRTUAL_REQUIRED_CALL(_has_valid_export_configuration, p_preset, p_debug, ret)) {
+ r_error = config_error;
+ r_missing_templates = config_missing_templates;
+ }
+ return ret;
+}
+
+bool EditorExportPlatformExtension::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
+ bool ret = false;
+ config_error = r_error;
+ if (GDVIRTUAL_REQUIRED_CALL(_has_valid_project_configuration, p_preset, ret)) {
+ r_error = config_error;
+ }
+ return ret;
+}
+
+List<String> EditorExportPlatformExtension::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
+ List<String> ret_list;
+ Vector<String> ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_binary_extensions, p_preset, ret)) {
+ for (const String &E : ret) {
+ ret_list.push_back(E);
+ }
+ }
+ return ret_list;
+}
+
+Error EditorExportPlatformExtension::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
+ ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
+
+ Error ret = FAILED;
+ GDVIRTUAL_REQUIRED_CALL(_export_project, p_preset, p_debug, p_path, p_flags, ret);
+ return ret;
+}
+
+Error EditorExportPlatformExtension::export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
+ ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
+
+ Error ret = FAILED;
+ if (GDVIRTUAL_CALL(_export_pack, p_preset, p_debug, p_path, p_flags, ret)) {
+ return ret;
+ }
+ return save_pack(p_preset, p_debug, p_path);
+}
+
+Error EditorExportPlatformExtension::export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
+ ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
+
+ Error ret = FAILED;
+ if (GDVIRTUAL_CALL(_export_zip, p_preset, p_debug, p_path, p_flags, ret)) {
+ return ret;
+ }
+ return save_zip(p_preset, p_debug, p_path);
+}
+
+void EditorExportPlatformExtension::get_platform_features(List<String> *r_features) const {
+ Vector<String> ret;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_platform_features, ret) && r_features) {
+ for (const String &E : ret) {
+ r_features->push_back(E);
+ }
+ }
+}
+
+String EditorExportPlatformExtension::get_debug_protocol() const {
+ String ret;
+ if (GDVIRTUAL_CALL(_get_debug_protocol, ret)) {
+ return ret;
+ }
+ return EditorExportPlatform::get_debug_protocol();
+}
+
+EditorExportPlatformExtension::EditorExportPlatformExtension() {
+ //NOP
+}
+
+EditorExportPlatformExtension::~EditorExportPlatformExtension() {
+ //NOP
+}
diff --git a/editor/export/editor_export_platform_extension.h b/editor/export/editor_export_platform_extension.h
new file mode 100644
index 0000000000..6391e65ac1
--- /dev/null
+++ b/editor/export/editor_export_platform_extension.h
@@ -0,0 +1,149 @@
+/**************************************************************************/
+/* editor_export_platform_extension.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef EDITOR_EXPORT_PLATFORM_EXTENSION_H
+#define EDITOR_EXPORT_PLATFORM_EXTENSION_H
+
+#include "editor_export_platform.h"
+#include "editor_export_preset.h"
+
+class EditorExportPlatformExtension : public EditorExportPlatform {
+ GDCLASS(EditorExportPlatformExtension, EditorExportPlatform);
+
+ mutable String config_error;
+ mutable bool config_missing_templates = false;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override;
+ GDVIRTUAL1RC(Vector<String>, _get_preset_features, Ref<EditorExportPreset>);
+
+ virtual bool is_executable(const String &p_path) const override;
+ GDVIRTUAL1RC(bool, _is_executable, const String &);
+
+ virtual void get_export_options(List<ExportOption> *r_options) const override;
+ GDVIRTUAL0RC(TypedArray<Dictionary>, _get_export_options);
+
+ virtual bool should_update_export_options() override;
+ GDVIRTUAL0R(bool, _should_update_export_options);
+
+ virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const override;
+ GDVIRTUAL2RC(bool, _get_export_option_visibility, Ref<EditorExportPreset>, const String &);
+
+ virtual String get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const override;
+ GDVIRTUAL2RC(String, _get_export_option_warning, Ref<EditorExportPreset>, const StringName &);
+
+ virtual String get_os_name() const override;
+ GDVIRTUAL0RC(String, _get_os_name);
+
+ virtual String get_name() const override;
+ GDVIRTUAL0RC(String, _get_name);
+
+ virtual Ref<Texture2D> get_logo() const override;
+ GDVIRTUAL0RC(Ref<Texture2D>, _get_logo);
+
+ virtual bool poll_export() override;
+ GDVIRTUAL0R(bool, _poll_export);
+
+ virtual int get_options_count() const override;
+ GDVIRTUAL0RC(int, _get_options_count);
+
+ virtual String get_options_tooltip() const override;
+ GDVIRTUAL0RC(String, _get_options_tooltip);
+
+ virtual Ref<ImageTexture> get_option_icon(int p_index) const override;
+ GDVIRTUAL1RC(Ref<ImageTexture>, _get_option_icon, int);
+
+ virtual String get_option_label(int p_device) const override;
+ GDVIRTUAL1RC(String, _get_option_label, int);
+
+ virtual String get_option_tooltip(int p_device) const override;
+ GDVIRTUAL1RC(String, _get_option_tooltip, int);
+
+ virtual String get_device_architecture(int p_device) const override;
+ GDVIRTUAL1RC(String, _get_device_architecture, int);
+
+ virtual void cleanup() override;
+ GDVIRTUAL0(_cleanup);
+
+ virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) override;
+ GDVIRTUAL3R(Error, _run, Ref<EditorExportPreset>, int, BitField<EditorExportPlatform::DebugFlags>);
+
+ virtual Ref<Texture2D> get_run_icon() const override;
+ GDVIRTUAL0RC(Ref<Texture2D>, _get_run_icon);
+
+ void set_config_error(const String &p_error) const {
+ config_error = p_error;
+ }
+ String get_config_error() const {
+ return config_error;
+ }
+
+ void set_config_missing_templates(bool p_missing_templates) const {
+ config_missing_templates = p_missing_templates;
+ }
+ bool get_config_missing_templates() const {
+ return config_missing_templates;
+ }
+
+ virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override;
+ GDVIRTUAL2RC(bool, _can_export, Ref<EditorExportPreset>, bool);
+
+ virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override;
+ GDVIRTUAL2RC(bool, _has_valid_export_configuration, Ref<EditorExportPreset>, bool);
+
+ virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override;
+ GDVIRTUAL1RC(bool, _has_valid_project_configuration, Ref<EditorExportPreset>);
+
+ virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
+ GDVIRTUAL1RC(Vector<String>, _get_binary_extensions, Ref<EditorExportPreset>);
+
+ virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
+ GDVIRTUAL4R(Error, _export_project, Ref<EditorExportPreset>, bool, const String &, BitField<EditorExportPlatform::DebugFlags>);
+
+ virtual Error export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
+ GDVIRTUAL4R(Error, _export_pack, Ref<EditorExportPreset>, bool, const String &, BitField<EditorExportPlatform::DebugFlags>);
+
+ virtual Error export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
+ GDVIRTUAL4R(Error, _export_zip, Ref<EditorExportPreset>, bool, const String &, BitField<EditorExportPlatform::DebugFlags>);
+
+ virtual void get_platform_features(List<String> *r_features) const override;
+ GDVIRTUAL0RC(Vector<String>, _get_platform_features);
+
+ virtual String get_debug_protocol() const override;
+ GDVIRTUAL0RC(String, _get_debug_protocol);
+
+ EditorExportPlatformExtension();
+ ~EditorExportPlatformExtension();
+};
+
+#endif // EDITOR_EXPORT_PLATFORM_EXTENSION_H
diff --git a/editor/export/editor_export_platform_pc.cpp b/editor/export/editor_export_platform_pc.cpp
index cdaf18b346..24d89b7f34 100644
--- a/editor/export/editor_export_platform_pc.cpp
+++ b/editor/export/editor_export_platform_pc.cpp
@@ -115,7 +115,7 @@ bool EditorExportPlatformPC::has_valid_project_configuration(const Ref<EditorExp
return true;
}
-Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
+Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
Error err = prepare_template(p_preset, p_debug, p_path, p_flags);
@@ -129,7 +129,7 @@ Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_pr
return err;
}
-Error EditorExportPlatformPC::prepare_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
+Error EditorExportPlatformPC::prepare_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
if (!DirAccess::exists(p_path.get_base_dir())) {
add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Template"), TTR("The given export path doesn't exist."));
return ERR_FILE_BAD_PATH;
@@ -182,7 +182,7 @@ Error EditorExportPlatformPC::prepare_template(const Ref<EditorExportPreset> &p_
return err;
}
-Error EditorExportPlatformPC::export_project_data(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
+Error EditorExportPlatformPC::export_project_data(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
String pck_path;
if (p_preset->get("binary_format/embed_pck")) {
pck_path = p_path;
diff --git a/editor/export/editor_export_platform_pc.h b/editor/export/editor_export_platform_pc.h
index 53574c2333..668ddaf47e 100644
--- a/editor/export/editor_export_platform_pc.h
+++ b/editor/export/editor_export_platform_pc.h
@@ -54,13 +54,13 @@ public:
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override;
virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override;
- virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
+ virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
virtual String get_template_file_name(const String &p_target, const String &p_arch) const = 0;
- virtual Error prepare_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags);
- virtual Error modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { return OK; };
- virtual Error export_project_data(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags);
+ virtual Error prepare_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags);
+ virtual Error modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) { return OK; }
+ virtual Error export_project_data(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags);
void set_name(const String &p_name);
void set_os_name(const String &p_name);
diff --git a/editor/export/editor_export_plugin.cpp b/editor/export/editor_export_plugin.cpp
index 28d0750d5a..f56dd61e3b 100644
--- a/editor/export/editor_export_plugin.cpp
+++ b/editor/export/editor_export_plugin.cpp
@@ -48,6 +48,14 @@ Ref<EditorExportPreset> EditorExportPlugin::get_export_preset() const {
return export_preset;
}
+Ref<EditorExportPlatform> EditorExportPlugin::get_export_platform() const {
+ if (export_preset.is_valid()) {
+ return export_preset->get_platform();
+ } else {
+ return Ref<EditorExportPlatform>();
+ }
+}
+
void EditorExportPlugin::add_file(const String &p_path, const Vector<uint8_t> &p_file, bool p_remap) {
ExtraFile ef;
ef.data = p_file;
@@ -321,6 +329,9 @@ void EditorExportPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("skip"), &EditorExportPlugin::skip);
ClassDB::bind_method(D_METHOD("get_option", "name"), &EditorExportPlugin::get_option);
+ ClassDB::bind_method(D_METHOD("get_export_preset"), &EditorExportPlugin::get_export_preset);
+ ClassDB::bind_method(D_METHOD("get_export_platform"), &EditorExportPlugin::get_export_platform);
+
GDVIRTUAL_BIND(_export_file, "path", "type", "features");
GDVIRTUAL_BIND(_export_begin, "features", "is_debug", "path", "flags");
GDVIRTUAL_BIND(_export_end);
diff --git a/editor/export/editor_export_plugin.h b/editor/export/editor_export_plugin.h
index 56eea85010..7a355614c7 100644
--- a/editor/export/editor_export_plugin.h
+++ b/editor/export/editor_export_plugin.h
@@ -91,6 +91,7 @@ class EditorExportPlugin : public RefCounted {
protected:
void set_export_preset(const Ref<EditorExportPreset> &p_preset);
Ref<EditorExportPreset> get_export_preset() const;
+ Ref<EditorExportPlatform> get_export_platform() const;
void add_file(const String &p_path, const Vector<uint8_t> &p_file, bool p_remap);
void add_shared_object(const String &p_path, const Vector<String> &tags, const String &p_target = String());
diff --git a/editor/export/editor_export_preset.cpp b/editor/export/editor_export_preset.cpp
index e2e3e9d154..9f805666d0 100644
--- a/editor/export/editor_export_preset.cpp
+++ b/editor/export/editor_export_preset.cpp
@@ -62,6 +62,48 @@ bool EditorExportPreset::_get(const StringName &p_name, Variant &r_ret) const {
void EditorExportPreset::_bind_methods() {
ClassDB::bind_method(D_METHOD("_get_property_warning", "name"), &EditorExportPreset::_get_property_warning);
+
+ ClassDB::bind_method(D_METHOD("has", "property"), &EditorExportPreset::has);
+
+ ClassDB::bind_method(D_METHOD("get_files_to_export"), &EditorExportPreset::get_files_to_export);
+ ClassDB::bind_method(D_METHOD("get_customized_files"), &EditorExportPreset::get_customized_files);
+ ClassDB::bind_method(D_METHOD("get_customized_files_count"), &EditorExportPreset::get_customized_files_count);
+ ClassDB::bind_method(D_METHOD("has_export_file", "path"), &EditorExportPreset::has_export_file);
+ ClassDB::bind_method(D_METHOD("get_file_export_mode", "path", "default"), &EditorExportPreset::get_file_export_mode, DEFVAL(MODE_FILE_NOT_CUSTOMIZED));
+
+ ClassDB::bind_method(D_METHOD("get_preset_name"), &EditorExportPreset::get_name);
+ ClassDB::bind_method(D_METHOD("is_runnable"), &EditorExportPreset::is_runnable);
+ ClassDB::bind_method(D_METHOD("are_advanced_options_enabled"), &EditorExportPreset::are_advanced_options_enabled);
+ ClassDB::bind_method(D_METHOD("is_dedicated_server"), &EditorExportPreset::is_dedicated_server);
+ ClassDB::bind_method(D_METHOD("get_export_filter"), &EditorExportPreset::get_export_filter);
+ ClassDB::bind_method(D_METHOD("get_include_filter"), &EditorExportPreset::get_include_filter);
+ ClassDB::bind_method(D_METHOD("get_exclude_filter"), &EditorExportPreset::get_exclude_filter);
+ ClassDB::bind_method(D_METHOD("get_custom_features"), &EditorExportPreset::get_custom_features);
+ ClassDB::bind_method(D_METHOD("get_export_path"), &EditorExportPreset::get_export_path);
+ ClassDB::bind_method(D_METHOD("get_encryption_in_filter"), &EditorExportPreset::get_enc_in_filter);
+ ClassDB::bind_method(D_METHOD("get_encryption_ex_filter"), &EditorExportPreset::get_enc_ex_filter);
+ ClassDB::bind_method(D_METHOD("get_encrypt_pck"), &EditorExportPreset::get_enc_pck);
+ ClassDB::bind_method(D_METHOD("get_encrypt_directory"), &EditorExportPreset::get_enc_directory);
+ ClassDB::bind_method(D_METHOD("get_encryption_key"), &EditorExportPreset::get_script_encryption_key);
+ ClassDB::bind_method(D_METHOD("get_script_export_mode"), &EditorExportPreset::get_script_export_mode);
+
+ ClassDB::bind_method(D_METHOD("get_or_env", "name", "env_var"), &EditorExportPreset::_get_or_env);
+ ClassDB::bind_method(D_METHOD("get_version", "name", "windows_version"), &EditorExportPreset::get_version);
+
+ BIND_ENUM_CONSTANT(EXPORT_ALL_RESOURCES);
+ BIND_ENUM_CONSTANT(EXPORT_SELECTED_SCENES);
+ BIND_ENUM_CONSTANT(EXPORT_SELECTED_RESOURCES);
+ BIND_ENUM_CONSTANT(EXCLUDE_SELECTED_RESOURCES);
+ BIND_ENUM_CONSTANT(EXPORT_CUSTOMIZED);
+
+ BIND_ENUM_CONSTANT(MODE_FILE_NOT_CUSTOMIZED);
+ BIND_ENUM_CONSTANT(MODE_FILE_STRIP);
+ BIND_ENUM_CONSTANT(MODE_FILE_KEEP);
+ BIND_ENUM_CONSTANT(MODE_FILE_REMOVE);
+
+ BIND_ENUM_CONSTANT(MODE_SCRIPT_TEXT);
+ BIND_ENUM_CONSTANT(MODE_SCRIPT_BINARY_TOKENS);
+ BIND_ENUM_CONSTANT(MODE_SCRIPT_BINARY_TOKENS_COMPRESSED);
}
String EditorExportPreset::_get_property_warning(const StringName &p_name) const {
diff --git a/editor/export/editor_export_preset.h b/editor/export/editor_export_preset.h
index c6a8808af1..f220477461 100644
--- a/editor/export/editor_export_preset.h
+++ b/editor/export/editor_export_preset.h
@@ -168,6 +168,9 @@ public:
void set_script_export_mode(int p_mode);
int get_script_export_mode() const;
+ Variant _get_or_env(const StringName &p_name, const String &p_env_var) const {
+ return get_or_env(p_name, p_env_var);
+ }
Variant get_or_env(const StringName &p_name, const String &p_env_var, bool *r_valid = nullptr) const;
// Return the preset's version number, or fall back to the
@@ -183,4 +186,8 @@ public:
EditorExportPreset();
};
+VARIANT_ENUM_CAST(EditorExportPreset::ExportFilter);
+VARIANT_ENUM_CAST(EditorExportPreset::FileExportMode);
+VARIANT_ENUM_CAST(EditorExportPreset::ScriptExportMode);
+
#endif // EDITOR_EXPORT_PRESET_H
diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp
index 853e3e813b..b2795e4984 100644
--- a/editor/import/3d/resource_importer_scene.cpp
+++ b/editor/import/3d/resource_importer_scene.cpp
@@ -649,6 +649,9 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, HashMap<R
String name = p_node->get_name();
NodePath original_path = p_root->get_path_to(p_node); // Used to detect renames due to import hints.
+ Ref<Resource> original_meta = memnew(Resource); // Create temp resource to hold original meta
+ original_meta->merge_meta_from(p_node);
+
bool isroot = p_node == p_root;
if (!isroot && _teststr(name, "noimp")) {
@@ -1022,6 +1025,8 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, HashMap<R
print_verbose(vformat("Fix: Renamed %s to %s", original_path, new_path));
r_node_renames.push_back({ original_path, p_node });
}
+ // If we created new node instead, merge meta values from the original node.
+ p_node->merge_meta_from(*original_meta);
}
return p_node;
@@ -2452,6 +2457,8 @@ Node *ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_
mesh_node->set_transform(src_mesh_node->get_transform());
mesh_node->set_skin(src_mesh_node->get_skin());
mesh_node->set_skeleton_path(src_mesh_node->get_skeleton_path());
+ mesh_node->merge_meta_from(src_mesh_node);
+
if (src_mesh_node->get_mesh().is_valid()) {
Ref<ArrayMesh> mesh;
if (!src_mesh_node->get_mesh()->has_mesh()) {
@@ -2599,6 +2606,7 @@ Node *ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_
for (int i = 0; i < mesh->get_surface_count(); i++) {
mesh_node->set_surface_override_material(i, src_mesh_node->get_surface_material(i));
}
+ mesh->merge_meta_from(*src_mesh_node->get_mesh());
}
}
@@ -3114,7 +3122,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
progress.step(TTR("Saving..."), 104);
int flags = 0;
- if (EDITOR_GET("filesystem/on_save/compress_binary_resources")) {
+ if (EditorSettings::get_singleton() && EDITOR_GET("filesystem/on_save/compress_binary_resources")) {
flags |= ResourceSaver::FLAG_COMPRESS;
}
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index 660e4647a1..bed9983461 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -2165,7 +2165,7 @@ void AnimationPlayerEditorPlugin::_property_keyed(const String &p_keyed, const V
return;
}
te->_clear_selection();
- te->insert_value_key(p_keyed, p_value, p_advance);
+ te->insert_value_key(p_keyed, p_advance);
}
void AnimationPlayerEditorPlugin::_transform_key_request(Object *sp, const String &p_sub, const Transform3D &p_key) {
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index ec42cb31be..6e41e98360 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -4331,13 +4331,13 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation,
Node2D *n2d = Object::cast_to<Node2D>(ci);
if (key_pos && p_location) {
- te->insert_node_value_key(n2d, "position", n2d->get_position(), p_on_existing);
+ te->insert_node_value_key(n2d, "position", p_on_existing);
}
if (key_rot && p_rotation) {
- te->insert_node_value_key(n2d, "rotation", n2d->get_rotation(), p_on_existing);
+ te->insert_node_value_key(n2d, "rotation", p_on_existing);
}
if (key_scale && p_scale) {
- te->insert_node_value_key(n2d, "scale", n2d->get_scale(), p_on_existing);
+ te->insert_node_value_key(n2d, "scale", p_on_existing);
}
if (n2d->has_meta("_edit_bone_") && n2d->get_parent_item()) {
@@ -4363,13 +4363,13 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation,
if (has_chain && ik_chain.size()) {
for (Node2D *&F : ik_chain) {
if (key_pos) {
- te->insert_node_value_key(F, "position", F->get_position(), p_on_existing);
+ te->insert_node_value_key(F, "position", p_on_existing);
}
if (key_rot) {
- te->insert_node_value_key(F, "rotation", F->get_rotation(), p_on_existing);
+ te->insert_node_value_key(F, "rotation", p_on_existing);
}
if (key_scale) {
- te->insert_node_value_key(F, "scale", F->get_scale(), p_on_existing);
+ te->insert_node_value_key(F, "scale", p_on_existing);
}
}
}
@@ -4379,13 +4379,13 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation,
Control *ctrl = Object::cast_to<Control>(ci);
if (key_pos) {
- te->insert_node_value_key(ctrl, "position", ctrl->get_position(), p_on_existing);
+ te->insert_node_value_key(ctrl, "position", p_on_existing);
}
if (key_rot) {
- te->insert_node_value_key(ctrl, "rotation", ctrl->get_rotation(), p_on_existing);
+ te->insert_node_value_key(ctrl, "rotation", p_on_existing);
}
if (key_scale) {
- te->insert_node_value_key(ctrl, "size", ctrl->get_size(), p_on_existing);
+ te->insert_node_value_key(ctrl, "size", p_on_existing);
}
}
}
diff --git a/editor/plugins/editor_plugin.cpp b/editor/plugins/editor_plugin.cpp
index d9f60e155d..8ce667568f 100644
--- a/editor/plugins/editor_plugin.cpp
+++ b/editor/plugins/editor_plugin.cpp
@@ -40,6 +40,7 @@
#include "editor/editor_translation_parser.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/export/editor_export.h"
+#include "editor/export/editor_export_platform.h"
#include "editor/gui/editor_bottom_panel.h"
#include "editor/gui/editor_title_bar.h"
#include "editor/import/3d/resource_importer_scene.h"
@@ -441,6 +442,16 @@ void EditorPlugin::remove_export_plugin(const Ref<EditorExportPlugin> &p_exporte
EditorExport::get_singleton()->remove_export_plugin(p_exporter);
}
+void EditorPlugin::add_export_platform(const Ref<EditorExportPlatform> &p_platform) {
+ ERR_FAIL_COND(p_platform.is_null());
+ EditorExport::get_singleton()->add_export_platform(p_platform);
+}
+
+void EditorPlugin::remove_export_platform(const Ref<EditorExportPlatform> &p_platform) {
+ ERR_FAIL_COND(p_platform.is_null());
+ EditorExport::get_singleton()->remove_export_platform(p_platform);
+}
+
void EditorPlugin::add_node_3d_gizmo_plugin(const Ref<EditorNode3DGizmoPlugin> &p_gizmo_plugin) {
ERR_FAIL_COND(!p_gizmo_plugin.is_valid());
Node3DEditor::get_singleton()->add_gizmo_plugin(p_gizmo_plugin);
@@ -608,6 +619,8 @@ void EditorPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_scene_post_import_plugin", "scene_import_plugin"), &EditorPlugin::remove_scene_post_import_plugin);
ClassDB::bind_method(D_METHOD("add_export_plugin", "plugin"), &EditorPlugin::add_export_plugin);
ClassDB::bind_method(D_METHOD("remove_export_plugin", "plugin"), &EditorPlugin::remove_export_plugin);
+ ClassDB::bind_method(D_METHOD("add_export_platform", "platform"), &EditorPlugin::add_export_platform);
+ ClassDB::bind_method(D_METHOD("remove_export_platform", "platform"), &EditorPlugin::remove_export_platform);
ClassDB::bind_method(D_METHOD("add_node_3d_gizmo_plugin", "plugin"), &EditorPlugin::add_node_3d_gizmo_plugin);
ClassDB::bind_method(D_METHOD("remove_node_3d_gizmo_plugin", "plugin"), &EditorPlugin::remove_node_3d_gizmo_plugin);
ClassDB::bind_method(D_METHOD("add_inspector_plugin", "plugin"), &EditorPlugin::add_inspector_plugin);
diff --git a/editor/plugins/editor_plugin.h b/editor/plugins/editor_plugin.h
index f6c4b35407..2e0771f906 100644
--- a/editor/plugins/editor_plugin.h
+++ b/editor/plugins/editor_plugin.h
@@ -41,6 +41,7 @@ class PopupMenu;
class EditorDebuggerPlugin;
class EditorExport;
class EditorExportPlugin;
+class EditorExportPlatform;
class EditorImportPlugin;
class EditorInspectorPlugin;
class EditorInterface;
@@ -224,6 +225,9 @@ public:
void add_export_plugin(const Ref<EditorExportPlugin> &p_exporter);
void remove_export_plugin(const Ref<EditorExportPlugin> &p_exporter);
+ void add_export_platform(const Ref<EditorExportPlatform> &p_platform);
+ void remove_export_platform(const Ref<EditorExportPlatform> &p_platform);
+
void add_node_3d_gizmo_plugin(const Ref<EditorNode3DGizmoPlugin> &p_gizmo_plugin);
void remove_node_3d_gizmo_plugin(const Ref<EditorNode3DGizmoPlugin> &p_gizmo_plugin);
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index a33fc47955..d7de5a7223 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -578,8 +578,8 @@ void ScriptEditor::_clear_breakpoints() {
script_editor_cache->get_sections(&cached_editors);
for (const String &E : cached_editors) {
Array breakpoints = _get_cached_breakpoints_for_script(E);
- for (int i = 0; i < breakpoints.size(); i++) {
- EditorDebuggerNode::get_singleton()->set_breakpoint(E, (int)breakpoints[i] + 1, false);
+ for (int breakpoint : breakpoints) {
+ EditorDebuggerNode::get_singleton()->set_breakpoint(E, (int)breakpoint + 1, false);
}
if (breakpoints.size() > 0) {
@@ -1820,6 +1820,48 @@ void ScriptEditor::notify_script_changed(const Ref<Script> &p_script) {
emit_signal(SNAME("editor_script_changed"), p_script);
}
+Vector<String> ScriptEditor::_get_breakpoints() {
+ Vector<String> ret;
+ HashSet<String> loaded_scripts;
+ for (int i = 0; i < tab_container->get_tab_count(); i++) {
+ ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_tab_control(i));
+ if (!se) {
+ continue;
+ }
+
+ Ref<Script> scr = se->get_edited_resource();
+ if (scr.is_null()) {
+ continue;
+ }
+
+ String base = scr->get_path();
+ loaded_scripts.insert(base);
+ if (base.is_empty() || base.begins_with("local://")) {
+ continue;
+ }
+
+ PackedInt32Array bpoints = se->get_breakpoints();
+ for (int32_t bpoint : bpoints) {
+ ret.push_back(base + ":" + itos((int)bpoint + 1));
+ }
+ }
+
+ // Load breakpoints that are in closed scripts.
+ List<String> cached_editors;
+ script_editor_cache->get_sections(&cached_editors);
+ for (const String &E : cached_editors) {
+ if (loaded_scripts.has(E)) {
+ continue;
+ }
+
+ Array breakpoints = _get_cached_breakpoints_for_script(E);
+ for (int breakpoint : breakpoints) {
+ ret.push_back(E + ":" + itos((int)breakpoint + 1));
+ }
+ }
+ return ret;
+}
+
void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) {
HashSet<String> loaded_scripts;
for (int i = 0; i < tab_container->get_tab_count(); i++) {
@@ -1829,19 +1871,19 @@ void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) {
}
Ref<Script> scr = se->get_edited_resource();
- if (scr == nullptr) {
+ if (scr.is_null()) {
continue;
}
String base = scr->get_path();
loaded_scripts.insert(base);
- if (base.begins_with("local://") || base.is_empty()) {
+ if (base.is_empty() || base.begins_with("local://")) {
continue;
}
PackedInt32Array bpoints = se->get_breakpoints();
- for (int j = 0; j < bpoints.size(); j++) {
- p_breakpoints->push_back(base + ":" + itos((int)bpoints[j] + 1));
+ for (int32_t bpoint : bpoints) {
+ p_breakpoints->push_back(base + ":" + itos((int)bpoint + 1));
}
}
@@ -1854,8 +1896,8 @@ void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) {
}
Array breakpoints = _get_cached_breakpoints_for_script(E);
- for (int i = 0; i < breakpoints.size(); i++) {
- p_breakpoints->push_back(E + ":" + itos((int)breakpoints[i] + 1));
+ for (int breakpoint : breakpoints) {
+ p_breakpoints->push_back(E + ":" + itos((int)breakpoint + 1));
}
}
}
@@ -2944,8 +2986,8 @@ void ScriptEditor::_files_moved(const String &p_old_file, const String &p_new_fi
// If Script, update breakpoints with debugger.
Array breakpoints = _get_cached_breakpoints_for_script(p_new_file);
- for (int i = 0; i < breakpoints.size(); i++) {
- int line = (int)breakpoints[i] + 1;
+ for (int breakpoint : breakpoints) {
+ int line = (int)breakpoint + 1;
EditorDebuggerNode::get_singleton()->set_breakpoint(p_old_file, line, false);
if (!p_new_file.begins_with("local://") && ResourceLoader::exists(p_new_file, "Script")) {
EditorDebuggerNode::get_singleton()->set_breakpoint(p_new_file, line, true);
@@ -2969,8 +3011,8 @@ void ScriptEditor::_file_removed(const String &p_removed_file) {
// Check closed.
if (script_editor_cache->has_section(p_removed_file)) {
Array breakpoints = _get_cached_breakpoints_for_script(p_removed_file);
- for (int i = 0; i < breakpoints.size(); i++) {
- EditorDebuggerNode::get_singleton()->set_breakpoint(p_removed_file, (int)breakpoints[i] + 1, false);
+ for (int breakpoint : breakpoints) {
+ EditorDebuggerNode::get_singleton()->set_breakpoint(p_removed_file, (int)breakpoint + 1, false);
}
script_editor_cache->erase_section(p_removed_file);
}
@@ -3425,8 +3467,8 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) {
}
Array breakpoints = _get_cached_breakpoints_for_script(E);
- for (int i = 0; i < breakpoints.size(); i++) {
- EditorDebuggerNode::get_singleton()->set_breakpoint(E, (int)breakpoints[i] + 1, true);
+ for (int breakpoint : breakpoints) {
+ EditorDebuggerNode::get_singleton()->set_breakpoint(E, (int)breakpoint + 1, true);
}
}
@@ -3972,6 +4014,7 @@ void ScriptEditor::_bind_methods() {
ClassDB::bind_method("_help_tab_goto", &ScriptEditor::_help_tab_goto);
ClassDB::bind_method("get_current_editor", &ScriptEditor::_get_current_editor);
ClassDB::bind_method("get_open_script_editors", &ScriptEditor::_get_open_script_editors);
+ ClassDB::bind_method("get_breakpoints", &ScriptEditor::_get_breakpoints);
ClassDB::bind_method(D_METHOD("register_syntax_highlighter", "syntax_highlighter"), &ScriptEditor::register_syntax_highlighter);
ClassDB::bind_method(D_METHOD("unregister_syntax_highlighter", "syntax_highlighter"), &ScriptEditor::unregister_syntax_highlighter);
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index 66885790a7..e0fac5d0c6 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -539,6 +539,7 @@ public:
_FORCE_INLINE_ bool edit(const Ref<Resource> &p_resource, bool p_grab_focus = true) { return edit(p_resource, -1, 0, p_grab_focus); }
bool edit(const Ref<Resource> &p_resource, int p_line, int p_col, bool p_grab_focus = true);
+ Vector<String> _get_breakpoints();
void get_breakpoints(List<String> *p_breakpoints);
PackedStringArray get_unsaved_scripts() const;
diff --git a/editor/register_editor_types.cpp b/editor/register_editor_types.cpp
index 610ad3efdf..8e135e7eae 100644
--- a/editor/register_editor_types.cpp
+++ b/editor/register_editor_types.cpp
@@ -46,6 +46,7 @@
#include "editor/editor_translation_parser.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/export/editor_export_platform.h"
+#include "editor/export/editor_export_platform_extension.h"
#include "editor/export/editor_export_platform_pc.h"
#include "editor/export/editor_export_plugin.h"
#include "editor/filesystem_dock.h"
@@ -161,6 +162,8 @@ void register_editor_types() {
GDREGISTER_CLASS(EditorExportPlugin);
GDREGISTER_ABSTRACT_CLASS(EditorExportPlatform);
GDREGISTER_ABSTRACT_CLASS(EditorExportPlatformPC);
+ GDREGISTER_CLASS(EditorExportPlatformExtension);
+ GDREGISTER_ABSTRACT_CLASS(EditorExportPreset);
register_exporter_types();
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index c72755642b..36c5ad0937 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -57,6 +57,7 @@
#include "scene/scene_string_names.h"
#ifdef TOOLS_ENABLED
+#include "core/extension/gdextension_manager.h"
#include "editor/editor_paths.h"
#endif
@@ -2177,9 +2178,26 @@ void GDScriptLanguage::_add_global(const StringName &p_name, const Variant &p_va
global_array.write[globals[p_name]] = p_value;
return;
}
- globals[p_name] = global_array.size();
- global_array.push_back(p_value);
- _global_array = global_array.ptrw();
+
+ if (global_array_empty_indexes.size()) {
+ int index = global_array_empty_indexes[global_array_empty_indexes.size() - 1];
+ globals[p_name] = index;
+ global_array.write[index] = p_value;
+ global_array_empty_indexes.resize(global_array_empty_indexes.size() - 1);
+ } else {
+ globals[p_name] = global_array.size();
+ global_array.push_back(p_value);
+ _global_array = global_array.ptrw();
+ }
+}
+
+void GDScriptLanguage::_remove_global(const StringName &p_name) {
+ if (!globals.has(p_name)) {
+ return;
+ }
+ global_array_empty_indexes.push_back(globals[p_name]);
+ global_array.write[globals[p_name]] = Variant::NIL;
+ globals.erase(p_name);
}
void GDScriptLanguage::add_global_constant(const StringName &p_variable, const Variant &p_value) {
@@ -2237,11 +2255,40 @@ void GDScriptLanguage::init() {
_add_global(E.name, E.ptr);
}
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ GDExtensionManager::get_singleton()->connect("extension_loaded", callable_mp(this, &GDScriptLanguage::_extension_loaded));
+ GDExtensionManager::get_singleton()->connect("extension_unloading", callable_mp(this, &GDScriptLanguage::_extension_unloading));
+ }
+#endif
+
#ifdef TESTS_ENABLED
GDScriptTests::GDScriptTestRunner::handle_cmdline();
#endif
}
+#ifdef TOOLS_ENABLED
+void GDScriptLanguage::_extension_loaded(const Ref<GDExtension> &p_extension) {
+ List<StringName> class_list;
+ ClassDB::get_extension_class_list(p_extension, &class_list);
+ for (const StringName &n : class_list) {
+ if (globals.has(n)) {
+ continue;
+ }
+ Ref<GDScriptNativeClass> nc = memnew(GDScriptNativeClass(n));
+ _add_global(n, nc);
+ }
+}
+
+void GDScriptLanguage::_extension_unloading(const Ref<GDExtension> &p_extension) {
+ List<StringName> class_list;
+ ClassDB::get_extension_class_list(p_extension, &class_list);
+ for (const StringName &n : class_list) {
+ _remove_global(n);
+ }
+}
+#endif
+
String GDScriptLanguage::get_type() const {
return "GDScript";
}
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 6527a0ea4d..4d21651365 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -417,6 +417,7 @@ class GDScriptLanguage : public ScriptLanguage {
Vector<Variant> global_array;
HashMap<StringName, int> globals;
HashMap<StringName, Variant> named_globals;
+ Vector<int> global_array_empty_indexes;
struct CallLevel {
Variant *stack = nullptr;
@@ -448,6 +449,7 @@ class GDScriptLanguage : public ScriptLanguage {
int _debug_max_call_stack = 0;
void _add_global(const StringName &p_name, const Variant &p_value);
+ void _remove_global(const StringName &p_name);
friend class GDScriptInstance;
@@ -467,6 +469,11 @@ class GDScriptLanguage : public ScriptLanguage {
HashMap<String, ObjectID> orphan_subclasses;
+#ifdef TOOLS_ENABLED
+ void _extension_loaded(const Ref<GDExtension> &p_extension);
+ void _extension_unloading(const Ref<GDExtension> &p_extension);
+#endif
+
public:
int calls;
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 77bba940cb..69973a34dd 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -69,6 +69,24 @@
#include <stdlib.h>
#include <cstdint>
+static void _attach_extras_to_meta(const Dictionary &p_extras, Ref<Resource> p_node) {
+ if (!p_extras.is_empty()) {
+ p_node->set_meta("extras", p_extras);
+ }
+}
+
+static void _attach_meta_to_extras(Ref<Resource> p_node, Dictionary &p_json) {
+ if (p_node->has_meta("extras")) {
+ Dictionary node_extras = p_node->get_meta("extras");
+ if (p_json.has("extras")) {
+ Dictionary extras = p_json["extras"];
+ extras.merge(node_extras);
+ } else {
+ p_json["extras"] = node_extras;
+ }
+ }
+}
+
static Ref<ImporterMesh> _mesh_to_importer_mesh(Ref<Mesh> p_mesh) {
Ref<ImporterMesh> importer_mesh;
importer_mesh.instantiate();
@@ -101,6 +119,7 @@ static Ref<ImporterMesh> _mesh_to_importer_mesh(Ref<Mesh> p_mesh) {
array, p_mesh->surface_get_blend_shape_arrays(surface_i), p_mesh->surface_get_lods(surface_i), mat,
mat_name, p_mesh->surface_get_format(surface_i));
}
+ importer_mesh->merge_meta_from(*p_mesh);
return importer_mesh;
}
@@ -458,7 +477,7 @@ Error GLTFDocument::_serialize_nodes(Ref<GLTFState> p_state) {
if (extensions.is_empty()) {
node.erase("extensions");
}
-
+ _attach_meta_to_extras(gltf_node, node);
nodes.push_back(node);
}
if (!nodes.is_empty()) {
@@ -624,6 +643,10 @@ Error GLTFDocument::_parse_nodes(Ref<GLTFState> p_state) {
}
}
+ if (n.has("extras")) {
+ _attach_extras_to_meta(n["extras"], node);
+ }
+
if (n.has("children")) {
const Array &children = n["children"];
for (int j = 0; j < children.size(); j++) {
@@ -2727,6 +2750,8 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> p_state) {
Dictionary e;
e["targetNames"] = target_names;
+ gltf_mesh["extras"] = e;
+ _attach_meta_to_extras(import_mesh, gltf_mesh);
weights.resize(target_names.size());
for (int name_i = 0; name_i < target_names.size(); name_i++) {
@@ -2742,8 +2767,6 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> p_state) {
ERR_FAIL_COND_V(target_names.size() != weights.size(), FAILED);
- gltf_mesh["extras"] = e;
-
gltf_mesh["primitives"] = primitives;
meshes.push_back(gltf_mesh);
@@ -2776,6 +2799,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
Array primitives = d["primitives"];
const Dictionary &extras = d.has("extras") ? (Dictionary)d["extras"] : Dictionary();
+ _attach_extras_to_meta(extras, mesh);
Ref<ImporterMesh> import_mesh;
import_mesh.instantiate();
String mesh_name = "mesh";
@@ -4170,6 +4194,7 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> p_state) {
}
d["extensions"] = extensions;
+ _attach_meta_to_extras(material, d);
materials.push_back(d);
}
if (!materials.size()) {
@@ -4372,6 +4397,10 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) {
}
}
}
+
+ if (material_dict.has("extras")) {
+ _attach_extras_to_meta(material_dict["extras"], material);
+ }
p_state->materials.push_back(material);
}
@@ -5161,6 +5190,7 @@ ImporterMeshInstance3D *GLTFDocument::_generate_mesh_instance(Ref<GLTFState> p_s
return mi;
}
mi->set_mesh(import_mesh);
+ import_mesh->merge_meta_from(*mesh);
return mi;
}
@@ -5285,6 +5315,7 @@ void GLTFDocument::_convert_scene_node(Ref<GLTFState> p_state, Node *p_current,
gltf_root = current_node_i;
p_state->root_nodes.push_back(gltf_root);
}
+ gltf_node->merge_meta_from(p_current);
_create_gltf_node(p_state, p_current, current_node_i, p_gltf_parent, gltf_root, gltf_node);
for (int node_i = 0; node_i < p_current->get_child_count(); node_i++) {
_convert_scene_node(p_state, p_current->get_child(node_i), current_node_i, gltf_root);
@@ -5676,6 +5707,8 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> p_state, const GLTFNodeIn
current_node->set_transform(gltf_node->transform);
}
+ current_node->merge_meta_from(*gltf_node);
+
p_state->scene_nodes.insert(p_node_index, current_node);
for (int i = 0; i < gltf_node->children.size(); ++i) {
_generate_scene_node(p_state, gltf_node->children[i], current_node, p_scene_root);
diff --git a/modules/gltf/tests/test_gltf_extras.h b/modules/gltf/tests/test_gltf_extras.h
new file mode 100644
index 0000000000..96aadf3023
--- /dev/null
+++ b/modules/gltf/tests/test_gltf_extras.h
@@ -0,0 +1,165 @@
+/**************************************************************************/
+/* test_gltf_extras.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef TEST_GLTF_EXTRAS_H
+#define TEST_GLTF_EXTRAS_H
+
+#include "tests/test_macros.h"
+
+#ifdef TOOLS_ENABLED
+
+#include "core/os/os.h"
+#include "editor/import/3d/resource_importer_scene.h"
+#include "modules/gltf/editor/editor_scene_importer_gltf.h"
+#include "modules/gltf/gltf_document.h"
+#include "modules/gltf/gltf_state.h"
+#include "scene/3d/mesh_instance_3d.h"
+#include "scene/main/window.h"
+#include "scene/resources/3d/primitive_meshes.h"
+#include "scene/resources/material.h"
+#include "scene/resources/packed_scene.h"
+
+namespace TestGltfExtras {
+
+static Node *_gltf_export_then_import(Node *p_root, String &p_tempfilebase) {
+ Ref<GLTFDocument> doc;
+ doc.instantiate();
+ Ref<GLTFState> state;
+ state.instantiate();
+ Error err = doc->append_from_scene(p_root, state, EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS);
+ CHECK_MESSAGE(err == OK, "GLTF state generation failed.");
+ err = doc->write_to_filesystem(state, p_tempfilebase + ".gltf");
+ CHECK_MESSAGE(err == OK, "Writing GLTF to cache dir failed.");
+
+ // Setting up importers.
+ Ref<ResourceImporterScene> import_scene = memnew(ResourceImporterScene("PackedScene", true));
+ ResourceFormatImporter::get_singleton()->add_importer(import_scene);
+ Ref<EditorSceneFormatImporterGLTF> import_gltf;
+ import_gltf.instantiate();
+ ResourceImporterScene::add_scene_importer(import_gltf);
+
+ // GTLF importer behaves differently outside of editor, it's too late to modify Engine::get_editor_hint
+ // as the registration of runtime extensions already happened, so remove them. See modules/gltf/register_types.cpp
+ GLTFDocument::unregister_all_gltf_document_extensions();
+
+ HashMap<StringName, Variant> options(20);
+ options["nodes/root_type"] = "";
+ options["nodes/root_name"] = "";
+ options["nodes/apply_root_scale"] = true;
+ options["nodes/root_scale"] = 1.0;
+ options["meshes/ensure_tangents"] = true;
+ options["meshes/generate_lods"] = false;
+ options["meshes/create_shadow_meshes"] = true;
+ options["meshes/light_baking"] = 1;
+ options["meshes/lightmap_texel_size"] = 0.2;
+ options["meshes/force_disable_compression"] = false;
+ options["skins/use_named_skins"] = true;
+ options["animation/import"] = true;
+ options["animation/fps"] = 30;
+ options["animation/trimming"] = false;
+ options["animation/remove_immutable_tracks"] = true;
+ options["import_script/path"] = "";
+ options["_subresources"] = Dictionary();
+ options["gltf/naming_version"] = 1;
+
+ // Process gltf file, note that this generates `.scn` resource from the 2nd argument.
+ err = import_scene->import(p_tempfilebase + ".gltf", p_tempfilebase, options, nullptr, nullptr, nullptr);
+ CHECK_MESSAGE(err == OK, "GLTF import failed.");
+ ResourceImporterScene::remove_scene_importer(import_gltf);
+
+ Ref<PackedScene> packed_scene = ResourceLoader::load(p_tempfilebase + ".scn", "", ResourceFormatLoader::CACHE_MODE_REPLACE, &err);
+ CHECK_MESSAGE(err == OK, "Loading scene failed.");
+ Node *p_scene = packed_scene->instantiate();
+ return p_scene;
+}
+
+TEST_CASE("[SceneTree][Node] GLTF test mesh and material meta export and import") {
+ // Setup scene.
+ Ref<StandardMaterial3D> original_material = memnew(StandardMaterial3D);
+ original_material->set_albedo(Color(1.0, .0, .0));
+ original_material->set_name("material");
+ Dictionary material_dict;
+ material_dict["node_type"] = "material";
+ original_material->set_meta("extras", material_dict);
+
+ Ref<PlaneMesh> original_meshdata = memnew(PlaneMesh);
+ original_meshdata->set_name("planemesh");
+ Dictionary meshdata_dict;
+ meshdata_dict["node_type"] = "planemesh";
+ original_meshdata->set_meta("extras", meshdata_dict);
+ original_meshdata->surface_set_material(0, original_material);
+
+ MeshInstance3D *original_mesh_instance = memnew(MeshInstance3D);
+ original_mesh_instance->set_mesh(original_meshdata);
+ original_mesh_instance->set_name("mesh_instance_3d");
+ Dictionary mesh_instance_dict;
+ mesh_instance_dict["node_type"] = "mesh_instance_3d";
+ original_mesh_instance->set_meta("extras", mesh_instance_dict);
+
+ Node3D *original = memnew(Node3D);
+ SceneTree::get_singleton()->get_root()->add_child(original);
+ original->add_child(original_mesh_instance);
+ original->set_name("node3d");
+ Dictionary node_dict;
+ node_dict["node_type"] = "node3d";
+ original->set_meta("extras", node_dict);
+ original->set_meta("meta_not_nested_under_extras", "should not propagate");
+
+ // Convert to GLFT and back.
+ String tempfile = OS::get_singleton()->get_cache_path().path_join("gltf_extras");
+ Node *loaded = _gltf_export_then_import(original, tempfile);
+
+ // Compare the results.
+ CHECK(loaded->get_name() == "node3d");
+ CHECK(Dictionary(loaded->get_meta("extras")).size() == 1);
+ CHECK(Dictionary(loaded->get_meta("extras"))["node_type"] == "node3d");
+ CHECK_FALSE(loaded->has_meta("meta_not_nested_under_extras"));
+ CHECK_FALSE(Dictionary(loaded->get_meta("extras")).has("meta_not_nested_under_extras"));
+
+ MeshInstance3D *mesh_instance_3d = Object::cast_to<MeshInstance3D>(loaded->find_child("mesh_instance_3d", false, true));
+ CHECK(mesh_instance_3d->get_name() == "mesh_instance_3d");
+ CHECK(Dictionary(mesh_instance_3d->get_meta("extras"))["node_type"] == "mesh_instance_3d");
+
+ Ref<Mesh> mesh = mesh_instance_3d->get_mesh();
+ CHECK(Dictionary(mesh->get_meta("extras"))["node_type"] == "planemesh");
+
+ Ref<Material> material = mesh->surface_get_material(0);
+ CHECK(material->get_name() == "material");
+ CHECK(Dictionary(material->get_meta("extras"))["node_type"] == "material");
+
+ memdelete(original_mesh_instance);
+ memdelete(original);
+ memdelete(loaded);
+}
+} // namespace TestGltfExtras
+
+#endif // TOOLS_ENABLED
+
+#endif // TEST_GLTF_EXTRAS_H
diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp
index 5720f844bb..c6c35efb59 100644
--- a/modules/minimp3/audio_stream_mp3.cpp
+++ b/modules/minimp3/audio_stream_mp3.cpp
@@ -57,7 +57,7 @@ int AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) {
mp3dec_frame_info_t frame_info;
mp3d_sample_t *buf_frame = nullptr;
- int samples_mixed = mp3dec_ex_read_frame(mp3d, &buf_frame, &frame_info, mp3_stream->channels);
+ int samples_mixed = mp3dec_ex_read_frame(&mp3d, &buf_frame, &frame_info, mp3_stream->channels);
if (samples_mixed) {
p_buffer[p_frames - todo] = AudioFrame(buf_frame[0], buf_frame[samples_mixed - 1]);
@@ -70,7 +70,7 @@ int AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) {
if (beat_loop && (int)frames_mixed >= beat_length_frames) {
for (int i = 0; i < FADE_SIZE; i++) {
- samples_mixed = mp3dec_ex_read_frame(mp3d, &buf_frame, &frame_info, mp3_stream->channels);
+ samples_mixed = mp3dec_ex_read_frame(&mp3d, &buf_frame, &frame_info, mp3_stream->channels);
loop_fade[i] = AudioFrame(buf_frame[0], buf_frame[samples_mixed - 1]);
if (!samples_mixed) {
break;
@@ -138,7 +138,7 @@ void AudioStreamPlaybackMP3::seek(double p_time) {
}
frames_mixed = uint32_t(mp3_stream->sample_rate * p_time);
- mp3dec_ex_seek(mp3d, (uint64_t)frames_mixed * mp3_stream->channels);
+ mp3dec_ex_seek(&mp3d, (uint64_t)frames_mixed * mp3_stream->channels);
}
void AudioStreamPlaybackMP3::tag_used_streams() {
@@ -181,10 +181,7 @@ Variant AudioStreamPlaybackMP3::get_parameter(const StringName &p_name) const {
}
AudioStreamPlaybackMP3::~AudioStreamPlaybackMP3() {
- if (mp3d) {
- mp3dec_ex_close(mp3d);
- memfree(mp3d);
- }
+ mp3dec_ex_close(&mp3d);
}
Ref<AudioStreamPlayback> AudioStreamMP3::instantiate_playback() {
@@ -197,9 +194,8 @@ Ref<AudioStreamPlayback> AudioStreamMP3::instantiate_playback() {
mp3s.instantiate();
mp3s->mp3_stream = Ref<AudioStreamMP3>(this);
- mp3s->mp3d = (mp3dec_ex_t *)memalloc(sizeof(mp3dec_ex_t));
- int errorcode = mp3dec_ex_open_buf(mp3s->mp3d, data.ptr(), data_len, MP3D_SEEK_TO_SAMPLE);
+ int errorcode = mp3dec_ex_open_buf(&mp3s->mp3d, data.ptr(), data_len, MP3D_SEEK_TO_SAMPLE);
mp3s->frames_mixed = 0;
mp3s->active = false;
diff --git a/modules/minimp3/audio_stream_mp3.h b/modules/minimp3/audio_stream_mp3.h
index 81e8f8633c..39d389b8cd 100644
--- a/modules/minimp3/audio_stream_mp3.h
+++ b/modules/minimp3/audio_stream_mp3.h
@@ -49,7 +49,7 @@ class AudioStreamPlaybackMP3 : public AudioStreamPlaybackResampled {
bool looping_override = false;
bool looping = false;
- mp3dec_ex_t *mp3d = nullptr;
+ mp3dec_ex_t mp3d = {};
uint32_t frames_mixed = 0;
bool active = false;
int loops = 0;
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 0fdaca4839..391f9a7b37 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -2002,7 +2002,7 @@ String EditorExportPlatformAndroid::get_device_architecture(int p_index) const {
return devices[p_index].architecture;
}
-Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) {
+Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) {
ERR_FAIL_INDEX_V(p_device, devices.size(), ERR_INVALID_PARAMETER);
String can_export_error;
@@ -2024,11 +2024,11 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset,
}
const bool use_wifi_for_remote_debug = EDITOR_GET("export/android/use_wifi_for_remote_debug");
- const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT);
+ const bool use_remote = p_debug_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG) || p_debug_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT);
const bool use_reverse = devices[p_device].api_level >= 21 && !use_wifi_for_remote_debug;
if (use_reverse) {
- p_debug_flags |= DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST;
+ p_debug_flags.set_flag(DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST);
}
String tmp_export_path = EditorPaths::get_singleton()->get_cache_dir().path_join("tmpexport." + uitos(OS::get_singleton()->get_unix_time()) + ".apk");
@@ -2107,7 +2107,7 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset,
OS::get_singleton()->execute(adb, args, &output, &rv, true);
print_verbose(output);
- if (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) {
+ if (p_debug_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG)) {
int dbg_port = EDITOR_GET("network/debug/remote_port");
args.clear();
args.push_back("-s");
@@ -2122,7 +2122,7 @@ Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset,
print_line("Reverse result: " + itos(rv));
}
- if (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT) {
+ if (p_debug_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT)) {
int fs_port = EDITOR_GET("filesystem/file_server/port");
args.clear();
@@ -2667,7 +2667,7 @@ Error EditorExportPlatformAndroid::save_apk_expansion_file(const Ref<EditorExpor
return err;
}
-void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportPreset> &p_preset, const String &p_path, int p_flags, Vector<uint8_t> &r_command_line_flags) {
+void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportPreset> &p_preset, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags, Vector<uint8_t> &r_command_line_flags) {
String cmdline = p_preset->get("command_line/extra_args");
Vector<String> command_line_strings = cmdline.strip_edges().split(" ");
for (int i = 0; i < command_line_strings.size(); i++) {
@@ -2677,7 +2677,7 @@ void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportP
}
}
- gen_export_flags(command_line_strings, p_flags);
+ command_line_strings.append_array(gen_export_flags(p_flags));
bool apk_expansion = p_preset->get("apk_expansion/enable");
if (apk_expansion) {
@@ -3000,13 +3000,13 @@ bool EditorExportPlatformAndroid::_is_clean_build_required(const Ref<EditorExpor
return have_plugins_changed || has_build_dir_changed || first_build;
}
-Error EditorExportPlatformAndroid::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
+Error EditorExportPlatformAndroid::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
int export_format = int(p_preset->get("gradle_build/export_format"));
bool should_sign = p_preset->get("package/signed");
return export_project_helper(p_preset, p_debug, p_path, export_format, should_sign, p_flags);
}
-Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int export_format, bool should_sign, int p_flags) {
+Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int export_format, bool should_sign, BitField<EditorExportPlatform::DebugFlags> p_flags) {
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
const String base_dir = p_path.get_base_dir();
@@ -3022,7 +3022,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
bool use_gradle_build = bool(p_preset->get("gradle_build/use_gradle_build"));
String gradle_build_directory = use_gradle_build ? ExportTemplateManager::get_android_build_directory(p_preset) : "";
- bool p_give_internet = p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG);
+ bool p_give_internet = p_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT) || p_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG);
bool apk_expansion = p_preset->get("apk_expansion/enable");
Vector<ABI> enabled_abis = get_enabled_abis(p_preset);
@@ -3127,7 +3127,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
user_data.assets_directory = assets_directory;
user_data.libs_directory = gradle_build_directory.path_join("libs");
user_data.debug = p_debug;
- if (p_flags & DEBUG_FLAG_DUMB_CLIENT) {
+ if (p_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT)) {
err = export_project_files(p_preset, p_debug, ignore_apk_file, &user_data, copy_gradle_so);
} else {
err = export_project_files(p_preset, p_debug, rename_and_store_file_in_gradle_project, &user_data, copy_gradle_so);
@@ -3500,7 +3500,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
}
err = OK;
- if (p_flags & DEBUG_FLAG_DUMB_CLIENT) {
+ if (p_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT)) {
APKExportData ed;
ed.ep = &ep;
ed.apk = unaligned_apk;
diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h
index 97bbd0c7bc..708288fbf4 100644
--- a/platform/android/export/export_plugin.h
+++ b/platform/android/export/export_plugin.h
@@ -214,7 +214,7 @@ public:
virtual String get_device_architecture(int p_index) const override;
- virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override;
+ virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) override;
virtual Ref<Texture2D> get_run_icon() const override;
@@ -242,7 +242,7 @@ public:
Error save_apk_expansion_file(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
- void get_command_line_flags(const Ref<EditorExportPreset> &p_preset, const String &p_path, int p_flags, Vector<uint8_t> &r_command_line_flags);
+ void get_command_line_flags(const Ref<EditorExportPreset> &p_preset, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags, Vector<uint8_t> &r_command_line_flags);
Error sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep);
@@ -253,9 +253,9 @@ public:
static String join_list(const List<String> &p_parts, const String &p_separator);
static String join_abis(const Vector<ABI> &p_parts, const String &p_separator, bool p_use_arch);
- virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
+ virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
- Error export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int export_format, bool should_sign, int p_flags);
+ Error export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int export_format, bool should_sign, BitField<EditorExportPlatform::DebugFlags> p_flags);
virtual void get_platform_features(List<String> *r_features) const override;
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index e4b5392c4e..b99e825540 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -2017,11 +2017,11 @@ Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset>
return OK;
}
-Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
+Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
return _export_project_helper(p_preset, p_debug, p_path, p_flags, false, false);
}
-Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags, bool p_simulator, bool p_oneclick) {
+Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags, bool p_simulator, bool p_oneclick) {
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
const String dest_dir = p_path.get_base_dir() + "/";
@@ -2983,7 +2983,7 @@ void EditorExportPlatformIOS::_update_preset_status() {
}
#endif
-Error EditorExportPlatformIOS::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) {
+Error EditorExportPlatformIOS::run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) {
#ifdef MACOS_ENABLED
ERR_FAIL_INDEX_V(p_device, devices.size(), ERR_INVALID_PARAMETER);
@@ -3029,11 +3029,11 @@ Error EditorExportPlatformIOS::run(const Ref<EditorExportPreset> &p_preset, int
String host = EDITOR_GET("network/debug/remote_host");
int remote_port = (int)EDITOR_GET("network/debug/remote_port");
- if (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST) {
+ if (p_debug_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST)) {
host = "localhost";
}
- if (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT) {
+ if (p_debug_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT)) {
int port = EDITOR_GET("filesystem/file_server/port");
String passwd = EDITOR_GET("filesystem/file_server/password");
cmd_args_list.push_back("--remote-fs");
@@ -3044,7 +3044,7 @@ Error EditorExportPlatformIOS::run(const Ref<EditorExportPreset> &p_preset, int
}
}
- if (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) {
+ if (p_debug_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG)) {
cmd_args_list.push_back("--remote-debug");
cmd_args_list.push_back(get_debug_protocol() + host + ":" + String::num(remote_port));
@@ -3066,11 +3066,11 @@ Error EditorExportPlatformIOS::run(const Ref<EditorExportPreset> &p_preset, int
}
}
- if (p_debug_flags & DEBUG_FLAG_VIEW_COLLISIONS) {
+ if (p_debug_flags.has_flag(DEBUG_FLAG_VIEW_COLLISIONS)) {
cmd_args_list.push_back("--debug-collisions");
}
- if (p_debug_flags & DEBUG_FLAG_VIEW_NAVIGATION) {
+ if (p_debug_flags.has_flag(DEBUG_FLAG_VIEW_NAVIGATION)) {
cmd_args_list.push_back("--debug-navigation");
}
diff --git a/platform/ios/export/export_plugin.h b/platform/ios/export/export_plugin.h
index 1964906c27..db7c0553dd 100644
--- a/platform/ios/export/export_plugin.h
+++ b/platform/ios/export/export_plugin.h
@@ -146,7 +146,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
Error _export_additional_assets(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets);
Error _export_ios_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets, bool p_debug);
- Error _export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags, bool p_simulator, bool p_oneclick);
+ Error _export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags, bool p_simulator, bool p_oneclick);
bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const;
@@ -169,7 +169,7 @@ public:
virtual Ref<ImageTexture> get_option_icon(int p_index) const override;
virtual String get_option_label(int p_index) const override;
virtual String get_option_tooltip(int p_index) const override;
- virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override;
+ virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) override;
virtual bool poll_export() override {
bool dc = devices_changed.is_set();
@@ -202,7 +202,7 @@ public:
return list;
}
- virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
+ virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override;
virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override;
diff --git a/platform/linuxbsd/export/export_plugin.cpp b/platform/linuxbsd/export/export_plugin.cpp
index 0032b898d2..69ba742f72 100644
--- a/platform/linuxbsd/export/export_plugin.cpp
+++ b/platform/linuxbsd/export/export_plugin.cpp
@@ -60,7 +60,7 @@ Error EditorExportPlatformLinuxBSD::_export_debug_script(const Ref<EditorExportP
return OK;
}
-Error EditorExportPlatformLinuxBSD::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
+Error EditorExportPlatformLinuxBSD::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
String custom_debug = p_preset->get("custom_template/debug");
String custom_release = p_preset->get("custom_template/release");
String arch = p_preset->get("binary_format/architecture");
@@ -458,7 +458,7 @@ void EditorExportPlatformLinuxBSD::cleanup() {
cleanup_commands.clear();
}
-Error EditorExportPlatformLinuxBSD::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) {
+Error EditorExportPlatformLinuxBSD::run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) {
cleanup();
if (p_device) { // Stop command, cleanup only.
return OK;
@@ -512,8 +512,7 @@ Error EditorExportPlatformLinuxBSD::run(const Ref<EditorExportPreset> &p_preset,
String cmd_args;
{
- Vector<String> cmd_args_list;
- gen_debug_flags(cmd_args_list, p_debug_flags);
+ Vector<String> cmd_args_list = gen_export_flags(p_debug_flags);
for (int i = 0; i < cmd_args_list.size(); i++) {
if (i != 0) {
cmd_args += " ";
@@ -522,7 +521,7 @@ Error EditorExportPlatformLinuxBSD::run(const Ref<EditorExportPreset> &p_preset,
}
}
- const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT);
+ const bool use_remote = p_debug_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG) || p_debug_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT);
int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port");
print_line("Creating temporary directory...");
diff --git a/platform/linuxbsd/export/export_plugin.h b/platform/linuxbsd/export/export_plugin.h
index bbc55b82ce..1d9ef01d1a 100644
--- a/platform/linuxbsd/export/export_plugin.h
+++ b/platform/linuxbsd/export/export_plugin.h
@@ -76,7 +76,7 @@ public:
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const override;
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override;
- virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
+ virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
virtual String get_template_file_name(const String &p_target, const String &p_arch) const override;
virtual Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) override;
virtual bool is_executable(const String &p_path) const override;
@@ -87,7 +87,7 @@ public:
virtual int get_options_count() const override;
virtual String get_option_label(int p_index) const override;
virtual String get_option_tooltip(int p_index) const override;
- virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override;
+ virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) override;
virtual void cleanup() override;
EditorExportPlatformLinuxBSD();
diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp
index 290b0082fc..8372600ae9 100644
--- a/platform/macos/export/export_plugin.cpp
+++ b/platform/macos/export/export_plugin.cpp
@@ -1505,7 +1505,7 @@ Error EditorExportPlatformMacOS::_export_debug_script(const Ref<EditorExportPres
return OK;
}
-Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
+Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
const String base_dir = p_path.get_base_dir();
@@ -2511,7 +2511,7 @@ void EditorExportPlatformMacOS::cleanup() {
cleanup_commands.clear();
}
-Error EditorExportPlatformMacOS::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) {
+Error EditorExportPlatformMacOS::run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) {
cleanup();
if (p_device) { // Stop command, cleanup only.
return OK;
@@ -2573,8 +2573,7 @@ Error EditorExportPlatformMacOS::run(const Ref<EditorExportPreset> &p_preset, in
String cmd_args;
{
- Vector<String> cmd_args_list;
- gen_debug_flags(cmd_args_list, p_debug_flags);
+ Vector<String> cmd_args_list = gen_export_flags(p_debug_flags);
for (int i = 0; i < cmd_args_list.size(); i++) {
if (i != 0) {
cmd_args += " ";
@@ -2583,7 +2582,7 @@ Error EditorExportPlatformMacOS::run(const Ref<EditorExportPreset> &p_preset, in
}
}
- const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT);
+ const bool use_remote = p_debug_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG) || p_debug_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT);
int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port");
print_line("Creating temporary directory...");
diff --git a/platform/macos/export/export_plugin.h b/platform/macos/export/export_plugin.h
index 062a2e5f95..5457c687d3 100644
--- a/platform/macos/export/export_plugin.h
+++ b/platform/macos/export/export_plugin.h
@@ -147,7 +147,7 @@ public:
virtual bool is_executable(const String &p_path) const override;
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
- virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
+ virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override;
virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override;
@@ -167,7 +167,7 @@ public:
virtual int get_options_count() const override;
virtual String get_option_label(int p_index) const override;
virtual String get_option_tooltip(int p_index) const override;
- virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override;
+ virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) override;
virtual void cleanup() override;
EditorExportPlatformMacOS();
diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp
index d8c1b6033d..5faab74d7b 100644
--- a/platform/web/export/export_plugin.cpp
+++ b/platform/web/export/export_plugin.cpp
@@ -130,15 +130,14 @@ void EditorExportPlatformWeb::_replace_strings(const HashMap<String, String> &p_
}
}
-void EditorExportPlatformWeb::_fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects, const Dictionary &p_file_sizes) {
+void EditorExportPlatformWeb::_fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, BitField<EditorExportPlatform::DebugFlags> p_flags, const Vector<SharedObject> p_shared_objects, const Dictionary &p_file_sizes) {
// Engine.js config
Dictionary config;
Array libs;
for (int i = 0; i < p_shared_objects.size(); i++) {
libs.push_back(p_shared_objects[i].path.get_file());
}
- Vector<String> flags;
- gen_export_flags(flags, p_flags & (~DEBUG_FLAG_DUMB_CLIENT));
+ Vector<String> flags = gen_export_flags(p_flags & (~DEBUG_FLAG_DUMB_CLIENT));
Array args;
for (int i = 0; i < flags.size(); i++) {
args.push_back(flags[i]);
@@ -450,7 +449,7 @@ List<String> EditorExportPlatformWeb::get_binary_extensions(const Ref<EditorExpo
return list;
}
-Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
+Error EditorExportPlatformWeb::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
const String custom_debug = p_preset->get("custom_template/debug");
@@ -744,7 +743,7 @@ String EditorExportPlatformWeb::get_option_tooltip(int p_index) const {
return "";
}
-Error EditorExportPlatformWeb::run(const Ref<EditorExportPreset> &p_preset, int p_option, int p_debug_flags) {
+Error EditorExportPlatformWeb::run(const Ref<EditorExportPreset> &p_preset, int p_option, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) {
const uint16_t bind_port = EDITOR_GET("export/web/http_port");
// Resolve host if needed.
const String bind_host = EDITOR_GET("export/web/http_host");
diff --git a/platform/web/export/export_plugin.h b/platform/web/export/export_plugin.h
index 2f67d8107f..3c743e2e74 100644
--- a/platform/web/export/export_plugin.h
+++ b/platform/web/export/export_plugin.h
@@ -98,7 +98,7 @@ class EditorExportPlatformWeb : public EditorExportPlatform {
Error _extract_template(const String &p_template, const String &p_dir, const String &p_name, bool pwa);
void _replace_strings(const HashMap<String, String> &p_replaces, Vector<uint8_t> &r_template);
- void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects, const Dictionary &p_file_sizes);
+ void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, BitField<EditorExportPlatform::DebugFlags> p_flags, const Vector<SharedObject> p_shared_objects, const Dictionary &p_file_sizes);
Error _add_manifest_icon(const String &p_path, const String &p_icon, int p_size, Array &r_arr);
Error _build_pwa(const Ref<EditorExportPreset> &p_preset, const String p_path, const Vector<SharedObject> &p_shared_objects);
Error _write_or_error(const uint8_t *p_content, int p_len, String p_path);
@@ -120,14 +120,14 @@ public:
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override;
virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override;
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
- virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
+ virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
virtual bool poll_export() override;
virtual int get_options_count() const override;
virtual String get_option_label(int p_index) const override;
virtual String get_option_tooltip(int p_index) const override;
virtual Ref<ImageTexture> get_option_icon(int p_index) const override;
- virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_option, int p_debug_flags) override;
+ virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_option, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) override;
virtual Ref<Texture2D> get_run_icon() const override;
virtual void get_platform_features(List<String> *r_features) const override {
diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp
index b465bd4ecd..8d3f4bb269 100644
--- a/platform/windows/export/export_plugin.cpp
+++ b/platform/windows/export/export_plugin.cpp
@@ -167,7 +167,7 @@ Error EditorExportPlatformWindows::sign_shared_object(const Ref<EditorExportPres
}
}
-Error EditorExportPlatformWindows::modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
+Error EditorExportPlatformWindows::modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
if (p_preset->get("application/modify_resources")) {
_rcedit_add_data(p_preset, p_path, false);
String wrapper_path = p_path.get_basename() + ".console.exe";
@@ -178,7 +178,7 @@ Error EditorExportPlatformWindows::modify_template(const Ref<EditorExportPreset>
return OK;
}
-Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
+Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
String custom_debug = p_preset->get("custom_template/debug");
String custom_release = p_preset->get("custom_template/release");
String arch = p_preset->get("binary_format/architecture");
@@ -996,7 +996,7 @@ void EditorExportPlatformWindows::cleanup() {
cleanup_commands.clear();
}
-Error EditorExportPlatformWindows::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) {
+Error EditorExportPlatformWindows::run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) {
cleanup();
if (p_device) { // Stop command, cleanup only.
return OK;
@@ -1050,8 +1050,7 @@ Error EditorExportPlatformWindows::run(const Ref<EditorExportPreset> &p_preset,
String cmd_args;
{
- Vector<String> cmd_args_list;
- gen_debug_flags(cmd_args_list, p_debug_flags);
+ Vector<String> cmd_args_list = gen_export_flags(p_debug_flags);
for (int i = 0; i < cmd_args_list.size(); i++) {
if (i != 0) {
cmd_args += " ";
@@ -1060,7 +1059,7 @@ Error EditorExportPlatformWindows::run(const Ref<EditorExportPreset> &p_preset,
}
}
- const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT);
+ const bool use_remote = p_debug_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG) || p_debug_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT);
int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port");
print_line("Creating temporary directory...");
diff --git a/platform/windows/export/export_plugin.h b/platform/windows/export/export_plugin.h
index 6ccb4a15a7..e86aac83d4 100644
--- a/platform/windows/export/export_plugin.h
+++ b/platform/windows/export/export_plugin.h
@@ -76,8 +76,8 @@ class EditorExportPlatformWindows : public EditorExportPlatformPC {
String _get_exe_arch(const String &p_path) const;
public:
- virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
- virtual Error modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) override;
+ virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
+ virtual Error modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) override;
virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) override;
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
virtual void get_export_options(List<ExportOption> *r_options) const override;
@@ -95,7 +95,7 @@ public:
virtual int get_options_count() const override;
virtual String get_option_label(int p_index) const override;
virtual String get_option_tooltip(int p_index) const override;
- virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override;
+ virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) override;
virtual void cleanup() override;
EditorExportPlatformWindows();
diff --git a/scene/resources/audio_stream_wav.cpp b/scene/resources/audio_stream_wav.cpp
index de6a069567..b293a91123 100644
--- a/scene/resources/audio_stream_wav.cpp
+++ b/scene/resources/audio_stream_wav.cpp
@@ -179,17 +179,17 @@ void AudioStreamPlaybackWAV::do_resample(const Depth *p_src, AudioFrame *p_dst,
if (pos != p_qoa->cache_pos) { // Prevents triple decoding on lower mix rates.
for (int i = 0; i < 2; i++) {
// Sign operations prevent triple decoding on backward loops, maxing prevents pop.
- uint32_t interp_pos = MIN(pos + (i * sign) + (sign < 0), p_qoa->desc->samples - 1);
+ uint32_t interp_pos = MIN(pos + (i * sign) + (sign < 0), p_qoa->desc.samples - 1);
uint32_t new_data_ofs = 8 + interp_pos / QOA_FRAME_LEN * p_qoa->frame_len;
if (p_qoa->data_ofs != new_data_ofs) {
p_qoa->data_ofs = new_data_ofs;
const uint8_t *src_ptr = (const uint8_t *)base->data;
src_ptr += p_qoa->data_ofs + AudioStreamWAV::DATA_PAD;
- qoa_decode_frame(src_ptr, p_qoa->frame_len, p_qoa->desc, p_qoa->dec, &p_qoa->dec_len);
+ qoa_decode_frame(src_ptr, p_qoa->frame_len, &p_qoa->desc, p_qoa->dec, &p_qoa->dec_len);
}
- uint32_t dec_idx = (interp_pos % QOA_FRAME_LEN) * p_qoa->desc->channels;
+ uint32_t dec_idx = (interp_pos % QOA_FRAME_LEN) * p_qoa->desc.channels;
if ((sign > 0 && i == 0) || (sign < 0 && i == 1)) {
final = p_qoa->dec[dec_idx];
@@ -286,7 +286,7 @@ int AudioStreamPlaybackWAV::mix(AudioFrame *p_buffer, float p_rate_scale, int p_
len *= 2;
break;
case AudioStreamWAV::FORMAT_QOA:
- len = qoa.desc->samples * qoa.desc->channels;
+ len = qoa.desc.samples * qoa.desc.channels;
break;
}
@@ -484,10 +484,6 @@ void AudioStreamPlaybackWAV::set_sample_playback(const Ref<AudioSamplePlayback>
AudioStreamPlaybackWAV::AudioStreamPlaybackWAV() {}
AudioStreamPlaybackWAV::~AudioStreamPlaybackWAV() {
- if (qoa.desc) {
- memfree(qoa.desc);
- }
-
if (qoa.dec) {
memfree(qoa.dec);
}
@@ -557,7 +553,7 @@ double AudioStreamWAV::get_length() const {
len *= 2;
break;
case AudioStreamWAV::FORMAT_QOA:
- qoa_desc desc = { 0, 0, 0, { { { 0 }, { 0 } } } };
+ qoa_desc desc = {};
qoa_decode_header((uint8_t *)data + DATA_PAD, data_bytes, &desc);
len = desc.samples * desc.channels;
break;
@@ -697,12 +693,11 @@ Ref<AudioStreamPlayback> AudioStreamWAV::instantiate_playback() {
sample->base = Ref<AudioStreamWAV>(this);
if (format == AudioStreamWAV::FORMAT_QOA) {
- sample->qoa.desc = (qoa_desc *)memalloc(sizeof(qoa_desc));
- uint32_t ffp = qoa_decode_header((uint8_t *)data + DATA_PAD, data_bytes, sample->qoa.desc);
+ uint32_t ffp = qoa_decode_header((uint8_t *)data + DATA_PAD, data_bytes, &sample->qoa.desc);
ERR_FAIL_COND_V(ffp != 8, Ref<AudioStreamPlaybackWAV>());
- sample->qoa.frame_len = qoa_max_frame_size(sample->qoa.desc);
- int samples_len = (sample->qoa.desc->samples > QOA_FRAME_LEN ? QOA_FRAME_LEN : sample->qoa.desc->samples);
- int alloc_len = sample->qoa.desc->channels * samples_len * sizeof(int16_t);
+ sample->qoa.frame_len = qoa_max_frame_size(&sample->qoa.desc);
+ int samples_len = (sample->qoa.desc.samples > QOA_FRAME_LEN ? QOA_FRAME_LEN : sample->qoa.desc.samples);
+ int alloc_len = sample->qoa.desc.channels * samples_len * sizeof(int16_t);
sample->qoa.dec = (int16_t *)memalloc(alloc_len);
}
diff --git a/scene/resources/audio_stream_wav.h b/scene/resources/audio_stream_wav.h
index 806db675b6..47aa10e790 100644
--- a/scene/resources/audio_stream_wav.h
+++ b/scene/resources/audio_stream_wav.h
@@ -59,7 +59,7 @@ class AudioStreamPlaybackWAV : public AudioStreamPlayback {
} ima_adpcm[2];
struct QOA_State {
- qoa_desc *desc = nullptr;
+ qoa_desc desc = {};
uint32_t data_ofs = 0;
uint32_t frame_len = 0;
int16_t *dec = nullptr;
diff --git a/tests/core/io/test_json_native.h b/tests/core/io/test_json_native.h
new file mode 100644
index 0000000000..819078ac57
--- /dev/null
+++ b/tests/core/io/test_json_native.h
@@ -0,0 +1,160 @@
+/**************************************************************************/
+/* test_json_native.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef TEST_JSON_NATIVE_H
+#define TEST_JSON_NATIVE_H
+
+#include "core/io/json.h"
+
+namespace TestJSONNative {
+
+bool compare_variants(Variant variant_1, Variant variant_2, int depth = 0) {
+ if (depth > 100) {
+ return false;
+ }
+ if (variant_1.get_type() == Variant::RID && variant_2.get_type() == Variant::RID) {
+ return true;
+ }
+ if (variant_1.get_type() == Variant::CALLABLE || variant_2.get_type() == Variant::CALLABLE) {
+ return true;
+ }
+
+ List<PropertyInfo> variant_1_properties;
+ variant_1.get_property_list(&variant_1_properties);
+ List<PropertyInfo> variant_2_properties;
+ variant_2.get_property_list(&variant_2_properties);
+
+ if (variant_1_properties.size() != variant_2_properties.size()) {
+ return false;
+ }
+
+ for (List<PropertyInfo>::Element *E = variant_1_properties.front(); E; E = E->next()) {
+ String name = E->get().name;
+ Variant variant_1_value = variant_1.get(name);
+ Variant variant_2_value = variant_2.get(name);
+
+ if (!compare_variants(variant_1_value, variant_2_value, depth + 1)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+TEST_CASE("[JSON][Native][SceneTree] Conversion between native and JSON formats") {
+ for (int variant_i = 0; variant_i < Variant::VARIANT_MAX; variant_i++) {
+ Variant::Type type = static_cast<Variant::Type>(variant_i);
+ Variant native_data;
+ Callable::CallError error;
+
+ if (type == Variant::Type::INT || type == Variant::Type::FLOAT) {
+ Variant value = int64_t(INT64_MAX);
+ const Variant *args[] = { &value };
+ Variant::construct(type, native_data, args, 1, error);
+ } else if (type == Variant::Type::OBJECT) {
+ Ref<JSON> json = memnew(JSON);
+ native_data = json;
+ } else if (type == Variant::Type::DICTIONARY) {
+ Dictionary dictionary;
+ dictionary["key"] = "value";
+ native_data = dictionary;
+ } else if (type == Variant::Type::ARRAY) {
+ Array array;
+ array.push_back("element1");
+ array.push_back("element2");
+ native_data = array;
+ } else if (type == Variant::Type::PACKED_BYTE_ARRAY) {
+ PackedByteArray packed_array;
+ packed_array.push_back(1);
+ packed_array.push_back(2);
+ native_data = packed_array;
+ } else if (type == Variant::Type::PACKED_INT32_ARRAY) {
+ PackedInt32Array packed_array;
+ packed_array.push_back(INT32_MIN);
+ packed_array.push_back(INT32_MAX);
+ native_data = packed_array;
+ } else if (type == Variant::Type::PACKED_INT64_ARRAY) {
+ PackedInt64Array packed_array;
+ packed_array.push_back(INT64_MIN);
+ packed_array.push_back(INT64_MAX);
+ native_data = packed_array;
+ } else if (type == Variant::Type::PACKED_FLOAT32_ARRAY) {
+ PackedFloat32Array packed_array;
+ packed_array.push_back(FLT_MIN);
+ packed_array.push_back(FLT_MAX);
+ native_data = packed_array;
+ } else if (type == Variant::Type::PACKED_FLOAT64_ARRAY) {
+ PackedFloat64Array packed_array;
+ packed_array.push_back(DBL_MIN);
+ packed_array.push_back(DBL_MAX);
+ native_data = packed_array;
+ } else if (type == Variant::Type::PACKED_STRING_ARRAY) {
+ PackedStringArray packed_array;
+ packed_array.push_back("string1");
+ packed_array.push_back("string2");
+ native_data = packed_array;
+ } else if (type == Variant::Type::PACKED_VECTOR2_ARRAY) {
+ PackedVector2Array packed_array;
+ Vector2 vector(1.0, 2.0);
+ packed_array.push_back(vector);
+ native_data = packed_array;
+ } else if (type == Variant::Type::PACKED_VECTOR3_ARRAY) {
+ PackedVector3Array packed_array;
+ Vector3 vector(1.0, 2.0, 3.0);
+ packed_array.push_back(vector);
+ native_data = packed_array;
+ } else if (type == Variant::Type::PACKED_COLOR_ARRAY) {
+ PackedColorArray packed_array;
+ Color color(1.0, 1.0, 1.0);
+ packed_array.push_back(color);
+ native_data = packed_array;
+ } else if (type == Variant::Type::PACKED_VECTOR4_ARRAY) {
+ PackedVector4Array packed_array;
+ Vector4 vector(1.0, 2.0, 3.0, 4.0);
+ packed_array.push_back(vector);
+ native_data = packed_array;
+ } else {
+ Variant::construct(type, native_data, nullptr, 0, error);
+ }
+ Variant json_converted_from_native = JSON::from_native(native_data, true, true);
+ Variant variant_native_converted = JSON::to_native(json_converted_from_native, true, true);
+ CHECK_MESSAGE(compare_variants(native_data, variant_native_converted),
+ vformat("Conversion from native to JSON type %s and back successful. \nNative: %s \nNative Converted: %s \nError: %s\nConversion from native to JSON type %s successful: %s",
+ Variant::get_type_name(type),
+ native_data,
+ variant_native_converted,
+ itos(error.error),
+ Variant::get_type_name(type),
+ json_converted_from_native));
+ }
+}
+} // namespace TestJSONNative
+
+#endif // TEST_JSON_NATIVE_H
diff --git a/tests/core/object/test_object.h b/tests/core/object/test_object.h
index 57bc65328a..f1bb62cb70 100644
--- a/tests/core/object/test_object.h
+++ b/tests/core/object/test_object.h
@@ -174,6 +174,31 @@ TEST_CASE("[Object] Metadata") {
CHECK_MESSAGE(
meta_list2.size() == 0,
"The metadata list should contain 0 items after removing all metadata items.");
+
+ Object other;
+ object.set_meta("conflicting_meta", "string");
+ object.set_meta("not_conflicting_meta", 123);
+ other.set_meta("conflicting_meta", Color(0, 1, 0));
+ other.set_meta("other_meta", "other");
+ object.merge_meta_from(&other);
+
+ CHECK_MESSAGE(
+ Color(object.get_meta("conflicting_meta")).is_equal_approx(Color(0, 1, 0)),
+ "String meta should be overwritten with Color after merging.");
+
+ CHECK_MESSAGE(
+ int(object.get_meta("not_conflicting_meta")) == 123,
+ "Not conflicting meta on destination should be kept intact.");
+
+ CHECK_MESSAGE(
+ object.get_meta("other_meta", String()) == "other",
+ "Not conflicting meta name on source should merged.");
+
+ List<StringName> meta_list3;
+ object.get_meta_list(&meta_list3);
+ CHECK_MESSAGE(
+ meta_list3.size() == 3,
+ "The metadata list should contain 3 items after merging meta from two objects.");
}
TEST_CASE("[Object] Construction") {
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 46714a2627..7e1c431a3c 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -48,6 +48,7 @@
#include "tests/core/io/test_image.h"
#include "tests/core/io/test_ip.h"
#include "tests/core/io/test_json.h"
+#include "tests/core/io/test_json_native.h"
#include "tests/core/io/test_marshalls.h"
#include "tests/core/io/test_pck_packer.h"
#include "tests/core/io/test_resource.h"