summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/csg/csg_shape.cpp24
-rw-r--r--modules/csg/csg_shape.h4
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml74
-rw-r--r--modules/gdscript/gdscript.cpp1
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp76
-rw-r--r--modules/gdscript/gdscript_analyzer.h11
-rw-r--r--modules/gdscript/gdscript_editor.cpp8
-rw-r--r--modules/gdscript/gdscript_parser.cpp84
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp17
-rw-r--r--modules/gdscript/gdscript_vm.cpp27
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.cpp1
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser/features/allow_id_similar_to_keyword_in_ascii.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser/features/allow_id_similar_to_keyword_in_ascii.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/allow_strings_as_comments.gd21
-rw-r--r--modules/gdscript/tests/scripts/parser/features/allow_strings_as_comments.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/allowed_keywords_as_identifiers.gd16
-rw-r--r--modules/gdscript/tests/scripts/parser/features/allowed_keywords_as_identifiers.out6
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/confusable_identifier.gd7
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/confusable_identifier.out6
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/standalone_expression.gd1
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/standalone_expression.out6
-rw-r--r--modules/gltf/editor/editor_scene_importer_blend.cpp7
-rw-r--r--modules/gltf/editor/editor_scene_importer_fbx.cpp7
-rw-r--r--modules/gltf/editor/editor_scene_importer_gltf.cpp24
-rw-r--r--modules/gltf/gltf_document.cpp1
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs10
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs8
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs3
-rw-r--r--modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs4
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs35
-rw-r--r--modules/multiplayer/multiplayer_spawner.cpp18
-rw-r--r--modules/multiplayer/scene_replication_interface.cpp6
-rw-r--r--modules/navigation/godot_navigation_server.cpp2
-rw-r--r--modules/webp/SCsub1
-rw-r--r--modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml2
-rw-r--r--modules/webxr/native/library_godot_webxr.js7
38 files changed, 362 insertions, 177 deletions
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
index 13c7a8202c..afb8e62eea 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -1255,6 +1255,30 @@ Vector3 CSGBox3D::get_size() const {
return size;
}
+#ifndef DISABLE_DEPRECATED
+// Kept for compatibility from 3.x to 4.0.
+bool CSGBox3D::_set(const StringName &p_name, const Variant &p_value) {
+ if (p_name == "width") {
+ size.x = p_value;
+ _make_dirty();
+ update_gizmos();
+ return true;
+ } else if (p_name == "height") {
+ size.y = p_value;
+ _make_dirty();
+ update_gizmos();
+ return true;
+ } else if (p_name == "depth") {
+ size.z = p_value;
+ _make_dirty();
+ update_gizmos();
+ return true;
+ } else {
+ return false;
+ }
+}
+#endif
+
void CSGBox3D::set_material(const Ref<Material> &p_material) {
material = p_material;
_make_dirty();
diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h
index 9012c37679..c244107bfb 100644
--- a/modules/csg/csg_shape.h
+++ b/modules/csg/csg_shape.h
@@ -248,6 +248,10 @@ class CSGBox3D : public CSGPrimitive3D {
protected:
static void _bind_methods();
+#ifndef DISABLE_DEPRECATED
+ // Kept for compatibility from 3.x to 4.0.
+ bool _set(const StringName &p_name, const Variant &p_value);
+#endif
public:
void set_size(const Vector3 &p_size);
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 923b2fe30d..0b7e4e50e6 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -4,7 +4,7 @@
Built-in GDScript functions.
</brief_description>
<description>
- A list of GDScript-specific utility functions accessed in any script.
+ A list of GDScript-specific utility functions and annotations accessible from any script.
For the list of the global functions and constants see [@GlobalScope].
</description>
<tutorials>
@@ -20,7 +20,7 @@
<description>
Returns a [Color] constructed from red ([param r8]), green ([param g8]), blue ([param b8]), and optionally alpha ([param a8]) integer channels, each divided by [code]255.0[/code] for their final value.
[codeblock]
- var red = Color8(255, 0, 0) # Same as Color(1, 0, 0)
+ var red = Color8(255, 0, 0) # Same as Color(1, 0, 0).
var dark_blue = Color8(0, 0, 51) # Same as Color(0, 0, 0.2).
var my_color = Color8(306, 255, 0, 102) # Same as Color(1.2, 1, 0, 0.4).
[/codeblock]
@@ -37,10 +37,10 @@
[codeblock]
# Imagine we always want speed to be between 0 and 20.
var speed = -10
- assert(speed &lt; 20) # True, the program will continue
- assert(speed &gt;= 0) # False, the program will stop
- assert(speed &gt;= 0 and speed &lt; 20) # You can also combine the two conditional statements in one check
- assert(speed &lt; 20, "the speed limit is 20") # Show a message
+ assert(speed &lt; 20) # True, the program will continue.
+ assert(speed &gt;= 0) # False, the program will stop.
+ assert(speed &gt;= 0 and speed &lt; 20) # You can also combine the two conditional statements in one check.
+ assert(speed &lt; 20, "the speed limit is 20") # Show a message.
[/codeblock]
</description>
</method>
@@ -140,7 +140,7 @@
<param index="0" name="path" type="String" />
<description>
Returns a [Resource] from the filesystem located at the absolute [param path]. Unless it's already referenced elsewhere (such as in another script or in the scene), the resource is loaded from disk on function call, which might cause a slight delay, especially when loading large scenes. To avoid unnecessary delays when loading something multiple times, either store the resource in a variable or use [method preload].
- [b]Note:[/b] Resource paths can be obtained by right-clicking on a resource in the FileSystem dock and choosing "Copy Path" or by dragging the file from the FileSystem dock into the script.
+ [b]Note:[/b] Resource paths can be obtained by right-clicking on a resource in the FileSystem dock and choosing "Copy Path", or by dragging the file from the FileSystem dock into the current script.
[codeblock]
# Load a scene called "main" located in the root of the project directory and cache it in a variable.
var main = load("res://main.tscn") # main will contain a PackedScene resource.
@@ -155,7 +155,7 @@
<param index="0" name="path" type="String" />
<description>
Returns a [Resource] from the filesystem located at [param path]. During run-time, the resource is loaded when the script is being parsed. This function effectively acts as a reference to that resource. Note that this function requires [param path] to be a constant [String]. If you want to load a resource from a dynamic/variable path, use [method load].
- [b]Note:[/b] Resource paths can be obtained by right clicking on a resource in the Assets Panel and choosing "Copy Path" or by dragging the file from the FileSystem dock into the script.
+ [b]Note:[/b] Resource paths can be obtained by right-clicking on a resource in the Assets Panel and choosing "Copy Path", or by dragging the file from the FileSystem dock into the current script.
[codeblock]
# Create instance of a scene.
var diamond = preload("res://diamond.tscn").instantiate()
@@ -259,10 +259,12 @@
<annotation name="@export">
<return type="void" />
<description>
- Mark the following property as exported (editable in the Inspector dock and saved to disk). To control the type of the exported property use the type hint notation.
+ Mark the following property as exported (editable in the Inspector dock and saved to disk). To control the type of the exported property, use the type hint notation.
[codeblock]
+ @export var string = ""
@export var int_number = 5
@export var float_number: float = 5
+ @export var image : Image
[/codeblock]
</description>
</annotation>
@@ -273,20 +275,20 @@
Define a new category for the following exported properties. This helps to organize properties in the Inspector dock.
See also [constant PROPERTY_USAGE_CATEGORY].
[codeblock]
- @export_category("My Properties")
- @export var number = 3
- @export var string = ""
+ @export_category("Statistics")
+ @export var hp = 30
+ @export var speed = 1.25
[/codeblock]
- [b]Note:[/b] Categories in the property list are supposed to indicate different base types, so the use of this annotation is not encouraged. See [annotation @export_group] and [annotation @export_subgroup] instead.
+ [b]Note:[/b] Categories in the Inspector dock's list usually divide properties coming from different classes (Node, Node2D, Sprite, etc.). For better clarity, it's recommended to use [annotation @export_group] and [annotation @export_subgroup], instead.
</description>
</annotation>
<annotation name="@export_color_no_alpha">
<return type="void" />
<description>
- Export a [Color] property without transparency (its alpha fixed as [code]1.0[/code]).
+ Export a [Color] property without allowing its transparency ([member Color.a]) to be edited.
See also [constant PROPERTY_HINT_COLOR_NO_ALPHA].
[codeblock]
- @export_color_no_alpha var modulate_color: Color
+ @export_color_no_alpha var dye_color : Color
[/codeblock]
</description>
</annotation>
@@ -296,7 +298,7 @@
Export a [String] property as a path to a directory. The path will be limited to the project folder and its subfolders. See [annotation @export_global_dir] to allow picking from the entire filesystem.
See also [constant PROPERTY_HINT_DIR].
[codeblock]
- @export_dir var sprite_folder: String
+ @export_dir var sprite_folder_path: String
[/codeblock]
</description>
</annotation>
@@ -343,8 +345,8 @@
If [param filter] is provided, only matching files will be available for picking.
See also [constant PROPERTY_HINT_FILE].
[codeblock]
- @export_file var sound_effect_file: String
- @export_file("*.txt") var notes_file: String
+ @export_file var sound_effect_path: String
+ @export_file("*.txt") var notes_path: String
[/codeblock]
</description>
</annotation>
@@ -436,10 +438,10 @@
<annotation name="@export_global_dir">
<return type="void" />
<description>
- Export a [String] property as a path to a directory. The path can be picked from the entire filesystem. See [annotation @export_dir] to limit it to the project folder and its subfolders.
+ Export a [String] property as an absolute path to a directory. The path can be picked from the entire filesystem. See [annotation @export_dir] to limit it to the project folder and its subfolders.
See also [constant PROPERTY_HINT_GLOBAL_DIR].
[codeblock]
- @export_global_dir var sprite_folder: String
+ @export_global_dir var sprite_folder_path: String
[/codeblock]
</description>
</annotation>
@@ -447,12 +449,12 @@
<return type="void" />
<param index="0" name="filter" type="String" default="&quot;&quot;" />
<description>
- Export a [String] property as a path to a file. The path can be picked from the entire filesystem. See [annotation @export_file] to limit it to the project folder and its subfolders.
+ Export a [String] property as an absolute path to a file. The path can be picked from the entire filesystem. See [annotation @export_file] to limit it to the project folder and its subfolders.
If [param filter] is provided, only matching files will be available for picking.
See also [constant PROPERTY_HINT_GLOBAL_FILE].
[codeblock]
- @export_global_file var sound_effect_file: String
- @export_global_file("*.txt") var notes_file: String
+ @export_global_file var sound_effect_path: String
+ @export_global_file("*.txt") var notes_path: String
[/codeblock]
</description>
</annotation>
@@ -466,13 +468,13 @@
Groups cannot be nested, use [annotation @export_subgroup] to add subgroups within groups.
See also [constant PROPERTY_USAGE_GROUP].
[codeblock]
- @export_group("My Properties")
- @export var number = 3
- @export var string = ""
+ @export_group("Racer Properties")
+ @export var nickname = "Nick"
+ @export var age = 26
- @export_group("Prefixed Properties", "prefix_")
- @export var prefix_number = 3
- @export var prefix_string = ""
+ @export_group("Car Properties", "car_")
+ @export var car_label = "Speedy"
+ @export var car_number = 3
@export_group("", "")
@export var ungrouped_number = 3
@@ -544,13 +546,13 @@
Define a new subgroup for the following exported properties. This helps to organize properties in the Inspector dock. Subgroups work exactly like groups, except they need a parent group to exist. See [annotation @export_group].
See also [constant PROPERTY_USAGE_SUBGROUP].
[codeblock]
- @export_group("My Properties")
- @export var number = 3
- @export var string = ""
+ @export_group("Racer Properties")
+ @export var nickname = "Nick"
+ @export var age = 26
- @export_subgroup("My Prefixed Properties", "prefix_")
- @export var prefix_number = 3
- @export var prefix_string = ""
+ @export_subgroup("Car Properties", "car_")
+ @export var car_label = "Speedy"
+ @export var car_number = 3
[/codeblock]
[b]Note:[/b] Subgroups cannot be nested, they only provide one extra level of depth. Just like the next group ends the previous group, so do the subsequent subgroups.
</description>
@@ -571,7 +573,7 @@
<annotation name="@onready">
<return type="void" />
<description>
- Mark the following property as assigned on [Node]'s ready state change. Values for these properties are not assigned immediately upon the node's creation, and instead are computed and stored right before [method Node._ready].
+ Mark the following property as assigned when the [Node] is ready. Values for these properties are not assigned immediately when the node is initialized ([method Object._init]), and instead are computed and stored right before [method Node._ready].
[codeblock]
@onready var character_name: Label = $Label
[/codeblock]
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index a876229276..fe79f37454 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -2578,7 +2578,6 @@ GDScriptLanguage::GDScriptLanguage() {
#ifdef DEBUG_ENABLED
GLOBAL_DEF("debug/gdscript/warnings/enable", true);
- GLOBAL_DEF("debug/gdscript/warnings/treat_warnings_as_errors", false);
GLOBAL_DEF("debug/gdscript/warnings/exclude_addons", true);
for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) {
GDScriptWarning::Code code = (GDScriptWarning::Code)i;
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 602d07d9a7..d0525be853 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -42,6 +42,11 @@
#include "gdscript_utility_functions.h"
#include "scene/resources/packed_scene.h"
+#if defined(TOOLS_ENABLED) && !defined(DISABLE_DEPRECATED)
+#define SUGGEST_GODOT4_RENAMES
+#include "editor/renames_map_3_to_4.h"
+#endif
+
#define UNNAMED_ENUM "<anonymous enum>"
#define ENUM_SEPARATOR "::"
@@ -1737,6 +1742,7 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
}
type.is_constant = is_constant;
+ type.is_read_only = false;
p_assignable->set_datatype(type);
}
@@ -2601,9 +2607,8 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
p_binary_op->set_datatype(result);
}
-#ifdef TOOLS_ENABLED
-#ifndef DISABLE_DEPRECATED
-const char *GDScriptAnalyzer::get_rename_from_map(const char *map[][2], String key) {
+#ifdef SUGGEST_GODOT4_RENAMES
+const char *get_rename_from_map(const char *map[][2], String key) {
for (int index = 0; map[index][0]; index++) {
if (map[index][0] == key) {
return map[index][1];
@@ -2614,39 +2619,39 @@ const char *GDScriptAnalyzer::get_rename_from_map(const char *map[][2], String k
// Checks if an identifier/function name has been renamed in Godot 4, uses ProjectConverter3To4 for rename map.
// Returns the new name if found, nullptr otherwise.
-const char *GDScriptAnalyzer::check_for_renamed_identifier(String identifier, GDScriptParser::Node::Type type) {
+const char *check_for_renamed_identifier(String identifier, GDScriptParser::Node::Type type) {
switch (type) {
case GDScriptParser::Node::IDENTIFIER: {
// Check properties
- const char *result = get_rename_from_map(ProjectConverter3To4::gdscript_properties_renames, identifier);
+ const char *result = get_rename_from_map(RenamesMap3To4::gdscript_properties_renames, identifier);
if (result) {
return result;
}
// Check enum values
- result = get_rename_from_map(ProjectConverter3To4::enum_renames, identifier);
+ result = get_rename_from_map(RenamesMap3To4::enum_renames, identifier);
if (result) {
return result;
}
// Check color constants
- result = get_rename_from_map(ProjectConverter3To4::color_renames, identifier);
+ result = get_rename_from_map(RenamesMap3To4::color_renames, identifier);
if (result) {
return result;
}
// Check type names
- result = get_rename_from_map(ProjectConverter3To4::class_renames, identifier);
+ result = get_rename_from_map(RenamesMap3To4::class_renames, identifier);
if (result) {
return result;
}
- return get_rename_from_map(ProjectConverter3To4::builtin_types_renames, identifier);
+ return get_rename_from_map(RenamesMap3To4::builtin_types_renames, identifier);
}
case GDScriptParser::Node::CALL: {
- const char *result = get_rename_from_map(ProjectConverter3To4::gdscript_function_renames, identifier);
+ const char *result = get_rename_from_map(RenamesMap3To4::gdscript_function_renames, identifier);
if (result) {
return result;
}
// Built-in Types are mistaken for function calls when the built-in type is not found.
// Check built-in types if function rename not found
- return get_rename_from_map(ProjectConverter3To4::builtin_types_renames, identifier);
+ return get_rename_from_map(RenamesMap3To4::builtin_types_renames, identifier);
}
// Signal references don't get parsed through the GDScriptAnalyzer. No support for signal rename hints.
default:
@@ -2654,8 +2659,7 @@ const char *GDScriptAnalyzer::check_for_renamed_identifier(String identifier, GD
return nullptr;
}
}
-#endif // DISABLE_DEPRECATED
-#endif // TOOLS_ENABLED
+#endif // SUGGEST_GODOT4_RENAMES
void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_await, bool p_is_root) {
bool all_is_constant = true;
@@ -3078,8 +3082,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
}
if (!found && (is_self || (base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::BUILTIN))) {
String base_name = is_self && !p_call->is_super ? "self" : base_type.to_string();
-#ifdef TOOLS_ENABLED
-#ifndef DISABLE_DEPRECATED
+#ifdef SUGGEST_GODOT4_RENAMES
String rename_hint = String();
if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) {
const char *renamed_function_name = check_for_renamed_identifier(p_call->function_name, p_call->type);
@@ -3088,12 +3091,9 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
}
}
push_error(vformat(R"*(Function "%s()" not found in base %s.%s)*", p_call->function_name, base_name, rename_hint), p_call->is_super ? p_call : p_call->callee);
-#else // !DISABLE_DEPRECATED
- push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee);
-#endif // DISABLE_DEPRECATED
#else
push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee);
-#endif
+#endif // SUGGEST_GODOT4_RENAMES
} else if (!found && (!p_call->is_super && base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::NATIVE && base_type.is_meta_type)) {
push_error(vformat(R"*(Static function "%s()" not found in base "%s".)*", p_call->function_name, base_type.native_type), p_call);
}
@@ -3283,8 +3283,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
p_identifier->reduced_value = result;
p_identifier->set_datatype(type_from_variant(result, p_identifier));
} else if (base.is_hard_type()) {
-#ifdef TOOLS_ENABLED
-#ifndef DISABLE_DEPRECATED
+#ifdef SUGGEST_GODOT4_RENAMES
String rename_hint = String();
if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) {
const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type);
@@ -3293,12 +3292,9 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
}
}
push_error(vformat(R"(Cannot find constant "%s" on base "%s".%s)", name, base.to_string(), rename_hint), p_identifier);
-#else // !DISABLE_DEPRECATED
- push_error(vformat(R"(Cannot find constant "%s" on base "%s".)", name, base.to_string()), p_identifier);
-#endif // DISABLE_DEPRECATED
#else
push_error(vformat(R"(Cannot find constant "%s" on base "%s".)", name, base.to_string()), p_identifier);
-#endif
+#endif // SUGGEST_GODOT4_RENAMES
}
} else {
switch (base.builtin_type) {
@@ -3327,8 +3323,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
}
}
if (base.is_hard_type()) {
-#ifdef TOOLS_ENABLED
-#ifndef DISABLE_DEPRECATED
+#ifdef SUGGEST_GODOT4_RENAMES
String rename_hint = String();
if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) {
const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type);
@@ -3337,12 +3332,9 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
}
}
push_error(vformat(R"(Cannot find property "%s" on base "%s".%s)", name, base.to_string(), rename_hint), p_identifier);
-#else // !DISABLE_DEPRECATED
- push_error(vformat(R"(Cannot find property "%s" on base "%s".)", name, base.to_string()), p_identifier);
-#endif // DISABLE_DEPRECATED
#else
push_error(vformat(R"(Cannot find property "%s" on base "%s".)", name, base.to_string()), p_identifier);
-#endif
+#endif // SUGGEST_GODOT4_RENAMES
}
}
}
@@ -3682,8 +3674,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
if (GDScriptUtilityFunctions::function_exists(name)) {
push_error(vformat(R"(Built-in function "%s" cannot be used as an identifier.)", name), p_identifier);
} else {
-#ifdef TOOLS_ENABLED
-#ifndef DISABLE_DEPRECATED
+#ifdef SUGGEST_GODOT4_RENAMES
String rename_hint = String();
if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) {
const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type);
@@ -3692,12 +3683,9 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
}
}
push_error(vformat(R"(Identifier "%s" not declared in the current scope.%s)", name, rename_hint), p_identifier);
-#else // !DISABLE_DEPRECATED
- push_error(vformat(R"(Identifier "%s" not declared in the current scope.)", name), p_identifier);
-#endif // DISABLE_DEPRECATED
#else
push_error(vformat(R"(Identifier "%s" not declared in the current scope.)", name), p_identifier);
-#endif
+#endif // SUGGEST_GODOT4_RENAMES
}
GDScriptParser::DataType dummy;
dummy.kind = GDScriptParser::DataType::VARIANT;
@@ -4291,11 +4279,15 @@ Variant GDScriptAnalyzer::make_variable_default_value(GDScriptParser::VariableNo
}
} else {
GDScriptParser::DataType datatype = p_variable->get_datatype();
- if (datatype.is_hard_type() && datatype.kind == GDScriptParser::DataType::BUILTIN && datatype.builtin_type != Variant::OBJECT) {
- if (datatype.builtin_type == Variant::ARRAY && datatype.has_container_element_type()) {
- result = make_array_from_element_datatype(datatype.get_container_element_type());
- } else {
- VariantInternal::initialize(&result, datatype.builtin_type);
+ if (datatype.is_hard_type()) {
+ if (datatype.kind == GDScriptParser::DataType::BUILTIN && datatype.builtin_type != Variant::OBJECT) {
+ if (datatype.builtin_type == Variant::ARRAY && datatype.has_container_element_type()) {
+ result = make_array_from_element_datatype(datatype.get_container_element_type());
+ } else {
+ VariantInternal::initialize(&result, datatype.builtin_type);
+ }
+ } else if (datatype.kind == GDScriptParser::DataType::ENUM) {
+ result = 0;
}
}
}
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
index a4c84db6b9..cdeba374c7 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -37,10 +37,6 @@
#include "gdscript_cache.h"
#include "gdscript_parser.h"
-#ifdef TOOLS_ENABLED
-#include "editor/project_converter_3_to_4.h"
-#endif
-
class GDScriptAnalyzer {
GDScriptParser *parser = nullptr;
HashMap<String, Ref<GDScriptParserRef>> depended_parsers;
@@ -137,13 +133,6 @@ class GDScriptAnalyzer {
bool is_shadowing(GDScriptParser::IdentifierNode *p_local, const String &p_context);
#endif
-#ifdef TOOLS_ENABLED
-#ifndef DISABLE_DEPRECATED
- const char *get_rename_from_map(const char *map[][2], String key);
- const char *check_for_renamed_identifier(String identifier, GDScriptParser::Node::Type type);
-#endif // DISABLE_DEPRECATED
-#endif // TOOLS_ENABLED
-
public:
Error resolve_inheritance();
Error resolve_interface();
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 3543c0a79f..8cfd48b52b 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -3491,14 +3491,6 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
break;
}
- if (context.current_class) {
- if (context.type != GDScriptParser::COMPLETION_SUPER_METHOD) {
- base.type = context.current_class->get_datatype();
- } else {
- base.type = context.current_class->base_type;
- }
- }
-
if (_lookup_symbol_from_base(base.type, p_symbol, is_function, r_result) == OK) {
return OK;
}
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index c6e4222213..b5cb5a4680 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -175,7 +175,7 @@ void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_
warning.leftmost_column = p_source->leftmost_column;
warning.rightmost_column = p_source->rightmost_column;
- if (warn_level == GDScriptWarning::WarnLevel::ERROR || bool(GLOBAL_GET("debug/gdscript/warnings/treat_warnings_as_errors"))) {
+ if (warn_level == GDScriptWarning::WarnLevel::ERROR) {
push_error(warning.get_message() + String(" (Warning treated as error.)"), p_source);
return;
}
@@ -483,24 +483,34 @@ void GDScriptParser::parse_program() {
current_class = head;
bool can_have_class_or_extends = true;
- while (match(GDScriptTokenizer::Token::ANNOTATION)) {
- AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL);
- if (annotation != nullptr) {
- if (annotation->applies_to(AnnotationInfo::SCRIPT)) {
- // `@icon` needs to be applied in the parser. See GH-72444.
- if (annotation->name == SNAME("@icon")) {
- annotation->apply(this, head);
+ while (!check(GDScriptTokenizer::Token::TK_EOF)) {
+ if (match(GDScriptTokenizer::Token::ANNOTATION)) {
+ AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL);
+ if (annotation != nullptr) {
+ if (annotation->applies_to(AnnotationInfo::SCRIPT)) {
+ // `@icon` needs to be applied in the parser. See GH-72444.
+ if (annotation->name == SNAME("@icon")) {
+ annotation->apply(this, head);
+ } else {
+ head->annotations.push_back(annotation);
+ }
} else {
- head->annotations.push_back(annotation);
+ annotation_stack.push_back(annotation);
+ // This annotation must appear after script-level annotations
+ // and class_name/extends (ex: could be @onready or @export),
+ // so we stop looking for script-level stuff.
+ can_have_class_or_extends = false;
+ break;
}
- } else {
- annotation_stack.push_back(annotation);
- // This annotation must appear after script-level annotations
- // and class_name/extends (ex: could be @onready or @export),
- // so we stop looking for script-level stuff.
- can_have_class_or_extends = false;
- break;
}
+ } else if (check(GDScriptTokenizer::Token::LITERAL) && current.literal.get_type() == Variant::STRING) {
+ // Allow strings in class body as multiline comments.
+ advance();
+ if (!match(GDScriptTokenizer::Token::NEWLINE)) {
+ push_error("Expected newline after comment string.");
+ }
+ } else {
+ break;
}
}
@@ -524,6 +534,16 @@ void GDScriptParser::parse_program() {
end_statement("superclass");
}
break;
+ case GDScriptTokenizer::Token::LITERAL:
+ if (current.literal.get_type() == Variant::STRING) {
+ // Allow strings in class body as multiline comments.
+ advance();
+ if (!match(GDScriptTokenizer::Token::NEWLINE)) {
+ push_error("Expected newline after comment string.");
+ }
+ break;
+ }
+ [[fallthrough]];
default:
// No tokens are allowed between script annotations and class/extends.
can_have_class_or_extends = false;
@@ -829,6 +849,16 @@ void GDScriptParser::parse_class_body(bool p_is_multiline) {
case GDScriptTokenizer::Token::DEDENT:
class_end = true;
break;
+ case GDScriptTokenizer::Token::LITERAL:
+ if (current.literal.get_type() == Variant::STRING) {
+ // Allow strings in class body as multiline comments.
+ advance();
+ if (!match(GDScriptTokenizer::Token::NEWLINE)) {
+ push_error("Expected newline after comment string.");
+ }
+ break;
+ }
+ [[fallthrough]];
default:
// Display a completion with identifiers.
make_completion_context(COMPLETION_IDENTIFIER, nullptr);
@@ -1675,6 +1705,12 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
// Standalone lambdas can't be used, so make this an error.
push_error("Standalone lambdas cannot be accessed. Consider assigning it to a variable.", expression);
break;
+ case Node::LITERAL:
+ if (static_cast<GDScriptParser::LiteralNode *>(expression)->value.get_type() == Variant::STRING) {
+ // Allow strings as multiline comments.
+ break;
+ }
+ [[fallthrough]];
default:
push_warning(expression, GDScriptWarning::STANDALONE_EXPRESSION);
}
@@ -2145,7 +2181,12 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_pr
make_completion_context(COMPLETION_IDENTIFIER, nullptr);
GDScriptTokenizer::Token token = current;
- ParseFunction prefix_rule = get_rule(token.type)->prefix;
+ GDScriptTokenizer::Token::Type token_type = token.type;
+ if (token.is_identifier()) {
+ // Allow keywords that can be treated as identifiers.
+ token_type = GDScriptTokenizer::Token::IDENTIFIER;
+ }
+ ParseFunction prefix_rule = get_rule(token_type)->prefix;
if (prefix_rule == nullptr) {
// Expected expression. Let the caller give the proper error message.
@@ -3010,7 +3051,14 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p
path_state = PATH_STATE_NODE_NAME;
} else if (current.is_node_name()) {
advance();
- get_node->full_path += previous.get_identifier();
+ String identifier = previous.get_identifier();
+#ifdef DEBUG_ENABLED
+ // Check spoofing.
+ if (TS->has_feature(TextServer::FEATURE_UNICODE_SECURITY) && TS->spoof_check(identifier)) {
+ push_warning(get_node, GDScriptWarning::CONFUSABLE_IDENTIFIER, identifier);
+ }
+#endif
+ get_node->full_path += identifier;
path_state = PATH_STATE_NODE_NAME;
} else if (!check(GDScriptTokenizer::Token::SLASH) && !check(GDScriptTokenizer::Token::PERCENT)) {
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index d7f1114fd3..d586380c41 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -168,7 +168,11 @@ bool GDScriptTokenizer::Token::is_identifier() const {
switch (type) {
case IDENTIFIER:
case MATCH: // Used in String.match().
- case CONST_INF: // Used in Vector{2,3,4}.INF
+ // Allow constants to be treated as regular identifiers.
+ case CONST_PI:
+ case CONST_INF:
+ case CONST_NAN:
+ case CONST_TAU:
return true;
default:
return false;
@@ -188,6 +192,10 @@ bool GDScriptTokenizer::Token::is_node_name() const {
case CLASS_NAME:
case CLASS:
case CONST:
+ case CONST_PI:
+ case CONST_INF:
+ case CONST_NAN:
+ case CONST_TAU:
case CONTINUE:
case ELIF:
case ELSE:
@@ -530,9 +538,12 @@ void GDScriptTokenizer::make_keyword_list() {
#endif // DEBUG_ENABLED
GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() {
+ bool only_ascii = _peek(-1) < 128;
+
// Consume all identifier characters.
while (is_unicode_identifier_continue(_peek())) {
- _advance();
+ char32_t c = _advance();
+ only_ascii = only_ascii && c < 128;
}
int len = _current - _start;
@@ -587,7 +598,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() {
#ifdef DEBUG_ENABLED
// Additional checks for identifiers but only in debug and if it's available in TextServer.
- if (TS->has_feature(TextServer::FEATURE_UNICODE_SECURITY)) {
+ if (!only_ascii && TS->has_feature(TextServer::FEATURE_UNICODE_SECURITY)) {
int64_t confusable = TS->is_confusable(name, keyword_list);
if (confusable >= 0) {
push_error(vformat(R"(Identifier "%s" is visually similar to the GDScript keyword "%s" and thus not allowed.)", name, keyword_list[confusable]));
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index 6c26e226a5..7a11ea52f0 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -447,6 +447,9 @@ void (*type_init_function_table[])(Variant *) = {
#define OP_GET_BASIS get_basis
#define OP_GET_RID get_rid
+#define METHOD_CALL_ON_NULL_VALUE_ERROR(method_pointer) "Cannot call method '" + (method_pointer)->get_name() + "' on a null value."
+#define METHOD_CALL_ON_FREED_INSTANCE_ERROR(method_pointer) "Cannot call method '" + (method_pointer)->get_name() + "' on a previously freed instance."
+
Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_args, int p_argcount, Callable::CallError &r_err, CallState *p_state) {
OPCODES_TABLE;
@@ -1675,10 +1678,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
bool freed = false;
Object *base_obj = base->get_validated_object_with_check(freed);
if (freed) {
- err_text = "Trying to call a function on a previously freed instance.";
+ err_text = METHOD_CALL_ON_FREED_INSTANCE_ERROR(method);
OPCODE_BREAK;
} else if (!base_obj) {
- err_text = "Trying to call a function on a null value.";
+ err_text = METHOD_CALL_ON_NULL_VALUE_ERROR(method);
OPCODE_BREAK;
}
#else
@@ -1839,10 +1842,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
bool freed = false; \
Object *base_obj = base->get_validated_object_with_check(freed); \
if (freed) { \
- err_text = "Trying to call a function on a previously freed instance."; \
+ err_text = METHOD_CALL_ON_FREED_INSTANCE_ERROR(method); \
OPCODE_BREAK; \
} else if (!base_obj) { \
- err_text = "Trying to call a function on a null value."; \
+ err_text = METHOD_CALL_ON_NULL_VALUE_ERROR(method); \
OPCODE_BREAK; \
} \
const void **argptrs = call_args_ptr; \
@@ -1941,10 +1944,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
bool freed = false;
Object *base_obj = base->get_validated_object_with_check(freed);
if (freed) {
- err_text = "Trying to call a function on a previously freed instance.";
+ err_text = METHOD_CALL_ON_FREED_INSTANCE_ERROR(method);
OPCODE_BREAK;
} else if (!base_obj) {
- err_text = "Trying to call a function on a null value.";
+ err_text = METHOD_CALL_ON_NULL_VALUE_ERROR(method);
OPCODE_BREAK;
}
#else
@@ -1969,7 +1972,13 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
VariantInternal::initialize(ret, Variant::OBJECT);
Object **ret_opaque = VariantInternal::get_object(ret);
method->ptrcall(base_obj, argptrs, ret_opaque);
- VariantInternal::update_object_id(ret);
+ if (method->is_return_type_raw_object_ptr()) {
+ // The Variant has to participate in the ref count since the method returns a raw Object *.
+ VariantInternal::object_assign(ret, *ret_opaque);
+ } else {
+ // The method, in case it returns something, returns an already encapsulated object.
+ VariantInternal::update_object_id(ret);
+ }
#ifdef DEBUG_ENABLED
if (GDScriptLanguage::get_singleton()->profiling) {
@@ -1996,10 +2005,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
bool freed = false;
Object *base_obj = base->get_validated_object_with_check(freed);
if (freed) {
- err_text = "Trying to call a function on a previously freed instance.";
+ err_text = METHOD_CALL_ON_FREED_INSTANCE_ERROR(method);
OPCODE_BREAK;
} else if (!base_obj) {
- err_text = "Trying to call a function on a null value.";
+ err_text = METHOD_CALL_ON_NULL_VALUE_ERROR(method);
OPCODE_BREAK;
}
#else
diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp
index b9e6921034..35fbdca949 100644
--- a/modules/gdscript/language_server/gdscript_text_document.cpp
+++ b/modules/gdscript/language_server/gdscript_text_document.cpp
@@ -108,6 +108,7 @@ void GDScriptTextDocument::didSave(const Variant &p_param) {
scr->reload(true);
}
scr->update_exports();
+ ScriptEditor::get_singleton()->reload_scripts(true);
ScriptEditor::get_singleton()->update_docs_from_script(scr);
}
}
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 092ae49d00..7416ecd87a 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd
@@ -86,7 +86,8 @@ func test():
var typed_int := 556
var converted_floats: Array[float] = [typed_int]
- assert(str(converted_floats) == '[556]')
+ converted_floats.push_back(498)
+ assert(str(converted_floats) == '[556, 498]')
assert(converted_floats.get_typed_builtin() == TYPE_FLOAT)
diff --git a/modules/gdscript/tests/scripts/parser/features/allow_id_similar_to_keyword_in_ascii.gd b/modules/gdscript/tests/scripts/parser/features/allow_id_similar_to_keyword_in_ascii.gd
new file mode 100644
index 0000000000..390d314b94
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/allow_id_similar_to_keyword_in_ascii.gd
@@ -0,0 +1,3 @@
+func test():
+ var P1 = "ok" # Technically it is visually similar to keyword "PI" but allowed since it's in ASCII range.
+ print(P1)
diff --git a/modules/gdscript/tests/scripts/parser/features/allow_id_similar_to_keyword_in_ascii.out b/modules/gdscript/tests/scripts/parser/features/allow_id_similar_to_keyword_in_ascii.out
new file mode 100644
index 0000000000..1b47ed10dc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/allow_id_similar_to_keyword_in_ascii.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+ok
diff --git a/modules/gdscript/tests/scripts/parser/features/allow_strings_as_comments.gd b/modules/gdscript/tests/scripts/parser/features/allow_strings_as_comments.gd
new file mode 100644
index 0000000000..3ecd65ad9c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/allow_strings_as_comments.gd
@@ -0,0 +1,21 @@
+"""
+This is a comment.
+"""
+
+@tool
+
+"""
+This is also a comment.
+"""
+
+extends RefCounted
+
+'''
+This is a comment too.
+'''
+
+func test():
+ """
+ This too is a comment.
+ """
+ print("ok")
diff --git a/modules/gdscript/tests/scripts/parser/features/allow_strings_as_comments.out b/modules/gdscript/tests/scripts/parser/features/allow_strings_as_comments.out
new file mode 100644
index 0000000000..1b47ed10dc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/allow_strings_as_comments.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+ok
diff --git a/modules/gdscript/tests/scripts/parser/features/allowed_keywords_as_identifiers.gd b/modules/gdscript/tests/scripts/parser/features/allowed_keywords_as_identifiers.gd
new file mode 100644
index 0000000000..7e1982597c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/allowed_keywords_as_identifiers.gd
@@ -0,0 +1,16 @@
+func test():
+ # The following keywords are allowed as identifiers:
+ var match = "match"
+ print(match)
+
+ var PI = "PI"
+ print(PI)
+
+ var INF = "INF"
+ print(INF)
+
+ var NAN = "NAN"
+ print(NAN)
+
+ var TAU = "TAU"
+ print(TAU)
diff --git a/modules/gdscript/tests/scripts/parser/features/allowed_keywords_as_identifiers.out b/modules/gdscript/tests/scripts/parser/features/allowed_keywords_as_identifiers.out
new file mode 100644
index 0000000000..aae2ae13d5
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/allowed_keywords_as_identifiers.out
@@ -0,0 +1,6 @@
+GDTEST_OK
+match
+PI
+INF
+NAN
+TAU
diff --git a/modules/gdscript/tests/scripts/parser/warnings/confusable_identifier.gd b/modules/gdscript/tests/scripts/parser/warnings/confusable_identifier.gd
index e2caac8ffd..41b38c4bba 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/confusable_identifier.gd
+++ b/modules/gdscript/tests/scripts/parser/warnings/confusable_identifier.gd
@@ -1,5 +1,12 @@
+extends Node
+
func test():
var port = 0 # Only latin characters.
var pοrt = 1 # The "ο" is Greek omicron.
prints(port, pοrt)
+
+# Do not call this since nodes aren't in the tree. It is just a parser check.
+func nodes():
+ var _node1 = $port # Only latin characters.
+ var _node2 = $pοrt # The "ο" is Greek omicron.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/confusable_identifier.out b/modules/gdscript/tests/scripts/parser/warnings/confusable_identifier.out
index c483396443..c189204285 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/confusable_identifier.out
+++ b/modules/gdscript/tests/scripts/parser/warnings/confusable_identifier.out
@@ -1,6 +1,10 @@
GDTEST_OK
>> WARNING
->> Line: 3
+>> Line: 5
+>> CONFUSABLE_IDENTIFIER
+>> The identifier "pοrt" has misleading characters and might be confused with something else.
+>> WARNING
+>> Line: 12
>> CONFUSABLE_IDENTIFIER
>> The identifier "pοrt" has misleading characters and might be confused with something else.
0 1
diff --git a/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.gd b/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.gd
index 18ea260fa2..dc4223ec2d 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.gd
+++ b/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.gd
@@ -1,6 +1,5 @@
func test():
# The following statements should all be reported as standalone expressions:
- "This is a standalone expression"
1234
0.0 + 0.0
Color(1, 1, 1)
diff --git a/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.out b/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.out
index 99ec87438e..a2c67a6e51 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.out
+++ b/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.out
@@ -8,14 +8,10 @@ GDTEST_OK
>> STANDALONE_EXPRESSION
>> Standalone expression (the line has no effect).
>> WARNING
->> Line: 5
+>> Line: 6
>> STANDALONE_EXPRESSION
>> Standalone expression (the line has no effect).
>> WARNING
>> Line: 7
>> STANDALONE_EXPRESSION
>> Standalone expression (the line has no effect).
->> WARNING
->> Line: 8
->> STANDALONE_EXPRESSION
->> Standalone expression (the line has no effect).
diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp
index 520f33261a..7dcdc8e7cf 100644
--- a/modules/gltf/editor/editor_scene_importer_blend.cpp
+++ b/modules/gltf/editor/editor_scene_importer_blend.cpp
@@ -214,7 +214,14 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_
}
return nullptr;
}
+
+#ifndef DISABLE_DEPRECATED
+ bool trimming = p_options.has("animation/trimming") ? (bool)p_options["animation/trimming"] : false;
+ bool remove_immutable = p_options.has("animation/remove_immutable_tracks") ? (bool)p_options["animation/remove_immutable_tracks"] : true;
+ return gltf->generate_scene(state, (float)p_options["animation/fps"], trimming, remove_immutable);
+#else
return gltf->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"], (bool)p_options["animation/remove_immutable_tracks"]);
+#endif
}
Variant EditorSceneFormatImporterBlend::get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option,
diff --git a/modules/gltf/editor/editor_scene_importer_fbx.cpp b/modules/gltf/editor/editor_scene_importer_fbx.cpp
index d829630032..f8f458fcc7 100644
--- a/modules/gltf/editor/editor_scene_importer_fbx.cpp
+++ b/modules/gltf/editor/editor_scene_importer_fbx.cpp
@@ -100,7 +100,14 @@ Node *EditorSceneFormatImporterFBX::import_scene(const String &p_path, uint32_t
}
return nullptr;
}
+
+#ifndef DISABLE_DEPRECATED
+ bool trimming = p_options.has("animation/trimming") ? (bool)p_options["animation/trimming"] : false;
+ bool remove_immutable = p_options.has("animation/remove_immutable_tracks") ? (bool)p_options["animation/remove_immutable_tracks"] : true;
+ return gltf->generate_scene(state, (float)p_options["animation/fps"], trimming, remove_immutable);
+#else
return gltf->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"], (bool)p_options["animation/remove_immutable_tracks"]);
+#endif
}
Variant EditorSceneFormatImporterFBX::get_option_visibility(const String &p_path, bool p_for_animation,
diff --git a/modules/gltf/editor/editor_scene_importer_gltf.cpp b/modules/gltf/editor/editor_scene_importer_gltf.cpp
index 012a144d52..5339275439 100644
--- a/modules/gltf/editor/editor_scene_importer_gltf.cpp
+++ b/modules/gltf/editor/editor_scene_importer_gltf.cpp
@@ -47,15 +47,15 @@ void EditorSceneFormatImporterGLTF::get_extensions(List<String> *r_extensions) c
Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t p_flags,
const HashMap<StringName, Variant> &p_options,
List<String> *r_missing_deps, Error *r_err) {
- Ref<GLTFDocument> doc;
- doc.instantiate();
+ Ref<GLTFDocument> gltf;
+ gltf.instantiate();
Ref<GLTFState> state;
state.instantiate();
if (p_options.has("gltf/embedded_image_handling")) {
int32_t enum_option = p_options["gltf/embedded_image_handling"];
state->set_handle_binary_image(enum_option);
}
- Error err = doc->append_from_file(p_path, state, p_flags);
+ Error err = gltf->append_from_file(p_path, state, p_flags);
if (err != OK) {
if (r_err) {
*r_err = err;
@@ -67,21 +67,11 @@ Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t
}
#ifndef DISABLE_DEPRECATED
- if (p_options.has("animation/trimming")) {
- if (p_options.has("animation/remove_immutable_tracks")) {
- return doc->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"], (bool)p_options["animation/remove_immutable_tracks"]);
- } else {
- return doc->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"], true);
- }
- } else {
- if (p_options.has("animation/remove_immutable_tracks")) {
- return doc->generate_scene(state, (float)p_options["animation/fps"], false, (bool)p_options["animation/remove_immutable_tracks"]);
- } else {
- return doc->generate_scene(state, (float)p_options["animation/fps"], false, true);
- }
- }
+ bool trimming = p_options.has("animation/trimming") ? (bool)p_options["animation/trimming"] : false;
+ bool remove_immutable = p_options.has("animation/remove_immutable_tracks") ? (bool)p_options["animation/remove_immutable_tracks"] : true;
+ return gltf->generate_scene(state, (float)p_options["animation/fps"], trimming, remove_immutable);
#else
- return doc->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"], (bool)p_options["animation/remove_immutable_tracks"]);
+ return gltf->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"], (bool)p_options["animation/remove_immutable_tracks"]);
#endif
}
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 028028a103..e3ba290eb2 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -3285,7 +3285,6 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p
tex.instantiate();
tex->set_name(img->get_name());
tex->set_keep_compressed_buffer(true);
- p_state->source_images.push_back(img);
tex->create_from_image(img, PortableCompressedTexture2D::COMPRESSION_MODE_BASIS_UNIVERSAL);
p_state->images.push_back(tex);
p_state->source_images.push_back(img);
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
index 1e4fd2f09a..7c02f29606 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
@@ -12,6 +12,7 @@ namespace GodotTools.Build
public sealed partial class BuildInfo : RefCounted // TODO Remove RefCounted once we have proper serialization
{
public string Solution { get; private set; }
+ public string Project { get; private set; }
public string Configuration { get; private set; }
public string? RuntimeIdentifier { get; private set; }
public string? PublishOutputDir { get; private set; }
@@ -28,6 +29,7 @@ namespace GodotTools.Build
{
return obj is BuildInfo other &&
other.Solution == Solution &&
+ other.Project == Project &&
other.Configuration == Configuration && other.RuntimeIdentifier == RuntimeIdentifier &&
other.PublishOutputDir == PublishOutputDir && other.Restore == Restore &&
other.Rebuild == Rebuild && other.OnlyClean == OnlyClean &&
@@ -41,6 +43,7 @@ namespace GodotTools.Build
{
int hash = 17;
hash = (hash * 29) + Solution.GetHashCode();
+ hash = (hash * 29) + Project.GetHashCode();
hash = (hash * 29) + Configuration.GetHashCode();
hash = (hash * 29) + (RuntimeIdentifier?.GetHashCode() ?? 0);
hash = (hash * 29) + (PublishOutputDir?.GetHashCode() ?? 0);
@@ -57,22 +60,25 @@ namespace GodotTools.Build
private BuildInfo()
{
Solution = string.Empty;
+ Project = string.Empty;
Configuration = string.Empty;
}
- public BuildInfo(string solution, string configuration, bool restore, bool rebuild, bool onlyClean)
+ public BuildInfo(string solution, string project, string configuration, bool restore, bool rebuild, bool onlyClean)
{
Solution = solution;
+ Project = project;
Configuration = configuration;
Restore = restore;
Rebuild = rebuild;
OnlyClean = onlyClean;
}
- public BuildInfo(string solution, string configuration, string runtimeIdentifier,
+ public BuildInfo(string solution, string project, string configuration, string runtimeIdentifier,
string publishOutputDir, bool restore, bool rebuild, bool onlyClean)
{
Solution = solution;
+ Project = project;
Configuration = configuration;
RuntimeIdentifier = runtimeIdentifier;
PublishOutputDir = publishOutputDir;
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
index 349f9d0cb8..ed3a4c6e26 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
@@ -262,7 +262,7 @@ namespace GodotTools.Build
bool onlyClean = false
)
{
- var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, configuration,
+ var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, GodotSharpDirs.ProjectCsProjPath, configuration,
restore: true, rebuild, onlyClean);
// If a platform was not specified, try determining the current one. If that fails, let MSBuild auto-detect it.
@@ -282,7 +282,7 @@ namespace GodotTools.Build
[DisallowNull] string publishOutputDir
)
{
- var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, configuration,
+ var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, GodotSharpDirs.ProjectCsProjPath, configuration,
runtimeIdentifier, publishOutputDir, restore: true, rebuild: false, onlyClean: false);
buildInfo.CustomProperties.Add($"GodotTargetPlatform={platform}");
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
index d6549c1b70..d550c36b82 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
@@ -139,8 +139,8 @@ namespace GodotTools.Build
// `dotnet clean` / `dotnet build` commands
arguments.Add(buildInfo.OnlyClean ? "clean" : "build");
- // Solution
- arguments.Add(buildInfo.Solution);
+ // C# Project
+ arguments.Add(buildInfo.Project);
// `dotnet clean` doesn't recognize these options
if (!buildInfo.OnlyClean)
@@ -180,8 +180,8 @@ namespace GodotTools.Build
{
arguments.Add("publish"); // `dotnet publish` command
- // Solution
- arguments.Add(buildInfo.Solution);
+ // C# Project
+ arguments.Add(buildInfo.Project);
// Restore
// `dotnet publish` restores by default, unless requested not to
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index 43ead4af69..060c01b3f9 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -272,8 +272,7 @@ namespace GodotTools
}
}
- string resourcePath = ProjectSettings.GlobalizePath("res://");
- args.Add(resourcePath);
+ args.Add(Path.GetDirectoryName(GodotSharpDirs.ProjectSlnPath));
string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath);
diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs
index 3226ca79e5..6b000cc89b 100644
--- a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs
+++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs
@@ -168,7 +168,9 @@ using Godot.NativeInterop;
{
var parameter = callback.Parameters[i];
- source.Append(parameter.ToDisplayString());
+ AppendRefKind(source, parameter.RefKind);
+ source.Append(' ');
+ source.Append(parameter.Type.FullQualifiedNameIncludeGlobal());
source.Append(' ');
source.Append(parameter.Name);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs
index 027eab30fc..79030c79cc 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs
@@ -1,17 +1,44 @@
using System;
using System.Collections.Concurrent;
-using System.Collections.Generic;
using System.Threading;
+using System.Threading.Tasks;
namespace Godot
{
public sealed class GodotSynchronizationContext : SynchronizationContext, IDisposable
{
- private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> _queue = new();
+ private readonly BlockingCollection<(SendOrPostCallback Callback, object State)> _queue = new();
+
+ public override void Send(SendOrPostCallback d, object state)
+ {
+ // Shortcut if we're already on this context
+ // Also necessary to avoid a deadlock, since Send is blocking
+ if (Current == this)
+ {
+ d(state);
+ return;
+ }
+
+ var source = new TaskCompletionSource();
+
+ _queue.Add((st =>
+ {
+ try
+ {
+ d(st);
+ }
+ finally
+ {
+ source.SetResult();
+ }
+ }, state));
+
+ source.Task.Wait();
+ }
public override void Post(SendOrPostCallback d, object state)
{
- _queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state));
+ _queue.Add((d, state));
}
/// <summary>
@@ -21,7 +48,7 @@ namespace Godot
{
while (_queue.TryTake(out var workItem))
{
- workItem.Key(workItem.Value);
+ workItem.Callback(workItem.State);
}
}
diff --git a/modules/multiplayer/multiplayer_spawner.cpp b/modules/multiplayer/multiplayer_spawner.cpp
index 0aa54b69f9..4b1b6b541d 100644
--- a/modules/multiplayer/multiplayer_spawner.cpp
+++ b/modules/multiplayer/multiplayer_spawner.cpp
@@ -103,6 +103,15 @@ void MultiplayerSpawner::add_spawnable_scene(const String &p_path) {
ERR_FAIL_COND(!FileAccess::exists(p_path));
}
spawnable_scenes.push_back(sc);
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
+#endif
+ Node *node = get_spawn_node();
+ if (spawnable_scenes.size() == 1 && node && !node->is_connected("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added))) {
+ node->connect("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added));
+ }
}
int MultiplayerSpawner::get_spawnable_scene_count() const {
@@ -116,6 +125,15 @@ String MultiplayerSpawner::get_spawnable_scene(int p_idx) const {
void MultiplayerSpawner::clear_spawnable_scenes() {
spawnable_scenes.clear();
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
+#endif
+ Node *node = get_spawn_node();
+ if (node && node->is_connected("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added))) {
+ node->disconnect("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added));
+ }
}
Vector<String> MultiplayerSpawner::_get_spawnable_scenes() const {
diff --git a/modules/multiplayer/scene_replication_interface.cpp b/modules/multiplayer/scene_replication_interface.cpp
index 3466cb10df..68b6bc4a24 100644
--- a/modules/multiplayer/scene_replication_interface.cpp
+++ b/modules/multiplayer/scene_replication_interface.cpp
@@ -742,6 +742,7 @@ Error SceneReplicationInterface::on_sync_receive(int p_from, const uint8_t *p_bu
ofs += 4;
uint32_t size = decode_uint32(&p_buffer[ofs]);
ofs += 4;
+ ERR_FAIL_COND_V(size > uint32_t(p_buffer_len - ofs), ERR_INVALID_DATA);
MultiplayerSynchronizer *sync = nullptr;
if (net_id & 0x80000000) {
sync = Object::cast_to<MultiplayerSynchronizer>(multiplayer->get_path_cache()->get_cached_object(p_from, net_id & 0x7FFFFFFF));
@@ -756,14 +757,15 @@ Error SceneReplicationInterface::on_sync_receive(int p_from, const uint8_t *p_bu
}
Node *node = sync->get_root_node();
if (sync->get_multiplayer_authority() != p_from || !node) {
- ERR_CONTINUE(true);
+ // Not valid for me.
+ ofs += size;
+ ERR_CONTINUE_MSG(true, "Ignoring sync data from non-authority or for missing node.");
}
if (!sync->update_inbound_sync_time(time)) {
// State is too old.
ofs += size;
continue;
}
- ERR_FAIL_COND_V(size > uint32_t(p_buffer_len - ofs), ERR_BUG);
const List<NodePath> props = sync->get_replication_config()->get_sync_properties();
Vector<Variant> vars;
vars.resize(props.size());
diff --git a/modules/navigation/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp
index c3cb1c5f13..79e8c3a6d6 100644
--- a/modules/navigation/godot_navigation_server.cpp
+++ b/modules/navigation/godot_navigation_server.cpp
@@ -757,7 +757,7 @@ COMMAND_1(free, RID, p_object) {
agent_owner.free(p_object);
} else {
- ERR_FAIL_COND("Invalid ID.");
+ ERR_FAIL_COND("Attempted to free a NavigationServer RID that did not exist (or was already freed).");
}
}
diff --git a/modules/webp/SCsub b/modules/webp/SCsub
index 72ad1ea5e4..e78236a60b 100644
--- a/modules/webp/SCsub
+++ b/modules/webp/SCsub
@@ -13,6 +13,7 @@ if env["builtin_libwebp"]:
thirdparty_dir = "#thirdparty/libwebp/"
thirdparty_sources = [
"sharpyuv/sharpyuv.c",
+ "sharpyuv/sharpyuv_cpu.c",
"sharpyuv/sharpyuv_csp.c",
"sharpyuv/sharpyuv_dsp.c",
"sharpyuv/sharpyuv_gamma.c",
diff --git a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml
index aaeb2025ee..e0d17277ef 100644
--- a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml
+++ b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml
@@ -16,7 +16,7 @@
<param index="1" name="tls_client_options" type="TLSOptions" default="null" />
<description>
Starts a new multiplayer client connecting to the given [param url]. TLS certificates will be verified against the hostname when connecting using the [code]wss://[/code] protocol. You can pass the optional [param tls_client_options] parameter to customize the trusted certification authorities, or disable the common name verification. See [method TLSOptions.client] and [method TLSOptions.client_unsafe].
- [b]Note[/b]: It is recommended to specify the scheme part of the URL, i.e. the [param url] should start with either [code]ws://[/code] or [code]wss://[/code].
+ [b]Note:[/b] It is recommended to specify the scheme part of the URL, i.e. the [param url] should start with either [code]ws://[/code] or [code]wss://[/code].
</description>
</method>
<method name="create_server">
diff --git a/modules/webxr/native/library_godot_webxr.js b/modules/webxr/native/library_godot_webxr.js
index 1c00ebebb4..5c01d88a30 100644
--- a/modules/webxr/native/library_godot_webxr.js
+++ b/modules/webxr/native/library_godot_webxr.js
@@ -584,12 +584,11 @@ const GodotWebXR = {
}
const buf = GodotRuntime.malloc(point_count * 3 * 4);
- GodotRuntime.setHeapValue(buf, point_count, 'i32');
for (let i = 0; i < point_count; i++) {
const point = GodotWebXR.space.boundsGeometry[i];
- GodotRuntime.setHeapValue(buf + ((i * 3) + 1) * 4, point.x, 'float');
- GodotRuntime.setHeapValue(buf + ((i * 3) + 2) * 4, point.y, 'float');
- GodotRuntime.setHeapValue(buf + ((i * 3) + 3) * 4, point.z, 'float');
+ GodotRuntime.setHeapValue(buf + ((i * 3) + 0) * 4, point.x, 'float');
+ GodotRuntime.setHeapValue(buf + ((i * 3) + 1) * 4, point.y, 'float');
+ GodotRuntime.setHeapValue(buf + ((i * 3) + 2) * 4, point.z, 'float');
}
GodotRuntime.setHeapValue(r_points, buf, 'i32');