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--core/string/string_name.cpp59
-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/Node.xml2
-rw-r--r--doc/classes/Viewport.xml4
-rw-r--r--editor/editor_file_system.cpp80
-rw-r--r--editor/editor_file_system.h3
-rw-r--r--editor/editor_node.cpp28
-rw-r--r--editor/editor_node.h1
-rw-r--r--editor/editor_properties.cpp2
-rw-r--r--editor/editor_properties_array_dict.cpp2
-rw-r--r--editor/import/editor_import_plugin.cpp16
-rw-r--r--editor/import/editor_import_plugin.h3
-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.cpp25
-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/gltf/gltf_document.cpp63
-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.cpp23
-rw-r--r--platform/android/export/gradle_export_util.cpp6
-rw-r--r--platform/android/java/lib/res/values/strings.xml1
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java20
-rw-r--r--scene/animation/animation_player.cpp15
-rw-r--r--scene/animation/animation_tree.cpp4
-rw-r--r--scene/gui/control.cpp6
-rw-r--r--scene/main/canvas_item.cpp11
-rw-r--r--scene/main/canvas_item.h1
-rw-r--r--scene/main/viewport.cpp31
-rw-r--r--scene/main/viewport.h3
56 files changed, 512 insertions, 246 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/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/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/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/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..f6fe6c9f76 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);
}
}
}
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 914dab0254..19a0e49a12 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -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/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/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..cd1dcf9a78 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -2471,30 +2471,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 +4110,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 +4117,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 12c10642ec..8cfd48b52b 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/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/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..8cc2b1eb97 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
@@ -1056,6 +1057,15 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
Vector<bool> feature_required_list;
Vector<int> feature_versions;
+ String current_renderer = GLOBAL_GET("rendering/renderer/rendering_method.mobile");
+ bool has_vulkan = current_renderer == "forward_plus" || current_renderer == "mobile";
+ if (has_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
@@ -2373,6 +2383,19 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit
err += "\n";
}
+ String current_renderer = GLOBAL_GET("rendering/renderer/rendering_method.mobile");
+ bool uses_vulkan = current_renderer == "forward_plus" || current_renderer == "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;
}
diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp
index 5e71116c10..7eb595f48d 100644
--- a/platform/android/export/gradle_export_util.cpp
+++ b/platform/android/export/gradle_export_util.cpp
@@ -273,6 +273,12 @@ 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";
}
}
+
+ String current_renderer = GLOBAL_GET("rendering/renderer/rendering_method.mobile");
+ bool has_vulkan = current_renderer == "forward_plus" || current_renderer == "mobile";
+ if (has_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/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..6296ee2c22 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);
@@ -279,6 +279,11 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
if (renderer.equals("gl_compatibility")) {
mRenderView = new GodotGLRenderView(activity, this, xrMode, use_debug_opengl);
} else {
+ 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);
}
@@ -317,6 +322,17 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
return true;
}
+ /**
+ * 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/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 2e25d685d6..8087ac6250 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();
@@ -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..8b1a5e3aac 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -1546,6 +1546,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();
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index f09e4962a9..a930b8d972 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();
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..7091dd0388 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"
@@ -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);
@@ -2864,6 +2884,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);
@@ -3798,6 +3826,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 +3979,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");
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 2142aaaaef..4144eaabb9 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;