summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/android_builds.yml7
-rw-r--r--.github/workflows/linux_builds.yml29
-rw-r--r--.github/workflows/static_checks.yml8
-rw-r--r--.gitignore2
-rw-r--r--core/extension/gdextension_interface.cpp24
-rw-r--r--core/extension/gdextension_interface.h10
-rw-r--r--core/input/input.cpp9
-rw-r--r--core/input/input_event.cpp2
-rw-r--r--core/input/input_event.h2
-rw-r--r--core/string/string_name.cpp59
-rw-r--r--core/variant/array.cpp3
-rw-r--r--core/variant/container_type_validate.h36
-rw-r--r--doc/classes/AnimationNode.xml17
-rw-r--r--doc/classes/Control.xml6
-rw-r--r--doc/classes/DisplayServer.xml8
-rw-r--r--doc/classes/EditorImportPlugin.xml10
-rw-r--r--doc/classes/FontFile.xml17
-rw-r--r--doc/classes/FontVariation.xml10
-rw-r--r--doc/classes/MultiplayerAPI.xml2
-rw-r--r--doc/classes/Node.xml2
-rw-r--r--doc/classes/Viewport.xml4
-rw-r--r--drivers/gles3/storage/light_storage.cpp7
-rw-r--r--drivers/gles3/storage/light_storage.h8
-rw-r--r--drivers/gles3/storage/texture_storage.h1
-rw-r--r--editor/animation_track_editor.cpp2
-rw-r--r--editor/animation_track_editor_plugins.cpp4
-rw-r--r--editor/connections_dialog.cpp24
-rw-r--r--editor/connections_dialog.h2
-rw-r--r--editor/debugger/script_editor_debugger.cpp6
-rw-r--r--editor/debugger/script_editor_debugger.h1
-rw-r--r--editor/editor_file_system.cpp80
-rw-r--r--editor/editor_file_system.h3
-rw-r--r--editor/editor_node.cpp50
-rw-r--r--editor/editor_node.h3
-rw-r--r--editor/editor_properties.cpp2
-rw-r--r--editor/editor_properties_array_dict.cpp2
-rw-r--r--editor/editor_spin_slider.cpp1
-rw-r--r--editor/import/editor_import_plugin.cpp16
-rw-r--r--editor/import/editor_import_plugin.h3
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp31
-rw-r--r--editor/project_converter_3_to_4.cpp7
-rw-r--r--editor/project_converter_3_to_4.h3
-rw-r--r--misc/ci/sources.list4
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml4
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp30
-rw-r--r--modules/gdscript/gdscript_editor.cpp45
-rw-r--r--modules/gdscript/gdscript_parser.cpp61
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/await_signal_no_infer.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/await_signal_no_infer.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_within_non_node.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_within_non_node.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node_inner_class.gd7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node_inner_class.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/ternary_weak_infer.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/ternary_weak_infer.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/await_type_inference.gd15
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/await_type_inference.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/onready_on_inner_class_with_non_node_outer.gd7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/onready_on_inner_class_with_non_node_outer.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/ternary_hard_infer.gd15
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/ternary_hard_infer.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd6
-rw-r--r--modules/gltf/editor/editor_import_blend_runner.cpp13
-rw-r--r--modules/gltf/gltf_document.cpp63
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs3
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs60
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs22
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs50
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs7
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/VerbosityLevelId.cs11
-rw-r--r--modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs2
-rw-r--r--modules/text_server_adv/text_server_adv.cpp50
-rw-r--r--modules/text_server_adv/text_server_adv.h3
-rw-r--r--platform/android/export/export_plugin.cpp94
-rw-r--r--platform/android/export/export_plugin.h10
-rw-r--r--platform/android/export/gradle_export_util.cpp8
-rw-r--r--platform/android/export/gradle_export_util.h4
-rw-r--r--platform/android/java/app/build.gradle8
-rw-r--r--platform/android/java/app/gradle.properties4
-rw-r--r--platform/android/java/app/settings.gradle2
-rw-r--r--platform/android/java/app/src/com/godot/game/GodotApp.java2
-rw-r--r--platform/android/java/build.gradle14
-rw-r--r--platform/android/java/gradle.properties2
-rw-r--r--platform/android/java/lib/res/values/strings.xml1
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java37
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp17
-rw-r--r--scene/3d/audio_stream_player_3d.cpp11
-rw-r--r--scene/animation/animation_blend_space_1d.cpp20
-rw-r--r--scene/animation/animation_blend_space_1d.h7
-rw-r--r--scene/animation/animation_blend_space_2d.cpp26
-rw-r--r--scene/animation/animation_blend_space_2d.h7
-rw-r--r--scene/animation/animation_blend_tree.cpp53
-rw-r--r--scene/animation/animation_blend_tree.h8
-rw-r--r--scene/animation/animation_node_state_machine.cpp20
-rw-r--r--scene/animation/animation_node_state_machine.h5
-rw-r--r--scene/animation/animation_player.cpp17
-rw-r--r--scene/animation/animation_tree.cpp60
-rw-r--r--scene/animation/animation_tree.h8
-rw-r--r--scene/gui/control.cpp8
-rw-r--r--scene/gui/dialogs.cpp4
-rw-r--r--scene/gui/option_button.cpp8
-rw-r--r--scene/main/canvas_item.cpp11
-rw-r--r--scene/main/canvas_item.h1
-rw-r--r--scene/main/viewport.cpp75
-rw-r--r--scene/main/viewport.h8
-rw-r--r--scene/main/window.cpp107
-rw-r--r--scene/main/window.h8
-rw-r--r--scene/resources/resource_format_text.cpp8
-rw-r--r--servers/debugger/servers_debugger.cpp1
-rw-r--r--servers/rendering/dummy/storage/light_storage.h1
-rw-r--r--servers/rendering/dummy/storage/texture_storage.h1
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp4
-rw-r--r--servers/rendering/renderer_rd/storage_rd/light_storage.cpp7
-rw-r--r--servers/rendering/renderer_rd/storage_rd/light_storage.h8
-rw-r--r--servers/rendering/renderer_rd/storage_rd/texture_storage.cpp9
-rw-r--r--servers/rendering/renderer_rd/storage_rd/texture_storage.h1
-rw-r--r--servers/rendering/renderer_scene_cull.cpp3
-rw-r--r--servers/rendering/renderer_scene_cull.h3
-rw-r--r--servers/rendering/storage/light_storage.h1
-rw-r--r--servers/rendering/storage/texture_storage.h1
124 files changed, 1229 insertions, 542 deletions
diff --git a/.github/workflows/android_builds.yml b/.github/workflows/android_builds.yml
index ed4ef01012..666b10d016 100644
--- a/.github/workflows/android_builds.yml
+++ b/.github/workflows/android_builds.yml
@@ -19,13 +19,6 @@ jobs:
steps:
- uses: actions/checkout@v3
- # Azure repositories are not reliable, we need to prevent azure giving us packages.
- - name: Make apt sources.list use the default Ubuntu repositories
- run: |
- sudo rm -f /etc/apt/sources.list.d/*
- sudo cp -f misc/ci/sources.list /etc/apt/sources.list
- sudo apt-get update
-
- name: Set up Java 11
uses: actions/setup-java@v3
with:
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml
index bfda7c72f8..46a9228616 100644
--- a/.github/workflows/linux_builds.yml
+++ b/.github/workflows/linux_builds.yml
@@ -74,16 +74,13 @@ jobs:
steps:
- uses: actions/checkout@v3
- - name: Linux dependencies
- shell: bash
+ # Need newer mesa for lavapipe to work properly.
+ - name: Linux dependencies for tests
+ if: ${{ matrix.proj-test }}
run: |
- # Azure repositories are not reliable, we need to prevent azure giving us packages.
- sudo rm -f /etc/apt/sources.list.d/*
- sudo cp -f misc/ci/sources.list /etc/apt/sources.list
+ sudo add-apt-repository ppa:kisak/kisak-mesa
sudo apt-get update
- # The actual dependencies
- sudo apt-get install build-essential pkg-config libgl1-mesa-dev libglu-dev \
- xvfb wget unzip llvm
+ sudo apt-get install -qq mesa-vulkan-drivers
- name: Setup Godot build cache
uses: ./.github/actions/godot-cache
@@ -138,16 +135,6 @@ jobs:
${{ matrix.bin }} --doctool --headless 2>&1 > /dev/null || true
git diff --color --exit-code && ! git ls-files --others --exclude-standard | sed -e 's/^/New doc file missing in PR: /' | grep 'xml$'
- # Download, unzip and setup SwiftShader library
- # See https://github.com/godotengine/regression-test-project/releases/tag/_ci-deps for details
- - name: Download SwiftShader
- if: ${{ matrix.tests }}
- run: |
- wget https://github.com/godotengine/regression-test-project/releases/download/_ci-deps/swiftshader-ubuntu20.04-20211002.zip
- unzip swiftshader-ubuntu20.04-20211002.zip
- curr="$(pwd)/libvk_swiftshader.so"
- sed -i "s|PATH_TO_CHANGE|$curr|" vk_swiftshader_icd.json
-
# Test 3.x -> 4.x project converter
- name: Test project converter
if: ${{ matrix.proj-conv }}
@@ -155,7 +142,7 @@ jobs:
mkdir converter_test
cd converter_test
touch project.godot
- ../${{ matrix.bin }} --headless --audio-driver Dummy --validate-conversion-3to4
+ ../${{ matrix.bin }} --headless --validate-conversion-3to4
cd ..
rm converter_test -rf
@@ -171,7 +158,7 @@ jobs:
- name: Open and close editor (Vulkan)
if: ${{ matrix.proj-test }}
run: |
- VK_ICD_FILENAMES=$(pwd)/vk_swiftshader_icd.json DRI_PRIME=0 xvfb-run ${{ matrix.bin }} --audio-driver Dummy --editor --quit --path test_project 2>&1 | tee sanitizers_log.txt || true
+ xvfb-run ${{ matrix.bin }} --audio-driver Dummy --editor --quit --path test_project 2>&1 | tee sanitizers_log.txt || true
misc/scripts/check_ci_log.py sanitizers_log.txt
- name: Open and close editor (GLES3)
@@ -184,7 +171,7 @@ jobs:
- name: Run project
if: ${{ matrix.proj-test }}
run: |
- VK_ICD_FILENAMES=$(pwd)/vk_swiftshader_icd.json DRI_PRIME=0 xvfb-run ${{ matrix.bin }} 40 --audio-driver Dummy --path test_project 2>&1 | tee sanitizers_log.txt || true
+ xvfb-run ${{ matrix.bin }} 40 --audio-driver Dummy --path test_project 2>&1 | tee sanitizers_log.txt || true
misc/scripts/check_ci_log.py sanitizers_log.txt
# Checkout godot-cpp
diff --git a/.github/workflows/static_checks.yml b/.github/workflows/static_checks.yml
index eece270dd9..a3722e19ce 100644
--- a/.github/workflows/static_checks.yml
+++ b/.github/workflows/static_checks.yml
@@ -13,17 +13,11 @@ jobs:
- name: Checkout
uses: actions/checkout@v3
- # Azure repositories are not reliable, we need to prevent Azure giving us packages.
- - name: Make apt sources.list use the default Ubuntu repositories
+ - name: Install dependencies
run: |
- sudo rm -f /etc/apt/sources.list.d/*
- sudo cp -f misc/ci/sources.list /etc/apt/sources.list
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-15 main"
sudo apt-get update
-
- - name: Install dependencies
- run: |
sudo apt-get install -qq dos2unix clang-format-15 libxml2-utils python3-pip moreutils
sudo update-alternatives --remove-all clang-format || true
sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-15 100
diff --git a/.gitignore b/.gitignore
index ca96220570..e9beb26e7e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,7 +30,7 @@ misc/hooks/pre-commit-custom-*
#############################
# Buildsystem
-bin/
+bin
*.gen.*
compile_commands.json
platform/windows/godot_res.res
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index 3bea013fab..b02a9ee368 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -54,14 +54,23 @@ static void gdextension_free(void *p_mem) {
}
// Helper print functions.
-static void gdextension_print_error(const char *p_description, const char *p_function, const char *p_file, int32_t p_line) {
- _err_print_error(p_function, p_file, p_line, p_description, false, ERR_HANDLER_ERROR);
+static void gdextension_print_error(const char *p_description, const char *p_function, const char *p_file, int32_t p_line, bool p_editor_notify) {
+ _err_print_error(p_function, p_file, p_line, p_description, p_editor_notify, ERR_HANDLER_ERROR);
}
-static void gdextension_print_warning(const char *p_description, const char *p_function, const char *p_file, int32_t p_line) {
- _err_print_error(p_function, p_file, p_line, p_description, false, ERR_HANDLER_WARNING);
+static void gdextension_print_error_with_message(const char *p_description, const char *p_message, const char *p_function, const char *p_file, int32_t p_line, bool p_editor_notify) {
+ _err_print_error(p_function, p_file, p_line, p_description, p_message, p_editor_notify, ERR_HANDLER_ERROR);
}
-static void gdextension_print_script_error(const char *p_description, const char *p_function, const char *p_file, int32_t p_line) {
- _err_print_error(p_function, p_file, p_line, p_description, false, ERR_HANDLER_SCRIPT);
+static void gdextension_print_warning(const char *p_description, const char *p_function, const char *p_file, int32_t p_line, bool p_editor_notify) {
+ _err_print_error(p_function, p_file, p_line, p_description, p_editor_notify, ERR_HANDLER_WARNING);
+}
+static void gdextension_print_warning_with_message(const char *p_description, const char *p_message, const char *p_function, const char *p_file, int32_t p_line, bool p_editor_notify) {
+ _err_print_error(p_function, p_file, p_line, p_description, p_message, p_editor_notify, ERR_HANDLER_WARNING);
+}
+static void gdextension_print_script_error(const char *p_description, const char *p_function, const char *p_file, int32_t p_line, bool p_editor_notify) {
+ _err_print_error(p_function, p_file, p_line, p_description, p_editor_notify, ERR_HANDLER_SCRIPT);
+}
+static void gdextension_print_script_error_with_message(const char *p_description, const char *p_message, const char *p_function, const char *p_file, int32_t p_line, bool p_editor_notify) {
+ _err_print_error(p_function, p_file, p_line, p_description, p_message, p_editor_notify, ERR_HANDLER_SCRIPT);
}
uint64_t gdextension_get_native_struct_size(GDExtensionConstStringNamePtr p_name) {
@@ -1014,8 +1023,11 @@ void gdextension_setup_interface(GDExtensionInterface *p_interface) {
gde_interface.mem_free = gdextension_free;
gde_interface.print_error = gdextension_print_error;
+ gde_interface.print_error_with_message = gdextension_print_error_with_message;
gde_interface.print_warning = gdextension_print_warning;
+ gde_interface.print_warning_with_message = gdextension_print_warning_with_message;
gde_interface.print_script_error = gdextension_print_script_error;
+ gde_interface.print_script_error_with_message = gdextension_print_script_error_with_message;
gde_interface.get_native_struct_size = gdextension_get_native_struct_size;
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index 876a09beff..d561d0ecbd 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -37,7 +37,6 @@
#include <stddef.h>
#include <stdint.h>
-#include <stdio.h>
#ifndef __cplusplus
typedef uint32_t char32_t;
@@ -415,9 +414,12 @@ typedef struct {
void *(*mem_realloc)(void *p_ptr, size_t p_bytes);
void (*mem_free)(void *p_ptr);
- void (*print_error)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line);
- void (*print_warning)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line);
- void (*print_script_error)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line);
+ void (*print_error)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line, bool p_editor_notify);
+ void (*print_error_with_message)(const char *p_description, const char *p_message, const char *p_function, const char *p_file, int32_t p_line, bool p_editor_notify);
+ void (*print_warning)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line, bool p_editor_notify);
+ void (*print_warning_with_message)(const char *p_description, const char *p_message, const char *p_function, const char *p_file, int32_t p_line, bool p_editor_notify);
+ void (*print_script_error)(const char *p_description, const char *p_function, const char *p_file, int32_t p_line, bool p_editor_notify);
+ void (*print_script_error_with_message)(const char *p_description, const char *p_message, const char *p_function, const char *p_file, int32_t p_line, bool p_editor_notify);
uint64_t (*get_native_struct_size)(GDExtensionConstStringNamePtr p_name);
diff --git a/core/input/input.cpp b/core/input/input.cpp
index 2e886f9093..071d9ba648 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -533,6 +533,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
touch_event->set_pressed(mb->is_pressed());
touch_event->set_position(mb->get_position());
touch_event->set_double_tap(mb->is_double_click());
+ touch_event->set_device(InputEvent::DEVICE_ID_EMULATION);
event_dispatch_function(touch_event);
}
}
@@ -557,6 +558,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
drag_event->set_pen_inverted(mm->get_pen_inverted());
drag_event->set_pressure(mm->get_pressure());
drag_event->set_velocity(get_last_mouse_velocity());
+ drag_event->set_device(InputEvent::DEVICE_ID_EMULATION);
event_dispatch_function(drag_event);
}
@@ -592,7 +594,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
Ref<InputEventMouseButton> button_event;
button_event.instantiate();
- button_event->set_device(InputEvent::DEVICE_ID_TOUCH_MOUSE);
+ button_event->set_device(InputEvent::DEVICE_ID_EMULATION);
button_event->set_position(st->get_position());
button_event->set_global_position(st->get_position());
button_event->set_pressed(st->is_pressed());
@@ -623,7 +625,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
Ref<InputEventMouseMotion> motion_event;
motion_event.instantiate();
- motion_event->set_device(InputEvent::DEVICE_ID_TOUCH_MOUSE);
+ motion_event->set_device(InputEvent::DEVICE_ID_EMULATION);
motion_event->set_tilt(sd->get_tilt());
motion_event->set_pen_inverted(sd->get_pen_inverted());
motion_event->set_pressure(sd->get_pressure());
@@ -832,7 +834,7 @@ void Input::ensure_touch_mouse_raised() {
Ref<InputEventMouseButton> button_event;
button_event.instantiate();
- button_event->set_device(InputEvent::DEVICE_ID_TOUCH_MOUSE);
+ button_event->set_device(InputEvent::DEVICE_ID_EMULATION);
button_event->set_position(mouse_pos);
button_event->set_global_position(mouse_pos);
button_event->set_pressed(false);
@@ -869,6 +871,7 @@ void Input::set_default_cursor_shape(CursorShape p_shape) {
mm.instantiate();
mm->set_position(mouse_pos);
mm->set_global_position(mouse_pos);
+ mm->set_device(InputEvent::DEVICE_ID_INTERNAL);
parse_input_event(mm);
}
diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp
index 5a9ec74184..7c4642a8a5 100644
--- a/core/input/input_event.cpp
+++ b/core/input/input_event.cpp
@@ -34,7 +34,7 @@
#include "core/input/shortcut.h"
#include "core/os/keyboard.h"
-const int InputEvent::DEVICE_ID_TOUCH_MOUSE = -1;
+const int InputEvent::DEVICE_ID_EMULATION = -1;
const int InputEvent::DEVICE_ID_INTERNAL = -2;
void InputEvent::set_device(int p_device) {
diff --git a/core/input/input_event.h b/core/input/input_event.h
index 797761b208..eff8d479db 100644
--- a/core/input/input_event.h
+++ b/core/input/input_event.h
@@ -59,7 +59,7 @@ protected:
static void _bind_methods();
public:
- static const int DEVICE_ID_TOUCH_MOUSE;
+ static const int DEVICE_ID_EMULATION;
static const int DEVICE_ID_INTERNAL;
void set_device(int p_device);
diff --git a/core/string/string_name.cpp b/core/string/string_name.cpp
index 95812fc311..df9b6b3f1a 100644
--- a/core/string/string_name.cpp
+++ b/core/string/string_name.cpp
@@ -226,19 +226,16 @@ StringName::StringName(const char *p_name, bool p_static) {
_data = _data->next;
}
- if (_data) {
- if (_data->refcount.ref()) {
- // exists
- if (p_static) {
- _data->static_count.increment();
- }
+ if (_data && _data->refcount.ref()) {
+ // exists
+ if (p_static) {
+ _data->static_count.increment();
+ }
#ifdef DEBUG_ENABLED
- if (unlikely(debug_stringname)) {
- _data->debug_references++;
- }
-#endif
+ if (unlikely(debug_stringname)) {
+ _data->debug_references++;
}
-
+#endif
return;
}
@@ -288,19 +285,17 @@ StringName::StringName(const StaticCString &p_static_string, bool p_static) {
_data = _data->next;
}
- if (_data) {
- if (_data->refcount.ref()) {
- // exists
- if (p_static) {
- _data->static_count.increment();
- }
+ if (_data && _data->refcount.ref()) {
+ // exists
+ if (p_static) {
+ _data->static_count.increment();
+ }
#ifdef DEBUG_ENABLED
- if (unlikely(debug_stringname)) {
- _data->debug_references++;
- }
-#endif
- return;
+ if (unlikely(debug_stringname)) {
+ _data->debug_references++;
}
+#endif
+ return;
}
_data = memnew(_Data);
@@ -348,19 +343,17 @@ StringName::StringName(const String &p_name, bool p_static) {
_data = _data->next;
}
- if (_data) {
- if (_data->refcount.ref()) {
- // exists
- if (p_static) {
- _data->static_count.increment();
- }
+ if (_data && _data->refcount.ref()) {
+ // exists
+ if (p_static) {
+ _data->static_count.increment();
+ }
#ifdef DEBUG_ENABLED
- if (unlikely(debug_stringname)) {
- _data->debug_references++;
- }
-#endif
- return;
+ if (unlikely(debug_stringname)) {
+ _data->debug_references++;
}
+#endif
+ return;
}
_data = memnew(_Data);
diff --git a/core/variant/array.cpp b/core/variant/array.cpp
index 2e1adb9167..d156c35343 100644
--- a/core/variant/array.cpp
+++ b/core/variant/array.cpp
@@ -225,6 +225,9 @@ void Array::assign(const Array &p_array) {
_p->array = p_array._p->array;
return;
}
+ if (typed.type == Variant::OBJECT || source_typed.type == Variant::OBJECT) {
+ ERR_FAIL_MSG(vformat(R"(Cannot assign contents of "Array[%s]" to "Array[%s]".)", Variant::get_type_name(source_typed.type), Variant::get_type_name(typed.type)));
+ }
Vector<Variant> array;
array.resize(size);
diff --git a/core/variant/container_type_validate.h b/core/variant/container_type_validate.h
index 796b66dc77..ad679db9d0 100644
--- a/core/variant/container_type_validate.h
+++ b/core/variant/container_type_validate.h
@@ -41,34 +41,26 @@ struct ContainerTypeValidate {
const char *where = "container";
_FORCE_INLINE_ bool can_reference(const ContainerTypeValidate &p_type) const {
- if (type == p_type.type) {
- if (type != Variant::OBJECT) {
- return true; //nothing else to check
- }
- } else {
+ if (type != p_type.type) {
return false;
+ } else if (type != Variant::OBJECT) {
+ return true;
}
- //both are object
-
- if ((class_name != StringName()) != (p_type.class_name != StringName())) {
- return false; //both need to have class or none
- }
-
- if (class_name != p_type.class_name) {
- if (!ClassDB::is_parent_class(p_type.class_name, class_name)) {
- return false;
- }
- }
-
- if (script.is_null() != p_type.script.is_null()) {
+ if (class_name == StringName()) {
+ return true;
+ } else if (p_type.class_name == StringName()) {
+ return false;
+ } else if (class_name != p_type.class_name && !ClassDB::is_parent_class(p_type.class_name, class_name)) {
return false;
}
- if (script != p_type.script) {
- if (!p_type.script->inherits_script(script)) {
- return false;
- }
+ if (script.is_null()) {
+ return true;
+ } else if (p_type.script.is_null()) {
+ return false;
+ } else if (script != p_type.script && !p_type.script->inherits_script(script)) {
+ return false;
}
return true;
diff --git a/doc/classes/AnimationNode.xml b/doc/classes/AnimationNode.xml
index bc65e6013b..4dd83c0d9f 100644
--- a/doc/classes/AnimationNode.xml
+++ b/doc/classes/AnimationNode.xml
@@ -187,9 +187,24 @@
</member>
</members>
<signals>
+ <signal name="animation_node_removed">
+ <param index="0" name="object_id" type="int" />
+ <param index="1" name="name" type="String" />
+ <description>
+ Emitted by nodes that inherit from this class and that have an internal tree when one of their nodes removes. The nodes that emit this signal are [AnimationNodeBlendSpace1D], [AnimationNodeBlendSpace2D], [AnimationNodeStateMachine], and [AnimationNodeBlendTree].
+ </description>
+ </signal>
+ <signal name="animation_node_renamed">
+ <param index="0" name="object_id" type="int" />
+ <param index="1" name="old_name" type="String" />
+ <param index="2" name="new_name" type="String" />
+ <description>
+ Emitted by nodes that inherit from this class and that have an internal tree when one of their node names changes. The nodes that emit this signal are [AnimationNodeBlendSpace1D], [AnimationNodeBlendSpace2D], [AnimationNodeStateMachine], and [AnimationNodeBlendTree].
+ </description>
+ </signal>
<signal name="tree_changed">
<description>
- Emitted by nodes that inherit from this class and that have an internal tree when one of their nodes changes. The nodes that emit this signal are [AnimationNodeBlendSpace1D], [AnimationNodeBlendSpace2D], [AnimationNodeStateMachine], and [AnimationNodeBlendTree].
+ Emitted by nodes that inherit from this class and that have an internal tree when one of their nodes changes. The nodes that emit this signal are [AnimationNodeBlendSpace1D], [AnimationNodeBlendSpace2D], [AnimationNodeStateMachine], [AnimationNodeBlendTree] and [AnimationNodeTransition].
</description>
</signal>
</signals>
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index d74ddba369..7d7925d61e 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -144,11 +144,11 @@
</method>
<method name="_has_point" qualifiers="virtual const">
<return type="bool" />
- <param index="0" name="position" type="Vector2" />
+ <param index="0" name="point" type="Vector2" />
<description>
- Virtual method to be implemented by the user. Returns whether the given [param position] is inside this control.
+ Virtual method to be implemented by the user. Returns whether the given [param point] is inside this control.
If not overridden, default behavior is checking if the point is within control's Rect.
- [b]Note:[/b] If you want to check if a point is inside the control, you can use [code]get_rect().has_point(point)[/code].
+ [b]Note:[/b] If you want to check if a point is inside the control, you can use [code]Rect2(Vector2.ZERO, size).has_point(point)[/code].
</description>
</method>
<method name="_make_custom_tooltip" qualifiers="virtual const">
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index 6f4a7fc13d..55ba1f4f0c 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -1325,6 +1325,7 @@
<param index="1" name="window_id" type="int" default="0" />
<description>
Sets the maximum size of the window specified by [param window_id] in pixels. Normally, the user will not be able to drag the window to make it smaller than the specified size. See also [method window_get_max_size].
+ [b]Note:[/b] It's recommended to change this value using [member Window.max_size] instead.
[b]Note:[/b] Using third-party tools, it is possible for users to disable window geometry restrictions and therefore bypass this limit.
</description>
</method>
@@ -1334,6 +1335,7 @@
<param index="1" name="window_id" type="int" default="0" />
<description>
Sets the minimum size for the given window to [param min_size] (in pixels). Normally, the user will not be able to drag the window to make it larger than the specified size. See also [method window_get_min_size].
+ [b]Note:[/b] It's recommended to change this value using [member Window.min_size] instead.
[b]Note:[/b] By default, the main window has a minimum size of [code]Vector2i(64, 64)[/code]. This prevents issues that can arise when the window is resized to a near-zero size.
[b]Note:[/b] Using third-party tools, it is possible for users to disable window geometry restrictions and therefore bypass this limit.
</description>
@@ -1403,6 +1405,7 @@
+-------------+ +-------+
[/codeblock]
See also [method window_get_position] and [method window_set_size].
+ [b]Note:[/b] It's recommended to change this value using [member Window.position] instead.
</description>
</method>
<method name="window_set_rect_changed_callback">
@@ -1419,6 +1422,7 @@
<param index="1" name="window_id" type="int" default="0" />
<description>
Sets the size of the given window to [param size] (in pixels). See also [method window_get_size] and [method window_get_position].
+ [b]Note:[/b] It's recommended to change this value using [member Window.size] instead.
</description>
</method>
<method name="window_set_title">
@@ -1427,6 +1431,7 @@
<param index="1" name="window_id" type="int" default="0" />
<description>
Sets the title of the given window to [param title].
+ [b]Note:[/b] It's recommended to change this value using [member Window.title] instead.
[b]Note:[/b] Avoid changing the window title every frame, as this can cause performance issues on certain window managers. Try to change the window title only a few times per second at most.
</description>
</method>
@@ -1436,7 +1441,8 @@
<param index="1" name="parent_window_id" type="int" />
<description>
Sets window transient parent. Transient window is will be destroyed with its transient parent and will return focus to their parent when closed. The transient window is displayed on top of a non-exclusive full-screen parent window. Transient windows can't enter full-screen mode.
- Note that behavior might be different depending on the platform.
+ [b]Note:[/b] It's recommended to change this value using [member Window.transient] instead.
+ [b]Note:[/b] The behavior might be different depending on the platform.
</description>
</method>
<method name="window_set_vsync_mode">
diff --git a/doc/classes/EditorImportPlugin.xml b/doc/classes/EditorImportPlugin.xml
index 6a976d218f..66b61f187e 100644
--- a/doc/classes/EditorImportPlugin.xml
+++ b/doc/classes/EditorImportPlugin.xml
@@ -227,5 +227,15 @@
This method must be overridden to do the actual importing work. See this class' description for an example of overriding this method.
</description>
</method>
+ <method name="append_import_external_resource">
+ <return type="int" enum="Error" />
+ <param index="0" name="path" type="String" />
+ <param index="1" name="custom_options" type="Dictionary" default="{}" />
+ <param index="2" name="custom_importer" type="String" default="&quot;&quot;" />
+ <param index="3" name="generator_parameters" type="Variant" default="null" />
+ <description>
+ This function can only be called during the [method _import] callback and it allows manually importing resources from it. This is useful when the imported file generates external resources that require importing (as example, images). Custom parameters for the ".import" file can be passed via the [param custom_options]. Additionally, in cases where multiple importers can handle a file, the [param custom_importer] ca be specified to force a specific one. This function performs a resource import and returns immediately with a success or error code. [param generator_parameters] defines optional extra metadata which will be stored as [code]generator_parameters[/code] in the [code]remap[/code] section of the [code].import[/code] file, for example to store a md5 hash of the source data.
+ </description>
+ </method>
</methods>
</class>
diff --git a/doc/classes/FontFile.xml b/doc/classes/FontFile.xml
index 69a7627774..a349c2b7b7 100644
--- a/doc/classes/FontFile.xml
+++ b/doc/classes/FontFile.xml
@@ -17,13 +17,13 @@
[codeblocks]
[gdscript]
var f = load("res://BarlowCondensed-Bold.ttf")
- $"Label".set("custom_fonts/font", f)
- $"Label".set("custom_fonts/font_size", 64)
+ $Label.add_theme_font_override("font", f)
+ $Label.add_theme_font_size_override("font_size", 64)
[/gdscript]
[csharp]
var f = ResourceLoader.Load&lt;FontFile&gt;("res://BarlowCondensed-Bold.ttf");
- GetNode("Label").Set("custom_fonts/font", f);
- GetNode("Label").Set("custom_font_sizes/font_size", 64);
+ GetNode("Label").AddThemeFontOverride("font", f);
+ GetNode("Label").AddThemeFontSizeOverride("font_size", 64);
[/csharp]
[/codeblocks]
</description>
@@ -88,6 +88,7 @@
<param index="0" name="cache_index" type="int" />
<param index="1" name="size" type="int" />
<description>
+ Returns the font descent (number of pixels below the baseline).
</description>
</method>
<method name="get_cache_scale" qualifiers="const">
@@ -95,6 +96,7 @@
<param index="0" name="cache_index" type="int" />
<param index="1" name="size" type="int" />
<description>
+ Returns scaling factor of the color bitmap font.
</description>
</method>
<method name="get_cache_underline_position" qualifiers="const">
@@ -102,6 +104,7 @@
<param index="0" name="cache_index" type="int" />
<param index="1" name="size" type="int" />
<description>
+ Returns pixel offset of the underline below the baseline.
</description>
</method>
<method name="get_cache_underline_thickness" qualifiers="const">
@@ -109,6 +112,7 @@
<param index="0" name="cache_index" type="int" />
<param index="1" name="size" type="int" />
<description>
+ Returns thickness of the underline in pixels.
</description>
</method>
<method name="get_embolden" qualifiers="const">
@@ -377,6 +381,7 @@
<param index="1" name="size" type="int" />
<param index="2" name="ascent" type="float" />
<description>
+ Sets the font ascent (number of pixels above the baseline).
</description>
</method>
<method name="set_cache_descent">
@@ -385,6 +390,7 @@
<param index="1" name="size" type="int" />
<param index="2" name="descent" type="float" />
<description>
+ Sets the font descent (number of pixels below the baseline).
</description>
</method>
<method name="set_cache_scale">
@@ -393,6 +399,7 @@
<param index="1" name="size" type="int" />
<param index="2" name="scale" type="float" />
<description>
+ Sets scaling factor of the color bitmap font.
</description>
</method>
<method name="set_cache_underline_position">
@@ -401,6 +408,7 @@
<param index="1" name="size" type="int" />
<param index="2" name="underline_position" type="float" />
<description>
+ Sets pixel offset of the underline below the baseline.
</description>
</method>
<method name="set_cache_underline_thickness">
@@ -409,6 +417,7 @@
<param index="1" name="size" type="int" />
<param index="2" name="underline_thickness" type="float" />
<description>
+ Sets thickness of the underline in pixels.
</description>
</method>
<method name="set_embolden">
diff --git a/doc/classes/FontVariation.xml b/doc/classes/FontVariation.xml
index e0fad126b9..5bc2606adb 100644
--- a/doc/classes/FontVariation.xml
+++ b/doc/classes/FontVariation.xml
@@ -10,16 +10,16 @@
[gdscript]
var fv = FontVariation.new()
fv.set_base_font(load("res://BarlowCondensed-Regular.ttf"))
- fv.set_variation_embolden(1.2);
- $"Label".set("custom_fonts/font", fv)
- $"Label".set("custom_fonts/font_size", 64)
+ fv.set_variation_embolden(1.2)
+ $Label.add_theme_font_override("font", fv)
+ $Label.add_theme_font_size_override("font_size", 64)
[/gdscript]
[csharp]
var fv = new FontVariation();
fv.SetBaseFont(ResourceLoader.Load&lt;FontFile&gt;("res://BarlowCondensed-Regular.ttf"));
fv.SetVariationEmbolden(1.2);
- GetNode("Label").Set("custom_fonts/font", fv);
- GetNode("Label").Set("custom_font_sizes/font_size", 64);
+ GetNode("Label").AddThemeFontOverride("font", fv);
+ GetNode("Label").AddThemeFontSizeOverride("font_size", 64);
[/csharp]
[/codeblocks]
</description>
diff --git a/doc/classes/MultiplayerAPI.xml b/doc/classes/MultiplayerAPI.xml
index 020ce4f468..29011cd016 100644
--- a/doc/classes/MultiplayerAPI.xml
+++ b/doc/classes/MultiplayerAPI.xml
@@ -138,7 +138,7 @@
Used with [method Node.rpc_config] to disable a method or property for all RPC calls, making it unavailable. Default for all methods.
</constant>
<constant name="RPC_MODE_ANY_PEER" value="1" enum="RPCMode">
- Used with [method Node.rpc_config] to set a method to be callable remotely by any peer. Analogous to the [code]@rpc("any")[/code] annotation. Calls are accepted from all remote peers, no matter if they are node's authority or not.
+ Used with [method Node.rpc_config] to set a method to be callable remotely by any peer. Analogous to the [code]@rpc("any_peer")[/code] annotation. Calls are accepted from all remote peers, no matter if they are node's authority or not.
</constant>
<constant name="RPC_MODE_AUTHORITY" value="2" enum="RPCMode">
Used with [method Node.rpc_config] to set a method to be callable remotely only by the current multiplayer authority (which is the server by default). Analogous to the [code]@rpc("authority")[/code] annotation. See [method Node.set_multiplayer_authority].
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index 22665c8ffb..bc43f228a7 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -666,7 +666,7 @@
channel = 0,
}
[/codeblock]
- See [enum MultiplayerAPI.RPCMode] and [enum MultiplayerPeer.TransferMode]. An alternative is annotating methods and properties with the corresponding [annotation @GDScript.@rpc] annotation ([code]@rpc(any_peer)[/code], [code]@rpc(authority)[/code]). By default, methods are not exposed to networking (and RPCs).
+ See [enum MultiplayerAPI.RPCMode] and [enum MultiplayerPeer.TransferMode]. An alternative is annotating methods and properties with the corresponding [annotation @GDScript.@rpc] annotation ([code]@rpc("any_peer")[/code], [code]@rpc("authority")[/code]). By default, methods are not exposed to networking (and RPCs).
</description>
</method>
<method name="rpc_id" qualifiers="vararg">
diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml
index ab2de14638..e76f805e3c 100644
--- a/doc/classes/Viewport.xml
+++ b/doc/classes/Viewport.xml
@@ -277,6 +277,10 @@
<member name="physics_object_picking" type="bool" setter="set_physics_object_picking" getter="get_physics_object_picking" default="false">
If [code]true[/code], the objects rendered by viewport become subjects of mouse picking process.
</member>
+ <member name="physics_object_picking_sort" type="bool" setter="set_physics_object_picking_sort" getter="get_physics_object_picking_sort" default="false">
+ If [code]true[/code], objects receive mouse picking events sorted primarily by their [member CanvasItem.z_index] and secondarily by their position in the scene tree. If [code]false[/code], the order is undetermined.
+ [b]Note:[/b] This setting is disabled by default because of its potential expensive computational cost.
+ </member>
<member name="positional_shadow_atlas_16_bits" type="bool" setter="set_positional_shadow_atlas_16_bits" getter="get_positional_shadow_atlas_16_bits" default="true">
</member>
<member name="positional_shadow_atlas_quad_0" type="int" setter="set_positional_shadow_atlas_quadrant_subdiv" getter="get_positional_shadow_atlas_quadrant_subdiv" enum="Viewport.PositionalShadowAtlasQuadrantSubdiv" default="2">
diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp
index 138498220d..026f7467a8 100644
--- a/drivers/gles3/storage/light_storage.cpp
+++ b/drivers/gles3/storage/light_storage.cpp
@@ -311,6 +311,13 @@ uint64_t LightStorage::light_get_version(RID p_light) const {
return light->version;
}
+uint32_t LightStorage::light_get_cull_mask(RID p_light) const {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, 0);
+
+ return light->cull_mask;
+}
+
AABB LightStorage::light_get_aabb(RID p_light) const {
const Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_COND_V(!light, AABB());
diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h
index f6f7628a90..8e6480869b 100644
--- a/drivers/gles3/storage/light_storage.h
+++ b/drivers/gles3/storage/light_storage.h
@@ -239,13 +239,6 @@ public:
return light->color;
}
- _FORCE_INLINE_ uint32_t light_get_cull_mask(RID p_light) {
- const Light *light = light_owner.get_or_null(p_light);
- ERR_FAIL_COND_V(!light, 0);
-
- return light->cull_mask;
- }
-
_FORCE_INLINE_ bool light_is_distance_fade_enabled(RID p_light) {
const Light *light = light_owner.get_or_null(p_light);
return light->distance_fade;
@@ -297,6 +290,7 @@ public:
virtual RS::LightBakeMode light_get_bake_mode(RID p_light) override;
virtual uint32_t light_get_max_sdfgi_cascade(RID p_light) override { return 0; }
virtual uint64_t light_get_version(RID p_light) const override;
+ virtual uint32_t light_get_cull_mask(RID p_light) const override;
/* LIGHT INSTANCE API */
diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h
index 017e74fc4d..fedda6b260 100644
--- a/drivers/gles3/storage/texture_storage.h
+++ b/drivers/gles3/storage/texture_storage.h
@@ -585,6 +585,7 @@ public:
virtual void decal_set_normal_fade(RID p_decal, float p_fade) override;
virtual AABB decal_get_aabb(RID p_decal) const override;
+ virtual uint32_t decal_get_cull_mask(RID p_decal) const override { return 0; }
virtual void texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) override {}
virtual void texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) override {}
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index ee4163bc14..8426dfd1ac 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -1108,7 +1108,7 @@ void AnimationMultiTrackKeyEdit::_get_property_list(List<PropertyInfo> *p_list)
p_list->push_back(PropertyInfo(Variant::VECTOR3, "position"));
} break;
case Animation::TYPE_ROTATION_3D: {
- p_list->push_back(PropertyInfo(Variant::QUATERNION, "scale"));
+ p_list->push_back(PropertyInfo(Variant::QUATERNION, "rotation"));
} break;
case Animation::TYPE_SCALE_3D: {
p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale"));
diff --git a/editor/animation_track_editor_plugins.cpp b/editor/animation_track_editor_plugins.cpp
index ba73a63245..2895aa9710 100644
--- a/editor/animation_track_editor_plugins.cpp
+++ b/editor/animation_track_editor_plugins.cpp
@@ -389,7 +389,7 @@ Rect2 AnimationTrackEditSpriteFrame::get_key_rect(int p_index, float p_pixels_se
size = texture->get_size();
- if (bool(object->call("is_region"))) {
+ if (bool(object->call("is_region_enabled"))) {
size = Rect2(object->call("get_region_rect")).size;
}
@@ -479,7 +479,7 @@ void AnimationTrackEditSpriteFrame::draw_key(int p_index, float p_pixels_sec, in
region.size = texture->get_size();
- if (bool(object->call("is_region"))) {
+ if (bool(object->call("is_region_enabled"))) {
region = Rect2(object->call("get_region_rect"));
}
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index f4d293e9f4..20a9e633a8 100644
--- a/editor/connections_dialog.cpp
+++ b/editor/connections_dialog.cpp
@@ -170,6 +170,10 @@ void ConnectDialog::_tree_node_selected() {
_update_ok_enabled();
}
+void ConnectDialog::_focus_currently_connected() {
+ tree->set_selected(source);
+}
+
void ConnectDialog::_unbind_count_changed(double p_count) {
for (Control *control : bind_controls) {
BaseButton *b = Object::cast_to<BaseButton>(control);
@@ -577,6 +581,8 @@ void ConnectDialog::init(const ConnectionData &p_cd, const PackedStringArray &p_
void ConnectDialog::popup_dialog(const String p_for_signal) {
from_signal->set_text(p_for_signal);
error_label->add_theme_color_override("font_color", error_label->get_theme_color(SNAME("error_color"), SNAME("Editor")));
+ filter_nodes->clear();
+
if (!advanced->is_pressed()) {
error_label->set_visible(!_find_first_script(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root()));
}
@@ -628,12 +634,28 @@ ConnectDialog::ConnectDialog() {
tree = memnew(SceneTreeEditor(false));
tree->set_connecting_signal(true);
tree->set_show_enabled_subscene(true);
+ tree->set_v_size_flags(Control::SIZE_FILL | Control::SIZE_EXPAND);
tree->get_scene_tree()->connect("item_activated", callable_mp(this, &ConnectDialog::_item_activated));
tree->connect("node_selected", callable_mp(this, &ConnectDialog::_tree_node_selected));
tree->set_connect_to_script_mode(true);
- Node *mc = vbc_left->add_margin_child(TTR("Connect to Script:"), tree, true);
+ HBoxContainer *hbc_filter = memnew(HBoxContainer);
+
+ filter_nodes = memnew(LineEdit);
+ hbc_filter->add_child(filter_nodes);
+ filter_nodes->set_h_size_flags(Control::SIZE_FILL | Control::SIZE_EXPAND);
+ filter_nodes->set_placeholder(TTR("Filter Nodes"));
+ filter_nodes->set_clear_button_enabled(true);
+ filter_nodes->connect("text_changed", callable_mp(tree, &SceneTreeEditor::set_filter));
+
+ Button *focus_current = memnew(Button);
+ hbc_filter->add_child(focus_current);
+ focus_current->set_text(TTR("Go to Source"));
+ focus_current->connect("pressed", callable_mp(this, &ConnectDialog::_focus_currently_connected));
+
+ Node *mc = vbc_left->add_margin_child(TTR("Connect to Script:"), hbc_filter, false);
connect_to_label = Object::cast_to<Label>(vbc_left->get_child(mc->get_index() - 1));
+ vbc_left->add_child(tree);
error_label = memnew(Label);
error_label->set_text(TTR("Scene does not contain any script."));
diff --git a/editor/connections_dialog.h b/editor/connections_dialog.h
index 277ea03cf7..e5375a3fc8 100644
--- a/editor/connections_dialog.h
+++ b/editor/connections_dialog.h
@@ -106,6 +106,7 @@ public:
private:
Label *connect_to_label = nullptr;
LineEdit *from_signal = nullptr;
+ LineEdit *filter_nodes = nullptr;
Node *source = nullptr;
ConnectionData source_connection_data;
StringName signal;
@@ -142,6 +143,7 @@ private:
void _item_activated();
void _text_submitted(const String &p_text);
void _tree_node_selected();
+ void _focus_currently_connected();
void _method_selected();
void _create_method_tree_items(const List<MethodInfo> &p_methods, TreeItem *p_parent_item);
diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp
index 32952a367d..304beec681 100644
--- a/editor/debugger/script_editor_debugger.cpp
+++ b/editor/debugger/script_editor_debugger.cpp
@@ -308,6 +308,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
String error = p_data[1];
bool has_stackdump = p_data[2];
breaked = true;
+ can_request_idle_draw = true;
can_debug = can_continue;
_update_buttons_state();
_set_reason_text(error, MESSAGE_ERROR);
@@ -378,6 +379,8 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
vmem_total->set_tooltip_text(TTR("Bytes:") + " " + itos(total));
vmem_total->set_text(String::humanize_size(total));
+ } else if (p_msg == "servers:drawn") {
+ can_request_idle_draw = true;
} else if (p_msg == "stack_dump") {
DebuggerMarshalls::ScriptStackDump stack;
stack.deserialize(p_data);
@@ -843,8 +846,9 @@ void ScriptEditorDebugger::_notification(int p_what) {
msg.push_back(cam->get_far());
_put_msg("scene:override_camera_3D:transform", msg);
}
- if (breaked) {
+ if (breaked && can_request_idle_draw) {
_put_msg("servers:draw", Array());
+ can_request_idle_draw = false;
}
}
diff --git a/editor/debugger/script_editor_debugger.h b/editor/debugger/script_editor_debugger.h
index a0c420522a..1659bbee8d 100644
--- a/editor/debugger/script_editor_debugger.h
+++ b/editor/debugger/script_editor_debugger.h
@@ -155,6 +155,7 @@ private:
bool breaked = false;
bool can_debug = false;
bool move_to_foreground = true;
+ bool can_request_idle_draw = false;
bool live_debug;
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index 644c32e8a4..ec1ef8a6bc 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -1900,49 +1900,56 @@ Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector
return err;
}
-void EditorFileSystem::_reimport_file(const String &p_file, const HashMap<StringName, Variant> *p_custom_options, const String &p_custom_importer) {
+Error EditorFileSystem::_reimport_file(const String &p_file, const HashMap<StringName, Variant> &p_custom_options, const String &p_custom_importer, Variant *p_generator_parameters) {
EditorFileSystemDirectory *fs = nullptr;
int cpos = -1;
bool found = _find_file(p_file, &fs, cpos);
- ERR_FAIL_COND_MSG(!found, "Can't find file '" + p_file + "'.");
+ ERR_FAIL_COND_V_MSG(!found, ERR_FILE_NOT_FOUND, "Can't find file '" + p_file + "'.");
//try to obtain existing params
- HashMap<StringName, Variant> params;
+ HashMap<StringName, Variant> params = p_custom_options;
String importer_name; //empty by default though
if (!p_custom_importer.is_empty()) {
importer_name = p_custom_importer;
}
- if (p_custom_options != nullptr) {
- params = *p_custom_options;
- }
ResourceUID::ID uid = ResourceUID::INVALID_ID;
+ Variant generator_parameters;
+ if (p_generator_parameters) {
+ generator_parameters = *p_generator_parameters;
+ }
if (FileAccess::exists(p_file + ".import")) {
//use existing
- if (p_custom_options == nullptr) {
- Ref<ConfigFile> cf;
- cf.instantiate();
- Error err = cf->load(p_file + ".import");
- if (err == OK) {
- if (cf->has_section("params")) {
- List<String> sk;
- cf->get_section_keys("params", &sk);
- for (const String &E : sk) {
+ Ref<ConfigFile> cf;
+ cf.instantiate();
+ Error err = cf->load(p_file + ".import");
+ if (err == OK) {
+ if (cf->has_section("params")) {
+ List<String> sk;
+ cf->get_section_keys("params", &sk);
+ for (const String &E : sk) {
+ if (!params.has(E)) {
params[E] = cf->get_value("params", E);
}
}
+ }
+
+ if (cf->has_section("remap")) {
+ if (p_custom_importer.is_empty()) {
+ importer_name = cf->get_value("remap", "importer");
+ }
- if (cf->has_section("remap")) {
- if (p_custom_importer.is_empty()) {
- importer_name = cf->get_value("remap", "importer");
- }
+ if (cf->has_section_key("remap", "uid")) {
+ String uidt = cf->get_value("remap", "uid");
+ uid = ResourceUID::get_singleton()->text_to_id(uidt);
+ }
- if (cf->has_section_key("remap", "uid")) {
- String uidt = cf->get_value("remap", "uid");
- uid = ResourceUID::get_singleton()->text_to_id(uidt);
+ if (!p_generator_parameters) {
+ if (cf->has_section_key("remap", "generator_parameters")) {
+ generator_parameters = cf->get_value("remap", "generator_parameters");
}
}
}
@@ -1957,7 +1964,7 @@ void EditorFileSystem::_reimport_file(const String &p_file, const HashMap<String
fs->files[cpos]->type = "";
fs->files[cpos]->import_valid = false;
EditorResourcePreview::get_singleton()->check_for_invalidation(p_file);
- return;
+ return OK;
}
Ref<ResourceImporter> importer;
bool load_default = false;
@@ -1971,8 +1978,7 @@ void EditorFileSystem::_reimport_file(const String &p_file, const HashMap<String
importer = ResourceFormatImporter::get_singleton()->get_importer_by_extension(p_file.get_extension());
load_default = true;
if (importer.is_null()) {
- ERR_PRINT("BUG: File queued for import, but can't be imported, importer for type '" + importer_name + "' not found.");
- ERR_FAIL();
+ ERR_FAIL_V_MSG(ERR_FILE_CANT_OPEN, "BUG: File queued for import, but can't be imported, importer for type '" + importer_name + "' not found.");
}
}
@@ -2005,16 +2011,14 @@ void EditorFileSystem::_reimport_file(const String &p_file, const HashMap<String
Variant meta;
Error err = importer->import(p_file, base_path, params, &import_variants, &gen_files, &meta);
- if (err != OK) {
- ERR_PRINT("Error importing '" + p_file + "'.");
- }
+ ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_UNRECOGNIZED, "Error importing '" + p_file + "'.");
//as import is complete, save the .import file
Vector<String> dest_paths;
{
Ref<FileAccess> f = FileAccess::open(p_file + ".import", FileAccess::WRITE);
- ERR_FAIL_COND_MSG(f.is_null(), "Cannot open file from path '" + p_file + ".import'.");
+ ERR_FAIL_COND_V_MSG(f.is_null(), ERR_FILE_CANT_OPEN, "Cannot open file from path '" + p_file + ".import'.");
//write manually, as order matters ([remap] has to go first for performance).
f->store_line("[remap]");
@@ -2059,6 +2063,10 @@ void EditorFileSystem::_reimport_file(const String &p_file, const HashMap<String
f->store_line("metadata=" + meta.get_construct_string());
}
+ if (generator_parameters != Variant()) {
+ f->store_line("generator_parameters=" + generator_parameters.get_construct_string());
+ }
+
f->store_line("");
f->store_line("[deps]\n");
@@ -2102,7 +2110,7 @@ void EditorFileSystem::_reimport_file(const String &p_file, const HashMap<String
// Store the md5's of the various files. These are stored separately so that the .import files can be version controlled.
{
Ref<FileAccess> md5s = FileAccess::open(base_path + ".md5", FileAccess::WRITE);
- ERR_FAIL_COND_MSG(md5s.is_null(), "Cannot open MD5 file '" + base_path + ".md5'.");
+ ERR_FAIL_COND_V_MSG(md5s.is_null(), ERR_FILE_CANT_OPEN, "Cannot open MD5 file '" + base_path + ".md5'.");
md5s->store_line("source_md5=\"" + FileAccess::get_md5(p_file) + "\"");
if (dest_paths.size()) {
@@ -2136,6 +2144,8 @@ void EditorFileSystem::_reimport_file(const String &p_file, const HashMap<String
}
EditorResourcePreview::get_singleton()->check_for_invalidation(p_file);
+
+ return OK;
}
void EditorFileSystem::_find_group_files(EditorFileSystemDirectory *efd, HashMap<String, Vector<String>> &group_files, HashSet<String> &groups_to_reimport) {
@@ -2156,7 +2166,7 @@ void EditorFileSystem::_find_group_files(EditorFileSystemDirectory *efd, HashMap
}
void EditorFileSystem::reimport_file_with_custom_parameters(const String &p_file, const String &p_importer, const HashMap<StringName, Variant> &p_custom_params) {
- _reimport_file(p_file, &p_custom_params, p_importer);
+ _reimport_file(p_file, p_custom_params, p_importer);
}
void EditorFileSystem::_reimport_thread(uint32_t p_index, ImportThreadData *p_import_data) {
@@ -2166,10 +2176,11 @@ void EditorFileSystem::_reimport_thread(uint32_t p_index, ImportThreadData *p_im
void EditorFileSystem::reimport_files(const Vector<String> &p_files) {
importing = true;
- EditorProgress pr("reimport", TTR("(Re)Importing Assets"), p_files.size());
Vector<String> reloads;
+ EditorProgress pr("reimport", TTR("(Re)Importing Assets"), p_files.size());
+
Vector<ImportFile> reimport_files;
HashSet<String> groups_to_reimport;
@@ -2292,6 +2303,11 @@ void EditorFileSystem::reimport_files(const Vector<String> &p_files) {
emit_signal(SNAME("resources_reimported"), reloads);
}
+Error EditorFileSystem::reimport_append(const String &p_file, const HashMap<StringName, Variant> &p_custom_options, const String &p_custom_importer, Variant p_generator_parameters) {
+ ERR_FAIL_COND_V_MSG(!importing, ERR_INVALID_PARAMETER, "Can only append files to import during a current reimport process.");
+ return _reimport_file(p_file, p_custom_options, p_custom_importer, &p_generator_parameters);
+}
+
Error EditorFileSystem::_resource_import(const String &p_path) {
Vector<String> files;
files.push_back(p_path);
diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h
index 2490bd31b3..0d558c84c5 100644
--- a/editor/editor_file_system.h
+++ b/editor/editor_file_system.h
@@ -242,7 +242,7 @@ class EditorFileSystem : public Node {
void _update_extensions();
- void _reimport_file(const String &p_file, const HashMap<StringName, Variant> *p_custom_options = nullptr, const String &p_custom_importer = String());
+ Error _reimport_file(const String &p_file, const HashMap<StringName, Variant> &p_custom_options = HashMap<StringName, Variant>(), const String &p_custom_importer = String(), Variant *generator_parameters = nullptr);
Error _reimport_group(const String &p_group_file, const Vector<String> &p_files);
bool _test_for_reimport(const String &p_path, bool p_only_imported_files);
@@ -315,6 +315,7 @@ public:
EditorFileSystemDirectory *find_file(const String &p_file, int *r_index) const;
void reimport_files(const Vector<String> &p_files);
+ Error reimport_append(const String &p_file, const HashMap<StringName, Variant> &p_custom_options, const String &p_custom_importer, Variant p_generator_parameters);
void reimport_file_with_custom_parameters(const String &p_file, const String &p_importer, const HashMap<StringName, Variant> &p_custom_params);
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index f317c23b83..3adebb2f8e 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -852,6 +852,18 @@ void EditorNode::_remove_plugin_from_enabled(const String &p_name) {
ps->set("editor_plugins/enabled", enabled_plugins);
}
+void EditorNode::_plugin_over_edit(EditorPlugin *p_plugin, Object *p_object) {
+ if (p_object) {
+ editor_plugins_over->add_plugin(p_plugin);
+ p_plugin->make_visible(true);
+ p_plugin->edit(p_object);
+ } else {
+ editor_plugins_over->remove_plugin(p_plugin);
+ p_plugin->make_visible(false);
+ p_plugin->edit(nullptr);
+ }
+}
+
void EditorNode::_resources_changed(const Vector<String> &p_resources) {
List<Ref<Resource>> changed;
@@ -2102,8 +2114,7 @@ void EditorNode::edit_item(Object *p_object, Object *p_editing_owner) {
if (!item_plugins.has(plugin)) {
// Remove plugins no longer used by this editing owner.
to_remove.push_back(plugin);
- plugin->make_visible(false);
- plugin->edit(nullptr);
+ _plugin_over_edit(plugin, nullptr);
}
}
@@ -2113,6 +2124,7 @@ void EditorNode::edit_item(Object *p_object, Object *p_editing_owner) {
for (EditorPlugin *plugin : item_plugins) {
if (active_plugins[owner_id].has(plugin)) {
+ plugin->edit(p_object);
continue;
}
@@ -2127,9 +2139,7 @@ void EditorNode::edit_item(Object *p_object, Object *p_editing_owner) {
}
}
active_plugins[owner_id].insert(plugin);
- editor_plugins_over->add_plugin(plugin);
- plugin->edit(p_object);
- plugin->make_visible(true);
+ _plugin_over_edit(plugin, p_object);
}
} else {
hide_unused_editors(p_editing_owner);
@@ -2181,9 +2191,7 @@ void EditorNode::hide_unused_editors(const Object *p_editing_owner) {
if (p_editing_owner) {
const ObjectID id = p_editing_owner->get_instance_id();
for (EditorPlugin *plugin : active_plugins[id]) {
- plugin->make_visible(false);
- plugin->edit(nullptr);
- editor_plugins_over->remove_plugin(plugin);
+ _plugin_over_edit(plugin, nullptr);
}
active_plugins.erase(id);
} else {
@@ -2194,9 +2202,7 @@ void EditorNode::hide_unused_editors(const Object *p_editing_owner) {
if (!ObjectDB::get_instance(kv.key)) {
to_remove.push_back(kv.key);
for (EditorPlugin *plugin : kv.value) {
- plugin->make_visible(false);
- plugin->edit(nullptr);
- editor_plugins_over->remove_plugin(plugin);
+ _plugin_over_edit(plugin, nullptr);
}
}
}
@@ -2947,7 +2953,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
} else if (export_template_manager->can_install_android_template()) {
install_android_build_template->popup_centered();
} else {
- custom_build_manage_templates->popup_centered();
+ gradle_build_manage_templates->popup_centered();
}
}
} break;
@@ -3037,7 +3043,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
#endif
} break;
case SETTINGS_INSTALL_ANDROID_BUILD_TEMPLATE: {
- custom_build_manage_templates->hide();
+ gradle_build_manage_templates->hide();
file_android_build_source->popup_centered_ratio();
} break;
case SETTINGS_MANAGE_FEATURE_PROFILES: {
@@ -5618,8 +5624,9 @@ void EditorNode::_bottom_panel_switch(bool p_enable, int p_idx) {
return;
}
- bottom_panel_updating = true;
if (p_enable) {
+ bottom_panel_updating = true;
+
for (int i = 0; i < bottom_panel_items.size(); i++) {
bottom_panel_items[i].button->set_pressed(i == p_idx);
bottom_panel_items[i].control->set_visible(i == p_idx);
@@ -5636,7 +5643,6 @@ void EditorNode::_bottom_panel_switch(bool p_enable, int p_idx) {
top_split->hide();
}
bottom_panel_raise->show();
-
} else {
bottom_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("BottomPanel"), SNAME("EditorStyles")));
bottom_panel_items[p_idx].button->set_pressed(false);
@@ -7700,12 +7706,12 @@ EditorNode::EditorNode() {
save_confirmation->connect("confirmed", callable_mp(this, &EditorNode::_menu_confirm_current));
save_confirmation->connect("custom_action", callable_mp(this, &EditorNode::_discard_changes));
- custom_build_manage_templates = memnew(ConfirmationDialog);
- custom_build_manage_templates->set_text(TTR("Android build template is missing, please install relevant templates."));
- custom_build_manage_templates->set_ok_button_text(TTR("Manage Templates"));
- custom_build_manage_templates->add_button(TTR("Install from file"))->connect("pressed", callable_mp(this, &EditorNode::_menu_option).bind(SETTINGS_INSTALL_ANDROID_BUILD_TEMPLATE));
- custom_build_manage_templates->connect("confirmed", callable_mp(this, &EditorNode::_menu_option).bind(SETTINGS_MANAGE_EXPORT_TEMPLATES));
- gui_base->add_child(custom_build_manage_templates);
+ gradle_build_manage_templates = memnew(ConfirmationDialog);
+ gradle_build_manage_templates->set_text(TTR("Android build template is missing, please install relevant templates."));
+ gradle_build_manage_templates->set_ok_button_text(TTR("Manage Templates"));
+ gradle_build_manage_templates->add_button(TTR("Install from file"))->connect("pressed", callable_mp(this, &EditorNode::_menu_option).bind(SETTINGS_INSTALL_ANDROID_BUILD_TEMPLATE));
+ gradle_build_manage_templates->connect("confirmed", callable_mp(this, &EditorNode::_menu_option).bind(SETTINGS_MANAGE_EXPORT_TEMPLATES));
+ gui_base->add_child(gradle_build_manage_templates);
file_android_build_source = memnew(EditorFileDialog);
file_android_build_source->set_title(TTR("Select Android sources file"));
@@ -7716,7 +7722,7 @@ EditorNode::EditorNode() {
gui_base->add_child(file_android_build_source);
install_android_build_template = memnew(ConfirmationDialog);
- install_android_build_template->set_text(TTR("This will set up your project for custom Android builds by installing the source template to \"res://android/build\".\nYou can then apply modifications and build your own custom APK on export (adding modules, changing the AndroidManifest.xml, etc.).\nNote that in order to make custom builds instead of using pre-built APKs, the \"Use Custom Build\" option should be enabled in the Android export preset."));
+ install_android_build_template->set_text(TTR("This will set up your project for gradle Android builds by installing the source template to \"res://android/build\".\nYou can then apply modifications and build your own custom APK on export (adding modules, changing the AndroidManifest.xml, etc.).\nNote that in order to make gradle builds instead of using pre-built APKs, the \"Use Gradle Build\" option should be enabled in the Android export preset."));
install_android_build_template->set_ok_button_text(TTR("Install"));
install_android_build_template->connect("confirmed", callable_mp(this, &EditorNode::_menu_confirm_current));
gui_base->add_child(install_android_build_template);
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 914dab0254..eefe45ca1f 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -398,7 +398,7 @@ private:
PopupMenu *editor_layouts = nullptr;
EditorLayoutsDialog *layout_dialog = nullptr;
- ConfirmationDialog *custom_build_manage_templates = nullptr;
+ ConfirmationDialog *gradle_build_manage_templates = nullptr;
ConfirmationDialog *install_android_build_template = nullptr;
ConfirmationDialog *remove_android_build_template = nullptr;
@@ -565,6 +565,7 @@ private:
void _update_file_menu_closed();
void _remove_plugin_from_enabled(const String &p_name);
+ void _plugin_over_edit(EditorPlugin *p_plugin, Object *p_object);
void _fs_changed();
void _resources_reimported(const Vector<String> &p_resources);
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index c9eae77b53..33bba90c70 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -4593,7 +4593,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
} break;
case Variant::PACKED_STRING_ARRAY: {
EditorPropertyArray *editor = memnew(EditorPropertyArray);
- editor->setup(Variant::PACKED_STRING_ARRAY);
+ editor->setup(Variant::PACKED_STRING_ARRAY, p_hint_text);
return editor;
} break;
case Variant::PACKED_VECTOR2_ARRAY: {
diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp
index b96ac9dbcb..24cfa7ad7b 100644
--- a/editor/editor_properties_array_dict.cpp
+++ b/editor/editor_properties_array_dict.cpp
@@ -574,7 +574,7 @@ void EditorPropertyArray::setup(Variant::Type p_array_type, const String &p_hint
// The format of p_hint_string is:
// subType/subTypeHint:nextSubtype ... etc.
- if (array_type == Variant::ARRAY && !p_hint_string.is_empty()) {
+ if (!p_hint_string.is_empty()) {
int hint_subtype_separator = p_hint_string.find(":");
if (hint_subtype_separator >= 0) {
String subtype_string = p_hint_string.substr(0, hint_subtype_separator);
diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp
index 54e14074d9..2d3ec437c6 100644
--- a/editor/editor_spin_slider.cpp
+++ b/editor/editor_spin_slider.cpp
@@ -367,7 +367,6 @@ void EditorSpinSlider::_draw_spin_slider() {
if (!hide_slider) {
if (get_step() == 1) {
- number_width -= updown->get_width();
Ref<Texture2D> updown2 = get_theme_icon(is_read_only() ? SNAME("updown_disabled") : SNAME("updown"), SNAME("SpinBox"));
int updown_vofs = (size.height - updown2->get_height()) / 2;
if (rtl) {
diff --git a/editor/import/editor_import_plugin.cpp b/editor/import/editor_import_plugin.cpp
index ef3d3d1276..7afce116b8 100644
--- a/editor/import/editor_import_plugin.cpp
+++ b/editor/import/editor_import_plugin.cpp
@@ -31,6 +31,7 @@
#include "editor_import_plugin.h"
#include "core/object/script_language.h"
+#include "editor/editor_file_system.h"
EditorImportPlugin::EditorImportPlugin() {
}
@@ -185,6 +186,20 @@ Error EditorImportPlugin::import(const String &p_source_file, const String &p_sa
ERR_FAIL_V_MSG(ERR_METHOD_NOT_FOUND, "Unimplemented _import in add-on.");
}
+Error EditorImportPlugin::_append_import_external_resource(const String &p_file, const Dictionary &p_custom_options, const String &p_custom_importer, Variant p_generator_parameters) {
+ HashMap<StringName, Variant> options;
+ List<Variant> keys;
+ p_custom_options.get_key_list(&keys);
+ for (const Variant &K : keys) {
+ options.insert(K, p_custom_options[K]);
+ }
+ return append_import_external_resource(p_file, options, p_custom_importer, p_generator_parameters);
+}
+
+Error EditorImportPlugin::append_import_external_resource(const String &p_file, const HashMap<StringName, Variant> &p_custom_options, const String &p_custom_importer, Variant p_generator_parameters) {
+ return EditorFileSystem::get_singleton()->reimport_append(p_file, p_custom_options, p_custom_importer, p_generator_parameters);
+}
+
void EditorImportPlugin::_bind_methods() {
GDVIRTUAL_BIND(_get_importer_name)
GDVIRTUAL_BIND(_get_visible_name)
@@ -198,4 +213,5 @@ void EditorImportPlugin::_bind_methods() {
GDVIRTUAL_BIND(_get_import_order)
GDVIRTUAL_BIND(_get_option_visibility, "path", "option_name", "options")
GDVIRTUAL_BIND(_import, "source_file", "save_path", "options", "platform_variants", "gen_files");
+ ClassDB::bind_method(D_METHOD("append_import_external_resource", "path", "custom_options", "custom_importer", "generator_parameters"), &EditorImportPlugin::_append_import_external_resource, DEFVAL(Dictionary()), DEFVAL(String()), DEFVAL(Variant()));
}
diff --git a/editor/import/editor_import_plugin.h b/editor/import/editor_import_plugin.h
index bf912058a2..fb164c7f15 100644
--- a/editor/import/editor_import_plugin.h
+++ b/editor/import/editor_import_plugin.h
@@ -53,6 +53,8 @@ protected:
GDVIRTUAL3RC(bool, _get_option_visibility, String, StringName, Dictionary)
GDVIRTUAL5RC(Error, _import, String, String, Dictionary, TypedArray<String>, TypedArray<String>)
+ Error _append_import_external_resource(const String &p_file, const Dictionary &p_custom_options = Dictionary(), const String &p_custom_importer = String(), Variant p_generator_parameters = Variant());
+
public:
EditorImportPlugin();
virtual String get_importer_name() const override;
@@ -67,6 +69,7 @@ public:
virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const override;
virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata = nullptr) override;
+ Error append_import_external_resource(const String &p_file, const HashMap<StringName, Variant> &p_custom_options = HashMap<StringName, Variant>(), const String &p_custom_importer = String(), Variant p_generator_parameters = Variant());
};
#endif // EDITOR_IMPORT_PLUGIN_H
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index 14b5f7cefb..c41bf4b8cc 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -1171,32 +1171,29 @@ void SpriteFramesEditor::edit(Ref<SpriteFrames> p_frames) {
if (!p_frames.is_valid()) {
frames.unref();
+ hide();
return;
}
frames = p_frames;
read_only = EditorNode::get_singleton()->is_resource_read_only(p_frames);
- if (p_frames.is_valid()) {
- if (!p_frames->has_animation(edited_anim)) {
- List<StringName> anim_names;
- frames->get_animation_list(&anim_names);
- anim_names.sort_custom<StringName::AlphCompare>();
- if (anim_names.size()) {
- edited_anim = anim_names.front()->get();
- } else {
- edited_anim = StringName();
- }
+ if (!p_frames->has_animation(edited_anim)) {
+ List<StringName> anim_names;
+ frames->get_animation_list(&anim_names);
+ anim_names.sort_custom<StringName::AlphCompare>();
+ if (anim_names.size()) {
+ edited_anim = anim_names.front()->get();
+ } else {
+ edited_anim = StringName();
}
-
- _update_library();
- // Clear zoom and split sheet texture
- split_sheet_preview->set_texture(Ref<Texture2D>());
- _zoom_reset();
- } else {
- hide();
}
+ _update_library();
+ // Clear zoom and split sheet texture
+ split_sheet_preview->set_texture(Ref<Texture2D>());
+ _zoom_reset();
+
add_anim->set_disabled(read_only);
delete_anim->set_disabled(read_only);
anim_speed->set_editable(!read_only);
diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp
index 26f872421e..706466a974 100644
--- a/editor/project_converter_3_to_4.cpp
+++ b/editor/project_converter_3_to_4.cpp
@@ -30,13 +30,14 @@
#include "project_converter_3_to_4.h"
-#include "modules/modules_enabled.gen.h"
-
#ifndef DISABLE_DEPRECATED
-#ifdef MODULE_REGEX_ENABLED
const int ERROR_CODE = 77;
+#include "modules/modules_enabled.gen.h" // For regex.
+
+#ifdef MODULE_REGEX_ENABLED
+
#include "modules/regex/regex.h"
#include "core/io/dir_access.h"
diff --git a/editor/project_converter_3_to_4.h b/editor/project_converter_3_to_4.h
index 6ec2dd188d..641bc467ac 100644
--- a/editor/project_converter_3_to_4.h
+++ b/editor/project_converter_3_to_4.h
@@ -29,9 +29,10 @@
/**************************************************************************/
#ifndef PROJECT_CONVERTER_3_TO_4_H
-#ifndef DISABLE_DEPRECATED
#define PROJECT_CONVERTER_3_TO_4_H
+#ifndef DISABLE_DEPRECATED
+
#include "core/io/file_access.h"
#include "core/object/ref_counted.h"
#include "core/string/ustring.h"
diff --git a/misc/ci/sources.list b/misc/ci/sources.list
deleted file mode 100644
index 4d8f94f35c..0000000000
--- a/misc/ci/sources.list
+++ /dev/null
@@ -1,4 +0,0 @@
-deb http://archive.ubuntu.com/ubuntu/ focal main restricted universe multiverse
-deb http://archive.ubuntu.com/ubuntu/ focal-updates main restricted universe multiverse
-deb http://archive.ubuntu.com/ubuntu/ focal-security main restricted universe multiverse
-deb http://archive.ubuntu.com/ubuntu/ focal-backports main restricted universe multiverse
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 026b603683..e05b17168d 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -576,10 +576,10 @@
@rpc
func fn(): pass
- @rpc(any_peer, unreliable_ordered)
+ @rpc("any_peer", "unreliable_ordered")
func fn_update_pos(): pass
- @rpc(authority, call_remote, unreliable, 0) # Equivalent to @rpc
+ @rpc("authority", "call_remote", "unreliable", 0) # Equivalent to @rpc
func fn_default(): pass
[/codeblock]
</description>
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index e84c79d681..602d07d9a7 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -1576,11 +1576,8 @@ void GDScriptAnalyzer::resolve_function_body(GDScriptParser::FunctionNode *p_fun
resolve_suite(p_function->body);
- GDScriptParser::DataType return_type = p_function->body->get_datatype();
-
- if (!p_function->get_datatype().is_hard_type() && return_type.is_set()) {
+ if (!p_function->get_datatype().is_hard_type() && p_function->body->get_datatype().is_set()) {
// Use the suite inferred type if return isn't explicitly set.
- return_type.type_source = GDScriptParser::DataType::INFERRED;
p_function->set_datatype(p_function->body->get_datatype());
} else if (p_function->get_datatype().is_hard_type() && (p_function->get_datatype().kind != GDScriptParser::DataType::BUILTIN || p_function->get_datatype().builtin_type != Variant::NIL)) {
if (!p_function->body->has_return && (p_is_lambda || p_function->identifier->name != GDScriptLanguage::get_singleton()->strings._init)) {
@@ -2471,30 +2468,27 @@ void GDScriptAnalyzer::reduce_await(GDScriptParser::AwaitNode *p_await) {
return;
}
- GDScriptParser::DataType awaiting_type;
-
if (p_await->to_await->type == GDScriptParser::Node::CALL) {
reduce_call(static_cast<GDScriptParser::CallNode *>(p_await->to_await), true);
- awaiting_type = p_await->to_await->get_datatype();
} else {
reduce_expression(p_await->to_await);
}
- if (p_await->to_await->is_constant) {
+ GDScriptParser::DataType await_type = p_await->to_await->get_datatype();
+ // We cannot infer the type of the result of waiting for a signal.
+ if (await_type.is_hard_type() && await_type.kind == GDScriptParser::DataType::BUILTIN && await_type.builtin_type == Variant::SIGNAL) {
+ await_type.kind = GDScriptParser::DataType::VARIANT;
+ await_type.type_source = GDScriptParser::DataType::UNDETECTED;
+ } else if (p_await->to_await->is_constant) {
p_await->is_constant = p_await->to_await->is_constant;
p_await->reduced_value = p_await->to_await->reduced_value;
-
- awaiting_type = p_await->to_await->get_datatype();
- } else {
- awaiting_type.kind = GDScriptParser::DataType::VARIANT;
- awaiting_type.type_source = GDScriptParser::DataType::UNDETECTED;
}
-
- p_await->set_datatype(awaiting_type);
+ await_type.is_coroutine = false;
+ p_await->set_datatype(await_type);
#ifdef DEBUG_ENABLED
- awaiting_type = p_await->to_await->get_datatype();
- if (!(awaiting_type.has_no_type() || awaiting_type.is_coroutine || awaiting_type.builtin_type == Variant::SIGNAL)) {
+ GDScriptParser::DataType to_await_type = p_await->to_await->get_datatype();
+ if (!(to_await_type.has_no_type() || to_await_type.is_coroutine || to_await_type.builtin_type == Variant::SIGNAL)) {
parser->push_warning(p_await, GDScriptWarning::REDUNDANT_AWAIT);
}
#endif
@@ -4113,7 +4107,6 @@ void GDScriptAnalyzer::reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternar
if (!is_type_compatible(true_type, false_type)) {
result = false_type;
if (!is_type_compatible(false_type, true_type)) {
- result.type_source = GDScriptParser::DataType::UNDETECTED;
result.kind = GDScriptParser::DataType::VARIANT;
#ifdef DEBUG_ENABLED
parser->push_warning(p_ternary_op, GDScriptWarning::INCOMPATIBLE_TERNARY);
@@ -4121,6 +4114,7 @@ void GDScriptAnalyzer::reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternar
}
}
}
+ result.type_source = true_type.is_hard_type() && false_type.is_hard_type() ? GDScriptParser::DataType::ANNOTATED_INFERRED : GDScriptParser::DataType::INFERRED;
p_ternary_op->set_datatype(result);
}
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 61ed88a9ca..3543c0a79f 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -800,6 +800,15 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
warning.insert_text = warning.display.quote(p_quote_style);
r_result.insert(warning.display, warning);
}
+ } else if (p_annotation->name == SNAME("@rpc")) {
+ if (p_argument == 0 || p_argument == 1 || p_argument == 2) {
+ static const char *options[7] = { "call_local", "call_remote", "any_peer", "authority", "reliable", "unreliable", "unreliable_ordered" };
+ for (int i = 0; i < 7; i++) {
+ ScriptLanguage::CodeCompletionOption option(options[i], ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
+ option.insert_text = option.display.quote(p_quote_style);
+ r_result.insert(option.display, option);
+ }
+ }
}
}
@@ -968,7 +977,7 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class,
}
break;
case GDScriptParser::ClassNode::Member::SIGNAL:
- if (p_only_functions || outer) {
+ if (p_only_functions || outer || p_static) {
continue;
}
option = ScriptLanguage::CodeCompletionOption(member.signal->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location);
@@ -1024,6 +1033,14 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);
r_result.insert(option.display, option);
}
+
+ List<MethodInfo> signals;
+ scr->get_script_signal_list(&signals);
+ for (const MethodInfo &E : signals) {
+ int location = p_recursion_depth + _get_signal_location(scr->get_class_name(), E.name);
+ ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location);
+ r_result.insert(option.display, option);
+ }
}
HashMap<StringName, Variant> constants;
scr->get_constants(&constants);
@@ -1032,14 +1049,6 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
ScriptLanguage::CodeCompletionOption option(E.key.operator String(), ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);
r_result.insert(option.display, option);
}
-
- List<MethodInfo> signals;
- scr->get_script_signal_list(&signals);
- for (const MethodInfo &E : signals) {
- int location = p_recursion_depth + _get_signal_location(scr->get_class_name(), E.name);
- ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location);
- r_result.insert(option.display, option);
- }
}
List<MethodInfo> methods;
@@ -1084,14 +1093,6 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
r_result.insert(option.display, option);
}
- List<MethodInfo> signals;
- ClassDB::get_signal_list(type, &signals);
- for (const MethodInfo &E : signals) {
- int location = p_recursion_depth + _get_signal_location(type, StringName(E.name));
- ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location);
- r_result.insert(option.display, option);
- }
-
if (!base_type.is_meta_type || Engine::get_singleton()->has_singleton(type)) {
List<PropertyInfo> pinfo;
ClassDB::get_property_list(type, &pinfo);
@@ -1106,6 +1107,14 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);
r_result.insert(option.display, option);
}
+
+ List<MethodInfo> signals;
+ ClassDB::get_signal_list(type, &signals);
+ for (const MethodInfo &E : signals) {
+ int location = p_recursion_depth + _get_signal_location(type, StringName(E.name));
+ ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location);
+ r_result.insert(option.display, option);
+ }
}
}
@@ -2022,6 +2031,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
r_type.type.script_path = script;
r_type.type.class_type = parser->get_parser()->get_tree();
+ r_type.type.is_meta_type = true;
r_type.type.is_constant = false;
r_type.type.kind = GDScriptParser::DataType::CLASS;
r_type.value = Variant();
@@ -2133,6 +2143,7 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
r_type.type.kind = GDScriptParser::DataType::CLASS;
r_type.type.class_type = member.m_class;
+ r_type.type.is_meta_type = true;
return true;
case GDScriptParser::ClassNode::Member::GROUP:
return false; // No-op, but silences warnings.
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index d99a2b86a2..0a1a64cb59 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -1833,10 +1833,18 @@ GDScriptParser::IfNode *GDScriptParser::parse_if(const String &p_token) {
if (match(GDScriptTokenizer::Token::ELIF)) {
SuiteNode *else_block = alloc_node<SuiteNode>();
+ else_block->parent_function = current_function;
+ else_block->parent_block = current_suite;
+
+ SuiteNode *previous_suite = current_suite;
+ current_suite = else_block;
+
IfNode *elif = parse_if("elif");
else_block->statements.push_back(elif);
complete_extents(else_block);
n_if->false_block = else_block;
+
+ current_suite = previous_suite;
} else if (match(GDScriptTokenizer::Token::ELSE)) {
consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "else".)");
n_if->false_block = parse_suite(R"("else" block)");
@@ -3611,6 +3619,10 @@ bool GDScriptParser::icon_annotation(const AnnotationNode *p_annotation, Node *p
bool GDScriptParser::onready_annotation(const AnnotationNode *p_annotation, Node *p_node) {
ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE, false, R"("@onready" annotation can only be applied to class variables.)");
+ if (current_class && !ClassDB::is_parent_class(current_class->get_datatype().native_type, SNAME("Node"))) {
+ push_error(R"("@onready" can only be used in classes that inherit "Node".)", p_annotation);
+ }
+
VariableNode *variable = static_cast<VariableNode *>(p_node);
if (variable->onready) {
push_error(R"("@onready" annotation can only be used once per variable.)");
@@ -3685,6 +3697,13 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
variable->export_info.type = Variant::DICTIONARY;
return true;
+ } else if (export_type.builtin_type == Variant::PACKED_STRING_ARRAY) {
+ String hint_prefix = itos(Variant::STRING) + "/" + itos(variable->export_info.hint);
+ variable->export_info.hint = PROPERTY_HINT_TYPE_STRING;
+ variable->export_info.hint_string = hint_prefix + ":" + variable->export_info.hint_string;
+ variable->export_info.type = Variant::PACKED_STRING_ARRAY;
+
+ return true;
}
}
@@ -3902,26 +3921,46 @@ bool GDScriptParser::rpc_annotation(const AnnotationNode *p_annotation, Node *p_
push_error(R"(Invalid RPC arguments. At most 4 arguments are allowed, where only the last argument can be an integer to specify the channel.')", p_annotation);
return false;
}
+
+ unsigned char locality_args = 0;
+ unsigned char permission_args = 0;
+ unsigned char transfer_mode_args = 0;
+
for (int i = last; i >= 0; i--) {
- String mode = p_annotation->resolved_arguments[i].operator String();
- if (mode == "any_peer") {
- rpc_config["rpc_mode"] = MultiplayerAPI::RPC_MODE_ANY_PEER;
- } else if (mode == "authority") {
- rpc_config["rpc_mode"] = MultiplayerAPI::RPC_MODE_AUTHORITY;
- } else if (mode == "call_local") {
+ String arg = p_annotation->resolved_arguments[i].operator String();
+ if (arg == "call_local") {
+ locality_args++;
rpc_config["call_local"] = true;
- } else if (mode == "call_remote") {
+ } else if (arg == "call_remote") {
+ locality_args++;
rpc_config["call_local"] = false;
- } else if (mode == "reliable") {
+ } else if (arg == "any_peer") {
+ permission_args++;
+ rpc_config["rpc_mode"] = MultiplayerAPI::RPC_MODE_ANY_PEER;
+ } else if (arg == "authority") {
+ permission_args++;
+ rpc_config["rpc_mode"] = MultiplayerAPI::RPC_MODE_AUTHORITY;
+ } else if (arg == "reliable") {
+ transfer_mode_args++;
rpc_config["transfer_mode"] = MultiplayerPeer::TRANSFER_MODE_RELIABLE;
- } else if (mode == "unreliable") {
+ } else if (arg == "unreliable") {
+ transfer_mode_args++;
rpc_config["transfer_mode"] = MultiplayerPeer::TRANSFER_MODE_UNRELIABLE;
- } else if (mode == "unreliable_ordered") {
+ } else if (arg == "unreliable_ordered") {
+ transfer_mode_args++;
rpc_config["transfer_mode"] = MultiplayerPeer::TRANSFER_MODE_UNRELIABLE_ORDERED;
} else {
- push_error(R"(Invalid RPC argument. Must be one of: 'call_local'/'call_remote' (local calls), 'any_peer'/'authority' (permission), 'reliable'/'unreliable'/'unreliable_ordered' (transfer mode).)", p_annotation);
+ push_error(R"(Invalid RPC argument. Must be one of: "call_local"/"call_remote" (local calls), "any_peer"/"authority" (permission), "reliable"/"unreliable"/"unreliable_ordered" (transfer mode).)", p_annotation);
}
}
+
+ if (locality_args > 1) {
+ push_error(R"(Invalid RPC config. The locality ("call_local"/"call_remote") must be specified no more than once.)", p_annotation);
+ } else if (permission_args > 1) {
+ push_error(R"(Invalid RPC config. The permission ("any_peer"/"authority") must be specified no more than once.)", p_annotation);
+ } else if (transfer_mode_args > 1) {
+ push_error(R"(Invalid RPC config. The transfer mode ("reliable"/"unreliable"/"unreliable_ordered") must be specified no more than once.)", p_annotation);
+ }
}
function->rpc_config = rpc_config;
return true;
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/await_signal_no_infer.gd b/modules/gdscript/tests/scripts/analyzer/errors/await_signal_no_infer.gd
new file mode 100644
index 0000000000..c787d9e50e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/await_signal_no_infer.gd
@@ -0,0 +1,4 @@
+signal my_signal()
+
+func test():
+ var _a := await my_signal
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/await_signal_no_infer.out b/modules/gdscript/tests/scripts/analyzer/errors/await_signal_no_infer.out
new file mode 100644
index 0000000000..8f8744ad7e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/await_signal_no_infer.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot infer the type of "_a" variable because the value doesn't have a set type.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_within_non_node.gd b/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_within_non_node.gd
new file mode 100644
index 0000000000..91f5071fa9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_within_non_node.gd
@@ -0,0 +1,5 @@
+extends RefCounted
+
+func test():
+ var nope := $Node
+ print("Cannot use $ without a Node base")
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_within_non_node.out b/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_within_non_node.out
new file mode 100644
index 0000000000..33365908bf
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_within_non_node.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot use shorthand "get_node()" notation ("$") on a class that isn't a node.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node.gd b/modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node.gd
new file mode 100644
index 0000000000..e781315266
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node.gd
@@ -0,0 +1,6 @@
+extends RefCounted
+
+@onready var nope := 0
+
+func test():
+ print("Cannot use @onready without a Node base")
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node.out b/modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node.out
new file mode 100644
index 0000000000..8088d28329
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+"@onready" can only be used in classes that inherit "Node".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node_inner_class.gd b/modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node_inner_class.gd
new file mode 100644
index 0000000000..1639bbbd52
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node_inner_class.gd
@@ -0,0 +1,7 @@
+extends Node
+
+class Inner extends RefCounted:
+ @onready var nope = 0
+
+func test():
+ print("Cannot use @onready without a Node base")
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node_inner_class.out b/modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node_inner_class.out
new file mode 100644
index 0000000000..8088d28329
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node_inner_class.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+"@onready" can only be used in classes that inherit "Node".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/ternary_weak_infer.gd b/modules/gdscript/tests/scripts/analyzer/errors/ternary_weak_infer.gd
new file mode 100644
index 0000000000..fac0e8756c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/ternary_weak_infer.gd
@@ -0,0 +1,6 @@
+func test():
+ var left_hard_int := 1
+ var right_weak_int = 2
+ var result_hm_int := left_hard_int if true else right_weak_int
+
+ print('not ok')
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/ternary_weak_infer.out b/modules/gdscript/tests/scripts/analyzer/errors/ternary_weak_infer.out
new file mode 100644
index 0000000000..71d1e2f8ae
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/ternary_weak_infer.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot infer the type of "result_hm_int" variable because the value doesn't have a set type.
diff --git a/modules/gdscript/tests/scripts/analyzer/features/await_type_inference.gd b/modules/gdscript/tests/scripts/analyzer/features/await_type_inference.gd
new file mode 100644
index 0000000000..9d8cfc7f99
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/await_type_inference.gd
@@ -0,0 +1,15 @@
+func coroutine() -> int:
+ @warning_ignore("redundant_await")
+ await 0
+ return 1
+
+func not_coroutine() -> int:
+ return 2
+
+func test():
+ var a := await coroutine()
+ @warning_ignore("redundant_await")
+ var b := await not_coroutine()
+ @warning_ignore("redundant_await")
+ var c := await 3
+ prints(a, b, c)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/await_type_inference.out b/modules/gdscript/tests/scripts/analyzer/features/await_type_inference.out
new file mode 100644
index 0000000000..2920e2ce9c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/await_type_inference.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+1 2 3
diff --git a/modules/gdscript/tests/scripts/analyzer/features/onready_on_inner_class_with_non_node_outer.gd b/modules/gdscript/tests/scripts/analyzer/features/onready_on_inner_class_with_non_node_outer.gd
new file mode 100644
index 0000000000..1ac03c2181
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/onready_on_inner_class_with_non_node_outer.gd
@@ -0,0 +1,7 @@
+extends RefCounted
+
+func test():
+ print("ok")
+
+class Inner extends Node:
+ @onready var okay = 0
diff --git a/modules/gdscript/tests/scripts/analyzer/features/onready_on_inner_class_with_non_node_outer.out b/modules/gdscript/tests/scripts/analyzer/features/onready_on_inner_class_with_non_node_outer.out
new file mode 100644
index 0000000000..1b47ed10dc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/onready_on_inner_class_with_non_node_outer.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+ok
diff --git a/modules/gdscript/tests/scripts/analyzer/features/ternary_hard_infer.gd b/modules/gdscript/tests/scripts/analyzer/features/ternary_hard_infer.gd
new file mode 100644
index 0000000000..44ca5f4dd0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/ternary_hard_infer.gd
@@ -0,0 +1,15 @@
+func test():
+ var left_hard_int := 1
+ var right_hard_int := 2
+ var result_hard_int := left_hard_int if true else right_hard_int
+ assert(result_hard_int == 1)
+
+ @warning_ignore("inference_on_variant")
+ var left_hard_variant := 1 as Variant
+ @warning_ignore("inference_on_variant")
+ var right_hard_variant := 2.0 as Variant
+ @warning_ignore("inference_on_variant")
+ var result_hard_variant := left_hard_variant if true else right_hard_variant
+ assert(result_hard_variant == 1)
+
+ print('ok')
diff --git a/modules/gdscript/tests/scripts/analyzer/features/ternary_hard_infer.out b/modules/gdscript/tests/scripts/analyzer/features/ternary_hard_infer.out
new file mode 100644
index 0000000000..1b47ed10dc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/ternary_hard_infer.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+ok
diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd b/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd
index e1e6134fd4..092ae49d00 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd
@@ -201,4 +201,10 @@ func test():
assert(typed_enums.get_typed_builtin() == TYPE_INT)
+ var a := A.new()
+ var typed_natives: Array[RefCounted] = [a]
+ var typed_scripts = Array(typed_natives, TYPE_OBJECT, "RefCounted", A)
+ assert(typed_scripts[0] == a)
+
+
print('ok')
diff --git a/modules/gltf/editor/editor_import_blend_runner.cpp b/modules/gltf/editor/editor_import_blend_runner.cpp
index c203a91834..659a60e6a1 100644
--- a/modules/gltf/editor/editor_import_blend_runner.cpp
+++ b/modules/gltf/editor/editor_import_blend_runner.cpp
@@ -181,7 +181,18 @@ Error EditorImportBlendRunner::start_blender(const String &p_python_script, bool
Error EditorImportBlendRunner::do_import(const Dictionary &p_options) {
if (is_using_rpc()) {
- return do_import_rpc(p_options);
+ Error err = do_import_rpc(p_options);
+ if (err != OK) {
+ // Retry without using RPC (slow, but better than the import failing completely).
+ if (err == ERR_CONNECTION_ERROR) {
+ // Disable RPC if the connection could not be established.
+ print_error(vformat("Failed to connect to Blender via RPC, switching to direct imports of .blend files. Check your proxy and firewall settings, then RPC can be re-enabled by changing the editor setting `filesystem/import/blender/rpc_port` to %d.", rpc_port));
+ EditorSettings::get_singleton()->set_manually("filesystem/import/blender/rpc_port", 0);
+ rpc_port = 0;
+ }
+ err = do_import_direct(p_options);
+ }
+ return err;
} else {
return do_import_direct(p_options);
}
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 1a09b5bdcc..bd3ee1881f 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -54,6 +54,9 @@
#include "modules/modules_enabled.gen.h" // For csg, gridmap.
+#ifdef TOOLS_ENABLED
+#include "editor/editor_file_system.h"
+#endif
#ifdef MODULE_CSG_ENABLED
#include "modules/csg/csg_shape.h"
#endif // MODULE_CSG_ENABLED
@@ -3232,54 +3235,38 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p
p_state->source_images.push_back(Ref<Image>());
} else {
Error err = OK;
- bool must_import = false;
+ bool must_import = true;
+ Vector<uint8_t> img_data = img->get_data();
+ Dictionary generator_parameters;
String file_path = p_state->get_base_path() + "/" + p_state->filename.get_basename() + "_" + img->get_name() + ".png";
- if (!FileAccess::exists(file_path + ".import")) {
+ if (FileAccess::exists(file_path + ".import")) {
Ref<ConfigFile> config;
config.instantiate();
- config->set_value("remap", "importer", "texture");
- config->set_value("remap", "type", "Texture2D");
- // Currently, it will likely use project defaults of Detect 3D, so textures will be reimported again.
- if (!config->has_section_key("params", "mipmaps/generate")) {
- config->set_value("params", "mipmaps/generate", true);
+ config->load(file_path + ".import");
+ if (config->has_section_key("remap", "generator_parameters")) {
+ generator_parameters = (Dictionary)config->get_value("remap", "generator_parameters");
}
-
- if (ProjectSettings::get_singleton()->has_setting("importer_defaults/texture")) {
- //use defaults if exist
- Dictionary importer_defaults = GLOBAL_GET("importer_defaults/texture");
- List<Variant> importer_def_keys;
- importer_defaults.get_key_list(&importer_def_keys);
- for (const Variant &key : importer_def_keys) {
- if (!config->has_section_key("params", (String)key)) {
- config->set_value("params", (String)key, importer_defaults[key]);
- }
- }
+ if (!generator_parameters.has("md5")) {
+ must_import = false; // Didn't come form a gltf document; don't overwrite.
}
- err = config->save(file_path + ".import");
- ERR_FAIL_COND_V(err != OK, err);
- must_import = true;
- }
- Vector<uint8_t> png_buffer = img->save_png_to_buffer();
- if (ResourceLoader::exists(file_path)) {
- Ref<FileAccess> file = FileAccess::open(file_path, FileAccess::READ, &err);
- if (err == OK && file.is_valid()) {
- Vector<uint8_t> orig_png_buffer = file->get_buffer(file->get_length());
- if (png_buffer != orig_png_buffer) {
- must_import = true;
- }
+ String existing_md5 = generator_parameters["md5"];
+ unsigned char md5_hash[16];
+ CryptoCore::md5(img_data.ptr(), img_data.size(), md5_hash);
+ String new_md5 = String::hex_encode_buffer(md5_hash, 16);
+ generator_parameters["md5"] = new_md5;
+ if (new_md5 == existing_md5) {
+ must_import = false;
}
- } else {
- must_import = true;
}
if (must_import) {
- Ref<FileAccess> file = FileAccess::open(file_path, FileAccess::WRITE, &err);
+ err = img->save_png(file_path);
ERR_FAIL_COND_V(err != OK, err);
- ERR_FAIL_COND_V(file.is_null(), FAILED);
- file->store_buffer(png_buffer);
- file->flush();
- file.unref();
// ResourceLoader::import will crash if not is_editor_hint(), so this case is protected above and will fall through to uncompressed.
- ResourceLoader::import(file_path);
+ HashMap<StringName, Variant> custom_options;
+ custom_options[SNAME("mipmaps/generate")] = true;
+ // Will only use project settings defaults if custom_importer is empty.
+ EditorFileSystem::get_singleton()->update_file(file_path);
+ EditorFileSystem::get_singleton()->reimport_append(file_path, custom_options, String(), generator_parameters);
}
Ref<Texture2D> saved_image = ResourceLoader::load(file_path, "Texture2D");
if (saved_image.is_valid()) {
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
index a4d5e1a569..1e4fd2f09a 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
@@ -22,8 +22,7 @@ namespace GodotTools.Build
// TODO Use List once we have proper serialization
public Godot.Collections.Array CustomProperties { get; private set; } = new();
- public string LogsDirPath =>
- Path.Combine(GodotSharpDirs.BuildLogsDirs, $"{Solution.Md5Text()}_{Configuration}");
+ public string LogsDirPath => GodotSharpDirs.LogsDirPathFor(Solution, Configuration);
public override bool Equals(object? obj)
{
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
index ae0ffaf4cb..d6549c1b70 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
@@ -7,6 +7,7 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using Godot;
using GodotTools.BuildLogger;
using GodotTools.Utils;
@@ -22,9 +23,11 @@ namespace GodotTools.Build
if (dotnetPath == null)
throw new FileNotFoundException("Cannot find the dotnet executable.");
+ var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
+
var startInfo = new ProcessStartInfo(dotnetPath);
- BuildArguments(buildInfo, startInfo.ArgumentList);
+ BuildArguments(buildInfo, startInfo.ArgumentList, editorSettings);
string launchMessage = startInfo.GetCommandLineDisplay(new StringBuilder("Running: ")).ToString();
stdOutHandler?.Invoke(launchMessage);
@@ -35,6 +38,8 @@ namespace GodotTools.Build
startInfo.RedirectStandardError = true;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
+ startInfo.EnvironmentVariables["DOTNET_CLI_UI_LANGUAGE"]
+ = ((string)editorSettings.GetSetting("interface/editor/editor_language")).Replace('_', '-');
// Needed when running from Developer Command Prompt for VS
RemovePlatformVariable(startInfo.EnvironmentVariables);
@@ -83,9 +88,11 @@ namespace GodotTools.Build
if (dotnetPath == null)
throw new FileNotFoundException("Cannot find the dotnet executable.");
+ var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
+
var startInfo = new ProcessStartInfo(dotnetPath);
- BuildPublishArguments(buildInfo, startInfo.ArgumentList);
+ BuildPublishArguments(buildInfo, startInfo.ArgumentList, editorSettings);
string launchMessage = startInfo.GetCommandLineDisplay(new StringBuilder("Running: ")).ToString();
stdOutHandler?.Invoke(launchMessage);
@@ -95,6 +102,8 @@ namespace GodotTools.Build
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.UseShellExecute = false;
+ startInfo.EnvironmentVariables["DOTNET_CLI_UI_LANGUAGE"]
+ = ((string)editorSettings.GetSetting("interface/editor/editor_language")).Replace('_', '-');
// Needed when running from Developer Command Prompt for VS
RemovePlatformVariable(startInfo.EnvironmentVariables);
@@ -124,7 +133,8 @@ namespace GodotTools.Build
}
}
- private static void BuildArguments(BuildInfo buildInfo, Collection<string> arguments)
+ private static void BuildArguments(BuildInfo buildInfo, Collection<string> arguments,
+ EditorSettings editorSettings)
{
// `dotnet clean` / `dotnet build` commands
arguments.Add(buildInfo.OnlyClean ? "clean" : "build");
@@ -150,12 +160,14 @@ namespace GodotTools.Build
arguments.Add(buildInfo.Configuration);
// Verbosity
- arguments.Add("-v");
- arguments.Add("normal");
+ AddVerbosityArguments(buildInfo, arguments, editorSettings);
// Logger
AddLoggerArgument(buildInfo, arguments);
+ // Binary log
+ AddBinaryLogArgument(buildInfo, arguments, editorSettings);
+
// Custom properties
foreach (var customProperty in buildInfo.CustomProperties)
{
@@ -163,7 +175,8 @@ namespace GodotTools.Build
}
}
- private static void BuildPublishArguments(BuildInfo buildInfo, Collection<string> arguments)
+ private static void BuildPublishArguments(BuildInfo buildInfo, Collection<string> arguments,
+ EditorSettings editorSettings)
{
arguments.Add("publish"); // `dotnet publish` command
@@ -193,12 +206,14 @@ namespace GodotTools.Build
arguments.Add("true");
// Verbosity
- arguments.Add("-v");
- arguments.Add("normal");
+ AddVerbosityArguments(buildInfo, arguments, editorSettings);
// Logger
AddLoggerArgument(buildInfo, arguments);
+ // Binary log
+ AddBinaryLogArgument(buildInfo, arguments, editorSettings);
+
// Custom properties
foreach (var customProperty in buildInfo.CustomProperties)
{
@@ -213,6 +228,25 @@ namespace GodotTools.Build
}
}
+ private static void AddVerbosityArguments(BuildInfo buildInfo, Collection<string> arguments,
+ EditorSettings editorSettings)
+ {
+ var verbosityLevel =
+ editorSettings.GetSetting(GodotSharpEditor.Settings.VerbosityLevel).As<VerbosityLevelId>();
+ arguments.Add("-v");
+ arguments.Add(verbosityLevel switch
+ {
+ VerbosityLevelId.Quiet => "quiet",
+ VerbosityLevelId.Minimal => "minimal",
+ VerbosityLevelId.Detailed => "detailed",
+ VerbosityLevelId.Diagnostic => "diagnostic",
+ _ => "normal",
+ });
+
+ if ((bool)editorSettings.GetSetting(GodotSharpEditor.Settings.NoConsoleLogging))
+ arguments.Add("-noconlog");
+ }
+
private static void AddLoggerArgument(BuildInfo buildInfo, Collection<string> arguments)
{
string buildLoggerPath = Path.Combine(Internals.GodotSharpDirs.DataEditorToolsDir,
@@ -222,6 +256,16 @@ namespace GodotTools.Build
$"-l:{typeof(GodotBuildLogger).FullName},{buildLoggerPath};{buildInfo.LogsDirPath}");
}
+ private static void AddBinaryLogArgument(BuildInfo buildInfo, Collection<string> arguments,
+ EditorSettings editorSettings)
+ {
+ if (!(bool)editorSettings.GetSetting(GodotSharpEditor.Settings.CreateBinaryLog))
+ return;
+
+ arguments.Add($"-bl:{Path.Combine(buildInfo.LogsDirPath, "msbuild.binlog")}");
+ arguments.Add("-ds:False"); // Honestly never understood why -bl also switches -ds on.
+ }
+
private static void RemovePlatformVariable(StringDictionary environmentVariables)
{
// EnvironmentVariables is case sensitive? Seriously?
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
index 262de024ca..cf1b84e37f 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
@@ -1,4 +1,5 @@
using System;
+using System.IO;
using Godot;
using GodotTools.Internals;
using static GodotTools.Internals.Globals;
@@ -14,6 +15,7 @@ namespace GodotTools.Build
private Button _errorsBtn;
private Button _warningsBtn;
private Button _viewLogBtn;
+ private Button _openLogsFolderBtn;
private void WarningsToggled(bool pressed)
{
@@ -93,6 +95,10 @@ namespace GodotTools.Build
private void ViewLogToggled(bool pressed) => BuildOutputView.LogVisible = pressed;
+ private void OpenLogsFolderPressed() => OS.ShellOpen(
+ $"file://{GodotSharpDirs.LogsDirPathFor("Debug")}"
+ );
+
private void BuildMenuOptionPressed(long id)
{
switch ((BuildMenuOptions)id)
@@ -171,6 +177,22 @@ namespace GodotTools.Build
_viewLogBtn.Toggled += ViewLogToggled;
toolBarHBox.AddChild(_viewLogBtn);
+ // Horizontal spacer, push everything to the right.
+ toolBarHBox.AddChild(new Control
+ {
+ SizeFlagsHorizontal = SizeFlags.ExpandFill,
+ });
+
+ _openLogsFolderBtn = new Button
+ {
+ Text = "Show Logs in File Manager".TTR(),
+ Icon = GetThemeIcon("Filesystem", "EditorIcons"),
+ ExpandIcon = false,
+ FocusMode = FocusModeEnum.None,
+ };
+ _openLogsFolderBtn.Pressed += OpenLogsFolderPressed;
+ toolBarHBox.AddChild(_openLogsFolderBtn);
+
BuildOutputView = new BuildOutputView();
AddChild(BuildOutputView);
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index 5abbe8752c..43ead4af69 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -22,6 +22,14 @@ namespace GodotTools
{
public partial class GodotSharpEditor : EditorPlugin, ISerializationListener
{
+ public static class Settings
+ {
+ public const string ExternalEditor = "dotnet/editor/external_editor";
+ public const string VerbosityLevel = "dotnet/build/verbosity_level";
+ public const string NoConsoleLogging = "dotnet/build/no_console_logging";
+ public const string CreateBinaryLog = "dotnet/build/create_binary_log";
+ }
+
private EditorSettings _editorSettings;
private PopupMenu _menuPopup;
@@ -171,7 +179,7 @@ namespace GodotTools
[UsedImplicitly]
public Error OpenInExternalEditor(Script script, int line, int col)
{
- var editorId = (ExternalEditorId)(int)_editorSettings.GetSetting("mono/editor/external_editor");
+ var editorId = _editorSettings.GetSetting(Settings.ExternalEditor).As<ExternalEditorId>();
switch (editorId)
{
@@ -323,8 +331,7 @@ namespace GodotTools
[UsedImplicitly]
public bool OverridesExternalEditor()
{
- return (ExternalEditorId)(int)_editorSettings.GetSetting("mono/editor/external_editor") !=
- ExternalEditorId.None;
+ return _editorSettings.GetSetting(Settings.ExternalEditor).As<ExternalEditorId>() != ExternalEditorId.None;
}
public override bool _Build()
@@ -453,7 +460,10 @@ namespace GodotTools
_menuPopup.IdPressed += _MenuOptionPressed;
// External editor settings
- EditorDef("mono/editor/external_editor", Variant.From(ExternalEditorId.None));
+ EditorDef(Settings.ExternalEditor, Variant.From(ExternalEditorId.None));
+ EditorDef(Settings.VerbosityLevel, Variant.From(VerbosityLevelId.Normal));
+ EditorDef(Settings.NoConsoleLogging, false);
+ EditorDef(Settings.CreateBinaryLog, false);
string settingsHintStr = "Disabled";
@@ -481,11 +491,23 @@ namespace GodotTools
_editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
{
["type"] = (int)Variant.Type.Int,
- ["name"] = "mono/editor/external_editor",
+ ["name"] = Settings.ExternalEditor,
["hint"] = (int)PropertyHint.Enum,
["hint_string"] = settingsHintStr
});
+ var verbosityLevels = Enum.GetValues<VerbosityLevelId>().Select(level => $"{Enum.GetName(level)}:{(int)level}");
+ _editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
+ {
+ ["type"] = (int)Variant.Type.Int,
+ ["name"] = Settings.VerbosityLevel,
+ ["hint"] = (int)PropertyHint.Enum,
+ ["hint_string"] = string.Join(",", verbosityLevels),
+ });
+
+ OnSettingsChanged();
+ _editorSettings.SettingsChanged += OnSettingsChanged;
+
// Export plugin
var exportPlugin = new ExportPlugin();
AddExportPlugin(exportPlugin);
@@ -510,6 +532,24 @@ namespace GodotTools
AddChild(GodotIdeManager);
}
+ public override void _DisablePlugin()
+ {
+ base._DisablePlugin();
+
+ _editorSettings.SettingsChanged -= OnSettingsChanged;
+ }
+
+ private void OnSettingsChanged()
+ {
+ // We want to force NoConsoleLogging to true when the VerbosityLevel is at Detailed or above.
+ // At that point, there's so much info logged that it doesn't make sense to display it in
+ // the tiny editor window, and it'd make the editor hang or crash anyway.
+ var verbosityLevel = _editorSettings.GetSetting(Settings.VerbosityLevel).As<VerbosityLevelId>();
+ var hideConsoleLog = (bool)_editorSettings.GetSetting(Settings.NoConsoleLogging);
+ if (verbosityLevel >= VerbosityLevelId.Detailed && !hideConsoleLog)
+ _editorSettings.SetSetting(Settings.NoConsoleLogging, Variant.From(true));
+ }
+
protected override void Dispose(bool disposing)
{
if (disposing)
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
index 5de2c9833b..83621ce5af 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
@@ -21,7 +21,8 @@ namespace GodotTools.Ides
return _messagingServer;
_messagingServer?.Dispose();
- _messagingServer = new MessagingServer(OS.GetExecutablePath(), ProjectSettings.GlobalizePath(GodotSharpDirs.ResMetadataDir), new GodotLogger());
+ _messagingServer = new MessagingServer(OS.GetExecutablePath(),
+ ProjectSettings.GlobalizePath(GodotSharpDirs.ResMetadataDir), new GodotLogger());
_ = _messagingServer.Listen();
@@ -76,8 +77,8 @@ namespace GodotTools.Ides
public async Task<EditorPick?> LaunchIdeAsync(int millisecondsTimeout = 10000)
{
- var editorId = (ExternalEditorId)(int)GodotSharpEditor.Instance.GetEditorInterface()
- .GetEditorSettings().GetSetting("mono/editor/external_editor");
+ var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
+ var editorId = editorSettings.GetSetting(GodotSharpEditor.Settings.ExternalEditor).As<ExternalEditorId>();
string editorIdentity = GetExternalEditorIdentity(editorId);
var runningServer = GetRunningOrNewServer();
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
index 60602a5847..f55ca4c7d7 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
@@ -9,7 +9,7 @@ namespace GodotTools.Ides.Rider
{
public static class RiderPathManager
{
- public static readonly string EditorPathSettingName = "mono/editor/editor_path_optional";
+ public static readonly string EditorPathSettingName = "dotnet/editor/editor_path_optional";
private static string GetRiderPathFromSettings()
{
@@ -22,7 +22,7 @@ namespace GodotTools.Ides.Rider
public static void Initialize()
{
var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
- var editor = (ExternalEditorId)(int)editorSettings.GetSetting("mono/editor/external_editor");
+ var editor = editorSettings.GetSetting(GodotSharpEditor.Settings.ExternalEditor).As<ExternalEditorId>();
if (editor == ExternalEditorId.Rider)
{
if (!editorSettings.HasSetting(EditorPathSettingName))
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs
index 7624989092..fb68fcbae6 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs
@@ -115,5 +115,11 @@ namespace GodotTools.Internals
return _projectCsProjPath;
}
}
+
+ public static string LogsDirPathFor(string solution, string configuration)
+ => Path.Combine(BuildLogsDirs, $"{solution.Md5Text()}_{configuration}");
+
+ public static string LogsDirPathFor(string configuration)
+ => LogsDirPathFor(ProjectSlnPath, configuration);
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/VerbosityLevelId.cs b/modules/mono/editor/GodotTools/GodotTools/VerbosityLevelId.cs
new file mode 100644
index 0000000000..0e1afe6bbf
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/VerbosityLevelId.cs
@@ -0,0 +1,11 @@
+namespace GodotTools
+{
+ public enum VerbosityLevelId : long
+ {
+ Quiet,
+ Minimal,
+ Normal,
+ Detailed,
+ Diagnostic,
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs b/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs
index 02d0226e90..93baf4e51c 100644
--- a/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs
+++ b/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs
@@ -30,7 +30,7 @@ namespace GodotPlugins
if (baseDirectory != null)
{
if (!Path.EndsInDirectorySeparator(baseDirectory))
- baseDirectory += Path.PathSeparator;
+ baseDirectory += Path.DirectorySeparatorChar;
// This SetData call effectively sets AppContext.BaseDirectory
// See https://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/System.Private.CoreLib/src/System/AppContext.cs#L21-L25
AppDomain.CurrentDomain.SetData("APP_CONTEXT_BASE_DIRECTORY", baseDirectory);
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 9b474bf2ce..b55188ce0c 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -6118,20 +6118,22 @@ int64_t TextServerAdvanced::_is_confusable(const String &p_string, const PackedS
Vector<UChar *> skeletons;
skeletons.resize(p_dict.size());
- USpoofChecker *sc = uspoof_open(&status);
- uspoof_setChecks(sc, USPOOF_CONFUSABLE, &status);
+ if (sc_conf == nullptr) {
+ sc_conf = uspoof_open(&status);
+ uspoof_setChecks(sc_conf, USPOOF_CONFUSABLE, &status);
+ }
for (int i = 0; i < p_dict.size(); i++) {
Char16String word = p_dict[i].utf16();
- int32_t len = uspoof_getSkeleton(sc, 0, word.get_data(), -1, NULL, 0, &status);
+ int32_t len = uspoof_getSkeleton(sc_conf, 0, word.get_data(), -1, NULL, 0, &status);
skeletons.write[i] = (UChar *)memalloc(++len * sizeof(UChar));
status = U_ZERO_ERROR;
- uspoof_getSkeleton(sc, 0, word.get_data(), -1, skeletons.write[i], len, &status);
+ uspoof_getSkeleton(sc_conf, 0, word.get_data(), -1, skeletons.write[i], len, &status);
}
- int32_t len = uspoof_getSkeleton(sc, 0, utf16.get_data(), -1, NULL, 0, &status);
+ int32_t len = uspoof_getSkeleton(sc_conf, 0, utf16.get_data(), -1, NULL, 0, &status);
UChar *skel = (UChar *)memalloc(++len * sizeof(UChar));
status = U_ZERO_ERROR;
- uspoof_getSkeleton(sc, 0, utf16.get_data(), -1, skel, len, &status);
+ uspoof_getSkeleton(sc_conf, 0, utf16.get_data(), -1, skel, len, &status);
for (int i = 0; i < skeletons.size(); i++) {
if (u_strcmp(skel, skeletons[i]) == 0) {
match_index = i;
@@ -6143,7 +6145,6 @@ int64_t TextServerAdvanced::_is_confusable(const String &p_string, const PackedS
for (int i = 0; i < skeletons.size(); i++) {
memfree(skeletons.write[i]);
}
- uspoof_close(sc);
ERR_FAIL_COND_V_MSG(U_FAILURE(status), -1, u_errorName(status));
@@ -6159,19 +6160,18 @@ bool TextServerAdvanced::_spoof_check(const String &p_string) const {
UErrorCode status = U_ZERO_ERROR;
Char16String utf16 = p_string.utf16();
- USet *allowed = uset_openEmpty();
- uset_addAll(allowed, uspoof_getRecommendedSet(&status));
- uset_addAll(allowed, uspoof_getInclusionSet(&status));
-
- USpoofChecker *sc = uspoof_open(&status);
- uspoof_setAllowedChars(sc, allowed, &status);
- uspoof_setRestrictionLevel(sc, USPOOF_MODERATELY_RESTRICTIVE);
-
- int32_t bitmask = uspoof_check(sc, utf16.get_data(), -1, NULL, &status);
-
- uspoof_close(sc);
- uset_close(allowed);
+ if (allowed == nullptr) {
+ allowed = uset_openEmpty();
+ uset_addAll(allowed, uspoof_getRecommendedSet(&status));
+ uset_addAll(allowed, uspoof_getInclusionSet(&status));
+ }
+ if (sc_spoof == nullptr) {
+ sc_spoof = uspoof_open(&status);
+ uspoof_setAllowedChars(sc_spoof, allowed, &status);
+ uspoof_setRestrictionLevel(sc_spoof, USPOOF_MODERATELY_RESTRICTIVE);
+ }
+ int32_t bitmask = uspoof_check(sc_spoof, utf16.get_data(), -1, NULL, &status);
ERR_FAIL_COND_V_MSG(U_FAILURE(status), false, u_errorName(status));
return (bitmask != 0);
@@ -6587,5 +6587,17 @@ TextServerAdvanced::~TextServerAdvanced() {
FT_Done_FreeType(ft_library);
}
#endif
+ if (sc_spoof != nullptr) {
+ uspoof_close(sc_spoof);
+ sc_spoof = nullptr;
+ }
+ if (sc_conf != nullptr) {
+ uspoof_close(sc_conf);
+ sc_conf = nullptr;
+ }
+ if (allowed != nullptr) {
+ uset_close(allowed);
+ allowed = nullptr;
+ }
u_cleanup();
}
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index c7fe46d554..1acf5b21f0 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -159,6 +159,9 @@ class TextServerAdvanced : public TextServerExtension {
// ICU support data.
bool icu_data_loaded = false;
+ mutable USet *allowed = nullptr;
+ mutable USpoofChecker *sc_spoof = nullptr;
+ mutable USpoofChecker *sc_conf = nullptr;
// Font cache data.
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index c02acbee83..fdd2fed836 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -252,6 +252,7 @@ static const char *APK_ASSETS_DIRECTORY = "res://android/build/assets";
static const char *AAB_ASSETS_DIRECTORY = "res://android/build/assetPacks/installTime/src/main/assets";
static const int DEFAULT_MIN_SDK_VERSION = 21; // Should match the value in 'platform/android/java/app/config.gradle#minSdk'
+static const int VULKAN_MIN_SDK_VERSION = 24;
static const int DEFAULT_TARGET_SDK_VERSION = 32; // Should match the value in 'platform/android/java/app/config.gradle#targetSdk'
#ifndef ANDROID_ENABLED
@@ -799,6 +800,12 @@ bool EditorExportPlatformAndroid::_has_manage_external_storage_permission(const
return p_permissions.find("android.permission.MANAGE_EXTERNAL_STORAGE") != -1;
}
+bool EditorExportPlatformAndroid::_uses_vulkan() {
+ String current_renderer = GLOBAL_GET("rendering/renderer/rendering_method.mobile");
+ bool uses_vulkan = (current_renderer == "forward_plus" || current_renderer == "mobile") && GLOBAL_GET("rendering/rendering_device/driver.android") == "vulkan";
+ return uses_vulkan;
+}
+
void EditorExportPlatformAndroid::_get_permissions(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions) {
const char **aperms = android_perms;
while (*aperms) {
@@ -853,7 +860,7 @@ void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPres
}
}
- manifest_text += _get_xr_features_tag(p_preset);
+ manifest_text += _get_xr_features_tag(p_preset, _uses_vulkan());
manifest_text += _get_application_tag(p_preset, _has_read_write_storage_permission(perms));
manifest_text += "</manifest>\n";
String manifest_path = vformat("res://android/build/src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release"));
@@ -1056,6 +1063,13 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
Vector<bool> feature_required_list;
Vector<int> feature_versions;
+ if (_uses_vulkan()) {
+ // Require vulkan hardware level 1 support
+ feature_names.push_back("android.hardware.vulkan.level");
+ feature_required_list.push_back(true);
+ feature_versions.push_back(1);
+ }
+
if (feature_names.size() > 0) {
ofs += 24; // skip over end tag
@@ -1688,12 +1702,12 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "custom_build/use_custom_build"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "custom_build/export_format", PROPERTY_HINT_ENUM, "Export APK,Export AAB"), EXPORT_FORMAT_APK));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "gradle_build/use_gradle_build"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "gradle_build/export_format", PROPERTY_HINT_ENUM, "Export APK,Export AAB"), EXPORT_FORMAT_APK));
// Using String instead of int to default to an empty string (no override) with placeholder for instructions (see GH-62465).
// This implies doing validation that the string is a proper int.
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_build/min_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", DEFAULT_MIN_SDK_VERSION)), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_build/target_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", DEFAULT_TARGET_SDK_VERSION)), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/min_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", DEFAULT_MIN_SDK_VERSION)), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/target_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", DEFAULT_TARGET_SDK_VERSION)), ""));
Vector<PluginConfigAndroid> plugins_configs = get_plugins();
for (int i = 0; i < plugins_configs.size(); i++) {
@@ -2118,11 +2132,11 @@ String EditorExportPlatformAndroid::get_apksigner_path(int p_target_sdk, bool p_
bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
String err;
bool valid = false;
- const bool custom_build_enabled = p_preset->get("custom_build/use_custom_build");
+ const bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build");
// Look for export templates (first official, and if defined custom templates).
- if (!custom_build_enabled) {
+ if (!gradle_build_enabled) {
String template_err;
bool dvalid = false;
bool rvalid = false;
@@ -2229,7 +2243,7 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
valid = false;
}
- String target_sdk_version = p_preset->get("custom_build/target_sdk");
+ String target_sdk_version = p_preset->get("gradle_build/target_sdk");
if (!target_sdk_version.is_valid_int()) {
target_sdk_version = itos(DEFAULT_TARGET_SDK_VERSION);
}
@@ -2253,7 +2267,7 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
String err;
bool valid = true;
- const bool custom_build_enabled = p_preset->get("custom_build/use_custom_build");
+ const bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build");
// Validate the project configuration.
bool apk_expansion = p_preset->get("apk_expansion/enable");
@@ -2282,11 +2296,11 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit
err += etc_error;
}
- // Ensure that `Use Custom Build` is enabled if a plugin is selected.
+ // Ensure that `Use Gradle Build` is enabled if a plugin is selected.
String enabled_plugins_names = PluginConfigAndroid::get_plugins_names(get_enabled_plugins(p_preset));
- if (!enabled_plugins_names.is_empty() && !custom_build_enabled) {
+ if (!enabled_plugins_names.is_empty() && !gradle_build_enabled) {
valid = false;
- err += TTR("\"Use Custom Build\" must be enabled to use the plugins.");
+ err += TTR("\"Use Gradle Build\" must be enabled to use the plugins.");
err += "\n";
}
@@ -2294,9 +2308,9 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit
int xr_mode_index = p_preset->get("xr_features/xr_mode");
int hand_tracking = p_preset->get("xr_features/hand_tracking");
int passthrough_mode = p_preset->get("xr_features/passthrough");
- if (xr_mode_index == XR_MODE_OPENXR && !custom_build_enabled) {
+ if (xr_mode_index == XR_MODE_OPENXR && !gradle_build_enabled) {
valid = false;
- err += TTR("OpenXR requires \"Use Custom Build\" to be enabled");
+ err += TTR("OpenXR requires \"Use Gradle Build\" to be enabled");
err += "\n";
}
@@ -2314,20 +2328,20 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit
}
}
- if (int(p_preset->get("custom_build/export_format")) == EXPORT_FORMAT_AAB &&
- !custom_build_enabled) {
+ if (int(p_preset->get("gradle_build/export_format")) == EXPORT_FORMAT_AAB &&
+ !gradle_build_enabled) {
valid = false;
- err += TTR("\"Export AAB\" is only valid when \"Use Custom Build\" is enabled.");
+ err += TTR("\"Export AAB\" is only valid when \"Use Gradle Build\" is enabled.");
err += "\n";
}
// Check the min sdk version.
- String min_sdk_str = p_preset->get("custom_build/min_sdk");
+ String min_sdk_str = p_preset->get("gradle_build/min_sdk");
int min_sdk_int = DEFAULT_MIN_SDK_VERSION;
if (!min_sdk_str.is_empty()) { // Empty means no override, nothing to do.
- if (!custom_build_enabled) {
+ if (!gradle_build_enabled) {
valid = false;
- err += TTR("\"Min SDK\" can only be overridden when \"Use Custom Build\" is enabled.");
+ err += TTR("\"Min SDK\" can only be overridden when \"Use Gradle Build\" is enabled.");
err += "\n";
}
if (!min_sdk_str.is_valid_int()) {
@@ -2345,12 +2359,12 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit
}
// Check the target sdk version.
- String target_sdk_str = p_preset->get("custom_build/target_sdk");
+ String target_sdk_str = p_preset->get("gradle_build/target_sdk");
int target_sdk_int = DEFAULT_TARGET_SDK_VERSION;
if (!target_sdk_str.is_empty()) { // Empty means no override, nothing to do.
- if (!custom_build_enabled) {
+ if (!gradle_build_enabled) {
valid = false;
- err += TTR("\"Target SDK\" can only be overridden when \"Use Custom Build\" is enabled.");
+ err += TTR("\"Target SDK\" can only be overridden when \"Use Gradle Build\" is enabled.");
err += "\n";
}
if (!target_sdk_str.is_valid_int()) {
@@ -2373,6 +2387,18 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit
err += "\n";
}
+ String current_renderer = GLOBAL_GET("rendering/renderer/rendering_method.mobile");
+ if (current_renderer == "forward_plus") {
+ // Warning only, so don't override `valid`.
+ err += vformat(TTR("The \"%s\" renderer is designed for Desktop devices, and is not suitable for Android devices."), current_renderer);
+ err += "\n";
+ }
+ if (_uses_vulkan() && min_sdk_int < VULKAN_MIN_SDK_VERSION) {
+ // Warning only, so don't override `valid`.
+ err += vformat(TTR("\"Min SDK\" should be greater or equal to %d for the \"%s\" renderer."), VULKAN_MIN_SDK_VERSION, current_renderer);
+ err += "\n";
+ }
+
r_error = err;
return valid;
}
@@ -2458,12 +2484,12 @@ void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportP
}
Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep) {
- int export_format = int(p_preset->get("custom_build/export_format"));
+ int export_format = int(p_preset->get("gradle_build/export_format"));
String export_label = export_format == EXPORT_FORMAT_AAB ? "AAB" : "APK";
String release_keystore = p_preset->get("keystore/release");
String release_username = p_preset->get("keystore/release_user");
String release_password = p_preset->get("keystore/release_password");
- String target_sdk_version = p_preset->get("custom_build/target_sdk");
+ String target_sdk_version = p_preset->get("gradle_build/target_sdk");
if (!target_sdk_version.is_valid_int()) {
target_sdk_version = itos(DEFAULT_TARGET_SDK_VERSION);
}
@@ -2640,7 +2666,7 @@ String EditorExportPlatformAndroid::join_abis(const Vector<EditorExportPlatformA
}
Error EditorExportPlatformAndroid::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
- int export_format = int(p_preset->get("custom_build/export_format"));
+ 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);
}
@@ -2653,7 +2679,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
EditorProgress ep("export", TTR("Exporting for Android"), 105, true);
- bool use_custom_build = bool(p_preset->get("custom_build/use_custom_build"));
+ bool use_gradle_build = bool(p_preset->get("gradle_build/use_gradle_build"));
bool p_give_internet = p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG);
bool apk_expansion = p_preset->get("apk_expansion/enable");
Vector<ABI> enabled_abis = get_enabled_abis(p_preset);
@@ -2663,7 +2689,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
print_verbose("- export path: " + p_path);
print_verbose("- export format: " + itos(export_format));
print_verbose("- sign build: " + bool_to_string(should_sign));
- print_verbose("- custom build enabled: " + bool_to_string(use_custom_build));
+ print_verbose("- gradle build enabled: " + bool_to_string(use_gradle_build));
print_verbose("- apk expansion enabled: " + bool_to_string(apk_expansion));
print_verbose("- enabled abis: " + join_abis(enabled_abis, ",", false));
print_verbose("- export filter: " + itos(p_preset->get_export_filter()));
@@ -2703,14 +2729,14 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
return ERR_UNCONFIGURED;
}
- if (use_custom_build) {
- print_verbose("Starting custom build...");
+ if (use_gradle_build) {
+ print_verbose("Starting gradle build...");
//test that installed build version is alright
{
print_verbose("Checking build version...");
Ref<FileAccess> f = FileAccess::open("res://android/.build_version", FileAccess::READ);
if (f.is_null()) {
- add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Trying to build from a custom built template, but no version info for it exists. Please reinstall from the 'Project' menu."));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Trying to build from a gradle built template, but no version info for it exists. Please reinstall from the 'Project' menu."));
return ERR_UNCONFIGURED;
}
String version = f->get_line().strip_edges();
@@ -2780,11 +2806,11 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
String package_name = get_package_name(p_preset->get("package/unique_name"));
String version_code = itos(p_preset->get("version/code"));
String version_name = p_preset->get("version/name");
- String min_sdk_version = p_preset->get("custom_build/min_sdk");
+ String min_sdk_version = p_preset->get("gradle_build/min_sdk");
if (!min_sdk_version.is_valid_int()) {
min_sdk_version = itos(DEFAULT_MIN_SDK_VERSION);
}
- String target_sdk_version = p_preset->get("custom_build/target_sdk");
+ String target_sdk_version = p_preset->get("gradle_build/target_sdk");
if (!target_sdk_version.is_valid_int()) {
target_sdk_version = itos(DEFAULT_TARGET_SDK_VERSION);
}
@@ -2910,7 +2936,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
return ERR_CANT_CREATE;
}
- print_verbose("Successfully completed Android custom build.");
+ print_verbose("Successfully completed Android gradle build.");
return OK;
}
// This is the start of the Legacy build system
diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h
index bff769fcba..337a0228d0 100644
--- a/platform/android/export/export_plugin.h
+++ b/platform/android/export/export_plugin.h
@@ -74,7 +74,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
Vector<PluginConfigAndroid> plugins;
String last_plugin_names;
- uint64_t last_custom_build_time = 0;
+ uint64_t last_gradle_build_time = 0;
SafeFlag plugins_changed;
Mutex plugins_lock;
Vector<Device> devices;
@@ -172,6 +172,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
static Vector<ABI> get_enabled_abis(const Ref<EditorExportPreset> &p_preset);
+ static bool _uses_vulkan();
+
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);
@@ -213,14 +215,14 @@ public:
inline bool is_clean_build_required(Vector<PluginConfigAndroid> enabled_plugins) {
String plugin_names = PluginConfigAndroid::get_plugins_names(enabled_plugins);
- bool first_build = last_custom_build_time == 0;
+ bool first_build = last_gradle_build_time == 0;
bool have_plugins_changed = false;
if (!first_build) {
have_plugins_changed = plugin_names != last_plugin_names;
if (!have_plugins_changed) {
for (int i = 0; i < enabled_plugins.size(); i++) {
- if (enabled_plugins.get(i).last_updated > last_custom_build_time) {
+ if (enabled_plugins.get(i).last_updated > last_gradle_build_time) {
have_plugins_changed = true;
break;
}
@@ -228,7 +230,7 @@ public:
}
}
- last_custom_build_time = OS::get_singleton()->get_unix_time();
+ last_gradle_build_time = OS::get_singleton()->get_unix_time();
last_plugin_names = plugin_names;
return have_plugins_changed || first_build;
diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp
index 5e71116c10..b889d58199 100644
--- a/platform/android/export/gradle_export_util.cpp
+++ b/platform/android/export/gradle_export_util.cpp
@@ -166,7 +166,7 @@ Error store_string_at_path(const String &p_path, const String &p_data) {
// This method will only be called as an input to export_project_files.
// It is used by the export_project_files method to save all the asset files into the gradle project.
// It's functionality mirrors that of the method save_apk_file.
-// This method will be called ONLY when custom build is enabled.
+// This method will be called ONLY when gradle build is enabled.
Error rename_and_store_file_in_gradle_project(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) {
CustomExportData *export_data = static_cast<CustomExportData *>(p_userdata);
String dst_path = p_path.replace_first("res://", export_data->assets_directory + "/");
@@ -254,7 +254,7 @@ String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset) {
return manifest_screen_sizes;
}
-String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset) {
+String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset, bool p_uses_vulkan) {
String manifest_xr_features;
int xr_mode_index = (int)(p_preset->get("xr_features/xr_mode"));
bool uses_xr = xr_mode_index == XR_MODE_OPENXR;
@@ -273,6 +273,10 @@ String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset) {
manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"com.oculus.feature.PASSTHROUGH\" android:required=\"true\" />\n";
}
}
+
+ if (p_uses_vulkan) {
+ manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"android.hardware.vulkan.level\" android:required=\"true\" android:version=\"1\" />\n";
+ }
return manifest_xr_features;
}
diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h
index fe5888e11c..8a885a0d12 100644
--- a/platform/android/export/gradle_export_util.h
+++ b/platform/android/export/gradle_export_util.h
@@ -104,7 +104,7 @@ Error store_string_at_path(const String &p_path, const String &p_data);
// This method will only be called as an input to export_project_files.
// It is used by the export_project_files method to save all the asset files into the gradle project.
// It's functionality mirrors that of the method save_apk_file.
-// This method will be called ONLY when custom build is enabled.
+// This method will be called ONLY when gradle build is enabled.
Error rename_and_store_file_in_gradle_project(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);
// Creates strings.xml files inside the gradle project for different locales.
@@ -116,7 +116,7 @@ String _get_gles_tag();
String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset);
-String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset);
+String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset, bool p_uses_vulkan);
String _get_activity_tag(const Ref<EditorExportPreset> &p_preset, bool p_uses_xr);
diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle
index 63b10e62b1..01b148aeef 100644
--- a/platform/android/java/app/build.gradle
+++ b/platform/android/java/app/build.gradle
@@ -50,7 +50,7 @@ dependencies {
} else if (rootProject.findProject(":godot:lib")) {
implementation project(":godot:lib")
} else {
- // Custom build mode. In this scenario this project is the only one around and the Godot
+ // Godot gradle build mode. In this scenario this project is the only one around and the Godot
// library is available through the pre-generated godot-lib.*.aar android archive files.
debugImplementation fileTree(dir: 'libs/debug', include: ['*.jar', '*.aar'])
devImplementation fileTree(dir: 'libs/dev', include: ['*.jar', '*.aar'])
@@ -153,7 +153,7 @@ android {
debug {
// Signing and zip-aligning are skipped for prebuilt builds, but
- // performed for custom builds.
+ // performed for Godot gradle builds.
zipAlignEnabled shouldZipAlign()
if (shouldSign()) {
signingConfig signingConfigs.debug
@@ -165,7 +165,7 @@ android {
dev {
initWith debug
// Signing and zip-aligning are skipped for prebuilt builds, but
- // performed for custom builds.
+ // performed for Godot gradle builds.
zipAlignEnabled shouldZipAlign()
if (shouldSign()) {
signingConfig signingConfigs.debug
@@ -176,7 +176,7 @@ android {
release {
// Signing and zip-aligning are skipped for prebuilt builds, but
- // performed for custom builds.
+ // performed for Godot gradle builds.
zipAlignEnabled shouldZipAlign()
if (shouldSign()) {
signingConfig signingConfigs.release
diff --git a/platform/android/java/app/gradle.properties b/platform/android/java/app/gradle.properties
index 0ad8e611ca..d9f79b6818 100644
--- a/platform/android/java/app/gradle.properties
+++ b/platform/android/java/app/gradle.properties
@@ -1,5 +1,5 @@
-# Godot custom build Gradle settings.
-# These properties apply when running custom build from the Godot editor.
+# Godot gradle build settings.
+# These properties apply when running a gradle build from the Godot editor.
# NOTE: This should be kept in sync with 'godot/platform/android/java/gradle.properties' except
# where otherwise specified.
diff --git a/platform/android/java/app/settings.gradle b/platform/android/java/app/settings.gradle
index ba53aefe7f..b4524a3f60 100644
--- a/platform/android/java/app/settings.gradle
+++ b/platform/android/java/app/settings.gradle
@@ -1,4 +1,4 @@
-// This is the root directory of the Godot custom build.
+// This is the root directory of the Godot Android gradle build.
pluginManagement {
apply from: 'config.gradle'
diff --git a/platform/android/java/app/src/com/godot/game/GodotApp.java b/platform/android/java/app/src/com/godot/game/GodotApp.java
index a43e289b6b..1d2cc05715 100644
--- a/platform/android/java/app/src/com/godot/game/GodotApp.java
+++ b/platform/android/java/app/src/com/godot/game/GodotApp.java
@@ -35,7 +35,7 @@ import org.godotengine.godot.FullScreenGodotApp;
import android.os.Bundle;
/**
- * Template activity for Godot Android custom builds.
+ * Template activity for Godot Android builds.
* Feel free to extend and modify this class for your custom logic.
*/
public class GodotApp extends FullScreenGodotApp {
diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle
index 5a91e5ce32..cffe0a33d9 100644
--- a/platform/android/java/build.gradle
+++ b/platform/android/java/build.gradle
@@ -152,14 +152,14 @@ task copyReleaseAARToBin(type: Copy) {
}
/**
- * Generate Godot custom build template by zipping the source files from the app directory, as well
+ * Generate Godot gradle build template by zipping the source files from the app directory, as well
* as the AAR files generated by 'copyDebugAAR', 'copyDevAAR' and 'copyReleaseAAR'.
- * The zip file also includes some gradle tools to allow building of the custom build.
+ * The zip file also includes some gradle tools to enable gradle builds from the Godot Editor.
*/
-task zipCustomBuild(type: Zip) {
+task zipGradleBuild(type: Zip) {
onlyIf { generateGodotTemplates.state.executed || generateDevTemplate.state.executed }
doFirst {
- logger.lifecycle("Generating Godot custom build template")
+ logger.lifecycle("Generating Godot gradle build template")
}
from(fileTree(dir: 'app', excludes: ['**/build/**', '**/.gradle/**', '**/*.iml']), fileTree(dir: '.', includes: ['gradlew', 'gradlew.bat', 'gradle/**']))
include '**/*'
@@ -195,7 +195,7 @@ def templateBuildTasks() {
&& targetLibs.listFiles() != null
&& targetLibs.listFiles().length > 0) {
String capitalizedTarget = target.capitalize()
- // Copy the generated aar library files to the custom build directory.
+ // Copy the generated aar library files to the build directory.
tasks += "copy" + capitalizedTarget + "AARToAppModule"
// Copy the generated aar library files to the bin directory.
tasks += "copy" + capitalizedTarget + "AARToBin"
@@ -260,7 +260,7 @@ task generateGodotTemplates {
gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
dependsOn = templateBuildTasks()
- finalizedBy 'zipCustomBuild'
+ finalizedBy 'zipGradleBuild'
}
/**
@@ -273,7 +273,7 @@ task generateDevTemplate {
gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
dependsOn = templateBuildTasks()
- finalizedBy 'zipCustomBuild'
+ finalizedBy 'zipGradleBuild'
}
task clean(type: Delete) {
diff --git a/platform/android/java/gradle.properties b/platform/android/java/gradle.properties
index 5cd94e85d9..39a0dcda16 100644
--- a/platform/android/java/gradle.properties
+++ b/platform/android/java/gradle.properties
@@ -24,5 +24,5 @@ org.gradle.jvmargs=-Xmx4536m
org.gradle.warning.mode=all
# Disable resource optimizations for template release build.
-# NOTE: This is turned on for custom build in order to improve the release build.
+# NOTE: This is turned on for Godot Editor's gradle builds in order to improve the release build.
android.enableResourceOptimizations=false
diff --git a/platform/android/java/lib/res/values/strings.xml b/platform/android/java/lib/res/values/strings.xml
index 7efac4ce71..f76f597140 100644
--- a/platform/android/java/lib/res/values/strings.xml
+++ b/platform/android/java/lib/res/values/strings.xml
@@ -14,6 +14,7 @@
<string name="text_button_cancel_verify">Cancel Verification</string>
<string name="text_error_title">Error!</string>
<string name="error_engine_setup_message">Unable to setup the Godot Engine! Aborting…</string>
+ <string name="error_missing_vulkan_requirements_message">This device does not meet the requirements for Vulkan support! Aborting…</string>
<!-- APK Expansion Strings -->
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
index 50263bc392..307fa7bae1 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -258,13 +258,13 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
*/
@Keep
private boolean onVideoInit() {
- final Activity activity = getActivity();
+ final Activity activity = requireActivity();
containerLayout = new FrameLayout(activity);
containerLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
// GodotEditText layout
GodotEditText editText = new GodotEditText(activity);
- editText.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT,
+ editText.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
(int)getResources().getDimension(R.dimen.text_edit_height)));
// ...add to FrameLayout
containerLayout.addView(editText);
@@ -275,11 +275,16 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
return false;
}
- final String renderer = GodotLib.getGlobal("rendering/renderer/rendering_method");
- if (renderer.equals("gl_compatibility")) {
- mRenderView = new GodotGLRenderView(activity, this, xrMode, use_debug_opengl);
- } else {
+ if (usesVulkan()) {
+ if (!meetsVulkanRequirements(activity.getPackageManager())) {
+ Log.e(TAG, "Missing requirements for vulkan support! Aborting...");
+ alert(R.string.error_missing_vulkan_requirements_message, R.string.text_error_title, this::forceQuit);
+ return false;
+ }
mRenderView = new GodotVulkanRenderView(activity, this);
+ } else {
+ // Fallback to openGl
+ mRenderView = new GodotGLRenderView(activity, this, xrMode, use_debug_opengl);
}
View view = mRenderView.getView();
@@ -317,6 +322,26 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
return true;
}
+ /**
+ * Returns true if `Vulkan` is used for rendering.
+ */
+ private boolean usesVulkan() {
+ final String renderer = GodotLib.getGlobal("rendering/renderer/rendering_method");
+ final String renderingDevice = GodotLib.getGlobal("rendering/rendering_device/driver");
+ return ("forward_plus".equals(renderer) || "mobile".equals(renderer)) && "vulkan".equals(renderingDevice);
+ }
+
+ /**
+ * Returns true if the device meets the base requirements for Vulkan support, false otherwise.
+ */
+ private boolean meetsVulkanRequirements(@Nullable PackageManager packageManager) {
+ if (packageManager == null) {
+ return false;
+ }
+
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && packageManager.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL, 1);
+ }
+
public void setKeepScreenOn(final boolean p_enabled) {
runOnUiThread(() -> {
if (p_enabled) {
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 493e4ad20f..525c62fbf2 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -3834,10 +3834,6 @@ void DisplayServerX11::process_events() {
for (uint32_t event_index = 0; event_index < events.size(); ++event_index) {
XEvent &event = events[event_index];
- if (ignore_events) {
- XFreeEventData(x11_display, &event.xcookie);
- continue;
- }
bool ime_window_event = false;
WindowID window_id = MAIN_WINDOW_ID;
@@ -3867,7 +3863,7 @@ void DisplayServerX11::process_events() {
_refresh_device_info();
} break;
case XI_RawMotion: {
- if (ime_window_event) {
+ if (ime_window_event || ignore_events) {
break;
}
XIRawEvent *raw_event = (XIRawEvent *)event_data;
@@ -3972,7 +3968,7 @@ void DisplayServerX11::process_events() {
#ifdef TOUCH_ENABLED
case XI_TouchBegin:
case XI_TouchEnd: {
- if (ime_window_event) {
+ if (ime_window_event || ignore_events) {
break;
}
bool is_begin = event_data->evtype == XI_TouchBegin;
@@ -4005,7 +4001,7 @@ void DisplayServerX11::process_events() {
} break;
case XI_TouchUpdate: {
- if (ime_window_event) {
+ if (ime_window_event || ignore_events) {
break;
}
HashMap<int, Vector2>::Iterator curr_pos_elem = xi.state.find(index);
@@ -4227,7 +4223,7 @@ void DisplayServerX11::process_events() {
case ButtonPress:
case ButtonRelease: {
- if (ime_window_event) {
+ if (ime_window_event || ignore_events) {
break;
}
/* exit in case of a mouse button press */
@@ -4328,7 +4324,7 @@ void DisplayServerX11::process_events() {
} break;
case MotionNotify: {
- if (ime_window_event) {
+ if (ime_window_event || ignore_events) {
break;
}
// The X11 API requires filtering one-by-one through the motion
@@ -4476,6 +4472,9 @@ void DisplayServerX11::process_events() {
} break;
case KeyPress:
case KeyRelease: {
+ if (ignore_events) {
+ break;
+ }
#ifdef DISPLAY_SERVER_X11_DEBUG_LOGS_ENABLED
if (event.type == KeyPress) {
DEBUG_LOG_X11("[%u] KeyPress window=%lu (%u), keycode=%u, time=%lu \n", frame, event.xkey.window, window_id, event.xkey.keycode, event.xkey.time);
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index 436b936586..77bf15125e 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -162,12 +162,10 @@ void AudioStreamPlayer3D::_calc_reverb_vol(Area3D *area, Vector3 listener_area_p
rev_pos.y = 0;
rev_pos.normalize();
- if (channel_count >= 1) {
- // Stereo pair
- float c = rev_pos.x * 0.5 + 0.5;
- reverb_vol.write[0].l = 1.0 - c;
- reverb_vol.write[0].r = c;
- }
+ // Stereo pair.
+ float c = rev_pos.x * 0.5 + 0.5;
+ reverb_vol.write[0].l = 1.0 - c;
+ reverb_vol.write[0].r = c;
if (channel_count >= 3) {
// Center pair + Side pair
@@ -183,7 +181,6 @@ void AudioStreamPlayer3D::_calc_reverb_vol(Area3D *area, Vector3 listener_area_p
if (channel_count >= 4) {
// Rear pair
// FIXME: Not sure what math should be done here
- float c = rev_pos.x * 0.5 + 0.5;
reverb_vol.write[3].l = 1.0 - c;
reverb_vol.write[3].r = c;
}
diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp
index d28a6fcc04..0e9e02f247 100644
--- a/scene/animation/animation_blend_space_1d.cpp
+++ b/scene/animation/animation_blend_space_1d.cpp
@@ -61,7 +61,15 @@ void AnimationNodeBlendSpace1D::_validate_property(PropertyInfo &p_property) con
}
void AnimationNodeBlendSpace1D::_tree_changed() {
- emit_signal(SNAME("tree_changed"));
+ AnimationRootNode::_tree_changed();
+}
+
+void AnimationNodeBlendSpace1D::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {
+ AnimationRootNode::_animation_node_renamed(p_oid, p_old_name, p_new_name);
+}
+
+void AnimationNodeBlendSpace1D::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {
+ AnimationRootNode::_animation_node_removed(p_oid, p_node);
}
void AnimationNodeBlendSpace1D::_bind_methods() {
@@ -137,6 +145,8 @@ void AnimationNodeBlendSpace1D::add_blend_point(const Ref<AnimationRootNode> &p_
blend_points[p_at_index].position = p_position;
blend_points[p_at_index].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed), CONNECT_REFERENCE_COUNTED);
+ blend_points[p_at_index].node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
+ blend_points[p_at_index].node->connect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
blend_points_used++;
emit_signal(SNAME("tree_changed"));
@@ -154,10 +164,14 @@ void AnimationNodeBlendSpace1D::set_blend_point_node(int p_point, const Ref<Anim
if (blend_points[p_point].node.is_valid()) {
blend_points[p_point].node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed));
+ blend_points[p_point].node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_renamed));
+ blend_points[p_point].node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_removed));
}
blend_points[p_point].node = p_node;
blend_points[p_point].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed), CONNECT_REFERENCE_COUNTED);
+ blend_points[p_point].node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
+ blend_points[p_point].node->connect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
emit_signal(SNAME("tree_changed"));
}
@@ -177,12 +191,16 @@ void AnimationNodeBlendSpace1D::remove_blend_point(int p_point) {
ERR_FAIL_COND(blend_points[p_point].node.is_null());
blend_points[p_point].node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed));
+ blend_points[p_point].node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_renamed));
+ blend_points[p_point].node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_removed));
for (int i = p_point; i < blend_points_used - 1; i++) {
blend_points[i] = blend_points[i + 1];
}
blend_points_used--;
+
+ emit_signal(SNAME("animation_node_removed"), get_instance_id(), itos(p_point));
emit_signal(SNAME("tree_changed"));
}
diff --git a/scene/animation/animation_blend_space_1d.h b/scene/animation/animation_blend_space_1d.h
index a1e9a7a764..4007df0ded 100644
--- a/scene/animation/animation_blend_space_1d.h
+++ b/scene/animation/animation_blend_space_1d.h
@@ -66,20 +66,21 @@ protected:
void _add_blend_point(int p_index, const Ref<AnimationRootNode> &p_node);
- void _tree_changed();
-
StringName blend_position = "blend_position";
StringName closest = "closest";
StringName length_internal = "length_internal";
BlendMode blend_mode = BLEND_MODE_INTERPOLATED;
-protected:
bool sync = false;
void _validate_property(PropertyInfo &p_property) const;
static void _bind_methods();
+ virtual void _tree_changed() override;
+ virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) override;
+ virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node) override;
+
public:
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp
index c37d54961e..ae5b0d5779 100644
--- a/scene/animation/animation_blend_space_2d.cpp
+++ b/scene/animation/animation_blend_space_2d.cpp
@@ -81,6 +81,8 @@ void AnimationNodeBlendSpace2D::add_blend_point(const Ref<AnimationRootNode> &p_
blend_points[p_at_index].position = p_position;
blend_points[p_at_index].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed), CONNECT_REFERENCE_COUNTED);
+ blend_points[p_at_index].node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
+ blend_points[p_at_index].node->connect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
blend_points_used++;
_queue_auto_triangles();
@@ -100,9 +102,13 @@ void AnimationNodeBlendSpace2D::set_blend_point_node(int p_point, const Ref<Anim
if (blend_points[p_point].node.is_valid()) {
blend_points[p_point].node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed));
+ blend_points[p_point].node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_renamed));
+ blend_points[p_point].node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_removed));
}
blend_points[p_point].node = p_node;
blend_points[p_point].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed), CONNECT_REFERENCE_COUNTED);
+ blend_points[p_point].node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
+ blend_points[p_point].node->connect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
emit_signal(SNAME("tree_changed"));
}
@@ -122,6 +128,8 @@ void AnimationNodeBlendSpace2D::remove_blend_point(int p_point) {
ERR_FAIL_COND(blend_points[p_point].node.is_null());
blend_points[p_point].node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed));
+ blend_points[p_point].node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_renamed));
+ blend_points[p_point].node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_removed));
for (int i = 0; i < triangles.size(); i++) {
bool erase = false;
@@ -144,6 +152,8 @@ void AnimationNodeBlendSpace2D::remove_blend_point(int p_point) {
blend_points[i] = blend_points[i + 1];
}
blend_points_used--;
+
+ emit_signal(SNAME("animation_node_removed"), get_instance_id(), itos(p_point));
emit_signal(SNAME("tree_changed"));
}
@@ -598,10 +608,6 @@ Ref<AnimationNode> AnimationNodeBlendSpace2D::get_child_by_name(const StringName
return get_blend_point_node(p_name.operator String().to_int());
}
-void AnimationNodeBlendSpace2D::_tree_changed() {
- emit_signal(SNAME("tree_changed"));
-}
-
void AnimationNodeBlendSpace2D::set_blend_mode(BlendMode p_blend_mode) {
blend_mode = p_blend_mode;
}
@@ -618,6 +624,18 @@ bool AnimationNodeBlendSpace2D::is_using_sync() const {
return sync;
}
+void AnimationNodeBlendSpace2D::_tree_changed() {
+ AnimationRootNode::_tree_changed();
+}
+
+void AnimationNodeBlendSpace2D::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {
+ AnimationRootNode::_animation_node_renamed(p_oid, p_old_name, p_new_name);
+}
+
+void AnimationNodeBlendSpace2D::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {
+ AnimationRootNode::_animation_node_removed(p_oid, p_node);
+}
+
void AnimationNodeBlendSpace2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace2D::add_blend_point, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("set_blend_point_position", "point", "pos"), &AnimationNodeBlendSpace2D::set_blend_point_position);
diff --git a/scene/animation/animation_blend_space_2d.h b/scene/animation/animation_blend_space_2d.h
index 044c93d9f6..a770bf01ee 100644
--- a/scene/animation/animation_blend_space_2d.h
+++ b/scene/animation/animation_blend_space_2d.h
@@ -85,14 +85,15 @@ protected:
void _update_triangles();
void _queue_auto_triangles();
- void _tree_changed();
-
-protected:
bool sync = false;
void _validate_property(PropertyInfo &p_property) const;
static void _bind_methods();
+ virtual void _tree_changed() override;
+ virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) override;
+ virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node) override;
+
public:
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index 12a96c8679..3fe46b380f 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -721,12 +721,10 @@ void AnimationNodeTransition::get_parameter_list(List<PropertyInfo> *r_list) con
Variant AnimationNodeTransition::get_parameter_default_value(const StringName &p_parameter) const {
if (p_parameter == time || p_parameter == prev_xfading) {
return 0.0;
- } else if (p_parameter == prev_index) {
+ } else if (p_parameter == prev_index || p_parameter == current_index) {
return -1;
- } else if (p_parameter == transition_request || p_parameter == current_state) {
- return String();
} else {
- return 0;
+ return String();
}
}
@@ -748,6 +746,10 @@ void AnimationNodeTransition::set_input_count(int p_inputs) {
while (get_input_count() > p_inputs) {
remove_input(get_input_count() - 1);
}
+
+ pending_update = true;
+
+ emit_signal(SNAME("tree_changed")); // For updating connect activity map.
notify_property_list_changed();
}
@@ -764,6 +766,11 @@ void AnimationNodeTransition::remove_input(int p_index) {
AnimationNode::remove_input(p_index);
}
+bool AnimationNodeTransition::set_input_name(int p_input, const String &p_name) {
+ pending_update = true;
+ return AnimationNode::set_input_name(p_input, p_name);
+}
+
void AnimationNodeTransition::set_input_as_auto_advance(int p_input, bool p_enable) {
ERR_FAIL_INDEX(p_input, get_input_count());
input_data.write[p_input].auto_advance = p_enable;
@@ -819,6 +826,22 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_ex
bool switched = false;
bool restart = false;
+ if (pending_update) {
+ if (cur_current_index < 0 || cur_current_index >= get_input_count()) {
+ set_parameter(prev_index, -1);
+ if (get_input_count() > 0) {
+ set_parameter(current_index, 0);
+ set_parameter(current_state, get_input_name(0));
+ } else {
+ set_parameter(current_index, -1);
+ set_parameter(current_state, StringName());
+ }
+ } else {
+ set_parameter(current_state, get_input_name(cur_current_index));
+ }
+ pending_update = false;
+ }
+
if (!cur_transition_request.is_empty()) {
int new_idx = find_input(cur_transition_request);
if (new_idx >= 0) {
@@ -985,6 +1008,8 @@ void AnimationNodeBlendTree::add_node(const StringName &p_name, Ref<AnimationNod
emit_signal(SNAME("tree_changed"));
p_node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendTree::_tree_changed), CONNECT_REFERENCE_COUNTED);
+ p_node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendTree::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
+ p_node->connect("animation_node_removed", callable_mp(this, &AnimationNodeBlendTree::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
p_node->connect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed).bind(p_name), CONNECT_REFERENCE_COUNTED);
}
@@ -1047,6 +1072,8 @@ void AnimationNodeBlendTree::remove_node(const StringName &p_name) {
{
Ref<AnimationNode> node = nodes[p_name].node;
node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendTree::_tree_changed));
+ node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendTree::_animation_node_renamed));
+ node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeBlendTree::_animation_node_removed));
node->disconnect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed));
}
@@ -1061,6 +1088,7 @@ void AnimationNodeBlendTree::remove_node(const StringName &p_name) {
}
}
+ emit_signal(SNAME("animation_node_removed"), get_instance_id(), p_name);
emit_changed();
emit_signal(SNAME("tree_changed"));
}
@@ -1087,6 +1115,7 @@ void AnimationNodeBlendTree::rename_node(const StringName &p_name, const StringN
// Connection must be done with new name.
nodes[p_new_name].node->connect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed).bind(p_new_name), CONNECT_REFERENCE_COUNTED);
+ emit_signal(SNAME("animation_node_renamed"), get_instance_id(), p_name, p_new_name);
emit_signal(SNAME("tree_changed"));
}
@@ -1287,6 +1316,18 @@ void AnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) cons
p_list->push_back(PropertyInfo(Variant::ARRAY, "node_connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
+void AnimationNodeBlendTree::_tree_changed() {
+ AnimationRootNode::_tree_changed();
+}
+
+void AnimationNodeBlendTree::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {
+ AnimationRootNode::_animation_node_renamed(p_oid, p_old_name, p_new_name);
+}
+
+void AnimationNodeBlendTree::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {
+ AnimationRootNode::_animation_node_removed(p_oid, p_node);
+}
+
void AnimationNodeBlendTree::reset_state() {
graph_offset = Vector2();
nodes.clear();
@@ -1295,10 +1336,6 @@ void AnimationNodeBlendTree::reset_state() {
emit_signal(SNAME("tree_changed"));
}
-void AnimationNodeBlendTree::_tree_changed() {
- emit_signal(SNAME("tree_changed"));
-}
-
void AnimationNodeBlendTree::_node_changed(const StringName &p_node) {
ERR_FAIL_COND(!nodes.has(p_node));
nodes[p_node].connections.resize(nodes[p_node].node->get_input_count());
diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h
index 1e90952564..d4827180bb 100644
--- a/scene/animation/animation_blend_tree.h
+++ b/scene/animation/animation_blend_tree.h
@@ -296,6 +296,8 @@ class AnimationNodeTransition : public AnimationNodeSync {
Ref<Curve> xfade_curve;
bool allow_transition_to_self = false;
+ bool pending_update = false;
+
protected:
bool _get(const StringName &p_path, Variant &r_ret) const;
bool _set(const StringName &p_path, const Variant &p_value);
@@ -313,6 +315,7 @@ public:
virtual bool add_input(const String &p_name) override;
virtual void remove_input(int p_index) override;
+ virtual bool set_input_name(int p_input, const String &p_name) override;
void set_input_as_auto_advance(int p_input, bool p_enable);
bool is_input_set_as_auto_advance(int p_input) const;
@@ -358,7 +361,6 @@ class AnimationNodeBlendTree : public AnimationRootNode {
Vector2 graph_offset;
- void _tree_changed();
void _node_changed(const StringName &p_node);
void _initialize_node_tree();
@@ -369,6 +371,10 @@ protected:
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
+ virtual void _tree_changed() override;
+ virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) override;
+ virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node) override;
+
virtual void reset_state() override;
public:
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index ec28a5cca1..d19d3cc7a3 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -791,6 +791,8 @@ void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<Animation
emit_signal(SNAME("tree_changed"));
p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), CONNECT_REFERENCE_COUNTED);
+ p_node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeStateMachine::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
+ p_node->connect("animation_node_removed", callable_mp(this, &AnimationNodeStateMachine::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
}
void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<AnimationNode> p_node) {
@@ -802,6 +804,8 @@ void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<Anima
Ref<AnimationNode> node = states[p_name].node;
if (node.is_valid()) {
node->disconnect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed));
+ node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeStateMachine::_animation_node_renamed));
+ node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeStateMachine::_animation_node_removed));
}
}
@@ -811,6 +815,8 @@ void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<Anima
emit_signal(SNAME("tree_changed"));
p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), CONNECT_REFERENCE_COUNTED);
+ p_node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeStateMachine::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
+ p_node->connect("animation_node_removed", callable_mp(this, &AnimationNodeStateMachine::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
}
void AnimationNodeStateMachine::set_allow_transition_to_self(bool p_enable) {
@@ -884,10 +890,13 @@ void AnimationNodeStateMachine::remove_node(const StringName &p_name) {
Ref<AnimationNode> node = states[p_name].node;
ERR_FAIL_COND(node.is_null());
node->disconnect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed));
+ node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeStateMachine::_animation_node_renamed));
+ node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeStateMachine::_animation_node_removed));
}
states.erase(p_name);
+ emit_signal(SNAME("animation_node_removed"), get_instance_id(), p_name);
emit_changed();
emit_signal(SNAME("tree_changed"));
}
@@ -907,6 +916,7 @@ void AnimationNodeStateMachine::rename_node(const StringName &p_name, const Stri
_rename_transitions(p_name, p_new_name);
+ emit_signal(SNAME("animation_node_renamed"), get_instance_id(), p_name, p_new_name);
emit_changed();
emit_signal(SNAME("tree_changed"));
}
@@ -1365,7 +1375,15 @@ Vector2 AnimationNodeStateMachine::get_node_position(const StringName &p_name) c
void AnimationNodeStateMachine::_tree_changed() {
emit_changed();
- emit_signal(SNAME("tree_changed"));
+ AnimationRootNode::_tree_changed();
+}
+
+void AnimationNodeStateMachine::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {
+ AnimationRootNode::_animation_node_renamed(p_oid, p_old_name, p_new_name);
+}
+
+void AnimationNodeStateMachine::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {
+ AnimationRootNode::_animation_node_removed(p_oid, p_node);
}
void AnimationNodeStateMachine::_bind_methods() {
diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h
index 5c2a4d6264..5867b6c65a 100644
--- a/scene/animation/animation_node_state_machine.h
+++ b/scene/animation/animation_node_state_machine.h
@@ -207,7 +207,6 @@ private:
Vector2 graph_offset;
- void _tree_changed();
void _remove_transition(const Ref<AnimationNodeStateMachineTransition> p_transition);
void _rename_transitions(const StringName &p_name, const StringName &p_new_name);
bool _can_connect(const StringName &p_name, Vector<AnimationNodeStateMachine *> p_parents = Vector<AnimationNodeStateMachine *>());
@@ -221,6 +220,10 @@ protected:
void _get_property_list(List<PropertyInfo> *p_list) const;
bool _check_advance_condition(const Ref<AnimationNodeStateMachine> p_state_machine, const Ref<AnimationNodeStateMachineTransition> p_transition) const;
+ virtual void _tree_changed() override;
+ virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) override;
+ virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node) override;
+
virtual void reset_state() override;
public:
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 2e25d685d6..ad0c19b1e6 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -857,7 +857,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
HashMap<int, TrackNodeCache::PlayingAudioStreamInfo> &map = aa->playing_streams;
// Find stream.
int idx = -1;
- if (p_seeked) {
+ if (p_seeked || p_started) {
idx = a->track_find_key(i, p_time);
// Discard previous stream when seeking.
if (map.has(idx)) {
@@ -866,12 +866,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
}
} else {
List<int> to_play;
- if (p_started) {
- int first_key = a->track_find_key(i, p_prev_time, Animation::FIND_MODE_EXACT);
- if (first_key >= 0) {
- to_play.push_back(first_key);
- }
- }
+
a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_looped_flag);
if (to_play.size()) {
idx = to_play.back()->get();
@@ -888,6 +883,10 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
double end_ofs = a->audio_track_get_key_end_offset(i, idx);
double len = stream->get_length();
+ if (p_seeked || p_started) {
+ start_ofs += p_time - a->track_get_key_time(i, idx);
+ }
+
if (aa->object->call(SNAME("get_stream")) != aa->audio_stream) {
aa->object->call(SNAME("set_stream"), aa->audio_stream);
aa->audio_stream_playback.unref();
@@ -1234,7 +1233,7 @@ void AnimationPlayer::_animation_update_transforms() {
if (aa->time < pasi.start) {
stop = true;
}
- } else if (aa->backward) {
+ } else {
if (aa->time > pasi.start) {
stop = true;
}
@@ -1286,6 +1285,8 @@ void AnimationPlayer::_animation_process(double p_delta) {
_animation_update_transforms();
if (end_reached) {
+ _clear_audio_streams();
+ _stop_playing_caches(false);
if (queued.size()) {
String old = playback.assigned;
play(queued.front()->get());
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index dd5bf31c66..7c2edef1de 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -453,6 +453,8 @@ void AnimationNode::_bind_methods() {
GDVIRTUAL_BIND(_has_filter);
ADD_SIGNAL(MethodInfo("tree_changed"));
+ ADD_SIGNAL(MethodInfo("animation_node_renamed", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "old_name"), PropertyInfo(Variant::STRING, "new_name")));
+ ADD_SIGNAL(MethodInfo("animation_node_removed", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "name")));
BIND_ENUM_CONSTANT(FILTER_IGNORE);
BIND_ENUM_CONSTANT(FILTER_PASS);
@@ -465,15 +467,33 @@ AnimationNode::AnimationNode() {
////////////////////
+void AnimationRootNode::_tree_changed() {
+ emit_signal(SNAME("tree_changed"));
+}
+
+void AnimationRootNode::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {
+ emit_signal(SNAME("animation_node_renamed"), p_oid, p_old_name, p_new_name);
+}
+
+void AnimationRootNode::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {
+ emit_signal(SNAME("animation_node_removed"), p_oid, p_node);
+}
+
+////////////////////
+
void AnimationTree::set_tree_root(const Ref<AnimationNode> &p_root) {
if (root.is_valid()) {
root->disconnect("tree_changed", callable_mp(this, &AnimationTree::_tree_changed));
+ root->disconnect("animation_node_renamed", callable_mp(this, &AnimationTree::_animation_node_renamed));
+ root->disconnect("animation_node_removed", callable_mp(this, &AnimationTree::_animation_node_removed));
}
root = p_root;
if (root.is_valid()) {
root->connect("tree_changed", callable_mp(this, &AnimationTree::_tree_changed));
+ root->connect("animation_node_renamed", callable_mp(this, &AnimationTree::_animation_node_renamed));
+ root->connect("animation_node_removed", callable_mp(this, &AnimationTree::_animation_node_removed));
}
properties_dirty = true;
@@ -1546,6 +1566,10 @@ void AnimationTree::_process_graph(double p_delta) {
double end_ofs = a->audio_track_get_key_end_offset(i, idx);
double len = stream->get_length();
+ if (seeked) {
+ start_ofs += time - a->track_get_key_time(i, idx);
+ }
+
if (t->object->call(SNAME("get_stream")) != t->audio_stream) {
t->object->call(SNAME("set_stream"), t->audio_stream);
t->audio_stream_playback.unref();
@@ -1982,11 +2006,46 @@ void AnimationTree::_tree_changed() {
properties_dirty = true;
}
+void AnimationTree::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {
+ ERR_FAIL_COND(!property_reference_map.has(p_oid));
+ String base_path = property_reference_map[p_oid];
+ String old_base = base_path + p_old_name;
+ String new_base = base_path + p_new_name;
+ for (const PropertyInfo &E : properties) {
+ if (E.name.begins_with(old_base)) {
+ String new_name = E.name.replace_first(old_base, new_base);
+ property_map[new_name] = property_map[E.name];
+ property_map.erase(E.name);
+ }
+ }
+
+ //update tree second
+ properties_dirty = true;
+ _update_properties();
+}
+
+void AnimationTree::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {
+ ERR_FAIL_COND(!property_reference_map.has(p_oid));
+ String base_path = String(property_reference_map[p_oid]) + String(p_node);
+ for (const PropertyInfo &E : properties) {
+ if (E.name.begins_with(base_path)) {
+ property_map.erase(E.name);
+ }
+ }
+
+ //update tree second
+ properties_dirty = true;
+ _update_properties();
+}
+
void AnimationTree::_update_properties_for_node(const String &p_base_path, Ref<AnimationNode> node) {
ERR_FAIL_COND(node.is_null());
if (!property_parent_map.has(p_base_path)) {
property_parent_map[p_base_path] = HashMap<StringName, StringName>();
}
+ if (!property_reference_map.has(node->get_instance_id())) {
+ property_reference_map[node->get_instance_id()] = p_base_path;
+ }
if (node->get_input_count() && !input_activity_map.has(p_base_path)) {
Vector<Activity> activity;
@@ -2032,6 +2091,7 @@ void AnimationTree::_update_properties() {
}
properties.clear();
+ property_reference_map.clear();
property_parent_map.clear();
input_activity_map.clear();
input_activity_map_get.clear();
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
index c5c2790fae..0540add85b 100644
--- a/scene/animation/animation_tree.h
+++ b/scene/animation/animation_tree.h
@@ -167,6 +167,11 @@ VARIANT_ENUM_CAST(AnimationNode::FilterAction)
class AnimationRootNode : public AnimationNode {
GDCLASS(AnimationRootNode, AnimationNode);
+protected:
+ virtual void _tree_changed();
+ virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name);
+ virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node);
+
public:
AnimationRootNode() {}
};
@@ -326,9 +331,12 @@ private:
friend class AnimationNode;
bool properties_dirty = true;
void _tree_changed();
+ void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name);
+ void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node);
void _update_properties();
List<PropertyInfo> properties;
HashMap<StringName, HashMap<StringName, StringName>> property_parent_map;
+ HashMap<ObjectID, StringName> property_reference_map;
HashMap<StringName, Pair<Variant, bool>> property_map; // Property value and read-only flag.
struct Activity {
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index f09e4962a9..ec75fcb665 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -644,8 +644,10 @@ Rect2 Control::get_parent_anchorable_rect() const {
parent_rect = data.parent_canvas_item->get_anchorable_rect();
} else {
#ifdef TOOLS_ENABLED
- Node *edited_root = get_tree()->get_edited_scene_root();
- if (edited_root && (this == edited_root || edited_root->is_ancestor_of(this))) {
+ Node *edited_scene_root = get_tree()->get_edited_scene_root();
+ Node *scene_root_parent = edited_scene_root ? edited_scene_root->get_parent() : nullptr;
+
+ if (scene_root_parent && get_viewport() == scene_root_parent->get_viewport()) {
parent_rect.size = Size2(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height"));
} else {
parent_rect = get_viewport()->get_visible_rect();
@@ -3390,7 +3392,7 @@ void Control::_bind_methods() {
ADD_SIGNAL(MethodInfo("minimum_size_changed"));
ADD_SIGNAL(MethodInfo("theme_changed"));
- GDVIRTUAL_BIND(_has_point, "position");
+ GDVIRTUAL_BIND(_has_point, "point");
GDVIRTUAL_BIND(_structured_text_parser, "args", "text");
GDVIRTUAL_BIND(_get_minimum_size);
diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index dfe9ea3b08..760c86b2cc 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -276,10 +276,6 @@ Size2 AcceptDialog::_get_contents_minimum_size() const {
// Plus there is a separation size added on top.
content_minsize.y += theme_cache.buttons_separation;
- // Last, we make sure that we aren't below the minimum window size.
- Size2 window_minsize = get_min_size();
- content_minsize.x = MAX(window_minsize.x, content_minsize.x);
- content_minsize.y = MAX(window_minsize.y, content_minsize.y);
return content_minsize;
}
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index 027c97b383..dc1d6cc73e 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -491,9 +491,11 @@ void OptionButton::show_popup() {
return;
}
- Size2 button_size = get_global_transform_with_canvas().get_scale() * get_size();
- popup->set_position(get_screen_position() + Size2(0, button_size.height));
- popup->set_size(Size2i(button_size.width, 0));
+ Rect2 rect = get_screen_rect();
+ rect.position.y += rect.size.height;
+ rect.size.height = 0;
+ popup->set_position(rect.position);
+ popup->set_size(rect.size);
// If not triggered by the mouse, start the popup with the checked item (or the first enabled one) focused.
if (current != NONE_SELECTED && !popup->is_item_disabled(current)) {
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index 0ea8f6c5f1..906b478eb3 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -491,6 +491,17 @@ int CanvasItem::get_z_index() const {
return z_index;
}
+int CanvasItem::get_effective_z_index() const {
+ int effective_z_index = z_index;
+ if (is_z_relative()) {
+ CanvasItem *p = get_parent_item();
+ if (p) {
+ effective_z_index += p->get_effective_z_index();
+ }
+ }
+ return effective_z_index;
+}
+
void CanvasItem::set_y_sort_enabled(bool p_enabled) {
y_sort_enabled = p_enabled;
RS::get_singleton()->canvas_item_set_sort_children_by_y(canvas_item, y_sort_enabled);
diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h
index 2fa1d56667..5fbf043159 100644
--- a/scene/main/canvas_item.h
+++ b/scene/main/canvas_item.h
@@ -246,6 +246,7 @@ public:
void set_z_index(int p_z);
int get_z_index() const;
+ int get_effective_z_index() const;
void set_z_as_relative(bool p_enabled);
bool is_z_relative() const;
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 28521c5bbe..a1e7555a5d 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -36,6 +36,7 @@
#include "core/object/message_queue.h"
#include "core/string/translation.h"
#include "core/templates/pair.h"
+#include "core/templates/sort_array.h"
#include "scene/2d/audio_listener_2d.h"
#include "scene/2d/camera_2d.h"
#include "scene/2d/collision_object_2d.h"
@@ -379,7 +380,7 @@ void Viewport::_notification(int p_what) {
if (get_tree()->is_debugging_collisions_hint()) {
PhysicsServer2D::get_singleton()->space_set_debug_contacts(find_world_2d()->get_space(), get_tree()->get_collision_debug_contact_count());
contact_2d_debug = RenderingServer::get_singleton()->canvas_item_create();
- RenderingServer::get_singleton()->canvas_item_set_parent(contact_2d_debug, find_world_2d()->get_canvas());
+ RenderingServer::get_singleton()->canvas_item_set_parent(contact_2d_debug, current_canvas);
#ifndef _3D_DISABLED
PhysicsServer3D::get_singleton()->space_set_debug_contacts(find_world_3d()->get_space(), get_tree()->get_collision_debug_contact_count());
contact_3d_debug_multimesh = RenderingServer::get_singleton()->multimesh_create();
@@ -613,7 +614,7 @@ void Viewport::_process_picking() {
physics_last_mouse_state.mouse_mask.clear_flag(mouse_button_to_mask(mb->get_button_index()));
// If touch mouse raised, assume we don't know last mouse pos until new events come
- if (mb->get_device() == InputEvent::DEVICE_ID_TOUCH_MOUSE) {
+ if (mb->get_device() == InputEvent::DEVICE_ID_EMULATION) {
physics_has_last_mousepos = false;
}
}
@@ -669,6 +670,25 @@ void Viewport::_process_picking() {
point_params.pick_point = true;
int rc = ss2d->intersect_point(point_params, res, 64);
+ if (physics_object_picking_sort) {
+ struct ComparatorCollisionObjects {
+ bool operator()(const PhysicsDirectSpaceState2D::ShapeResult &p_a, const PhysicsDirectSpaceState2D::ShapeResult &p_b) const {
+ CollisionObject2D *a = Object::cast_to<CollisionObject2D>(p_a.collider);
+ CollisionObject2D *b = Object::cast_to<CollisionObject2D>(p_b.collider);
+ if (!a || !b) {
+ return false;
+ }
+ int za = a->get_effective_z_index();
+ int zb = b->get_effective_z_index();
+ if (za != zb) {
+ return zb < za;
+ }
+ return a->is_greater_than(b);
+ }
+ };
+ SortArray<PhysicsDirectSpaceState2D::ShapeResult, ComparatorCollisionObjects> sorter;
+ sorter.sort(res, rc);
+ }
for (int i = 0; i < rc; i++) {
if (res[i].collider_id.is_valid() && res[i].collider) {
CollisionObject2D *co = Object::cast_to<CollisionObject2D>(res[i].collider);
@@ -1152,11 +1172,16 @@ Ref<InputEvent> Viewport::_make_input_local(const Ref<InputEvent> &ev) {
}
Vector2 Viewport::get_mouse_position() const {
- return gui.last_mouse_pos;
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_MOUSE)) {
+ return get_screen_transform_internal(true).affine_inverse().xform(DisplayServer::get_singleton()->mouse_get_position());
+ } else {
+ // Fallback to Input for getting mouse position in case of emulated mouse.
+ return get_screen_transform_internal().affine_inverse().xform(Input::get_singleton()->get_mouse_position());
+ }
}
void Viewport::warp_mouse(const Vector2 &p_position) {
- Transform2D xform = get_screen_transform();
+ Transform2D xform = get_screen_transform_internal();
Vector2 gpos = xform.xform(p_position);
Input::get_singleton()->warp_mouse(gpos);
}
@@ -1328,7 +1353,7 @@ bool Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_inpu
Ref<InputEvent> ev = p_input;
// Returns true if an event should be impacted by a control's mouse filter.
- bool is_mouse_event = Ref<InputEventMouse>(p_input).is_valid();
+ bool is_pointer_event = Ref<InputEventMouse>(p_input).is_valid() || Ref<InputEventScreenDrag>(p_input).is_valid() || Ref<InputEventScreenTouch>(p_input).is_valid();
Ref<InputEventMouseButton> mb = p_input;
bool is_scroll_event = mb.is_valid() &&
@@ -1352,8 +1377,8 @@ bool Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_inpu
stopped = true;
break;
}
- if (control->data.mouse_filter == Control::MOUSE_FILTER_STOP && is_mouse_event && !(is_scroll_event && control->data.force_pass_scroll_events)) {
- // Mouse events are stopped by default with MOUSE_FILTER_STOP, unless we have a scroll event and force_pass_scroll_events set to true
+ if (control->data.mouse_filter == Control::MOUSE_FILTER_STOP && is_pointer_event && !(is_scroll_event && control->data.force_pass_scroll_events)) {
+ // Mouse, ScreenDrag and ScreenTouch events are stopped by default with MOUSE_FILTER_STOP, unless we have a scroll event and force_pass_scroll_events set to true
stopped = true;
break;
}
@@ -2270,6 +2295,7 @@ void Viewport::_drop_mouse_focus() {
mb->set_global_position(c->get_local_mouse_position());
mb->set_button_index(MouseButton(i + 1));
mb->set_pressed(false);
+ mb->set_device(InputEvent::DEVICE_ID_INTERNAL);
c->_call_gui_input(mb);
}
}
@@ -2381,6 +2407,7 @@ void Viewport::_post_gui_grab_click_focus() {
mb->set_position(click);
mb->set_button_index(MouseButton(i + 1));
mb->set_pressed(false);
+ mb->set_device(InputEvent::DEVICE_ID_INTERNAL);
gui.mouse_focus->_call_gui_input(mb);
}
}
@@ -2398,6 +2425,7 @@ void Viewport::_post_gui_grab_click_focus() {
mb->set_position(click);
mb->set_button_index(MouseButton(i + 1));
mb->set_pressed(true);
+ mb->set_device(InputEvent::DEVICE_ID_INTERNAL);
MessageQueue::get_singleton()->push_callable(callable_mp(gui.mouse_focus, &Control::_call_gui_input), mb);
}
}
@@ -2533,20 +2561,14 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) {
if (gui.subwindow_drag == SUB_WINDOW_DRAG_RESIZE) {
Vector2i diff = mm->get_position() - gui.subwindow_drag_from;
Size2i min_size = gui.subwindow_focused->get_min_size();
+ Size2i min_size_clamped = gui.subwindow_focused->get_clamped_minimum_size();
- Size2i min_size_adjusted = min_size;
- if (gui.subwindow_focused->is_wrapping_controls()) {
- Size2i cms = gui.subwindow_focused->get_contents_minimum_size();
- min_size_adjusted.x = MAX(cms.x, min_size.x);
- min_size_adjusted.y = MAX(cms.y, min_size.y);
- }
-
- min_size_adjusted.x = MAX(min_size_adjusted.x, 1);
- min_size_adjusted.y = MAX(min_size_adjusted.y, 1);
+ min_size_clamped.x = MAX(min_size_clamped.x, 1);
+ min_size_clamped.y = MAX(min_size_clamped.y, 1);
Rect2i r = gui.subwindow_resize_from_rect;
- Size2i limit = r.size - min_size_adjusted;
+ Size2i limit = r.size - min_size_clamped;
switch (gui.subwindow_resize_mode) {
case SUB_WINDOW_RESIZE_TOP_LEFT: {
@@ -2864,6 +2886,14 @@ bool Viewport::get_physics_object_picking() {
return physics_object_picking;
}
+void Viewport::set_physics_object_picking_sort(bool p_enable) {
+ physics_object_picking_sort = p_enable;
+}
+
+bool Viewport::get_physics_object_picking_sort() {
+ return physics_object_picking_sort;
+}
+
Vector2 Viewport::get_camera_coords(const Vector2 &p_viewport_coords) const {
Transform2D xf = stretch_transform * global_canvas_transform;
return xf.xform(p_viewport_coords);
@@ -3257,6 +3287,10 @@ Viewport::SDFScale Viewport::get_sdf_scale() const {
}
Transform2D Viewport::get_screen_transform() const {
+ return get_screen_transform_internal();
+}
+
+Transform2D Viewport::get_screen_transform_internal(bool p_absolute_position) const {
return get_final_transform();
}
@@ -3798,6 +3832,8 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_physics_object_picking", "enable"), &Viewport::set_physics_object_picking);
ClassDB::bind_method(D_METHOD("get_physics_object_picking"), &Viewport::get_physics_object_picking);
+ ClassDB::bind_method(D_METHOD("set_physics_object_picking_sort", "enable"), &Viewport::set_physics_object_picking_sort);
+ ClassDB::bind_method(D_METHOD("get_physics_object_picking_sort"), &Viewport::get_physics_object_picking_sort);
ClassDB::bind_method(D_METHOD("get_viewport_rid"), &Viewport::get_viewport_rid);
ClassDB::bind_method(D_METHOD("push_text_input", "text"), &Viewport::push_text_input);
@@ -3949,6 +3985,7 @@ void Viewport::_bind_methods() {
#endif
ADD_GROUP("Physics", "physics_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "physics_object_picking"), "set_physics_object_picking", "get_physics_object_picking");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "physics_object_picking_sort"), "set_physics_object_picking_sort", "get_physics_object_picking_sort");
ADD_GROUP("GUI", "gui_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gui_disable_input"), "set_disable_input", "is_input_disabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gui_snap_controls_to_pixels"), "set_snap_controls_to_pixels", "is_snap_controls_to_pixels_enabled");
@@ -4192,14 +4229,14 @@ DisplayServer::WindowID SubViewport::get_window_id() const {
return DisplayServer::INVALID_WINDOW_ID;
}
-Transform2D SubViewport::get_screen_transform() const {
+Transform2D SubViewport::get_screen_transform_internal(bool p_absolute_position) const {
Transform2D container_transform;
SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent());
if (c) {
if (c->is_stretch_enabled()) {
container_transform.scale(Vector2(c->get_stretch_shrink(), c->get_stretch_shrink()));
}
- container_transform = c->get_viewport()->get_screen_transform() * c->get_global_transform_with_canvas() * container_transform;
+ container_transform = c->get_viewport()->get_screen_transform_internal(p_absolute_position) * c->get_global_transform_with_canvas() * container_transform;
} else {
WARN_PRINT_ONCE("SubViewport is not a child of a SubViewportContainer. get_screen_transform doesn't return the actual screen position.");
}
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 2142aaaaef..055fad5369 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -246,6 +246,7 @@ private:
bool snap_2d_vertices_to_pixel = false;
bool physics_object_picking = false;
+ bool physics_object_picking_sort = false;
List<Ref<InputEvent>> physics_picking_events;
ObjectID physics_object_capture;
ObjectID physics_object_over;
@@ -574,6 +575,8 @@ public:
void set_physics_object_picking(bool p_enable);
bool get_physics_object_picking();
+ void set_physics_object_picking_sort(bool p_enable);
+ bool get_physics_object_picking_sort();
Variant gui_get_drag_data() const;
@@ -648,7 +651,8 @@ public:
virtual bool is_size_2d_override_stretch_enabled() const { return true; }
- virtual Transform2D get_screen_transform() const;
+ Transform2D get_screen_transform() const;
+ virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const;
virtual Transform2D get_popup_base_transform() const { return Transform2D(); }
#ifndef _3D_DISABLED
@@ -777,7 +781,7 @@ public:
void set_clear_mode(ClearMode p_mode);
ClearMode get_clear_mode() const;
- virtual Transform2D get_screen_transform() const override;
+ virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const override;
virtual Transform2D get_popup_base_transform() const override;
SubViewport();
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 33946246b0..44df648552 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -349,8 +349,30 @@ Size2i Window::get_size_with_decorations() const {
return size;
}
+Size2i Window::_clamp_limit_size(const Size2i &p_limit_size) {
+ // Force window limits to respect size limitations of rendering server.
+ Size2i max_window_size = RS::get_singleton()->get_maximum_viewport_size();
+ if (max_window_size != Size2i()) {
+ return p_limit_size.clamp(Vector2i(), max_window_size);
+ } else {
+ return p_limit_size.max(Vector2i());
+ }
+}
+
+void Window::_validate_limit_size() {
+ // When max_size is invalid, max_size_used falls back to respect size limitations of rendering server.
+ bool max_size_valid = (max_size.x > 0 || max_size.y > 0) && max_size.x >= min_size.x && max_size.y >= min_size.y;
+ max_size_used = max_size_valid ? max_size : RS::get_singleton()->get_maximum_viewport_size();
+}
+
void Window::set_max_size(const Size2i &p_max_size) {
- max_size = p_max_size;
+ Size2i max_size_clamped = _clamp_limit_size(p_max_size);
+ if (max_size == max_size_clamped) {
+ return;
+ }
+ max_size = max_size_clamped;
+
+ _validate_limit_size();
_update_window_size();
}
@@ -359,7 +381,13 @@ Size2i Window::get_max_size() const {
}
void Window::set_min_size(const Size2i &p_min_size) {
- min_size = p_min_size;
+ Size2i min_size_clamped = _clamp_limit_size(p_min_size);
+ if (min_size == min_size_clamped) {
+ return;
+ }
+ min_size = min_size_clamped;
+
+ _validate_limit_size();
_update_window_size();
}
@@ -647,6 +675,7 @@ void Window::update_mouse_cursor_shape() {
mm.instantiate();
mm->set_position(pos);
mm->set_global_position(xform.xform(pos));
+ mm->set_device(InputEvent::DEVICE_ID_INTERNAL);
push_input(mm);
}
@@ -832,49 +861,35 @@ bool Window::is_visible() const {
return visible;
}
-void Window::_update_window_size() {
- // Force window to respect size limitations of rendering server.
- RenderingServer *rendering_server = RenderingServer::get_singleton();
- if (rendering_server) {
- Size2i max_window_size = rendering_server->get_maximum_viewport_size();
+Size2i Window::_clamp_window_size(const Size2i &p_size) {
+ Size2i window_size_clamped = p_size;
+ Size2 minsize = get_clamped_minimum_size();
+ window_size_clamped = window_size_clamped.max(minsize);
- if (max_window_size != Size2i()) {
- size = size.min(max_window_size);
- min_size = min_size.min(max_window_size);
- max_size = max_size.min(max_window_size);
- }
+ if (max_size_used != Size2i()) {
+ window_size_clamped = window_size_clamped.min(max_size_used);
}
- Size2i size_limit;
- if (wrap_controls) {
- size_limit = get_contents_minimum_size();
- }
+ return window_size_clamped;
+}
- size_limit.x = MAX(size_limit.x, min_size.x);
- size_limit.y = MAX(size_limit.y, min_size.y);
+void Window::_update_window_size() {
+ Size2i size_limit = get_clamped_minimum_size();
- size.x = MAX(size_limit.x, size.x);
- size.y = MAX(size_limit.y, size.y);
+ size = size.max(size_limit);
bool reset_min_first = false;
- bool max_size_valid = false;
- if ((max_size.x > 0 || max_size.y > 0) && (max_size.x >= min_size.x && max_size.y >= min_size.y)) {
- max_size_valid = true;
+ if (max_size_used != Size2i()) {
+ // Force window size to respect size limitations of max_size_used.
+ size = size.min(max_size_used);
- if (size.x > max_size.x) {
- size.x = max_size.x;
- }
- if (size_limit.x > max_size.x) {
- size_limit.x = max_size.x;
+ if (size_limit.x > max_size_used.x) {
+ size_limit.x = max_size_used.x;
reset_min_first = true;
}
-
- if (size.y > max_size.y) {
- size.y = max_size.y;
- }
- if (size_limit.y > max_size.y) {
- size_limit.y = max_size.y;
+ if (size_limit.y > max_size_used.y) {
+ size_limit.y = max_size_used.y;
reset_min_first = true;
}
}
@@ -890,7 +905,7 @@ void Window::_update_window_size() {
DisplayServer::get_singleton()->window_set_min_size(Size2i(), window_id);
}
- DisplayServer::get_singleton()->window_set_max_size(max_size_valid ? max_size : Size2i(), window_id);
+ DisplayServer::get_singleton()->window_set_max_size(max_size_used, window_id);
DisplayServer::get_singleton()->window_set_min_size(size_limit, window_id);
DisplayServer::get_singleton()->window_set_size(size, window_id);
}
@@ -1422,6 +1437,8 @@ void Window::popup_centered_clamped(const Size2i &p_size, float p_fallback_ratio
Rect2i popup_rect;
popup_rect.size = Vector2i(MIN(size_ratio.x, p_size.x), MIN(size_ratio.y, p_size.y));
+ popup_rect.size = _clamp_window_size(popup_rect.size);
+
if (parent_rect != Rect2()) {
popup_rect.position = parent_rect.position + (parent_rect.size - popup_rect.size) / 2;
}
@@ -1445,9 +1462,7 @@ void Window::popup_centered(const Size2i &p_minsize) {
}
Rect2i popup_rect;
- Size2 contents_minsize = _get_contents_minimum_size();
- popup_rect.size.x = MAX(p_minsize.x, contents_minsize.x);
- popup_rect.size.y = MAX(p_minsize.y, contents_minsize.y);
+ popup_rect.size = _clamp_window_size(p_minsize);
if (parent_rect != Rect2()) {
popup_rect.position = parent_rect.position + (parent_rect.size - popup_rect.size) / 2;
@@ -1475,6 +1490,7 @@ void Window::popup_centered_ratio(float p_ratio) {
Rect2i popup_rect;
if (parent_rect != Rect2()) {
popup_rect.size = parent_rect.size * p_ratio;
+ popup_rect.size = _clamp_window_size(popup_rect.size);
popup_rect.position = parent_rect.position + (parent_rect.size - popup_rect.size) / 2;
}
@@ -1538,6 +1554,14 @@ Size2 Window::get_contents_minimum_size() const {
return _get_contents_minimum_size();
}
+Size2 Window::get_clamped_minimum_size() const {
+ if (!wrap_controls) {
+ return min_size;
+ }
+
+ return min_size.max(get_contents_minimum_size());
+}
+
void Window::grab_focus() {
if (embedder) {
embedder->_sub_window_grab_focus(this);
@@ -2119,11 +2143,13 @@ Transform2D Window::get_final_transform() const {
return window_transform * stretch_transform * global_canvas_transform;
}
-Transform2D Window::get_screen_transform() const {
+Transform2D Window::get_screen_transform_internal(bool p_absolute_position) const {
Transform2D embedder_transform;
if (_get_embedder()) {
embedder_transform.translate_local(get_position());
- embedder_transform = _get_embedder()->get_screen_transform() * embedder_transform;
+ embedder_transform = _get_embedder()->get_screen_transform_internal(p_absolute_position) * embedder_transform;
+ } else if (p_absolute_position) {
+ embedder_transform.translate_local(get_position());
}
return embedder_transform * get_final_transform();
}
@@ -2388,6 +2414,7 @@ Window::Window() {
RenderingServer *rendering_server = RenderingServer::get_singleton();
if (rendering_server) {
max_size = rendering_server->get_maximum_viewport_size();
+ max_size_used = max_size; // Update max_size_used.
}
theme_owner = memnew(ThemeOwner);
diff --git a/scene/main/window.h b/scene/main/window.h
index 9861fefc68..5359c37e9a 100644
--- a/scene/main/window.h
+++ b/scene/main/window.h
@@ -135,6 +135,11 @@ private:
void _clear_window();
void _update_from_window();
+ Size2i max_size_used;
+
+ Size2i _clamp_limit_size(const Size2i &p_limit_size);
+ Size2i _clamp_window_size(const Size2i &p_size);
+ void _validate_limit_size();
void _update_viewport_size();
void _update_window_size();
@@ -306,6 +311,7 @@ public:
void popup_centered_clamped(const Size2i &p_size = Size2i(), float p_fallback_ratio = 0.75);
Size2 get_contents_minimum_size() const;
+ Size2 get_clamped_minimum_size() const;
void grab_focus();
bool has_focus() const;
@@ -377,7 +383,7 @@ public:
//
virtual Transform2D get_final_transform() const override;
- virtual Transform2D get_screen_transform() const override;
+ virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const override;
virtual Transform2D get_popup_base_transform() const override;
Rect2i get_parent_rect() const;
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index 3006da5309..448e800900 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -601,9 +601,11 @@ Error ResourceLoaderText::load() {
*progress = resource_current / float(resources_total);
}
- int_resources[id] = res; //always assign int resources
- if (do_assign && cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
- res->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE);
+ int_resources[id] = res; // Always assign int resources.
+ if (do_assign) {
+ if (cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
+ res->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE);
+ }
res->set_scene_unique_id(id);
}
diff --git a/servers/debugger/servers_debugger.cpp b/servers/debugger/servers_debugger.cpp
index c44f9a3a1b..75452be49b 100644
--- a/servers/debugger/servers_debugger.cpp
+++ b/servers/debugger/servers_debugger.cpp
@@ -409,6 +409,7 @@ Error ServersDebugger::_capture(void *p_user, const String &p_cmd, const Array &
if (RenderingServer::get_singleton()->has_changed()) {
RenderingServer::get_singleton()->draw(true, delta);
}
+ EngineDebugger::get_singleton()->send_message("servers:drawn", Array());
} else if (p_cmd == "foreground") {
singleton->last_draw_time = 0.0;
DisplayServer::get_singleton()->window_move_to_foreground();
diff --git a/servers/rendering/dummy/storage/light_storage.h b/servers/rendering/dummy/storage/light_storage.h
index 9a3918fd86..b9e8bcc6f1 100644
--- a/servers/rendering/dummy/storage/light_storage.h
+++ b/servers/rendering/dummy/storage/light_storage.h
@@ -80,6 +80,7 @@ public:
virtual RS::LightBakeMode light_get_bake_mode(RID p_light) override { return RS::LIGHT_BAKE_DISABLED; }
virtual uint32_t light_get_max_sdfgi_cascade(RID p_light) override { return 0; }
virtual uint64_t light_get_version(RID p_light) const override { return 0; }
+ virtual uint32_t light_get_cull_mask(RID p_light) const override { return 0; }
/* LIGHT INSTANCE API */
diff --git a/servers/rendering/dummy/storage/texture_storage.h b/servers/rendering/dummy/storage/texture_storage.h
index 41251b348c..67661ce821 100644
--- a/servers/rendering/dummy/storage/texture_storage.h
+++ b/servers/rendering/dummy/storage/texture_storage.h
@@ -145,6 +145,7 @@ public:
virtual void decal_set_normal_fade(RID p_decal, float p_fade) override {}
virtual AABB decal_get_aabb(RID p_decal) const override { return AABB(); }
+ virtual uint32_t decal_get_cull_mask(RID p_decal) const override { return 0; }
virtual void texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) override {}
virtual void texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) override {}
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
index 7d9ca070b0..f9529de6dd 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -593,12 +593,10 @@ void RenderForwardMobile::_pre_opaque_render(RenderDataRD *p_render_data) {
bool render_shadows = p_render_data->directional_shadows.size() || p_render_data->shadows.size();
+ //prepare shadow rendering
if (render_shadows) {
RENDER_TIMESTAMP("Render Shadows");
- }
- //prepare shadow rendering
- if (render_shadows) {
_render_shadow_begin();
//render directional shadows
diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
index 968f804593..e65d842a67 100644
--- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
@@ -389,6 +389,13 @@ uint64_t LightStorage::light_get_version(RID p_light) const {
return light->version;
}
+uint32_t LightStorage::light_get_cull_mask(RID p_light) const {
+ const Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_COND_V(!light, 0);
+
+ return light->cull_mask;
+}
+
AABB LightStorage::light_get_aabb(RID p_light) const {
const Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_COND_V(!light, AABB());
diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.h b/servers/rendering/renderer_rd/storage_rd/light_storage.h
index 68f439ddef..c36d1ef503 100644
--- a/servers/rendering/renderer_rd/storage_rd/light_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/light_storage.h
@@ -517,13 +517,6 @@ public:
return light->color;
}
- _FORCE_INLINE_ uint32_t light_get_cull_mask(RID p_light) {
- const Light *light = light_owner.get_or_null(p_light);
- ERR_FAIL_COND_V(!light, 0);
-
- return light->cull_mask;
- }
-
_FORCE_INLINE_ bool light_is_distance_fade_enabled(RID p_light) {
const Light *light = light_owner.get_or_null(p_light);
return light->distance_fade;
@@ -575,6 +568,7 @@ public:
virtual RS::LightBakeMode light_get_bake_mode(RID p_light) override;
virtual uint32_t light_get_max_sdfgi_cascade(RID p_light) override;
virtual uint64_t light_get_version(RID p_light) const override;
+ virtual uint32_t light_get_cull_mask(RID p_light) const override;
Dependency *light_get_dependency(RID p_light) const;
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
index 5d845ce510..0ee9b28826 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
@@ -1906,7 +1906,7 @@ void TextureStorage::decal_set_cull_mask(RID p_decal, uint32_t p_layers) {
Decal *decal = decal_owner.get_or_null(p_decal);
ERR_FAIL_COND(!decal);
decal->cull_mask = p_layers;
- decal->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
+ decal->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_DECAL);
}
void TextureStorage::decal_set_distance_fade(RID p_decal, bool p_enabled, float p_begin, float p_length) {
@@ -1952,6 +1952,13 @@ AABB TextureStorage::decal_get_aabb(RID p_decal) const {
return AABB(-decal->size / 2, decal->size);
}
+uint32_t TextureStorage::decal_get_cull_mask(RID p_decal) const {
+ Decal *decal = decal_owner.get_or_null(p_decal);
+ ERR_FAIL_COND_V(!decal, 0);
+
+ return decal->cull_mask;
+}
+
Dependency *TextureStorage::decal_get_dependency(RID p_decal) {
Decal *decal = decal_owner.get_or_null(p_decal);
ERR_FAIL_COND_V(!decal, nullptr);
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.h b/servers/rendering/renderer_rd/storage_rd/texture_storage.h
index aeab3bf3cb..c16f5274ad 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.h
@@ -638,6 +638,7 @@ public:
}
virtual AABB decal_get_aabb(RID p_decal) const override;
+ virtual uint32_t decal_get_cull_mask(RID p_decal) const override;
Dependency *decal_get_dependency(RID p_decal);
/* DECAL INSTANCE API */
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index d696955800..7d2cd12959 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -1787,6 +1787,7 @@ void RendererSceneCull::_update_instance(Instance *p_instance) {
pair.pair_allocator = &pair_allocator;
pair.pair_pass = pair_pass;
pair.pair_mask = 0;
+ pair.cull_mask = 0xFFFFFFFF;
if ((1 << p_instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) {
pair.pair_mask |= 1 << RS::INSTANCE_LIGHT;
@@ -1807,12 +1808,14 @@ void RendererSceneCull::_update_instance(Instance *p_instance) {
pair.pair_mask |= (1 << RS::INSTANCE_VOXEL_GI);
pair.bvh2 = &p_instance->scenario->indexers[Scenario::INDEXER_VOLUMES];
}
+ pair.cull_mask = RSG::light_storage->light_get_cull_mask(p_instance->base);
} else if (geometry_instance_pair_mask & (1 << RS::INSTANCE_REFLECTION_PROBE) && (p_instance->base_type == RS::INSTANCE_REFLECTION_PROBE)) {
pair.pair_mask = RS::INSTANCE_GEOMETRY_MASK;
pair.bvh = &p_instance->scenario->indexers[Scenario::INDEXER_GEOMETRY];
} else if (geometry_instance_pair_mask & (1 << RS::INSTANCE_DECAL) && (p_instance->base_type == RS::INSTANCE_DECAL)) {
pair.pair_mask = RS::INSTANCE_GEOMETRY_MASK;
pair.bvh = &p_instance->scenario->indexers[Scenario::INDEXER_GEOMETRY];
+ pair.cull_mask = RSG::texture_storage->decal_get_cull_mask(p_instance->base);
} else if (p_instance->base_type == RS::INSTANCE_PARTICLES_COLLISION) {
pair.pair_mask = (1 << RS::INSTANCE_PARTICLES);
pair.bvh = &p_instance->scenario->indexers[Scenario::INDEXER_GEOMETRY];
diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h
index c3eec83d44..b3874ee7ae 100644
--- a/servers/rendering/renderer_scene_cull.h
+++ b/servers/rendering/renderer_scene_cull.h
@@ -733,11 +733,12 @@ public:
DynamicBVH *bvh2 = nullptr; //some may need to cull in two
uint32_t pair_mask;
uint64_t pair_pass;
+ uint32_t cull_mask = 0xFFFFFFFF; // Needed for decals and lights in the mobile and compatibility renderers.
_FORCE_INLINE_ bool operator()(void *p_data) {
Instance *p_instance = (Instance *)p_data;
- if (instance != p_instance && instance->transformed_aabb.intersects(p_instance->transformed_aabb) && (pair_mask & (1 << p_instance->base_type))) {
+ if (instance != p_instance && instance->transformed_aabb.intersects(p_instance->transformed_aabb) && (pair_mask & (1 << p_instance->base_type)) && (cull_mask & p_instance->layer_mask)) {
//test is more coarse in indexer
p_instance->pair_check = pair_pass;
InstancePair *pair = pair_allocator->alloc();
diff --git a/servers/rendering/storage/light_storage.h b/servers/rendering/storage/light_storage.h
index 9f3f5dd8e4..5bd4297179 100644
--- a/servers/rendering/storage/light_storage.h
+++ b/servers/rendering/storage/light_storage.h
@@ -84,6 +84,7 @@ public:
virtual RS::LightBakeMode light_get_bake_mode(RID p_light) = 0;
virtual uint32_t light_get_max_sdfgi_cascade(RID p_light) = 0;
virtual uint64_t light_get_version(RID p_light) const = 0;
+ virtual uint32_t light_get_cull_mask(RID p_light) const = 0;
/* LIGHT INSTANCE API */
diff --git a/servers/rendering/storage/texture_storage.h b/servers/rendering/storage/texture_storage.h
index 3a9034ad0d..227d44aa27 100644
--- a/servers/rendering/storage/texture_storage.h
+++ b/servers/rendering/storage/texture_storage.h
@@ -118,6 +118,7 @@ public:
virtual void decal_set_normal_fade(RID p_decal, float p_fade) = 0;
virtual AABB decal_get_aabb(RID p_decal) const = 0;
+ virtual uint32_t decal_get_cull_mask(RID p_decal) const = 0;
virtual void texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) = 0;
virtual void texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) = 0;