summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/input/input.cpp119
-rw-r--r--core/input/input.h19
-rw-r--r--core/input/input_map.cpp18
-rw-r--r--core/input/input_map.h5
-rw-r--r--core/io/resource.cpp4
-rw-r--r--core/io/resource.h1
-rw-r--r--core/io/resource_format_binary.cpp2
-rw-r--r--core/io/resource_loader.cpp2
-rw-r--r--doc/classes/ProjectSettings.xml3
-rw-r--r--drivers/gles3/shader_gles3.cpp4
-rw-r--r--editor/editor_node.cpp5
-rw-r--r--editor/editor_node.h2
-rw-r--r--main/main.cpp3
-rw-r--r--modules/gdscript/gdscript.cpp109
-rw-r--r--modules/gdscript/gdscript.h14
-rw-r--r--modules/gdscript/gdscript_lambda_callable.cpp4
-rw-r--r--modules/gdscript/gdscript_lambda_callable.h4
-rw-r--r--platform/android/export/export_plugin.cpp14
-rw-r--r--scene/resources/resource_format_text.cpp2
19 files changed, 248 insertions, 86 deletions
diff --git a/core/input/input.cpp b/core/input/input.cpp
index 2d48bdd4cf..257452b3d8 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -695,53 +695,34 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
}
for (const KeyValue<StringName, InputMap::Action> &E : InputMap::get_singleton()->get_action_map()) {
- if (InputMap::get_singleton()->event_is_action(p_event, E.key)) {
- Action &action = action_state[E.key];
- bool is_joypad_axis = jm.is_valid();
- bool is_pressed = false;
- if (!p_event->is_echo()) {
- if (p_event->is_action_pressed(E.key)) {
- bool is_joypad_axis_valid_zone_enter = false;
- if (is_joypad_axis) {
- if (!action.axis_pressed) {
- is_joypad_axis_valid_zone_enter = true;
- action.pressed++;
- action.axis_pressed = true;
- }
- } else {
- action.pressed++;
- }
- if (action.pressed == 1 && (is_joypad_axis_valid_zone_enter || !is_joypad_axis)) {
- action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
- action.pressed_process_frame = Engine::get_singleton()->get_process_frames();
- }
- is_pressed = true;
- } else {
- bool is_released = true;
- if (is_joypad_axis) {
- if (action.axis_pressed) {
- action.axis_pressed = false;
- } else {
- is_released = false;
- }
- }
+ const int event_index = InputMap::get_singleton()->event_get_index(p_event, E.key);
+ if (event_index == -1) {
+ continue;
+ }
- if (is_released) {
- if (action.pressed == 1) {
- action.released_physics_frame = Engine::get_singleton()->get_physics_frames();
- action.released_process_frame = Engine::get_singleton()->get_process_frames();
- }
- action.pressed = MAX(action.pressed - 1, 0);
- }
+ Action &action = action_state[E.key];
+ if (!p_event->is_echo()) {
+ if (p_event->is_action_pressed(E.key)) {
+ if (!action.pressed) {
+ action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
+ action.pressed_process_frame = Engine::get_singleton()->get_process_frames();
}
- action.exact = InputMap::get_singleton()->event_is_action(p_event, E.key, true);
- }
+ action.pressed |= ((uint64_t)1 << event_index);
+ } else {
+ action.pressed &= ~((uint64_t)1 << event_index);
+ action.pressed &= ~(1 << MAX_EVENT); // Always release the event from action_press() method.
- if (is_pressed || action.pressed == 0) {
- action.strength = p_event->get_action_strength(E.key);
- action.raw_strength = p_event->get_action_raw_strength(E.key);
+ if (!action.pressed) {
+ action.released_physics_frame = Engine::get_singleton()->get_physics_frames();
+ action.released_process_frame = Engine::get_singleton()->get_process_frames();
+ }
+ _update_action_strength(action, MAX_EVENT, 0.0);
+ _update_action_raw_strength(action, MAX_EVENT, 0.0);
}
+ action.exact = InputMap::get_singleton()->event_is_action(p_event, E.key, true);
}
+ _update_action_strength(action, event_index, p_event->get_action_strength(E.key));
+ _update_action_raw_strength(action, event_index, p_event->get_action_raw_strength(E.key));
}
if (event_dispatch_function) {
@@ -858,13 +839,13 @@ void Input::action_press(const StringName &p_action, float p_strength) {
// Create or retrieve existing action.
Action &action = action_state[p_action];
- action.pressed++;
- if (action.pressed == 1) {
+ if (!action.pressed) {
action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
action.pressed_process_frame = Engine::get_singleton()->get_process_frames();
}
- action.strength = p_strength;
- action.raw_strength = p_strength;
+ action.pressed |= 1 << MAX_EVENT;
+ _update_action_strength(action, MAX_EVENT, p_strength);
+ _update_action_raw_strength(action, MAX_EVENT, p_strength);
action.exact = true;
}
@@ -872,13 +853,15 @@ void Input::action_release(const StringName &p_action) {
// Create or retrieve existing action.
Action &action = action_state[p_action];
- action.pressed--;
- if (action.pressed == 0) {
- action.released_physics_frame = Engine::get_singleton()->get_physics_frames();
- action.released_process_frame = Engine::get_singleton()->get_process_frames();
+ action.pressed = 0;
+ action.strength = 0.0;
+ action.raw_strength = 0.0;
+ action.released_physics_frame = Engine::get_singleton()->get_physics_frames();
+ action.released_process_frame = Engine::get_singleton()->get_process_frames();
+ for (uint64_t i = 0; i <= MAX_EVENT; i++) {
+ action.strengths[i] = 0.0;
+ action.raw_strengths[i] = 0.0;
}
- action.strength = 0.0f;
- action.raw_strength = 0.0f;
action.exact = true;
}
@@ -1207,6 +1190,38 @@ void Input::_axis_event(int p_device, JoyAxis p_axis, float p_value) {
parse_input_event(ievent);
}
+void Input::_update_action_strength(Action &p_action, int p_event_index, float p_strength) {
+ ERR_FAIL_INDEX(p_event_index, (int)MAX_EVENT + 1);
+
+ float old_strength = p_action.strengths[p_event_index];
+ p_action.strengths[p_event_index] = p_strength;
+
+ if (p_strength > p_action.strength) {
+ p_action.strength = p_strength;
+ } else if (Math::is_equal_approx(old_strength, p_action.strength)) {
+ p_action.strength = p_strength;
+ for (uint64_t i = 0; i <= MAX_EVENT; i++) {
+ p_action.strength = MAX(p_action.strength, p_action.strengths[i]);
+ }
+ }
+}
+
+void Input::_update_action_raw_strength(Action &p_action, int p_event_index, float p_strength) {
+ ERR_FAIL_INDEX(p_event_index, (int)MAX_EVENT + 1);
+
+ float old_strength = p_action.raw_strengths[p_event_index];
+ p_action.raw_strengths[p_event_index] = p_strength;
+
+ if (p_strength > p_action.raw_strength) {
+ p_action.raw_strength = p_strength;
+ } else if (Math::is_equal_approx(old_strength, p_action.raw_strength)) {
+ p_action.raw_strength = p_strength;
+ for (uint64_t i = 0; i <= MAX_EVENT; i++) {
+ p_action.raw_strength = MAX(p_action.raw_strength, p_action.raw_strengths[i]);
+ }
+ }
+}
+
Input::JoyEvent Input::_get_mapped_button_event(const JoyDeviceMapping &mapping, JoyButton p_button) {
JoyEvent event;
diff --git a/core/input/input.h b/core/input/input.h
index bedc3fa0e3..dd613c4877 100644
--- a/core/input/input.h
+++ b/core/input/input.h
@@ -44,6 +44,8 @@ class Input : public Object {
static Input *singleton;
+ static constexpr uint64_t MAX_EVENT = 31;
+
public:
enum MouseMode {
MOUSE_MODE_VISIBLE,
@@ -103,11 +105,22 @@ private:
uint64_t pressed_process_frame = UINT64_MAX;
uint64_t released_physics_frame = UINT64_MAX;
uint64_t released_process_frame = UINT64_MAX;
- int pressed = 0;
- bool axis_pressed = false;
+ uint64_t pressed = 0;
bool exact = true;
float strength = 0.0f;
float raw_strength = 0.0f;
+ LocalVector<float> strengths;
+ LocalVector<float> raw_strengths;
+
+ Action() {
+ strengths.resize(MAX_EVENT + 1);
+ raw_strengths.resize(MAX_EVENT + 1);
+
+ for (uint64_t i = 0; i <= MAX_EVENT; i++) {
+ strengths[i] = 0.0;
+ raw_strengths[i] = 0.0;
+ }
+ }
};
HashMap<StringName, Action> action_state;
@@ -227,6 +240,8 @@ private:
JoyAxis _get_output_axis(String output);
void _button_event(int p_device, JoyButton p_index, bool p_pressed);
void _axis_event(int p_device, JoyAxis p_axis, float p_value);
+ void _update_action_strength(Action &p_action, int p_event_index, float p_strength);
+ void _update_action_raw_strength(Action &p_action, int p_event_index, float p_strength);
void _parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated);
diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp
index ddfde0e7cd..78b9ada884 100644
--- a/core/input/input_map.cpp
+++ b/core/input/input_map.cpp
@@ -127,16 +127,21 @@ List<StringName> InputMap::get_actions() const {
return actions;
}
-List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength) const {
+List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength, int *r_event_index) const {
ERR_FAIL_COND_V(!p_event.is_valid(), nullptr);
+ int i = 0;
for (List<Ref<InputEvent>>::Element *E = p_action.inputs.front(); E; E = E->next()) {
int device = E->get()->get_device();
if (device == ALL_DEVICES || device == p_event->get_device()) {
if (E->get()->action_match(p_event, p_exact_match, p_action.deadzone, r_pressed, r_strength, r_raw_strength)) {
+ if (r_event_index) {
+ *r_event_index = i;
+ }
return E;
}
}
+ i++;
}
return nullptr;
@@ -179,6 +184,7 @@ void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEve
List<Ref<InputEvent>>::Element *E = _find_event(input_map[p_action], p_event, true);
if (E) {
input_map[p_action].inputs.erase(E);
+
if (Input::get_singleton()->is_action_pressed(p_action)) {
Input::get_singleton()->action_release(p_action);
}
@@ -216,7 +222,13 @@ bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName
return event_get_action_status(p_event, p_action, p_exact_match);
}
-bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength) const {
+int InputMap::event_get_index(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match) const {
+ int index = -1;
+ event_get_action_status(p_event, p_action, p_exact_match, nullptr, nullptr, nullptr, &index);
+ return index;
+}
+
+bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength, int *r_event_index) const {
HashMap<StringName, Action>::Iterator E = input_map.find(p_action);
ERR_FAIL_COND_V_MSG(!E, false, suggest_actions(p_action));
@@ -236,7 +248,7 @@ bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const Str
return input_event_action->get_action() == p_action;
}
- List<Ref<InputEvent>>::Element *event = _find_event(E->value, p_event, p_exact_match, r_pressed, r_strength, r_raw_strength);
+ List<Ref<InputEvent>>::Element *event = _find_event(E->value, p_event, p_exact_match, r_pressed, r_strength, r_raw_strength, r_event_index);
return event != nullptr;
}
diff --git a/core/input/input_map.h b/core/input/input_map.h
index b4d5beacb3..6407ea489e 100644
--- a/core/input/input_map.h
+++ b/core/input/input_map.h
@@ -61,7 +61,7 @@ private:
HashMap<String, List<Ref<InputEvent>>> default_builtin_cache;
HashMap<String, List<Ref<InputEvent>>> default_builtin_with_overrides_cache;
- List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr) const;
+ List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr, int *r_event_index = nullptr) const;
TypedArray<InputEvent> _action_get_events(const StringName &p_action);
TypedArray<StringName> _get_actions();
@@ -86,7 +86,8 @@ public:
const List<Ref<InputEvent>> *action_get_events(const StringName &p_action);
bool event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false) const;
- bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr) const;
+ int event_get_index(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false) const;
+ bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr, int *r_event_index = nullptr) const;
const HashMap<StringName, Action> &get_action_map() const;
void load_from_project_settings();
diff --git a/core/io/resource.cpp b/core/io/resource.cpp
index e0d42a274a..64fa597a67 100644
--- a/core/io/resource.cpp
+++ b/core/io/resource.cpp
@@ -90,6 +90,10 @@ String Resource::get_path() const {
return path_cache;
}
+void Resource::set_path_cache(const String &p_path) {
+ path_cache = p_path;
+}
+
String Resource::generate_scene_unique_id() {
// Generate a unique enough hash, but still user-readable.
// If it's not unique it does not matter because the saver will try again.
diff --git a/core/io/resource.h b/core/io/resource.h
index a9b1a88f6b..610c2150db 100644
--- a/core/io/resource.h
+++ b/core/io/resource.h
@@ -103,6 +103,7 @@ public:
virtual void set_path(const String &p_path, bool p_take_over = false);
String get_path() const;
+ void set_path_cache(const String &p_path); // Set raw path without involving resource cache.
_FORCE_INLINE_ bool is_built_in() const { return path_cache.is_empty() || path_cache.contains("::") || path_cache.begins_with("local://"); }
static String generate_scene_unique_id();
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index ea97e5ecce..2a33f723dc 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -774,6 +774,8 @@ Error ResourceLoaderBinary::load() {
res = Ref<Resource>(r);
if (!path.is_empty() && cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
r->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE); //if got here because the resource with same path has different type, replace it
+ } else if (!path.is_resource_file()) {
+ r->set_path_cache(path);
}
r->set_scene_unique_id(id);
}
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index 6721ec0953..b1ebdff91f 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -341,6 +341,8 @@ void ResourceLoader::_thread_load_function(void *p_userdata) {
if (load_task.resource.is_valid()) {
if (load_task.cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
load_task.resource->set_path(load_task.local_path);
+ } else if (!load_task.local_path.is_resource_file()) {
+ load_task.resource->set_path_cache(load_task.local_path);
}
if (load_task.xl_remapped) {
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 93e0ed5491..3fc9e94427 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -328,7 +328,8 @@
Changes to this setting will only be applied upon restarting the application.
</member>
<member name="application/run/frame_delay_msec" type="int" setter="" getter="" default="0">
- Forces a delay between frames in the main loop (in milliseconds). This may be useful if you plan to disable vertical synchronization.
+ Forces a [i]constant[/i] delay between frames in the main loop (in milliseconds). In most situations, [member application/run/max_fps] should be preferred as an FPS limiter as it's more precise.
+ This setting can be overridden using the [code]--frame-delay &lt;ms;&gt;[/code] command line argument.
</member>
<member name="application/run/low_processor_mode" type="bool" setter="" getter="" default="false">
If [code]true[/code], enables low-processor usage mode. This setting only works on desktop platforms. The screen is not redrawn if nothing changes visually. This is meant for writing applications and editors, but is pretty useless (and can hurt performance) in most games.
diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp
index 77b870f8b2..75b2662a1c 100644
--- a/drivers/gles3/shader_gles3.cpp
+++ b/drivers/gles3/shader_gles3.cpp
@@ -328,7 +328,7 @@ void ShaderGLES3::_compile_specialization(Version::Specialization &spec, uint32_
}
char *ilogmem = (char *)Memory::alloc_static(iloglen + 1);
- ilogmem[iloglen] = '\0';
+ memset(ilogmem, 0, iloglen + 1);
glGetShaderInfoLog(spec.vert_id, iloglen, &iloglen, ilogmem);
String err_string = name + ": Vertex shader compilation failed:\n";
@@ -376,7 +376,7 @@ void ShaderGLES3::_compile_specialization(Version::Specialization &spec, uint32_
}
char *ilogmem = (char *)Memory::alloc_static(iloglen + 1);
- ilogmem[iloglen] = '\0';
+ memset(ilogmem, 0, iloglen + 1);
glGetShaderInfoLog(spec.frag_id, iloglen, &iloglen, ilogmem);
String err_string = name + ": Fragment shader compilation failed:\n";
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index c8ba1cfe5e..ad324c3ae6 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -6733,7 +6733,7 @@ static void _execute_thread(void *p_ud) {
eta->done.set();
}
-int EditorNode::execute_and_show_output(const String &p_title, const String &p_path, const List<String> &p_arguments, bool p_close_on_ok, bool p_close_on_errors) {
+int EditorNode::execute_and_show_output(const String &p_title, const String &p_path, const List<String> &p_arguments, bool p_close_on_ok, bool p_close_on_errors, String *r_output) {
if (execute_output_dialog) {
execute_output_dialog->set_title(p_title);
execute_output_dialog->get_ok_button()->set_disabled(true);
@@ -6779,6 +6779,9 @@ int EditorNode::execute_and_show_output(const String &p_title, const String &p_p
execute_output_dialog->get_ok_button()->set_disabled(false);
}
+ if (r_output) {
+ *r_output = eta.output;
+ }
return eta.exitcode;
}
diff --git a/editor/editor_node.h b/editor/editor_node.h
index fe99cf1f01..0ee2330927 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -929,7 +929,7 @@ public:
bool has_scenes_in_session();
- int execute_and_show_output(const String &p_title, const String &p_path, const List<String> &p_arguments, bool p_close_on_ok = true, bool p_close_on_errors = false);
+ int execute_and_show_output(const String &p_title, const String &p_path, const List<String> &p_arguments, bool p_close_on_ok = true, bool p_close_on_errors = false, String *r_output = nullptr);
EditorNode();
~EditorNode();
diff --git a/main/main.cpp b/main/main.cpp
index 7ca22d90ca..271791f368 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -2128,6 +2128,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
if (frame_delay == 0) {
frame_delay = GLOBAL_DEF(PropertyInfo(Variant::INT, "application/run/frame_delay_msec", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), 0);
+ if (Engine::get_singleton()->is_editor_hint()) {
+ frame_delay = 0;
+ }
}
if (audio_output_latency >= 1) {
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 6245cc85a0..4accdb4d21 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -992,6 +992,7 @@ void GDScript::set_path(const String &p_path, bool p_take_over) {
String old_path = path;
path = p_path;
+ path_valid = true;
GDScriptCache::move_script(old_path, p_path);
for (KeyValue<StringName, Ref<GDScript>> &kv : subclasses) {
@@ -1000,6 +1001,9 @@ void GDScript::set_path(const String &p_path, bool p_take_over) {
}
String GDScript::get_script_path() const {
+ if (!path_valid && !get_path().is_empty()) {
+ return get_path();
+ }
return path;
}
@@ -1035,6 +1039,7 @@ Error GDScript::load_source_code(const String &p_path) {
source = s;
path = p_path;
+ path_valid = true;
#ifdef TOOLS_ENABLED
source_changed_cache = true;
set_edited(false);
@@ -1387,33 +1392,106 @@ String GDScript::debug_get_script_name(const Ref<Script> &p_script) {
#endif
thread_local GDScript::UpdatableFuncPtr GDScript::func_ptrs_to_update_thread_local;
+GDScript::UpdatableFuncPtr *GDScript::func_ptrs_to_update_main_thread = &func_ptrs_to_update_thread_local;
+
+GDScript::UpdatableFuncPtrElement *GDScript::_add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr) {
+ MutexLock lock(func_ptrs_to_update_mutex);
-GDScript::UpdatableFuncPtrElement GDScript::_add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr) {
- UpdatableFuncPtrElement result = {};
+ List<UpdatableFuncPtrElement>::Element *result = func_ptrs_to_update_elems.push_back(UpdatableFuncPtrElement());
{
- MutexLock lock(func_ptrs_to_update_thread_local.mutex);
- result.element = func_ptrs_to_update_thread_local.ptrs.push_back(p_func_ptr_ptr);
- result.mutex = &func_ptrs_to_update_thread_local.mutex;
+ MutexLock lock2(func_ptrs_to_update_thread_local.mutex);
+ result->get().element = func_ptrs_to_update_thread_local.ptrs.push_back(p_func_ptr_ptr);
+ result->get().mutex = &func_ptrs_to_update_thread_local.mutex;
if (likely(func_ptrs_to_update_thread_local.initialized)) {
- return result;
+ return &result->get();
}
func_ptrs_to_update_thread_local.initialized = true;
}
- MutexLock lock(func_ptrs_to_update_mutex);
func_ptrs_to_update.push_back(&func_ptrs_to_update_thread_local);
- return result;
+ return &result->get();
+}
+
+void GDScript::_remove_func_ptr_to_update(const UpdatableFuncPtrElement *p_func_ptr_element) {
+ // None of these checks should ever fail, unless there's a bug.
+ // They can be removed once we are sure they never catch anything.
+ // Left here now due to extra safety needs late in the release cycle.
+ ERR_FAIL_NULL(p_func_ptr_element);
+ MutexLock lock(*p_func_ptr_element->mutex);
+ ERR_FAIL_NULL(p_func_ptr_element->element);
+ ERR_FAIL_NULL(p_func_ptr_element->mutex);
+ p_func_ptr_element->element->erase();
}
-void GDScript::_remove_func_ptr_to_update(const UpdatableFuncPtrElement p_func_ptr_element) {
- ERR_FAIL_NULL(p_func_ptr_element.element);
- ERR_FAIL_NULL(p_func_ptr_element.mutex);
- MutexLock lock(*p_func_ptr_element.mutex);
- p_func_ptr_element.element->erase();
+void GDScript::_fixup_thread_function_bookkeeping() {
+ // Transfer the ownership of these update items to the main thread,
+ // because the current one is dying, leaving theirs orphan, dangling.
+
+ HashSet<GDScript *> scripts;
+
+ DEV_ASSERT(!Thread::is_main_thread());
+ MutexLock lock(func_ptrs_to_update_main_thread->mutex);
+
+ {
+ MutexLock lock2(func_ptrs_to_update_thread_local.mutex);
+
+ while (!func_ptrs_to_update_thread_local.ptrs.is_empty()) {
+ // Transfer the thread-to-script records from the dying thread to the main one.
+
+ List<GDScriptFunction **>::Element *E = func_ptrs_to_update_thread_local.ptrs.front();
+ List<GDScriptFunction **>::Element *new_E = func_ptrs_to_update_main_thread->ptrs.push_front(E->get());
+
+ GDScript *script = (*E->get())->get_script();
+ if (!scripts.has(script)) {
+ scripts.insert(script);
+
+ // Replace dying thread by the main thread in the script-to-thread records.
+
+ MutexLock lock3(script->func_ptrs_to_update_mutex);
+ DEV_ASSERT(script->func_ptrs_to_update.find(&func_ptrs_to_update_thread_local));
+ {
+ for (List<UpdatableFuncPtrElement>::Element *F = script->func_ptrs_to_update_elems.front(); F; F = F->next()) {
+ bool is_dying_thread_entry = F->get().mutex == &func_ptrs_to_update_thread_local.mutex;
+ if (is_dying_thread_entry) {
+ // This may lead to multiple main-thread entries, but that's not a problem
+ // and allows to reuse the element, which is needed, since it's tracked by pointer.
+ F->get().element = new_E;
+ F->get().mutex = &func_ptrs_to_update_main_thread->mutex;
+ }
+ }
+ }
+ }
+
+ E->erase();
+ }
+ }
+ func_ptrs_to_update_main_thread->initialized = true;
+
+ {
+ // Remove orphan thread-to-script entries from every script.
+ // FIXME: This involves iterating through every script whenever a thread dies.
+ // While it's OK that thread creation/destruction are heavy operations,
+ // additional bookkeeping can be used to outperform this brute-force approach.
+
+ GDScriptLanguage *gd_lang = GDScriptLanguage::get_singleton();
+
+ MutexLock lock2(gd_lang->mutex);
+
+ for (SelfList<GDScript> *s = gd_lang->script_list.first(); s; s = s->next()) {
+ GDScript *script = s->self();
+ for (List<UpdatableFuncPtr *>::Element *E = script->func_ptrs_to_update.front(); E; E = E->next()) {
+ bool is_dying_thread_entry = &E->get()->mutex == &func_ptrs_to_update_thread_local.mutex;
+ if (is_dying_thread_entry) {
+ E->erase();
+ break;
+ }
+ }
+ }
+ }
}
void GDScript::clear(ClearData *p_clear_data) {
@@ -1441,6 +1519,7 @@ void GDScript::clear(ClearData *p_clear_data) {
*func_ptr_ptr = nullptr;
}
}
+ func_ptrs_to_update_elems.clear();
}
RBSet<GDScript *> must_clear_dependencies = get_must_clear_dependencies();
@@ -2060,6 +2139,10 @@ void GDScriptLanguage::remove_named_global_constant(const StringName &p_name) {
named_globals.erase(p_name);
}
+void GDScriptLanguage::thread_exit() {
+ GDScript::_fixup_thread_function_bookkeeping();
+}
+
void GDScriptLanguage::init() {
//populate global constants
int gcc = CoreConstants::get_global_constant_count();
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 04b0a1d786..9b99f5ca0b 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -128,11 +128,16 @@ class GDScript : public Script {
Mutex *mutex = nullptr;
};
static thread_local UpdatableFuncPtr func_ptrs_to_update_thread_local;
+ static thread_local LocalVector<List<UpdatableFuncPtr *>::Element> func_ptrs_to_update_entries_thread_local;
+ static UpdatableFuncPtr *func_ptrs_to_update_main_thread;
List<UpdatableFuncPtr *> func_ptrs_to_update;
+ List<UpdatableFuncPtrElement> func_ptrs_to_update_elems;
Mutex func_ptrs_to_update_mutex;
- UpdatableFuncPtrElement _add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr);
- static void _remove_func_ptr_to_update(const UpdatableFuncPtrElement p_func_ptr_element);
+ UpdatableFuncPtrElement *_add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr);
+ static void _remove_func_ptr_to_update(const UpdatableFuncPtrElement *p_func_ptr_element);
+
+ static void _fixup_thread_function_bookkeeping();
#ifdef TOOLS_ENABLED
// For static data storage during hot-reloading.
@@ -171,6 +176,7 @@ class GDScript : public Script {
//exported members
String source;
String path;
+ bool path_valid = false; // False if using default path.
StringName local_name; // Inner class identifier or `class_name`.
StringName global_name; // `class_name`.
String fully_qualified_name;
@@ -553,6 +559,10 @@ public:
virtual void add_named_global_constant(const StringName &p_name, const Variant &p_value) override;
virtual void remove_named_global_constant(const StringName &p_name) override;
+ /* MULTITHREAD FUNCTIONS */
+
+ virtual void thread_exit() override;
+
/* DEBUGGER FUNCTIONS */
virtual String debug_get_error() const override;
diff --git a/modules/gdscript/gdscript_lambda_callable.cpp b/modules/gdscript/gdscript_lambda_callable.cpp
index 547f5607d3..339d1ac08e 100644
--- a/modules/gdscript/gdscript_lambda_callable.cpp
+++ b/modules/gdscript/gdscript_lambda_callable.cpp
@@ -296,5 +296,7 @@ GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Object *p_self, GDScriptF
}
GDScriptLambdaSelfCallable::~GDScriptLambdaSelfCallable() {
- GDScript::_remove_func_ptr_to_update(updatable_func_ptr_element);
+ if (updatable_func_ptr_element) {
+ GDScript::_remove_func_ptr_to_update(updatable_func_ptr_element);
+ }
}
diff --git a/modules/gdscript/gdscript_lambda_callable.h b/modules/gdscript/gdscript_lambda_callable.h
index ee7d547544..d961f18852 100644
--- a/modules/gdscript/gdscript_lambda_callable.h
+++ b/modules/gdscript/gdscript_lambda_callable.h
@@ -45,7 +45,7 @@ class GDScriptLambdaCallable : public CallableCustom {
GDScriptFunction *function = nullptr;
Ref<GDScript> script;
uint32_t h;
- GDScript::UpdatableFuncPtrElement updatable_func_ptr_element;
+ GDScript::UpdatableFuncPtrElement *updatable_func_ptr_element = nullptr;
Vector<Variant> captures;
@@ -72,7 +72,7 @@ class GDScriptLambdaSelfCallable : public CallableCustom {
Ref<RefCounted> reference; // For objects that are RefCounted, keep a reference.
Object *object = nullptr; // For non RefCounted objects, use a direct pointer.
uint32_t h;
- GDScript::UpdatableFuncPtrElement updatable_func_ptr_element;
+ GDScript::UpdatableFuncPtrElement *updatable_func_ptr_element = nullptr;
Vector<Variant> captures;
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 842381567c..f0ee405b41 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -3041,10 +3041,13 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
}
}
- int result = EditorNode::get_singleton()->execute_and_show_output(TTR("Building Android Project (gradle)"), build_command, cmdline);
+ String build_project_output;
+ int result = EditorNode::get_singleton()->execute_and_show_output(TTR("Building Android Project (gradle)"), build_command, cmdline, true, false, &build_project_output);
if (result != 0) {
- add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Building of Android project failed, check output for the error. Alternatively visit docs.godotengine.org for Android build documentation."));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Building of Android project failed, check output for the error:") + "\n\n" + build_project_output);
return ERR_CANT_CREATE;
+ } else {
+ print_verbose(build_project_output);
}
List<String> copy_args;
@@ -3071,10 +3074,13 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
copy_args.push_back("-Pexport_filename=" + export_filename);
print_verbose("Copying Android binary using gradle command: " + String("\n") + build_command + " " + join_list(copy_args, String(" ")));
- int copy_result = EditorNode::get_singleton()->execute_and_show_output(TTR("Moving output"), build_command, copy_args);
+ String copy_binary_output;
+ int copy_result = EditorNode::get_singleton()->execute_and_show_output(TTR("Moving output"), build_command, copy_args, true, false, &copy_binary_output);
if (copy_result != 0) {
- add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Unable to copy and rename export file, check gradle project directory for outputs."));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Unable to copy and rename export file:") + "\n\n" + copy_binary_output);
return ERR_CANT_CREATE;
+ } else {
+ print_verbose(copy_binary_output);
}
print_verbose("Successfully completed Android gradle build.");
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index dd39f88352..19718f12be 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -577,6 +577,8 @@ Error ResourceLoaderText::load() {
if (do_assign) {
if (cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
res->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE);
+ } else if (!path.is_resource_file()) {
+ res->set_path_cache(path);
}
res->set_scene_unique_id(id);
}