diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp
index 3443496ba9f..514a22fa6bc 100644
--- a/core/bind/core_bind.cpp
+++ b/core/bind/core_bind.cpp
@@ -798,6 +798,8 @@ Dictionary _OS::get_time(bool utc) const {
* @return epoch calculated
*/
int64_t _OS::get_unix_time_from_datetime(Dictionary datetime) const {
+ // if datetime is an empty Dictionary throws an error
+ ERR_FAIL_COND_V_MSG(datetime.empty(), 0, "Invalid datetime Dictionary: Dictionary is empty");
// Bunch of conversion constants
static const unsigned int SECONDS_PER_MINUTE = 60;
diff --git a/core/os/thread.cpp b/core/os/thread.cpp
index ca1752ffb03..1f260c08e34 100644
--- a/core/os/thread.cpp
+++ b/core/os/thread.cpp
@@ -47,7 +47,7 @@ uint64_t Thread::_thread_id_hash(const std::thread::id &p_t) {
}
Thread::ID Thread::main_thread_id = _thread_id_hash(std::this_thread::get_id());
-thread_local Thread::ID Thread::caller_id = _thread_id_hash(std::this_thread::get_id());
+thread_local Thread::ID Thread::caller_id = 0;
void Thread::_set_platform_funcs(
Error (*p_set_name_func)(const String &),
@@ -112,6 +112,10 @@ Error Thread::set_name(const String &p_name) {
return ERR_UNAVAILABLE;
}
+Thread::Thread() {
+ caller_id = _thread_id_hash(std::this_thread::get_id());
+}
+
Thread::~Thread() {
if (id != _thread_id_hash(std::thread::id())) {
#ifdef DEBUG_ENABLED
diff --git a/core/os/thread.h b/core/os/thread.h
index 3c617648259..527c86c0221 100644
--- a/core/os/thread.h
+++ b/core/os/thread.h
@@ -98,6 +98,7 @@ public:
///< waits until thread is finished, and deallocates it.
void wait_to_finish();
+ Thread();
~Thread();
#else
_FORCE_INLINE_ ID get_id() const { return 0; }
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index 2278835f381..053a49acd40 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -549,6 +549,7 @@
Gets an epoch time value from a dictionary of time values.
[code]datetime[/code] must be populated with the following keys: [code]year[/code], [code]month[/code], [code]day[/code], [code]hour[/code], [code]minute[/code], [code]second[/code].
+ If the dictionary is empty [code]0[/code] is returned.
You can pass the output from [method get_datetime_from_unix_time] directly into this function. Daylight Savings Time ([code]dst[/code]), if present, is ignored.
diff --git a/doc/classes/Physics2DServer.xml b/doc/classes/Physics2DServer.xml
index ed9729105f3..bac71c46dae 100644
--- a/doc/classes/Physics2DServer.xml
+++ b/doc/classes/Physics2DServer.xml
@@ -58,7 +58,7 @@
- Creates an [Area2D].
+ Creates an [Area2D]. After creating an [Area2D] with this method, assign it to a space using [method area_set_space] to use the created [Area2D] in the physics world.
diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp
index 200b1055d1d..be633849291 100644
--- a/editor/editor_export.cpp
+++ b/editor/editor_export.cpp
@@ -299,6 +299,7 @@ void EditorExportPlatform::gen_debug_flags(Vector &r_flags, int p_flags)
}
Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_path, const Vector &p_data, int p_file, int p_total) {
+ ERR_FAIL_COND_V_MSG(p_total < 1, ERR_PARAMETER_RANGE_ERROR, "Must select at least one file to export.");
PackData *pd = (PackData *)p_userdata;
@@ -332,6 +333,7 @@ Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_pa
}
Error EditorExportPlatform::_save_zip_file(void *p_userdata, const String &p_path, const Vector &p_data, int p_file, int p_total) {
+ ERR_FAIL_COND_V_MSG(p_total < 1, ERR_PARAMETER_RANGE_ERROR, "Must select at least one file to export.");
String path = p_path.replace_first("res://", "");
@@ -740,18 +742,26 @@ Error EditorExportPlatform::export_project_files(const Ref &
// Ignore import files, since these are automatically added to the jar later with the resources
_edit_filter_list(paths, String("*.import"), true);
+ Error err = OK;
Vector[ > export_plugins = EditorExport::get_singleton()->get_export_plugins();
+
for (int i = 0; i < export_plugins.size(); i++) {
export_plugins.write[i]->set_export_preset(p_preset);
if (p_so_func) {
for (int j = 0; j < export_plugins[i]->shared_objects.size(); j++) {
- p_so_func(p_udata, export_plugins[i]->shared_objects[j]);
+ err = p_so_func(p_udata, export_plugins[i]->shared_objects[j]);
+ if (err != OK) {
+ return err;
+ }
}
}
for (int j = 0; j < export_plugins[i]->extra_files.size(); j++) {
- p_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, 0, paths.size());
+ err = p_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, 0, paths.size());
+ if (err != OK) {
+ return err;
+ }
}
export_plugins.write[i]->_clear();
@@ -774,7 +784,7 @@ Error EditorExportPlatform::export_project_files(const Ref &
//file is imported, replace by what it imports
Ref config;
config.instance();
- Error err = config->load(path + ".import");
+ err = config->load(path + ".import");
if (err != OK) {
ERR_PRINTS("Could not parse: '" + path + "', not exported.");
continue;
@@ -841,12 +851,18 @@ Error EditorExportPlatform::export_project_files(const Ref &
}
if (p_so_func) {
for (int j = 0; j < export_plugins[i]->shared_objects.size(); j++) {
- p_so_func(p_udata, export_plugins[i]->shared_objects[j]);
+ err = p_so_func(p_udata, export_plugins[i]->shared_objects[j]);
+ if (err != OK) {
+ return err;
+ }
}
}
for (int j = 0; j < export_plugins[i]->extra_files.size(); j++) {
- p_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, idx, total);
+ err = p_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, idx, total);
+ if (err != OK) {
+ return err;
+ }
if (export_plugins[i]->extra_files[j].remap) {
do_export = false; //if remap, do not
path_remaps.push_back(path);
@@ -865,7 +881,10 @@ Error EditorExportPlatform::export_project_files(const Ref &
//just store it as it comes
if (do_export) {
Vector array = FileAccess::get_file_as_array(path);
- p_func(p_udata, path, array, idx, total);
+ err = p_func(p_udata, path, array, idx, total);
+ if (err != OK) {
+ return err;
+ }
}
}
@@ -902,7 +921,10 @@ Error EditorExportPlatform::export_project_files(const Ref &
new_file.write[j] = utf8[j];
}
- p_func(p_udata, from + ".remap", new_file, idx, total);
+ err = p_func(p_udata, from + ".remap", new_file, idx, total);
+ if (err != OK) {
+ return err;
+ }
}
} else {
//old remap mode, will still work, but it's unused because it's not multiple pck export friendly
@@ -915,11 +937,17 @@ Error EditorExportPlatform::export_project_files(const Ref &
String splash = ProjectSettings::get_singleton()->get("application/boot_splash/image");
if (icon != String() && FileAccess::exists(icon)) {
Vector array = FileAccess::get_file_as_array(icon);
- p_func(p_udata, icon, array, idx, total);
+ err = p_func(p_udata, icon, array, idx, total);
+ if (err != OK) {
+ return err;
+ }
}
if (splash != String() && FileAccess::exists(splash) && icon != splash) {
Vector array = FileAccess::get_file_as_array(splash);
- p_func(p_udata, splash, array, idx, total);
+ err = p_func(p_udata, splash, array, idx, total);
+ if (err != OK) {
+ return err;
+ }
}
String config_file = "project.binary";
@@ -928,9 +956,7 @@ Error EditorExportPlatform::export_project_files(const Ref &
Vector data = FileAccess::get_file_as_array(engine_cfb);
DirAccess::remove_file_or_error(engine_cfb);
- p_func(p_udata, "res://" + config_file, data, idx, total);
-
- return OK;
+ return p_func(p_udata, "res://" + config_file, data, idx, total);
}
Error EditorExportPlatform::_add_shared_object(void *p_userdata, const SharedObject &p_so) {
@@ -965,6 +991,7 @@ Error EditorExportPlatform::save_pack(const Ref &p_preset, c
if (err != OK) {
DirAccess::remove_file_or_error(tmppath);
+ ERR_PRINT("Failed to export project files");
return err;
}
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index f55e3fc2fcd..c06fbe7ad3f 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -924,9 +924,11 @@ void CanvasItemEditor::_restore_canvas_item_state(List p_canvas_it
for (List::Element *E = drag_selection.front(); E; E = E->next()) {
CanvasItem *canvas_item = E->get();
CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data(canvas_item);
- canvas_item->_edit_set_state(se->undo_state);
- if (restore_bones) {
- _restore_canvas_item_ik_chain(canvas_item, &(se->pre_drag_bones_undo_state));
+ if (se) {
+ canvas_item->_edit_set_state(se->undo_state);
+ if (restore_bones) {
+ _restore_canvas_item_ik_chain(canvas_item, &(se->pre_drag_bones_undo_state));
+ }
}
}
}
@@ -951,13 +953,15 @@ void CanvasItemEditor::_commit_canvas_item_state(List p_canvas_ite
for (List::Element *E = modified_canvas_items.front(); E; E = E->next()) {
CanvasItem *canvas_item = E->get();
CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data(canvas_item);
- undo_redo->add_do_method(canvas_item, "_edit_set_state", canvas_item->_edit_get_state());
- undo_redo->add_undo_method(canvas_item, "_edit_set_state", se->undo_state);
- if (commit_bones) {
- for (List::Element *F = se->pre_drag_bones_undo_state.front(); F; F = F->next()) {
- canvas_item = Object::cast_to(canvas_item->get_parent());
- undo_redo->add_do_method(canvas_item, "_edit_set_state", canvas_item->_edit_get_state());
- undo_redo->add_undo_method(canvas_item, "_edit_set_state", F->get());
+ if (se) {
+ undo_redo->add_do_method(canvas_item, "_edit_set_state", canvas_item->_edit_get_state());
+ undo_redo->add_undo_method(canvas_item, "_edit_set_state", se->undo_state);
+ if (commit_bones) {
+ for (List::Element *F = se->pre_drag_bones_undo_state.front(); F; F = F->next()) {
+ canvas_item = Object::cast_to(canvas_item->get_parent());
+ undo_redo->add_do_method(canvas_item, "_edit_set_state", canvas_item->_edit_get_state());
+ undo_redo->add_undo_method(canvas_item, "_edit_set_state", F->get());
+ }
}
}
}
@@ -2126,17 +2130,19 @@ bool CanvasItemEditor::_gui_input_move(const Ref &p_event) {
for (List::Element *E = drag_selection.front(); E; E = E->next()) {
CanvasItem *canvas_item = E->get();
CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data(canvas_item);
- Transform2D xform = canvas_item->get_global_transform_with_canvas().affine_inverse() * canvas_item->get_transform();
+ if (se) {
+ Transform2D xform = canvas_item->get_global_transform_with_canvas().affine_inverse() * canvas_item->get_transform();
- Node2D *node2d = Object::cast_to(canvas_item);
- if (node2d && se->pre_drag_bones_undo_state.size() > 0 && !force_no_IK) {
- real_t initial_leaf_node_rotation = node2d->get_global_transform_with_canvas().get_rotation();
- _restore_canvas_item_ik_chain(node2d, &(all_bones_ik_states[index]));
- real_t final_leaf_node_rotation = node2d->get_global_transform_with_canvas().get_rotation();
- node2d->rotate(initial_leaf_node_rotation - final_leaf_node_rotation);
- _solve_IK(node2d, new_pos);
- } else {
- canvas_item->_edit_set_position(canvas_item->_edit_get_position() + xform.xform(new_pos) - xform.xform(previous_pos));
+ Node2D *node2d = Object::cast_to(canvas_item);
+ if (node2d && se->pre_drag_bones_undo_state.size() > 0 && !force_no_IK) {
+ real_t initial_leaf_node_rotation = node2d->get_global_transform_with_canvas().get_rotation();
+ _restore_canvas_item_ik_chain(node2d, &(all_bones_ik_states[index]));
+ real_t final_leaf_node_rotation = node2d->get_global_transform_with_canvas().get_rotation();
+ node2d->rotate(initial_leaf_node_rotation - final_leaf_node_rotation);
+ _solve_IK(node2d, new_pos);
+ } else {
+ canvas_item->_edit_set_position(canvas_item->_edit_get_position() + xform.xform(new_pos) - xform.xform(previous_pos));
+ }
}
index++;
}
@@ -2258,17 +2264,19 @@ bool CanvasItemEditor::_gui_input_move(const Ref &p_event) {
for (List::Element *E = drag_selection.front(); E; E = E->next()) {
CanvasItem *canvas_item = E->get();
CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data(canvas_item);
- Transform2D xform = canvas_item->get_global_transform_with_canvas().affine_inverse() * canvas_item->get_transform();
+ if (se) {
+ Transform2D xform = canvas_item->get_global_transform_with_canvas().affine_inverse() * canvas_item->get_transform();
- Node2D *node2d = Object::cast_to(canvas_item);
- if (node2d && se->pre_drag_bones_undo_state.size() > 0) {
- real_t initial_leaf_node_rotation = node2d->get_global_transform_with_canvas().get_rotation();
- _restore_canvas_item_ik_chain(node2d, &(all_bones_ik_states[index]));
- real_t final_leaf_node_rotation = node2d->get_global_transform_with_canvas().get_rotation();
- node2d->rotate(initial_leaf_node_rotation - final_leaf_node_rotation);
- _solve_IK(node2d, new_pos);
- } else {
- canvas_item->_edit_set_position(canvas_item->_edit_get_position() + xform.xform(new_pos) - xform.xform(previous_pos));
+ Node2D *node2d = Object::cast_to(canvas_item);
+ if (node2d && se->pre_drag_bones_undo_state.size() > 0) {
+ real_t initial_leaf_node_rotation = node2d->get_global_transform_with_canvas().get_rotation();
+ _restore_canvas_item_ik_chain(node2d, &(all_bones_ik_states[index]));
+ real_t final_leaf_node_rotation = node2d->get_global_transform_with_canvas().get_rotation();
+ node2d->rotate(initial_leaf_node_rotation - final_leaf_node_rotation);
+ _solve_IK(node2d, new_pos);
+ } else {
+ canvas_item->_edit_set_position(canvas_item->_edit_get_position() + xform.xform(new_pos) - xform.xform(previous_pos));
+ }
}
index++;
}
diff --git a/misc/dist/html/editor.html b/misc/dist/html/editor.html
index 3bf87f35065..99ac2379cef 100644
--- a/misc/dist/html/editor.html
+++ b/misc/dist/html/editor.html
@@ -502,7 +502,7 @@
showTab('loader');
setLoaderEnabled(true);
};
- editor.start({'args': args});
+ editor.start({'args': args, 'persistentDrops': is_project_manager});
});
}, 0);
OnEditorExit = null;
@@ -563,7 +563,7 @@
//selectVideoMode();
showTab('editor');
setLoaderEnabled(false);
- editor.start({'args': ['--video-driver', video_driver]}).then(function() {
+ editor.start({'args': ['--project-manager', '--video-driver', video_driver], 'persistentDrops': true}).then(function() {
setStatusMode('hidden');
initializing = false;
});
diff --git a/modules/gdnative/include/gdnative/gdnative.h b/modules/gdnative/include/gdnative/gdnative.h
index 63e2d446e77..b5316244fb5 100644
--- a/modules/gdnative/include/gdnative/gdnative.h
+++ b/modules/gdnative/include/gdnative/gdnative.h
@@ -53,7 +53,9 @@ extern "C" {
#endif
// This is for libraries *using* the header, NOT GODOT EXPOSING STUFF!!
-#ifdef _WIN32
+#ifdef __GNUC__
+#define GDN_EXPORT __attribute__((visibility("default")))
+#elif defined(_WIN32)
#define GDN_EXPORT __declspec(dllexport)
#else
#define GDN_EXPORT
diff --git a/platform/javascript/js/engine/config.js b/platform/javascript/js/engine/config.js
index 25d71d0905c..60727828757 100644
--- a/platform/javascript/js/engine/config.js
+++ b/platform/javascript/js/engine/config.js
@@ -103,6 +103,11 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
* @type {Array.}
*/
persistentPaths: ['/userfs'],
+ /**
+ * @ignore
+ * @type {boolean}
+ */
+ persistentDrops: false,
/**
* @ignore
* @type {Array.}
@@ -231,6 +236,7 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
this.locale = parse('locale', this.locale);
this.canvasResizePolicy = parse('canvasResizePolicy', this.canvasResizePolicy);
this.persistentPaths = parse('persistentPaths', this.persistentPaths);
+ this.persistentDrops = parse('persistentDrops', this.persistentDrops);
this.experimentalVK = parse('experimentalVK', this.experimentalVK);
this.gdnativeLibs = parse('gdnativeLibs', this.gdnativeLibs);
this.fileSizes = parse('fileSizes', this.fileSizes);
@@ -316,6 +322,7 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
'canvas': this.canvas,
'canvasResizePolicy': this.canvasResizePolicy,
'locale': locale,
+ 'persistentDrops': this.persistentDrops,
'virtualKeyboard': this.experimentalVK,
'onExecute': this.onExecute,
'onExit': function (p_code) {
diff --git a/platform/javascript/js/libs/library_godot_display.js b/platform/javascript/js/libs/library_godot_display.js
index 781a202f89f..99aa4793d92 100644
--- a/platform/javascript/js/libs/library_godot_display.js
+++ b/platform/javascript/js/libs/library_godot_display.js
@@ -192,33 +192,45 @@ const GodotDisplayDragDrop = {
GodotDisplayDragDrop.promises = [];
GodotDisplayDragDrop.pending_files = [];
callback(drops);
- const dirs = [DROP.substr(0, DROP.length - 1)];
- // Remove temporary files
- files.forEach(function (file) {
- FS.unlink(file);
- let dir = file.replace(DROP, '');
- let idx = dir.lastIndexOf('/');
- while (idx > 0) {
- dir = dir.substr(0, idx);
- if (dirs.indexOf(DROP + dir) === -1) {
- dirs.push(DROP + dir);
- }
- idx = dir.lastIndexOf('/');
+ if (GodotConfig.persistent_drops) {
+ // Delay removal at exit.
+ GodotOS.atexit(function (resolve, reject) {
+ GodotDisplayDragDrop.remove_drop(files, DROP);
+ resolve();
+ });
+ } else {
+ GodotDisplayDragDrop.remove_drop(files, DROP);
+ }
+ });
+ },
+
+ remove_drop: function (files, drop_path) {
+ const dirs = [drop_path.substr(0, drop_path.length - 1)];
+ // Remove temporary files
+ files.forEach(function (file) {
+ FS.unlink(file);
+ let dir = file.replace(drop_path, '');
+ let idx = dir.lastIndexOf('/');
+ while (idx > 0) {
+ dir = dir.substr(0, idx);
+ if (dirs.indexOf(drop_path + dir) === -1) {
+ dirs.push(drop_path + dir);
}
- });
- // Remove dirs.
- dirs.sort(function (a, b) {
- const al = (a.match(/\//g) || []).length;
- const bl = (b.match(/\//g) || []).length;
- if (al > bl) {
- return -1;
- } else if (al < bl) {
- return 1;
- }
- return 0;
- }).forEach(function (dir) {
- FS.rmdir(dir);
- });
+ idx = dir.lastIndexOf('/');
+ }
+ });
+ // Remove dirs.
+ dirs.sort(function (a, b) {
+ const al = (a.match(/\//g) || []).length;
+ const bl = (b.match(/\//g) || []).length;
+ if (al > bl) {
+ return -1;
+ } else if (al < bl) {
+ return 1;
+ }
+ return 0;
+ }).forEach(function (dir) {
+ FS.rmdir(dir);
});
},
@@ -908,6 +920,7 @@ const GodotDisplay = {
canvas.style.left = 0;
break;
}
+ GodotDisplayScreen.updateSize();
if (p_fullscreen) {
GodotDisplayScreen.requestFullscreen();
}
diff --git a/platform/javascript/js/libs/library_godot_os.js b/platform/javascript/js/libs/library_godot_os.js
index 775a822d881..1d9f889bce4 100644
--- a/platform/javascript/js/libs/library_godot_os.js
+++ b/platform/javascript/js/libs/library_godot_os.js
@@ -60,6 +60,7 @@ const GodotConfig = {
locale: 'en',
canvas_resize_policy: 2, // Adaptive
virtual_keyboard: false,
+ persistent_drops: false,
on_execute: null,
on_exit: null,
@@ -68,6 +69,7 @@ const GodotConfig = {
GodotConfig.canvas = p_opts['canvas'];
GodotConfig.locale = p_opts['locale'] || GodotConfig.locale;
GodotConfig.virtual_keyboard = p_opts['virtualKeyboard'];
+ GodotConfig.persistent_drops = !!p_opts['persistentDrops'];
GodotConfig.on_execute = p_opts['onExecute'];
GodotConfig.on_exit = p_opts['onExit'];
},
@@ -80,6 +82,7 @@ const GodotConfig = {
GodotConfig.locale = 'en';
GodotConfig.canvas_resize_policy = 2;
GodotConfig.virtual_keyboard = false;
+ GodotConfig.persistent_drops = false;
GodotConfig.on_execute = null;
GodotConfig.on_exit = null;
},
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 829daedd25c..5d482ad6885 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -70,7 +70,9 @@ Dictionary Control::_edit_get_state() const {
}
void Control::_edit_set_state(const Dictionary &p_state) {
-
+ ERR_FAIL_COND((p_state.size() <= 0) ||
+ !p_state.has("rotation") || !p_state.has("scale") ||
+ !p_state.has("pivot") || !p_state.has("anchors") || !p_state.has("offsets"));
Dictionary state = p_state;
set_rotation(state["rotation"]);
]