summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/SCsub45
-rw-r--r--modules/bullet/shape_bullet.h10
-rw-r--r--modules/camera/camera_osx.h1
-rw-r--r--modules/camera/camera_osx.mm27
-rw-r--r--modules/csg/doc_classes/CSGBox3D.xml2
-rw-r--r--modules/csg/doc_classes/CSGCombiner3D.xml2
-rw-r--r--modules/csg/doc_classes/CSGCylinder3D.xml2
-rw-r--r--modules/csg/doc_classes/CSGMesh3D.xml2
-rw-r--r--modules/csg/doc_classes/CSGPolygon3D.xml2
-rw-r--r--modules/csg/doc_classes/CSGPrimitive3D.xml2
-rw-r--r--modules/csg/doc_classes/CSGShape3D.xml2
-rw-r--r--modules/csg/doc_classes/CSGSphere3D.xml2
-rw-r--r--modules/csg/doc_classes/CSGTorus3D.xml2
-rw-r--r--modules/cvtt/SCsub11
-rw-r--r--modules/cvtt/image_compress_cvtt.cpp34
-rw-r--r--modules/fbx/editor_scene_importer_fbx.cpp2
-rw-r--r--modules/gdnative/gdnative/packed_arrays.cpp2
-rw-r--r--modules/gdnative/gdnative/rect2.cpp2
-rw-r--r--modules/gdnative/gdnative/transform2d.cpp1
-rw-r--r--modules/gdnative/gdnative/vector2.cpp2
-rw-r--r--modules/gdnative/gdnative_library_editor_plugin.cpp1
-rw-r--r--modules/gdnative/nativescript/api_generator.cpp8
-rw-r--r--modules/gdnative/nativescript/nativescript.cpp2
-rw-r--r--modules/gdnative/pluginscript/pluginscript_script.cpp2
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp22
-rw-r--r--modules/gdscript/editor_templates/CharacterBody2D/basic_movement.gd4
-rw-r--r--modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd4
-rw-r--r--modules/gdscript/editor_templates/VisualShaderNodeCustom/basic.gd3
-rw-r--r--modules/gdscript/gdscript.cpp18
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp147
-rw-r--r--modules/gdscript/gdscript_analyzer.h2
-rw-r--r--modules/gdscript/gdscript_compiler.cpp16
-rw-r--r--modules/gdscript/gdscript_editor.cpp30
-rw-r--r--modules/gdscript/gdscript_parser.cpp10
-rw-r--r--modules/gdscript/gdscript_parser.h5
-rw-r--r--modules/gdscript/gdscript_rpc_callable.cpp86
-rw-r--r--modules/gdscript/gdscript_rpc_callable.h61
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp50
-rw-r--r--modules/gdscript/gdscript_warning.cpp4
-rw-r--r--modules/gdscript/gdscript_warning.h1
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.cpp4
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp4
-rw-r--r--modules/gdscript/language_server/lsp.hpp2
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.cpp60
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.gd10
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.gd8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.gd8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_assign_enum_to_int_typed_var.gd13
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_assign_enum_to_int_typed_var.out5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_assign_int_cast_to_same_enum.gd13
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_assign_int_cast_to_same_enum.out5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_assign_other_enum_cast_to_same_enum.gd14
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_assign_other_enum_cast_to_same_enum.out5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_assign_same_enum.gd13
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_assign_same_enum.out5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_is_treated_as_int.gd21
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_is_treated_as_int.out7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_type_is_treated_as_dictionary.gd13
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_type_is_treated_as_dictionary.out7
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.gd15
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.out21
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.gd1
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.out2
-rw-r--r--modules/gltf/gltf_document.cpp42
-rw-r--r--modules/gridmap/grid_map.cpp1
-rw-r--r--modules/meshoptimizer/register_types.cpp6
-rw-r--r--modules/modules_builders.py7
-rw-r--r--modules/mono/csharp_script.cpp4
-rw-r--r--modules/mono/csharp_script.h2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs25
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs1
-rw-r--r--modules/mono/editor/bindings_generator.cpp4
-rw-r--r--modules/mono/editor_templates/CharacterBody2D/basic_movement.cs4
-rw-r--r--modules/mono/editor_templates/CharacterBody3D/basic_movement.cs4
-rw-r--r--modules/mono/editor_templates/VisualShaderNodeCustom/basic.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs47
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs24
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs25
-rw-r--r--modules/ogg/ogg_packet_sequence.h2
-rw-r--r--modules/raycast/raycast_occlusion_cull.cpp5
-rw-r--r--modules/text_server_adv/text_server_adv.cpp16
-rw-r--r--modules/text_server_fb/text_server_fb.cpp24
-rw-r--r--modules/visual_script/editor/visual_script_editor.cpp3
-rw-r--r--modules/visual_script/editor/visual_script_editor.h18
-rw-r--r--modules/visual_script/editor/visual_script_property_selector.cpp6
-rw-r--r--modules/visual_script/editor/visual_script_property_selector.h8
-rw-r--r--modules/visual_script/visual_script.cpp3
-rw-r--r--modules/visual_script/visual_script_expression.cpp16
-rw-r--r--modules/visual_script/visual_script_nodes.cpp10
-rw-r--r--modules/websocket/wsl_client.cpp72
-rw-r--r--modules/websocket/wsl_client.h5
96 files changed, 877 insertions, 413 deletions
diff --git a/modules/SCsub b/modules/SCsub
index 5ff4623743..fcc01e2c1b 100644
--- a/modules/SCsub
+++ b/modules/SCsub
@@ -22,36 +22,45 @@ env.CommandNoCache(
),
)
-# Header to be included in `tests/test_main.cpp` to run module-specific tests.
-if env["tests"]:
- env.Depends("modules_tests.gen.h", Value(env.module_list))
- env.CommandNoCache(
- "modules_tests.gen.h",
- Value(env.module_list),
- env.Run(
- modules_builders.generate_modules_tests,
- "Generating modules tests header.",
- # NOTE: No need to run in subprocess since this is still executed serially.
- subprocess=False,
- ),
- )
- env.AlwaysBuild("modules_tests.gen.h")
vs_sources = []
+test_headers = []
# libmodule_<name>.a for each active module.
for name, path in env.module_list.items():
env.modules_sources = []
- if not os.path.isabs(path):
- SConscript(name + "/SCsub") # Built-in.
- else:
- SConscript(path + "/SCsub") # Custom.
+ # Name for built-in modules, (absolute) path for custom ones.
+ base_path = path if os.path.isabs(path) else name
+ SConscript(base_path + "/SCsub")
lib = env_modules.add_library("module_%s" % name, env.modules_sources)
env.Prepend(LIBS=[lib])
if env["vsproj"]:
vs_sources += env.modules_sources
+ if env["tests"]:
+ # Lookup potential headers in `tests` subfolder.
+ import glob
+
+ module_tests = sorted(glob.glob(os.path.join(base_path, "tests", "*.h")))
+ if module_tests != []:
+ test_headers += module_tests
+
+
+# Generate header to be included in `tests/test_main.cpp` to run module-specific tests.
+if env["tests"]:
+ env.Depends("modules_tests.gen.h", test_headers)
+ env.CommandNoCache(
+ "modules_tests.gen.h",
+ test_headers,
+ env.Run(
+ modules_builders.generate_modules_tests,
+ "Generating modules tests header.",
+ # NOTE: No need to run in subprocess since this is still executed serially.
+ subprocess=False,
+ ),
+ )
+
# libmodules.a with only register_module_types.
# Must be last so that all libmodule_<name>.a libraries are on the right side
# in the linker command.
diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h
index 6377f8915d..dffcadbcdc 100644
--- a/modules/bullet/shape_bullet.h
+++ b/modules/bullet/shape_bullet.h
@@ -105,7 +105,7 @@ private:
};
class SphereShapeBullet : public ShapeBullet {
- real_t radius;
+ real_t radius = 0.0;
public:
SphereShapeBullet();
@@ -137,8 +137,8 @@ private:
};
class CapsuleShapeBullet : public ShapeBullet {
- real_t height;
- real_t radius;
+ real_t height = 0.0;
+ real_t radius = 0.0;
public:
CapsuleShapeBullet();
@@ -155,8 +155,8 @@ private:
};
class CylinderShapeBullet : public ShapeBullet {
- real_t height;
- real_t radius;
+ real_t height = 0.0;
+ real_t radius = 0.0;
public:
CylinderShapeBullet();
diff --git a/modules/camera/camera_osx.h b/modules/camera/camera_osx.h
index 9d67235839..b0db844599 100644
--- a/modules/camera/camera_osx.h
+++ b/modules/camera/camera_osx.h
@@ -39,7 +39,6 @@
class CameraOSX : public CameraServer {
public:
CameraOSX();
- ~CameraOSX();
void update_feeds();
};
diff --git a/modules/camera/camera_osx.mm b/modules/camera/camera_osx.mm
index 391006bfc2..d199c31b2f 100644
--- a/modules/camera/camera_osx.mm
+++ b/modules/camera/camera_osx.mm
@@ -114,18 +114,12 @@
if (output) {
[self removeOutput:output];
[output setSampleBufferDelegate:nil queue:nullptr];
- [output release];
output = nullptr;
}
[self commitConfiguration];
}
-- (void)dealloc {
- // bye bye
- [super dealloc];
-}
-
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
// This gets called every time our camera has a new image for us to process.
// May need to investigate in a way to throttle this if we get more images then we're rendering frames..
@@ -208,7 +202,6 @@ public:
AVCaptureDevice *get_device() const;
CameraFeedOSX();
- ~CameraFeedOSX();
void set_device(AVCaptureDevice *p_device);
@@ -227,7 +220,6 @@ CameraFeedOSX::CameraFeedOSX() {
void CameraFeedOSX::set_device(AVCaptureDevice *p_device) {
device = p_device;
- [device retain];
// get some info
NSString *device_name = p_device.localizedName;
@@ -240,18 +232,6 @@ void CameraFeedOSX::set_device(AVCaptureDevice *p_device) {
};
};
-CameraFeedOSX::~CameraFeedOSX() {
- if (capture_session != nullptr) {
- [capture_session release];
- capture_session = nullptr;
- };
-
- if (device != nullptr) {
- [device release];
- device = nullptr;
- };
-};
-
bool CameraFeedOSX::activate_feed() {
if (capture_session) {
// Already recording!
@@ -282,7 +262,6 @@ void CameraFeedOSX::deactivate_feed() {
// end camera capture if we have one
if (capture_session) {
[capture_session cleanup];
- [capture_session release];
capture_session = nullptr;
};
};
@@ -317,8 +296,6 @@ void CameraFeedOSX::deactivate_feed() {
// remove notifications
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceWasConnectedNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceWasDisconnectedNotification object:nil];
-
- [super dealloc];
}
@end
@@ -376,7 +353,3 @@ CameraOSX::CameraOSX() {
// should only have one of these....
device_notifications = [[MyDeviceNotifications alloc] initForServer:this];
};
-
-CameraOSX::~CameraOSX() {
- [device_notifications release];
-};
diff --git a/modules/csg/doc_classes/CSGBox3D.xml b/modules/csg/doc_classes/CSGBox3D.xml
index 4b479ed42e..64b07a6cbb 100644
--- a/modules/csg/doc_classes/CSGBox3D.xml
+++ b/modules/csg/doc_classes/CSGBox3D.xml
@@ -5,8 +5,10 @@
</brief_description>
<description>
This node allows you to create a box for use with the CSG system.
+ [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
+ <link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<members>
<member name="material" type="Material" setter="set_material" getter="get_material">
diff --git a/modules/csg/doc_classes/CSGCombiner3D.xml b/modules/csg/doc_classes/CSGCombiner3D.xml
index 422c5d35b7..16903141de 100644
--- a/modules/csg/doc_classes/CSGCombiner3D.xml
+++ b/modules/csg/doc_classes/CSGCombiner3D.xml
@@ -5,7 +5,9 @@
</brief_description>
<description>
For complex arrangements of shapes, it is sometimes needed to add structure to your CSG nodes. The CSGCombiner3D node allows you to create this structure. The node encapsulates the result of the CSG operations of its children. In this way, it is possible to do operations on one set of shapes that are children of one CSGCombiner3D node, and a set of separate operations on a second set of shapes that are children of a second CSGCombiner3D node, and then do an operation that takes the two end results as its input to create the final shape.
+ [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
+ <link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
</class>
diff --git a/modules/csg/doc_classes/CSGCylinder3D.xml b/modules/csg/doc_classes/CSGCylinder3D.xml
index 1fe2025bab..fa61957c84 100644
--- a/modules/csg/doc_classes/CSGCylinder3D.xml
+++ b/modules/csg/doc_classes/CSGCylinder3D.xml
@@ -5,8 +5,10 @@
</brief_description>
<description>
This node allows you to create a cylinder (or cone) for use with the CSG system.
+ [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
+ <link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<members>
<member name="cone" type="bool" setter="set_cone" getter="is_cone" default="false">
diff --git a/modules/csg/doc_classes/CSGMesh3D.xml b/modules/csg/doc_classes/CSGMesh3D.xml
index 42fcb7bd2b..660659a98b 100644
--- a/modules/csg/doc_classes/CSGMesh3D.xml
+++ b/modules/csg/doc_classes/CSGMesh3D.xml
@@ -5,8 +5,10 @@
</brief_description>
<description>
This CSG node allows you to use any mesh resource as a CSG shape, provided it is closed, does not self-intersect, does not contain internal faces and has no edges that connect to more than two faces. See also [CSGPolygon3D] for drawing 2D extruded polygons to be used as CSG nodes.
+ [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
+ <link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<members>
<member name="material" type="Material" setter="set_material" getter="get_material">
diff --git a/modules/csg/doc_classes/CSGPolygon3D.xml b/modules/csg/doc_classes/CSGPolygon3D.xml
index 5a49eebc7b..e8ef79a9e5 100644
--- a/modules/csg/doc_classes/CSGPolygon3D.xml
+++ b/modules/csg/doc_classes/CSGPolygon3D.xml
@@ -5,8 +5,10 @@
</brief_description>
<description>
An array of 2D points is extruded to quickly and easily create a variety of 3D meshes. See also [CSGMesh3D] for using 3D meshes as CSG nodes.
+ [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
+ <link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<members>
<member name="depth" type="float" setter="set_depth" getter="get_depth" default="1.0">
diff --git a/modules/csg/doc_classes/CSGPrimitive3D.xml b/modules/csg/doc_classes/CSGPrimitive3D.xml
index 8f4c8b9451..7eac85368e 100644
--- a/modules/csg/doc_classes/CSGPrimitive3D.xml
+++ b/modules/csg/doc_classes/CSGPrimitive3D.xml
@@ -5,8 +5,10 @@
</brief_description>
<description>
Parent class for various CSG primitives. It contains code and functionality that is common between them. It cannot be used directly. Instead use one of the various classes that inherit from it.
+ [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
+ <link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<members>
<member name="invert_faces" type="bool" setter="set_invert_faces" getter="is_inverting_faces" default="false">
diff --git a/modules/csg/doc_classes/CSGShape3D.xml b/modules/csg/doc_classes/CSGShape3D.xml
index f5031064d6..b2513703c0 100644
--- a/modules/csg/doc_classes/CSGShape3D.xml
+++ b/modules/csg/doc_classes/CSGShape3D.xml
@@ -5,8 +5,10 @@
</brief_description>
<description>
This is the CSG base class that provides CSG operation support to the various CSG nodes in Godot.
+ [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
+ <link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<methods>
<method name="get_collision_layer_value" qualifiers="const">
diff --git a/modules/csg/doc_classes/CSGSphere3D.xml b/modules/csg/doc_classes/CSGSphere3D.xml
index b8dfb4cf5f..1ef20bc7ca 100644
--- a/modules/csg/doc_classes/CSGSphere3D.xml
+++ b/modules/csg/doc_classes/CSGSphere3D.xml
@@ -5,8 +5,10 @@
</brief_description>
<description>
This node allows you to create a sphere for use with the CSG system.
+ [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
+ <link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<members>
<member name="material" type="Material" setter="set_material" getter="get_material">
diff --git a/modules/csg/doc_classes/CSGTorus3D.xml b/modules/csg/doc_classes/CSGTorus3D.xml
index 2c0eef8f09..09256af109 100644
--- a/modules/csg/doc_classes/CSGTorus3D.xml
+++ b/modules/csg/doc_classes/CSGTorus3D.xml
@@ -5,8 +5,10 @@
</brief_description>
<description>
This node allows you to create a torus for use with the CSG system.
+ [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
+ <link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<members>
<member name="inner_radius" type="float" setter="set_inner_radius" getter="get_inner_radius" default="0.5">
diff --git a/modules/cvtt/SCsub b/modules/cvtt/SCsub
index e56177d6e9..1d5a7ff6a3 100644
--- a/modules/cvtt/SCsub
+++ b/modules/cvtt/SCsub
@@ -11,7 +11,16 @@ thirdparty_obj = []
thirdparty_dir = "#thirdparty/cvtt/"
thirdparty_sources = [
- "ConvectionKernels.cpp",
+ "ConvectionKernels_API.cpp",
+ "ConvectionKernels_ETC.cpp",
+ "ConvectionKernels_BC67.cpp",
+ "ConvectionKernels_IndexSelector.cpp",
+ "ConvectionKernels_BC6H_IO.cpp",
+ "ConvectionKernels_S3TC.cpp",
+ "ConvectionKernels_BC7_PrioData.cpp",
+ "ConvectionKernels_SingleFile.cpp",
+ "ConvectionKernels_BCCommon.cpp",
+ "ConvectionKernels_Util.cpp",
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
diff --git a/modules/cvtt/image_compress_cvtt.cpp b/modules/cvtt/image_compress_cvtt.cpp
index 9e0579740b..d18340a2c8 100644
--- a/modules/cvtt/image_compress_cvtt.cpp
+++ b/modules/cvtt/image_compress_cvtt.cpp
@@ -41,7 +41,7 @@ struct CVTTCompressionJobParams {
bool is_hdr = false;
bool is_signed = false;
int bytes_per_pixel = 0;
-
+ cvtt::BC7EncodingPlan bc7_plan;
cvtt::Options options;
};
@@ -116,7 +116,7 @@ static void _digest_row_task(const CVTTCompressionJobParams &p_job_params, const
cvtt::Kernels::EncodeBC6HU(output_blocks, input_blocks_hdr, p_job_params.options);
}
} else {
- cvtt::Kernels::EncodeBC7(output_blocks, input_blocks_ldr, p_job_params.options);
+ cvtt::Kernels::EncodeBC7(output_blocks, input_blocks_ldr, p_job_params.options, p_job_params.bc7_plan);
}
unsigned int num_real_blocks = ((w - x_start) + 3) / 4;
@@ -141,7 +141,6 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChann
if (p_image->get_format() >= Image::FORMAT_BPTC_RGBA) {
return; //do not compress, already compressed
}
-
int w = p_image->get_width();
int h = p_image->get_height();
@@ -153,22 +152,8 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChann
}
cvtt::Options options;
- uint32_t flags = cvtt::Flags::Fastest;
-
- if (p_lossy_quality > 0.85) {
- flags = cvtt::Flags::Ultra;
- } else if (p_lossy_quality > 0.75) {
- flags = cvtt::Flags::Better;
- } else if (p_lossy_quality > 0.55) {
- flags = cvtt::Flags::Default;
- } else if (p_lossy_quality > 0.35) {
- flags = cvtt::Flags::Fast;
- } else if (p_lossy_quality > 0.15) {
- flags = cvtt::Flags::Faster;
- }
-
+ uint32_t flags = cvtt::Flags::Default;
flags |= cvtt::Flags::BC7_RespectPunchThrough;
-
if (p_channels == Image::USED_CHANNELS_RG) { //guessing this is a normal map
flags |= cvtt::Flags::Uniform;
}
@@ -215,12 +200,15 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChann
job_queue.job_params.is_signed = is_signed;
job_queue.job_params.options = options;
job_queue.job_params.bytes_per_pixel = is_hdr ? 6 : 4;
+ cvtt::Kernels::ConfigureBC7EncodingPlanFromQuality(job_queue.job_params.bc7_plan, 5);
-#ifdef NO_THREADS
int num_job_threads = 0;
-#else
- int num_job_threads = OS::get_singleton()->can_use_threads() ? (OS::get_singleton()->get_processor_count() - 1) : 0;
-#endif
+ // Amdahl's law (Wikipedia)
+ // If a program needs 20 hours to complete using a single thread, but a one-hour portion of the program cannot be parallelized,
+ // therefore only the remaining 19 hours (p = 0.95) of execution time can be parallelized, then regardless of how many threads are devoted
+ // to a parallelized execution of this program, the minimum execution time cannot be less than one hour.
+ //
+ // The number of executions with different inputs can be increased while the latency is the same.
Vector<CVTTCompressionRowTask> tasks;
@@ -278,7 +266,6 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChann
memdelete(threads_wb[i]);
}
}
-
p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
}
@@ -388,6 +375,5 @@ void image_decompress_cvtt(Image *p_image) {
w >>= 1;
h >>= 1;
}
-
p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
}
diff --git a/modules/fbx/editor_scene_importer_fbx.cpp b/modules/fbx/editor_scene_importer_fbx.cpp
index 4cca907bf2..758c47eecc 100644
--- a/modules/fbx/editor_scene_importer_fbx.cpp
+++ b/modules/fbx/editor_scene_importer_fbx.cpp
@@ -123,7 +123,7 @@ Node3D *EditorSceneFormatImporterFBX::import_scene(const String &p_path, uint32_
bool corrupt = false;
// safer to check this way as there can be different formatted headers
- if (fbx_header_string.find("Kaydara FBX Binary", 0) != -1) {
+ if (fbx_header_string.contains("Kaydara FBX Binary")) {
is_binary = true;
print_verbose("[doc] is binary");
diff --git a/modules/gdnative/gdnative/packed_arrays.cpp b/modules/gdnative/gdnative/packed_arrays.cpp
index bb6f0324a8..0c49694e0b 100644
--- a/modules/gdnative/gdnative/packed_arrays.cpp
+++ b/modules/gdnative/gdnative/packed_arrays.cpp
@@ -32,7 +32,7 @@
#include "core/variant/variant.h"
-#include "core/math/vector2.h"
+#include "core/math/vector2i.h"
#include "core/math/vector3i.h"
static_assert(sizeof(godot_packed_byte_array) == sizeof(PackedByteArray), "PackedByteArray size mismatch");
diff --git a/modules/gdnative/gdnative/rect2.cpp b/modules/gdnative/gdnative/rect2.cpp
index f4674850e3..7e0ce76c26 100644
--- a/modules/gdnative/gdnative/rect2.cpp
+++ b/modules/gdnative/gdnative/rect2.cpp
@@ -31,6 +31,8 @@
#include "gdnative/rect2.h"
#include "core/math/rect2.h"
+#include "core/math/rect2i.h"
+#include "core/os/memory.h"
static_assert(sizeof(godot_rect2) == sizeof(Rect2), "Rect2 size mismatch");
static_assert(sizeof(godot_rect2i) == sizeof(Rect2i), "Rect2i size mismatch");
diff --git a/modules/gdnative/gdnative/transform2d.cpp b/modules/gdnative/gdnative/transform2d.cpp
index 45ba790dc1..7dc07024e5 100644
--- a/modules/gdnative/gdnative/transform2d.cpp
+++ b/modules/gdnative/gdnative/transform2d.cpp
@@ -31,6 +31,7 @@
#include "gdnative/transform2d.h"
#include "core/math/transform_2d.h"
+#include "core/os/memory.h"
static_assert(sizeof(godot_transform2d) == sizeof(Transform2D), "Transform2D size mismatch");
diff --git a/modules/gdnative/gdnative/vector2.cpp b/modules/gdnative/gdnative/vector2.cpp
index eb8ffd74cd..a8d4281d25 100644
--- a/modules/gdnative/gdnative/vector2.cpp
+++ b/modules/gdnative/gdnative/vector2.cpp
@@ -31,6 +31,8 @@
#include "gdnative/vector2.h"
#include "core/math/vector2.h"
+#include "core/math/vector2i.h"
+#include "core/os/memory.h"
static_assert(sizeof(godot_vector2) == sizeof(Vector2), "Vector2 size mismatch");
static_assert(sizeof(godot_vector2i) == sizeof(Vector2i), "Vector2i size mismatch");
diff --git a/modules/gdnative/gdnative_library_editor_plugin.cpp b/modules/gdnative/gdnative_library_editor_plugin.cpp
index f94464826e..33ea078696 100644
--- a/modules/gdnative/gdnative_library_editor_plugin.cpp
+++ b/modules/gdnative/gdnative_library_editor_plugin.cpp
@@ -315,7 +315,6 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() {
NativePlatformConfig platform_ios;
platform_ios.name = "iOS";
- platform_ios.entries.push_back("armv7");
platform_ios.entries.push_back("arm64");
platform_ios.entries.push_back("x86_64");
// iOS can use both Static and Dynamic libraries.
diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp
index ddde28811c..0309d1d9c7 100644
--- a/modules/gdnative/nativescript/api_generator.cpp
+++ b/modules/gdnative/nativescript/api_generator.cpp
@@ -288,7 +288,7 @@ List<ClassAPI> generate_c_api_classes() {
String type;
String name = argument.name;
- if (argument.name.find(":") != -1) {
+ if (argument.name.contains(":")) {
type = argument.name.get_slice(":", 1);
name = argument.name.get_slice(":", 0);
} else {
@@ -324,7 +324,7 @@ List<ClassAPI> generate_c_api_classes() {
property_api.getter = ClassDB::get_property_getter(class_name, p->get().name);
property_api.setter = ClassDB::get_property_setter(class_name, p->get().name);
- if (p->get().name.find(":") != -1) {
+ if (p->get().name.contains(":")) {
property_api.type = p->get().name.get_slice(":", 1);
property_api.name = p->get().name.get_slice(":", 0);
} else {
@@ -355,7 +355,7 @@ List<ClassAPI> generate_c_api_classes() {
//method name
method_api.method_name = method_info.name;
//method return type
- if (method_api.method_name.find(":") != -1) {
+ if (method_api.method_name.contains(":")) {
method_api.return_type = method_api.method_name.get_slice(":", 1);
method_api.method_name = method_api.method_name.get_slice(":", 0);
} else {
@@ -388,7 +388,7 @@ List<ClassAPI> generate_c_api_classes() {
arg_name = arg_info.name;
- if (arg_info.name.find(":") != -1) {
+ if (arg_info.name.contains(":")) {
arg_type = arg_info.name.get_slice(":", 1);
arg_name = arg_info.name.get_slice(":", 0);
} else if (arg_info.hint == PROPERTY_HINT_RESOURCE_TYPE) {
diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp
index be304a43f0..5d5414c694 100644
--- a/modules/gdnative/nativescript/nativescript.cpp
+++ b/modules/gdnative/nativescript/nativescript.cpp
@@ -766,7 +766,7 @@ Variant NativeScriptInstance::call(const StringName &p_method, const Variant **p
void NativeScriptInstance::notification(int p_notification) {
#ifdef DEBUG_ENABLED
if (p_notification == MainLoop::NOTIFICATION_CRASH) {
- if (current_method_call != StringName("")) {
+ if (current_method_call != StringName()) {
ERR_PRINT("NativeScriptInstance detected crash on method: " + current_method_call);
current_method_call = "";
}
diff --git a/modules/gdnative/pluginscript/pluginscript_script.cpp b/modules/gdnative/pluginscript/pluginscript_script.cpp
index 71ab8ef0a2..ec3c9eb4ff 100644
--- a/modules/gdnative/pluginscript/pluginscript_script.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_script.cpp
@@ -93,7 +93,7 @@ Variant PluginScript::_new(const Variant **p_args, int p_argcount, Callable::Cal
REF ref;
Object *owner = nullptr;
- if (get_instance_base_type() == "") {
+ if (get_instance_base_type() == StringName()) {
owner = memnew(RefCounted);
} else {
owner = ClassDB::instantiate(get_instance_base_type());
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index 5cc295bbab..ac6684a29c 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -33,18 +33,6 @@
#include "../gdscript_tokenizer.h"
#include "editor/editor_settings.h"
-static bool _is_char(char32_t c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
-}
-
-static bool _is_hex_symbol(char32_t c) {
- return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
-}
-
-static bool _is_bin_symbol(char32_t c) {
- return (c == '0' || c == '1');
-}
-
Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
Dictionary color_map;
@@ -102,7 +90,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
color = font_color;
bool is_char = !is_symbol(str[j]);
bool is_a_symbol = is_symbol(str[j]);
- bool is_number = (str[j] >= '0' && str[j] <= '9');
+ bool is_number = is_digit(str[j]);
/* color regions */
if (is_a_symbol || in_region != -1) {
@@ -241,14 +229,14 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
}
// allow ABCDEF in hex notation
- if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) {
+ if (is_hex_notation && (is_hex_digit(str[j]) || is_number)) {
is_number = true;
} else {
is_hex_notation = false;
}
// disallow anything not a 0 or 1
- if (is_bin_notation && (_is_bin_symbol(str[j]))) {
+ if (is_bin_notation && (is_binary_digit(str[j]))) {
is_number = true;
} else if (is_bin_notation) {
is_bin_notation = false;
@@ -270,7 +258,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
}
}
- if (!in_word && _is_char(str[j]) && !is_number) {
+ if (!in_word && (is_ascii_char(str[j]) || is_underscore(str[j])) && !is_number) {
in_word = true;
}
@@ -585,7 +573,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
if (E.usage & PROPERTY_USAGE_CATEGORY || E.usage & PROPERTY_USAGE_GROUP || E.usage & PROPERTY_USAGE_SUBGROUP) {
continue;
}
- if (name.find("/") != -1) {
+ if (name.contains("/")) {
continue;
}
member_keywords[name] = member_variable_color;
diff --git a/modules/gdscript/editor_templates/CharacterBody2D/basic_movement.gd b/modules/gdscript/editor_templates/CharacterBody2D/basic_movement.gd
index 0824d894c5..edaccae018 100644
--- a/modules/gdscript/editor_templates/CharacterBody2D/basic_movement.gd
+++ b/modules/gdscript/editor_templates/CharacterBody2D/basic_movement.gd
@@ -3,7 +3,7 @@
extends _BASE_
const SPEED: float = 300.0
-const JUMP_FORCE: float = -400.0
+const JUMP_VELOCITY: float = -400.0
# Get the gravity from the project settings to be synced with RigidDynamicBody nodes.
var gravity: int = ProjectSettings.get_setting("physics/2d/default_gravity")
@@ -16,7 +16,7 @@ func _physics_process(delta: float) -> void:
# Handle Jump.
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
- motion_velocity.y = JUMP_FORCE
+ motion_velocity.y = JUMP_VELOCITY
# Get the input direction and handle the movement/deceleration.
# As good practice, you should replace UI actions with custom gameplay actions.
diff --git a/modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd b/modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd
index ce6d67ae84..e191e5451a 100644
--- a/modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd
+++ b/modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd
@@ -3,7 +3,7 @@
extends _BASE_
const SPEED: float = 5.0
-const JUMP_FORCE: float = 4.5
+const JUMP_VELOCITY: float = 4.5
# Get the gravity from the project settings to be synced with RigidDynamicBody nodes.
var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
@@ -16,7 +16,7 @@ func _physics_process(delta: float) -> void:
# Handle Jump.
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
- motion_velocity.y = JUMP_FORCE
+ motion_velocity.y = JUMP_VELOCITY
# Get the input direction and handle the movement/deceleration.
# As good practice, you should replace UI actions with custom gameplay actions.
diff --git a/modules/gdscript/editor_templates/VisualShaderNodeCustom/basic.gd b/modules/gdscript/editor_templates/VisualShaderNodeCustom/basic.gd
index 27383b878d..d71f2592fe 100644
--- a/modules/gdscript/editor_templates/VisualShaderNodeCustom/basic.gd
+++ b/modules/gdscript/editor_templates/VisualShaderNodeCustom/basic.gd
@@ -34,8 +34,5 @@ func _get_output_port_name(port: int) -> String:
func _get_output_port_type(port: int) -> int:
return PORT_TYPE_SCALAR
-func _get_global_code(mode: Shader.Mode) -> String:
- return ""
-
func _get_code(input_vars: Array[String], output_vars: Array[String], mode: Shader.Mode, type: VisualShader.Type) -> String:
return output_vars[0] + " = 0.0;"
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 84db97625b..58a788e255 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -43,6 +43,7 @@
#include "gdscript_cache.h"
#include "gdscript_compiler.h"
#include "gdscript_parser.h"
+#include "gdscript_rpc_callable.h"
#include "gdscript_warning.h"
#ifdef TESTS_ENABLED
@@ -827,7 +828,7 @@ Error GDScript::reload(bool p_keep_state) {
return OK;
}
#else
- if (source.find("_BASE_") != -1) {
+ if (source.contains("_BASE_")) {
return OK;
}
#endif
@@ -1375,7 +1376,13 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
while (sl) {
const Map<StringName, GDScriptFunction *>::Element *E = sl->member_functions.find(p_name);
if (E) {
- r_ret = Callable(this->owner, E->key());
+ Multiplayer::RPCConfig config;
+ config.name = p_name;
+ if (sptr->rpc_functions.find(config) != -1) {
+ r_ret = Callable(memnew(GDScriptRPCCallable(this->owner, E->key())));
+ } else {
+ r_ret = Callable(this->owner, E->key());
+ }
return true; //index found
}
sl = sl->_base;
@@ -2022,8 +2029,6 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
"preload",
"signal",
"super",
- "trait",
- "yield",
// var
"const",
"enum",
@@ -2040,6 +2045,11 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
"return",
"match",
"while",
+ // These keywords are not implemented currently, but reserved for (potential) future use.
+ // We highlight them as keywords to make errors easier to understand.
+ "trait",
+ "namespace",
+ "yield",
nullptr
};
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index d11174227a..94daba4bf6 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -108,7 +108,7 @@ static GDScriptParser::DataType make_native_enum_type(const StringName &p_native
GDScriptParser::DataType type;
type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
type.kind = GDScriptParser::DataType::ENUM;
- type.builtin_type = Variant::OBJECT;
+ type.builtin_type = Variant::INT;
type.is_constant = true;
type.is_meta_type = true;
@@ -196,7 +196,7 @@ Error GDScriptAnalyzer::check_class_member_name_conflict(const GDScriptParser::C
}
if (current_data_type && current_data_type->kind == GDScriptParser::DataType::Kind::NATIVE) {
- if (current_data_type->native_type != StringName("")) {
+ if (current_data_type->native_type != StringName()) {
return check_native_member_name_conflict(
p_member_name,
p_member_node,
@@ -250,7 +250,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
if (!p_class->extends_used) {
result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
result.kind = GDScriptParser::DataType::NATIVE;
- result.native_type = "RefCounted";
+ result.native_type = SNAME("RefCounted");
} else {
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
@@ -438,7 +438,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
StringName first = p_type->type_chain[0]->name;
- if (first == "Variant") {
+ if (first == SNAME("Variant")) {
result.kind = GDScriptParser::DataType::VARIANT;
if (p_type->type_chain.size() > 1) {
push_error(R"("Variant" type don't contain nested types.)", p_type->type_chain[1]);
@@ -447,9 +447,9 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
return result;
}
- if (first == "Object") {
+ if (first == SNAME("Object")) {
result.kind = GDScriptParser::DataType::NATIVE;
- result.native_type = "Object";
+ result.native_type = SNAME("Object");
if (p_type->type_chain.size() > 1) {
push_error(R"("Object" type don't contain nested types.)", p_type->type_chain[1]);
return GDScriptParser::DataType();
@@ -650,9 +650,9 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
datatype = specified_type;
if (member.variable->initializer != nullptr) {
- if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true)) {
+ if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true, member.variable->initializer)) {
// Try reverse test since it can be a masked subtype.
- if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true)) {
+ if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true, member.variable->initializer)) {
push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", member.variable->initializer->get_datatype().to_string(), datatype.to_string()), member.variable->initializer);
} else {
// TODO: Add warning.
@@ -1400,9 +1400,9 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
type.is_meta_type = false;
if (p_variable->initializer != nullptr) {
- if (!is_type_compatible(type, p_variable->initializer->get_datatype(), true)) {
+ if (!is_type_compatible(type, p_variable->initializer->get_datatype(), true, p_variable->initializer)) {
// Try reverse test since it can be a masked subtype.
- if (!is_type_compatible(p_variable->initializer->get_datatype(), type, true)) {
+ if (!is_type_compatible(p_variable->initializer->get_datatype(), type, true, p_variable->initializer)) {
push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", p_variable->initializer->get_datatype().to_string(), type.to_string()), p_variable->initializer);
} else {
// TODO: Add warning.
@@ -1877,11 +1877,11 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
if (!assignee_type.is_variant() && assigned_value_type.is_hard_type()) {
if (compatible) {
- compatible = is_type_compatible(assignee_type, op_type, true);
+ compatible = is_type_compatible(assignee_type, op_type, true, p_assignment->assigned_value);
if (!compatible) {
if (assignee_type.is_hard_type()) {
// Try reverse test since it can be a masked subtype.
- if (!is_type_compatible(op_type, assignee_type, true)) {
+ if (!is_type_compatible(op_type, assignee_type, true, p_assignment->assigned_value)) {
push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", assigned_value_type.to_string(), assignee_type.to_string()), p_assignment->assigned_value);
} else {
// TODO: Add warning.
@@ -2416,6 +2416,11 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
}
validate_call_arg(par_types, default_arg_count, is_vararg, p_call);
+ if (base_type.kind == GDScriptParser::DataType::ENUM && base_type.is_meta_type) {
+ // Enum type is treated as a dictionary value for function calls.
+ base_type.is_meta_type = false;
+ }
+
if (is_self && parser->current_function != nullptr && parser->current_function->is_static && !is_static) {
push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parser->current_function->identifier->name), p_call->callee);
} else if (!is_self && base_type.is_meta_type && !is_static) {
@@ -2474,17 +2479,24 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) {
GDScriptParser::DataType cast_type = resolve_datatype(p_cast->cast_type);
if (!cast_type.is_set()) {
+ mark_node_unsafe(p_cast);
return;
}
- cast_type.is_meta_type = false; // The casted value won't be a type name.
+ cast_type = type_from_metatype(cast_type); // The casted value won't be a type name.
p_cast->set_datatype(cast_type);
if (!cast_type.is_variant()) {
GDScriptParser::DataType op_type = p_cast->operand->get_datatype();
if (!op_type.is_variant()) {
bool valid = false;
- if (op_type.kind == GDScriptParser::DataType::BUILTIN && cast_type.kind == GDScriptParser::DataType::BUILTIN) {
+ if (op_type.kind == GDScriptParser::DataType::ENUM && cast_type.kind == GDScriptParser::DataType::ENUM) {
+ // Enum types are compatible between each other, so it's a safe cast.
+ valid = true;
+ } else if (op_type.kind == GDScriptParser::DataType::BUILTIN && op_type.builtin_type == Variant::INT && cast_type.kind == GDScriptParser::DataType::ENUM) {
+ // Convertint int to enum is always valid.
+ valid = true;
+ } else if (op_type.kind == GDScriptParser::DataType::BUILTIN && cast_type.kind == GDScriptParser::DataType::BUILTIN) {
valid = Variant::can_convert(op_type.builtin_type, cast_type.builtin_type);
} else if (op_type.kind != GDScriptParser::DataType::BUILTIN && cast_type.kind != GDScriptParser::DataType::BUILTIN) {
valid = is_type_compatible(cast_type, op_type) || is_type_compatible(op_type, cast_type);
@@ -2540,7 +2552,7 @@ void GDScriptAnalyzer::reduce_get_node(GDScriptParser::GetNodeNode *p_get_node)
GDScriptParser::DataType result;
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
result.kind = GDScriptParser::DataType::NATIVE;
- result.native_type = "Node";
+ result.native_type = SNAME("Node");
result.builtin_type = Variant::OBJECT;
if (!ClassDB::is_parent_class(parser->current_class->base_type.native_type, result.native_type)) {
@@ -2586,6 +2598,34 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
const StringName &name = p_identifier->name;
+ if (base.kind == GDScriptParser::DataType::ENUM) {
+ if (base.is_meta_type) {
+ if (base.enum_values.has(name)) {
+ p_identifier->is_constant = true;
+ p_identifier->reduced_value = base.enum_values[name];
+
+ GDScriptParser::DataType result;
+ result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ result.kind = GDScriptParser::DataType::ENUM;
+ result.is_constant = true;
+ result.builtin_type = Variant::INT;
+ result.native_type = base.native_type;
+ result.enum_type = base.enum_type;
+ p_identifier->set_datatype(result);
+ return;
+ } else {
+ // Consider as a Dictionary, so it can be anything.
+ // This will be evaluated in the next if block.
+ base.kind = GDScriptParser::DataType::BUILTIN;
+ base.builtin_type = Variant::DICTIONARY;
+ base.is_meta_type = false;
+ }
+ } else {
+ push_error(R"(Cannot get property from enum value.)", p_identifier);
+ return;
+ }
+ }
+
if (base.kind == GDScriptParser::DataType::BUILTIN) {
if (base.is_meta_type) {
bool valid = true;
@@ -2632,32 +2672,6 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
return;
}
- if (base.kind == GDScriptParser::DataType::ENUM) {
- if (base.is_meta_type) {
- if (base.enum_values.has(name)) {
- p_identifier->is_constant = true;
- p_identifier->reduced_value = base.enum_values[name];
-
- GDScriptParser::DataType result;
- result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- result.kind = GDScriptParser::DataType::ENUM_VALUE;
- result.is_constant = true;
- result.builtin_type = Variant::INT;
- result.native_type = base.native_type;
- result.enum_type = name;
- p_identifier->set_datatype(result);
- } else {
- // Consider as a Dictionary
- GDScriptParser::DataType dummy;
- dummy.kind = GDScriptParser::DataType::VARIANT;
- p_identifier->set_datatype(dummy);
- }
- } else {
- push_error(R"(Cannot get property from enum value.)", p_identifier);
- }
- return;
- }
-
GDScriptParser::ClassNode *base_class = base.class_type;
// TODO: Switch current class/function/suite here to avoid misrepresenting identifiers (in recursive reduce calls).
@@ -2793,7 +2807,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
if (element.identifier->name == p_identifier->name) {
GDScriptParser::DataType type;
type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- type.kind = element.parent_enum->identifier ? GDScriptParser::DataType::ENUM_VALUE : GDScriptParser::DataType::BUILTIN;
+ type.kind = element.parent_enum->identifier ? GDScriptParser::DataType::ENUM : GDScriptParser::DataType::BUILTIN;
type.builtin_type = Variant::INT;
type.is_constant = true;
if (element.parent_enum->identifier) {
@@ -3493,6 +3507,9 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_metatype(const GDScriptPars
GDScriptParser::DataType result = p_meta_type;
result.is_meta_type = false;
result.is_constant = false;
+ if (p_meta_type.kind == GDScriptParser::DataType::ENUM) {
+ result.builtin_type = Variant::INT;
+ }
return result;
}
@@ -3507,7 +3524,7 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo
result.builtin_type = p_property.type;
if (p_property.type == Variant::OBJECT) {
result.kind = GDScriptParser::DataType::NATIVE;
- result.native_type = p_property.class_name == StringName() ? "Object" : p_property.class_name;
+ result.native_type = p_property.class_name == StringName() ? SNAME("Object") : p_property.class_name;
} else {
result.kind = GDScriptParser::DataType::BUILTIN;
result.builtin_type = p_property.type;
@@ -3549,6 +3566,18 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::CallNode *p_source
r_default_arg_count = 0;
StringName function_name = p_function;
+ if (p_base_type.kind == GDScriptParser::DataType::ENUM) {
+ if (p_base_type.is_meta_type) {
+ // Enum type can be treated as a dictionary value.
+ p_base_type.kind = GDScriptParser::DataType::BUILTIN;
+ p_base_type.builtin_type = Variant::DICTIONARY;
+ p_base_type.is_meta_type = false;
+ } else {
+ push_error("Cannot call function on enum value.", p_source);
+ return false;
+ }
+ }
+
if (p_base_type.kind == GDScriptParser::DataType::BUILTIN) {
// Construct a base type to get methods.
Callable::CallError err;
@@ -3799,6 +3828,22 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
Variant::Type a_type = p_a.builtin_type;
Variant::Type b_type = p_b.builtin_type;
+
+ if (p_a.kind == GDScriptParser::DataType::ENUM) {
+ if (p_a.is_meta_type) {
+ a_type = Variant::DICTIONARY;
+ } else {
+ a_type = Variant::INT;
+ }
+ }
+ if (p_b.kind == GDScriptParser::DataType::ENUM) {
+ if (p_b.is_meta_type) {
+ b_type = Variant::DICTIONARY;
+ } else {
+ b_type = Variant::INT;
+ }
+ }
+
Variant::ValidatedOperatorEvaluator op_eval = Variant::get_validated_operator_evaluator(p_operation, a_type, b_type);
bool hard_operation = p_a.is_hard_type() && p_b.is_hard_type();
@@ -3828,7 +3873,7 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
}
// TODO: Add safe/unsafe return variable (for variant cases)
-bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion) const {
+bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion, const GDScriptParser::Node *p_source_node) {
// These return "true" so it doesn't affect users negatively.
ERR_FAIL_COND_V_MSG(!p_target.is_set(), true, "Parser bug (please report): Trying to check compatibility of unset target type");
ERR_FAIL_COND_V_MSG(!p_source.is_set(), true, "Parser bug (please report): Trying to check compatibility of unset value type");
@@ -3848,7 +3893,7 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
if (!valid && p_allow_implicit_conversion) {
valid = Variant::can_convert_strict(p_source.builtin_type, p_target.builtin_type);
}
- if (!valid && p_target.builtin_type == Variant::INT && p_source.kind == GDScriptParser::DataType::ENUM_VALUE) {
+ if (!valid && p_target.builtin_type == Variant::INT && p_source.kind == GDScriptParser::DataType::ENUM && !p_source.is_meta_type) {
// Enum value is also integer.
valid = true;
}
@@ -3869,6 +3914,11 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
if (p_target.kind == GDScriptParser::DataType::ENUM) {
if (p_source.kind == GDScriptParser::DataType::BUILTIN && p_source.builtin_type == Variant::INT) {
+#ifdef DEBUG_ENABLED
+ if (p_source_node) {
+ parser->push_warning(p_source_node, GDScriptWarning::INT_ASSIGNED_TO_ENUM);
+ }
+#endif
return true;
}
if (p_source.kind == GDScriptParser::DataType::ENUM) {
@@ -3876,11 +3926,6 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
return true;
}
}
- if (p_source.kind == GDScriptParser::DataType::ENUM_VALUE) {
- if (p_source.native_type == p_target.native_type && p_target.enum_values.has(p_source.enum_type)) {
- return true;
- }
- }
return false;
}
@@ -3935,7 +3980,6 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
case GDScriptParser::DataType::VARIANT:
case GDScriptParser::DataType::BUILTIN:
case GDScriptParser::DataType::ENUM:
- case GDScriptParser::DataType::ENUM_VALUE:
case GDScriptParser::DataType::UNRESOLVED:
break; // Already solved before.
}
@@ -3972,7 +4016,6 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
case GDScriptParser::DataType::VARIANT:
case GDScriptParser::DataType::BUILTIN:
case GDScriptParser::DataType::ENUM:
- case GDScriptParser::DataType::ENUM_VALUE:
case GDScriptParser::DataType::UNRESOLVED:
break; // Already solved before.
}
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
index 4cee5cb44a..2697a6ec2b 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -112,7 +112,7 @@ class GDScriptAnalyzer {
GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source);
GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source);
void update_array_literal_element_type(const GDScriptParser::DataType &p_base_type, GDScriptParser::ArrayNode *p_array_literal);
- bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false) const;
+ bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false, const GDScriptParser::Node *p_source_node = nullptr);
void push_error(const String &p_message, const GDScriptParser::Node *p_origin);
void mark_node_unsafe(const GDScriptParser::Node *p_node);
bool class_exists(const StringName &p_class) const;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index ca125d3a07..108c988add 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -141,10 +141,13 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
}
} break;
case GDScriptParser::DataType::ENUM:
- case GDScriptParser::DataType::ENUM_VALUE:
result.has_type = true;
result.kind = GDScriptDataType::BUILTIN;
- result.builtin_type = Variant::INT;
+ if (p_datatype.is_meta_type) {
+ result.builtin_type = Variant::DICTIONARY;
+ } else {
+ result.builtin_type = Variant::INT;
+ }
break;
case GDScriptParser::DataType::UNRESOLVED: {
ERR_PRINT("Parser bug: converting unresolved type.");
@@ -469,7 +472,14 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
} break;
case GDScriptParser::Node::CAST: {
const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);
- GDScriptDataType cast_type = _gdtype_from_datatype(cn->cast_type->get_datatype());
+ GDScriptParser::DataType og_cast_type = cn->cast_type->get_datatype();
+ GDScriptDataType cast_type = _gdtype_from_datatype(og_cast_type);
+
+ if (og_cast_type.kind == GDScriptParser::DataType::ENUM) {
+ // Enum types are usually treated as dictionaries, but in this case we want to cast to an integer.
+ cast_type.kind = GDScriptDataType::BUILTIN;
+ cast_type.builtin_type = Variant::INT;
+ }
// Create temporary for result first since it will be deleted last.
GDScriptCodeGenerator::Address result = codegen.add_temporary(cast_type);
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index d10e120410..f0dc830ed8 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -486,7 +486,7 @@ struct GDScriptCompletionIdentifier {
static String _get_visual_datatype(const PropertyInfo &p_info, bool p_is_arg = true) {
if (p_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
String enum_name = p_info.class_name;
- if (enum_name.find(".") == -1) {
+ if (!enum_name.contains(".")) {
return enum_name;
}
return enum_name.get_slice(".", 1);
@@ -610,7 +610,7 @@ static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_functio
case GDScriptParser::Node::SUBSCRIPT: {
const GDScriptParser::SubscriptNode *sub = static_cast<const GDScriptParser::SubscriptNode *>(par->default_value);
if (sub->is_constant) {
- if (sub->datatype.kind == GDScriptParser::DataType::ENUM_VALUE) {
+ if (sub->datatype.kind == GDScriptParser::DataType::ENUM) {
def_val = sub->get_datatype().to_string();
} else if (sub->reduced) {
const Variant::Type vt = sub->reduced_value.get_type();
@@ -652,7 +652,7 @@ static void _get_directory_contents(EditorFileSystemDirectory *p_dir, Map<String
}
static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_annotation, int p_argument, const String p_quote_style, Map<String, ScriptCodeCompletionOption> &r_result) {
- if (p_annotation->name == "@export_range") {
+ if (p_annotation->name == SNAME("@export_range")) {
if (p_argument == 3 || p_argument == 4) {
// Slider hint.
ScriptCodeCompletionOption slider1("or_greater", ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
@@ -662,7 +662,7 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
slider2.insert_text = slider2.display.quote(p_quote_style);
r_result.insert(slider2.display, slider2);
}
- } else if (p_annotation->name == "@export_exp_easing") {
+ } else if (p_annotation->name == SNAME("@export_exp_easing")) {
if (p_argument == 0 || p_argument == 1) {
// Easing hint.
ScriptCodeCompletionOption hint1("attenuation", ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
@@ -672,7 +672,7 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
hint2.insert_text = hint2.display.quote(p_quote_style);
r_result.insert(hint2.display, hint2);
}
- } else if (p_annotation->name == "@export_node_path") {
+ } else if (p_annotation->name == SNAME("@export_node_path")) {
ScriptCodeCompletionOption node("Node", ScriptCodeCompletionOption::KIND_CLASS);
r_result.insert(node.display, node);
List<StringName> node_types;
@@ -684,7 +684,7 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
ScriptCodeCompletionOption option(E, ScriptCodeCompletionOption::KIND_CLASS);
r_result.insert(option.display, option);
}
- } else if (p_annotation->name == "@warning_ignore") {
+ } else if (p_annotation->name == SNAME("@warning_ignore")) {
for (int warning_code = 0; warning_code < GDScriptWarning::WARNING_MAX; warning_code++) {
ScriptCodeCompletionOption warning(GDScriptWarning::get_name_from_code((GDScriptWarning::Code)warning_code).to_lower(), ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
r_result.insert(warning.display, warning);
@@ -955,7 +955,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
if (E.usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY)) {
continue;
}
- if (E.name.find("/") != -1) {
+ if (E.name.contains("/")) {
continue;
}
ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_MEMBER);
@@ -1000,7 +1000,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
}
for (const PropertyInfo &E : members) {
- if (String(E.name).find("/") == -1) {
+ if (!String(E.name).contains("/")) {
ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_MEMBER);
r_result.insert(option.display, option);
}
@@ -1384,7 +1384,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
Object *baseptr = base.value;
- if (all_is_const && String(call->function_name) == "get_node" && ClassDB::is_parent_class(native_type.native_type, "Node") && args.size()) {
+ if (all_is_const && call->function_name == SNAME("get_node") && ClassDB::is_parent_class(native_type.native_type, SNAME("Node")) && args.size()) {
String arg1 = args[0];
if (arg1.begins_with("/root/")) {
String which = arg1.get_slice("/", 2);
@@ -2085,7 +2085,7 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex
GDScriptParser::DataType base_type = p_base.type;
bool is_static = base_type.is_meta_type;
- if (is_static && p_method == "new") {
+ if (is_static && p_method == SNAME("new")) {
r_type.type = base_type;
r_type.type.is_meta_type = false;
r_type.type.is_constant = false;
@@ -2182,7 +2182,7 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex
}
static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_context, const String &p_enum_hint, Map<String, ScriptCodeCompletionOption> &r_result) {
- if (p_enum_hint.find(".") == -1) {
+ if (!p_enum_hint.contains(".")) {
// Global constant or in the current class.
StringName current_enum = p_enum_hint;
if (p_context.current_class && p_context.current_class->has_member(current_enum) && p_context.current_class->get_member(current_enum).type == GDScriptParser::ClassNode::Member::ENUM) {
@@ -2274,7 +2274,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
r_arghint = _make_arguments_hint(info, p_argidx);
}
- if (p_argidx == 0 && ClassDB::is_parent_class(class_name, "Node") && (p_method == "get_node" || p_method == "has_node")) {
+ if (p_argidx == 0 && ClassDB::is_parent_class(class_name, SNAME("Node")) && (p_method == SNAME("get_node") || p_method == SNAME("has_node"))) {
// Get autoloads
List<PropertyInfo> props;
ProjectSettings::get_singleton()->get_property_list(&props);
@@ -2291,7 +2291,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
}
- if (p_argidx == 0 && method_args > 0 && ClassDB::is_parent_class(class_name, "InputEvent") && p_method.operator String().find("action") != -1) {
+ if (p_argidx == 0 && method_args > 0 && ClassDB::is_parent_class(class_name, SNAME("InputEvent")) && p_method.operator String().contains("action")) {
// Get input actions
List<PropertyInfo> props;
ProjectSettings::get_singleton()->get_property_list(&props);
@@ -2639,7 +2639,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
ClassDB::get_virtual_methods(class_name, &virtual_methods);
for (const MethodInfo &mi : virtual_methods) {
String method_hint = mi.name;
- if (method_hint.find(":") != -1) {
+ if (method_hint.contains(":")) {
method_hint = method_hint.get_slice(":", 0);
}
method_hint += "(";
@@ -2650,7 +2650,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
method_hint += ", ";
}
String arg = mi.arguments[i].name;
- if (arg.find(":") != -1) {
+ if (arg.contains(":")) {
arg = arg.substr(0, arg.find(":"));
}
method_hint += arg;
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 460bd85a86..8e4e457ec1 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -519,7 +519,7 @@ void GDScriptParser::parse_program() {
// Check for @tool annotation.
AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::CLASS_LEVEL);
if (annotation != nullptr) {
- if (annotation->name == "@tool") {
+ if (annotation->name == SNAME("@tool")) {
// TODO: don't allow @tool anywhere else. (Should all script annotations be the first thing?).
_is_tool = true;
if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
@@ -573,7 +573,7 @@ void GDScriptParser::parse_program() {
// Check for @icon annotation.
AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::CLASS_LEVEL);
if (annotation != nullptr) {
- if (annotation->name == "@icon") {
+ if (annotation->name == SNAME("@icon")) {
if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
push_error(R"(Expected newline after "@icon" annotation.)");
}
@@ -3503,7 +3503,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
// This is called after the analyzer is done finding the type, so this should be set here.
DataType export_type = variable->get_datatype();
- if (p_annotation->name == "@export") {
+ if (p_annotation->name == SNAME("@export")) {
if (variable->datatype_specifier == nullptr && variable->initializer == nullptr) {
push_error(R"(Cannot use simple "@export" annotation with variable without type or initializer, since type can't be inferred.)", p_annotation);
return false;
@@ -3528,7 +3528,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
variable->export_info.hint_string = Variant::get_type_name(export_type.builtin_type);
break;
case GDScriptParser::DataType::NATIVE:
- if (ClassDB::is_parent_class(export_type.native_type, "Resource")) {
+ if (ClassDB::is_parent_class(export_type.native_type, SNAME("Resource"))) {
variable->export_info.type = Variant::OBJECT;
variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
variable->export_info.hint_string = export_type.native_type;
@@ -3740,8 +3740,6 @@ String GDScriptParser::DataType::to_string() const {
}
case ENUM:
return enum_type.operator String() + " (enum)";
- case ENUM_VALUE:
- return enum_type.operator String() + " (enum value)";
case UNRESOLVED:
return "<unresolved type>";
}
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index e4311d2d5e..c09b07282f 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -106,8 +106,7 @@ public:
NATIVE,
SCRIPT,
CLASS, // GDScript.
- ENUM, // Full enumeration.
- ENUM_VALUE, // Value from enumeration.
+ ENUM, // Enumeration.
VARIANT, // Can be any type.
UNRESOLVED,
};
@@ -185,8 +184,6 @@ public:
return builtin_type == p_other.builtin_type;
case NATIVE:
case ENUM:
- return native_type == p_other.native_type;
- case ENUM_VALUE:
return native_type == p_other.native_type && enum_type == p_other.enum_type;
case SCRIPT:
return script_type == p_other.script_type;
diff --git a/modules/gdscript/gdscript_rpc_callable.cpp b/modules/gdscript/gdscript_rpc_callable.cpp
new file mode 100644
index 0000000000..07e5ed4171
--- /dev/null
+++ b/modules/gdscript/gdscript_rpc_callable.cpp
@@ -0,0 +1,86 @@
+/*************************************************************************/
+/* gdscript_rpc_callable.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "gdscript_rpc_callable.h"
+
+#include "core/templates/hashfuncs.h"
+#include "scene/main/node.h"
+
+bool GDScriptRPCCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
+ return p_a->hash() == p_b->hash();
+}
+
+bool GDScriptRPCCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
+ return p_a->hash() < p_b->hash();
+}
+
+uint32_t GDScriptRPCCallable::hash() const {
+ return h;
+}
+
+String GDScriptRPCCallable::get_as_text() const {
+ String class_name = object->get_class();
+ Ref<Script> script = object->get_script();
+ return class_name + "(" + script->get_path().get_file() + ")::" + String(method) + " (rpc)";
+}
+
+CallableCustom::CompareEqualFunc GDScriptRPCCallable::get_compare_equal_func() const {
+ return compare_equal;
+}
+
+CallableCustom::CompareLessFunc GDScriptRPCCallable::get_compare_less_func() const {
+ return compare_less;
+}
+
+ObjectID GDScriptRPCCallable::get_object() const {
+ return object->get_instance_id();
+}
+
+void GDScriptRPCCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
+ r_return_value = object->call(method, p_arguments, p_argcount, r_call_error);
+}
+
+GDScriptRPCCallable::GDScriptRPCCallable(Object *p_object, const StringName &p_method) {
+ object = p_object;
+ method = p_method;
+ h = method.hash();
+ h = hash_djb2_one_64(object->get_instance_id(), h);
+ node = Object::cast_to<Node>(object);
+ ERR_FAIL_COND_MSG(!node, "RPC can only be defined on class that extends Node.");
+}
+
+void GDScriptRPCCallable::rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const {
+ if (unlikely(!node)) {
+ r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
+ return;
+ }
+ r_call_error.error = Callable::CallError::CALL_OK;
+ node->rpcp(p_peer_id, method, p_arguments, p_argcount);
+}
diff --git a/modules/gdscript/gdscript_rpc_callable.h b/modules/gdscript/gdscript_rpc_callable.h
new file mode 100644
index 0000000000..2c8734a74b
--- /dev/null
+++ b/modules/gdscript/gdscript_rpc_callable.h
@@ -0,0 +1,61 @@
+/*************************************************************************/
+/* gdscript_rpc_callable.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GDSCRIPT_RPC_CALLABLE
+#define GDSCRIPT_RPC_CALLABLE
+
+#include "core/variant/callable.h"
+#include "core/variant/variant.h"
+
+class Node;
+
+class GDScriptRPCCallable : public CallableCustom {
+ Object *object = nullptr;
+ Node *node = nullptr;
+ StringName method;
+ uint32_t h = 0;
+
+ static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
+ static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
+
+public:
+ uint32_t hash() const override;
+ String get_as_text() const override;
+ CompareEqualFunc get_compare_equal_func() const override;
+ CompareLessFunc get_compare_less_func() const override;
+ ObjectID get_object() const override;
+ void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
+ void rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const override;
+
+ GDScriptRPCCallable(Object *p_object, const StringName &p_method);
+ virtual ~GDScriptRPCCallable() = default;
+};
+
+#endif // GDSCRIPT_RPC_CALLABLE
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 9977b88aa1..d3287ab345 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -312,22 +312,6 @@ GDScriptTokenizer::Token GDScriptTokenizer::pop_error() {
return error;
}
-static bool _is_alphanumeric(char32_t c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
-}
-
-static bool _is_digit(char32_t c) {
- return (c >= '0' && c <= '9');
-}
-
-static bool _is_hex_digit(char32_t c) {
- return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
-}
-
-static bool _is_binary_digit(char32_t c) {
- return (c == '0' || c == '1');
-}
-
GDScriptTokenizer::Token GDScriptTokenizer::make_token(Token::Type p_type) {
Token token(p_type);
token.start_line = start_line;
@@ -448,10 +432,10 @@ GDScriptTokenizer::Token GDScriptTokenizer::check_vcs_marker(char32_t p_test, To
}
GDScriptTokenizer::Token GDScriptTokenizer::annotation() {
- if (!_is_alphanumeric(_peek())) {
+ if (!is_ascii_identifier_char(_peek())) {
push_error("Expected annotation identifier after \"@\".");
}
- while (_is_alphanumeric(_peek())) {
+ while (is_ascii_identifier_char(_peek())) {
// Consume all identifier characters.
_advance();
}
@@ -526,7 +510,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() {
#define MAX_KEYWORD_LENGTH 10
// Consume all alphanumeric characters.
- while (_is_alphanumeric(_peek())) {
+ while (is_ascii_identifier_char(_peek())) {
_advance();
}
@@ -612,7 +596,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
bool has_decimal = false;
bool has_exponent = false;
bool has_error = false;
- bool (*digit_check_func)(char32_t) = _is_digit;
+ bool (*digit_check_func)(char32_t) = is_digit;
if (_peek(-1) == '.') {
has_decimal = true;
@@ -620,20 +604,20 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
if (_peek() == 'x') {
// Hexadecimal.
base = 16;
- digit_check_func = _is_hex_digit;
+ digit_check_func = is_hex_digit;
_advance();
} else if (_peek() == 'b') {
// Binary.
base = 2;
- digit_check_func = _is_binary_digit;
+ digit_check_func = is_binary_digit;
_advance();
}
}
// Allow '_' to be used in a number, for readability.
bool previous_was_underscore = false;
- while (digit_check_func(_peek()) || _peek() == '_') {
- if (_peek() == '_') {
+ while (digit_check_func(_peek()) || is_underscore(_peek())) {
+ if (is_underscore(_peek())) {
if (previous_was_underscore) {
Token error = make_error(R"(Only one underscore can be used as a numeric separator.)");
error.start_column = column;
@@ -682,7 +666,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
_advance();
// Consume decimal digits.
- while (_is_digit(_peek()) || _peek() == '_') {
+ while (is_digit(_peek()) || is_underscore(_peek())) {
_advance();
}
}
@@ -696,7 +680,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
_advance();
}
// Consume exponent digits.
- if (!_is_digit(_peek())) {
+ if (!is_digit(_peek())) {
Token error = make_error(R"(Expected exponent value after "e".)");
error.start_column = column;
error.leftmost_column = column;
@@ -705,8 +689,8 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
push_error(error);
}
previous_was_underscore = false;
- while (_is_digit(_peek()) || _peek() == '_') {
- if (_peek() == '_') {
+ while (is_digit(_peek()) || is_underscore(_peek())) {
+ if (is_underscore(_peek())) {
if (previous_was_underscore) {
Token error = make_error(R"(Only one underscore can be used as a numeric separator.)");
error.start_column = column;
@@ -733,7 +717,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
error.rightmost_column = column + 1;
push_error(error);
has_error = true;
- } else if (_is_alphanumeric(_peek())) {
+ } else if (is_ascii_identifier_char(_peek())) {
// Letter at the end of the number.
push_error("Invalid numeric notation.");
}
@@ -865,7 +849,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
char32_t digit = _peek();
char32_t value = 0;
- if (digit >= '0' && digit <= '9') {
+ if (is_digit(digit)) {
value = digit - '0';
} else if (digit >= 'a' && digit <= 'f') {
value = digit - 'a';
@@ -1322,9 +1306,9 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() {
line_continuation = false;
- if (_is_digit(c)) {
+ if (is_digit(c)) {
return number();
- } else if (_is_alphanumeric(c)) {
+ } else if (is_ascii_identifier_char(c)) {
return potential_identifier();
}
@@ -1392,7 +1376,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() {
if (_peek() == '.') {
_advance();
return make_token(Token::PERIOD_PERIOD);
- } else if (_is_digit(_peek())) {
+ } else if (is_digit(_peek())) {
// Number starting with '.'.
return number();
} else {
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index 73536f5f8e..ad96e36640 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -152,6 +152,9 @@ String GDScriptWarning::get_message() const {
CHECK_SYMBOLS(3);
return vformat(R"(The %s '%s' has the same name as a %s.)", symbols[0], symbols[1], symbols[2]);
}
+ case INT_ASSIGNED_TO_ENUM: {
+ return "Integer used when an enum value is expected. If this is intended cast the integer to the enum type.";
+ }
case WARNING_MAX:
break; // Can't happen, but silences warning
}
@@ -199,6 +202,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
"REDUNDANT_AWAIT",
"EMPTY_FILE",
"SHADOWED_GLOBAL_IDENTIFIER",
+ "INT_ASSIGNED_TO_ENUM",
};
static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names.");
diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h
index 112b40781a..82efe3568f 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/modules/gdscript/gdscript_warning.h
@@ -70,6 +70,7 @@ public:
REDUNDANT_AWAIT, // await is used but expression is synchronous (not a signal nor a coroutine).
EMPTY_FILE, // A script file is empty.
SHADOWED_GLOBAL_IDENTIFIER, // A global class or function has the same name as variable.
+ INT_ASSIGNED_TO_ENUM, // An integer value was assigned to an enum-typed variable without casting.
WARNING_MAX,
};
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp
index 49f5303ae6..17886181d5 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.cpp
+++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp
@@ -541,7 +541,7 @@ String ExtendGDScriptParser::get_identifier_under_position(const lsp::Position &
for (int c = p_position.character; c >= 0; c--) {
start_pos = c;
char32_t ch = line[c];
- bool valid_char = (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_';
+ bool valid_char = is_ascii_identifier_char(ch);
if (!valid_char) {
break;
}
@@ -550,7 +550,7 @@ String ExtendGDScriptParser::get_identifier_under_position(const lsp::Position &
int end_pos = p_position.character;
for (int c = p_position.character; c < line.length(); c++) {
char32_t ch = line[c];
- bool valid_char = (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_';
+ bool valid_char = is_ascii_identifier_char(ch);
if (!valid_char) {
break;
}
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index ed879b088a..d20b243616 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -63,7 +63,7 @@ void GDScriptWorkspace::apply_new_signal(Object *obj, String function, PackedStr
String function_signature = "func " + function;
String source = script->get_source_code();
- if (source.find(function_signature) != -1) {
+ if (source.contains(function_signature)) {
return;
}
@@ -380,7 +380,7 @@ Error GDScriptWorkspace::initialize() {
symbol.children.push_back(symbol_arg);
}
- if (data.qualifiers.find("vararg") != -1) {
+ if (data.qualifiers.contains("vararg")) {
params += params.is_empty() ? "..." : ", ...";
}
diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp
index 8e503a9df9..a63f9df918 100644
--- a/modules/gdscript/language_server/lsp.hpp
+++ b/modules/gdscript/language_server/lsp.hpp
@@ -1940,7 +1940,7 @@ static String marked_documentation(const String &p_bbcode) {
line = "\t" + line.substr(code_block_indent, line.length());
}
- if (in_code_block && line.find("[/codeblock]") != -1) {
+ if (in_code_block && line.contains("[/codeblock]")) {
line = "\n";
in_code_block = false;
}
diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp
index 47772b8039..c2bb2caa29 100644
--- a/modules/gdscript/tests/gdscript_test_runner.cpp
+++ b/modules/gdscript/tests/gdscript_test_runner.cpp
@@ -73,23 +73,21 @@ void init_autoloads() {
RES res = ResourceLoader::load(info.path);
ERR_CONTINUE_MSG(res.is_null(), "Can't autoload: " + info.path);
Node *n = nullptr;
- if (res->is_class("PackedScene")) {
- Ref<PackedScene> ps = res;
- n = ps->instantiate();
- } else if (res->is_class("Script")) {
- Ref<Script> script_res = res;
- StringName ibt = script_res->get_instance_base_type();
+ Ref<PackedScene> scn = res;
+ Ref<Script> script = res;
+ if (scn.is_valid()) {
+ n = scn->instantiate();
+ } else if (script.is_valid()) {
+ StringName ibt = script->get_instance_base_type();
bool valid_type = ClassDB::is_parent_class(ibt, "Node");
ERR_CONTINUE_MSG(!valid_type, "Script does not inherit a Node: " + info.path);
Object *obj = ClassDB::instantiate(ibt);
- ERR_CONTINUE_MSG(obj == nullptr,
- "Cannot instance script for autoload, expected 'Node' inheritance, got: " +
- String(ibt));
+ ERR_CONTINUE_MSG(!obj, "Cannot instance script for autoload, expected 'Node' inheritance, got: " + String(ibt) + ".");
n = Object::cast_to<Node>(obj);
- n->set_script(script_res);
+ n->set_script(script);
}
ERR_CONTINUE_MSG(!n, "Path in autoload not a node or script: " + info.path);
@@ -134,12 +132,14 @@ GDScriptTestRunner::GDScriptTestRunner(const String &p_source_dir, bool p_init_l
if (do_init_languages) {
init_language(p_source_dir);
}
+#ifdef DEBUG_ENABLED
// Enable all warnings for GDScript, so we can test them.
ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/enable", true);
for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) {
String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower();
ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/" + warning, true);
}
+#endif
// Enable printing to show results
_print_line_enabled = true;
@@ -153,6 +153,21 @@ GDScriptTestRunner::~GDScriptTestRunner() {
}
}
+#ifndef DEBUG_ENABLED
+static String strip_warnings(const String &p_expected) {
+ // On release builds we don't have warnings. Here we remove them from the output before comparison
+ // so it doesn't fail just because of difference in warnings.
+ String expected_no_warnings;
+ for (String line : p_expected.split("\n")) {
+ if (line.begins_with(">> ")) {
+ continue;
+ }
+ expected_no_warnings += line + "\n";
+ }
+ return expected_no_warnings.strip_edges() + "\n";
+}
+#endif
+
int GDScriptTestRunner::run_tests() {
if (!make_tests()) {
FAIL("An error occurred while making the tests.");
@@ -170,6 +185,9 @@ int GDScriptTestRunner::run_tests() {
GDScriptTest::TestResult result = test.run_test();
String expected = FileAccess::get_file_as_string(test.get_output_file());
+#ifndef DEBUG_ENABLED
+ expected = strip_warnings(expected);
+#endif
INFO(test.get_source_file());
if (!result.passed) {
INFO(expected);
@@ -233,6 +251,22 @@ bool GDScriptTestRunner::make_tests_for_dir(const String &p_dir) {
}
} else {
if (next.get_extension().to_lower() == "gd") {
+#ifndef DEBUG_ENABLED
+ // On release builds, skip tests marked as debug only.
+ Error open_err = OK;
+ FileAccessRef script_file(FileAccess::open(current_dir.plus_file(next), FileAccess::READ, &open_err));
+ if (open_err != OK) {
+ ERR_PRINT(vformat(R"(Couldn't open test file "%s".)", next));
+ next = dir->get_next();
+ continue;
+ } else {
+ if (script_file->get_line() == "#debug-only") {
+ next = dir->get_next();
+ continue;
+ }
+ }
+#endif
+
String out_file = next.get_basename() + ".out";
if (!is_generating && !dir->file_exists(out_file)) {
ERR_FAIL_V_MSG(false, "Could not find output file for " + next);
@@ -387,6 +421,10 @@ bool GDScriptTest::check_output(const String &p_output) const {
String got = p_output.strip_edges(); // TODO: may be hacky.
got += "\n"; // Make sure to insert newline for CI static checks.
+#ifndef DEBUG_ENABLED
+ expected = strip_warnings(expected);
+#endif
+
return got == expected;
}
@@ -469,6 +507,7 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
return result;
}
+#ifdef DEBUG_ENABLED
StringBuilder warning_string;
for (const GDScriptWarning &E : parser.get_warnings()) {
const GDScriptWarning warning = E;
@@ -482,6 +521,7 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
warning_string.append("\n");
}
result.output += warning_string.as_string();
+#endif
// Test compiling.
GDScriptCompiler compiler;
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.gd
new file mode 100644
index 0000000000..928c886650
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.gd
@@ -0,0 +1,10 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 }
+
+# Different enum types can't be assigned without casting.
+var class_var: MyEnum = MyEnum.ENUM_VALUE_1
+
+func test():
+ print(class_var)
+ class_var = MyOtherEnum.OTHER_ENUM_VALUE_2
+ print(class_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out
new file mode 100644
index 0000000000..fde7e92f8c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot assign a value of type "MyOtherEnum (enum)" to a target of type "MyEnum (enum)".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.gd
new file mode 100644
index 0000000000..03a1711d7b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.gd
@@ -0,0 +1,8 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 }
+
+# Different enum types can't be assigned without casting.
+var class_var: MyEnum = MyOtherEnum.OTHER_ENUM_VALUE_1
+
+func test():
+ print(class_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out
new file mode 100644
index 0000000000..b1710c798d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Value of type "MyOtherEnum (enum)" cannot be assigned to a variable of type "MyEnum (enum)".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.gd
new file mode 100644
index 0000000000..d08d3dd7b2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.gd
@@ -0,0 +1,8 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 }
+
+func test():
+ var local_var: MyEnum = MyEnum.ENUM_VALUE_1
+ print(local_var)
+ local_var = MyOtherEnum.OTHER_ENUM_VALUE_2
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out
new file mode 100644
index 0000000000..fde7e92f8c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot assign a value of type "MyOtherEnum (enum)" to a target of type "MyEnum (enum)".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.gd
new file mode 100644
index 0000000000..ca6d892218
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.gd
@@ -0,0 +1,6 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 }
+
+func test():
+ var local_var: MyEnum = MyOtherEnum.OTHER_ENUM_VALUE_1
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out
new file mode 100644
index 0000000000..b1710c798d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Value of type "MyOtherEnum (enum)" cannot be assigned to a variable of type "MyEnum (enum)".
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_assign_enum_to_int_typed_var.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_enum_to_int_typed_var.gd
new file mode 100644
index 0000000000..edb785c8b6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_enum_to_int_typed_var.gd
@@ -0,0 +1,13 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+
+var class_var: int = MyEnum.ENUM_VALUE_1
+
+func test():
+ print(class_var)
+ class_var = MyEnum.ENUM_VALUE_2
+ print(class_var)
+
+ var local_var: int = MyEnum.ENUM_VALUE_1
+ print(local_var)
+ local_var = MyEnum.ENUM_VALUE_2
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_assign_enum_to_int_typed_var.out b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_enum_to_int_typed_var.out
new file mode 100644
index 0000000000..5f53802c33
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_enum_to_int_typed_var.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+0
+1
+0
+1
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_assign_int_cast_to_same_enum.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_int_cast_to_same_enum.gd
new file mode 100644
index 0000000000..726e4fd413
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_int_cast_to_same_enum.gd
@@ -0,0 +1,13 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+
+var class_var: MyEnum = 0 as MyEnum
+
+func test():
+ print(class_var)
+ class_var = 1 as MyEnum
+ print(class_var)
+
+ var local_var: MyEnum = 0 as MyEnum
+ print(local_var)
+ local_var = 1 as MyEnum
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_assign_int_cast_to_same_enum.out b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_int_cast_to_same_enum.out
new file mode 100644
index 0000000000..5f53802c33
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_int_cast_to_same_enum.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+0
+1
+0
+1
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_assign_other_enum_cast_to_same_enum.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_other_enum_cast_to_same_enum.gd
new file mode 100644
index 0000000000..798912c987
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_other_enum_cast_to_same_enum.gd
@@ -0,0 +1,14 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 }
+
+var class_var: MyEnum = MyOtherEnum.OTHER_ENUM_VALUE_1 as MyEnum
+
+func test():
+ print(class_var)
+ class_var = MyOtherEnum.OTHER_ENUM_VALUE_2 as MyEnum
+ print(class_var)
+
+ var local_var: MyEnum = MyOtherEnum.OTHER_ENUM_VALUE_1 as MyEnum
+ print(local_var)
+ local_var = MyOtherEnum.OTHER_ENUM_VALUE_2 as MyEnum
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_assign_other_enum_cast_to_same_enum.out b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_other_enum_cast_to_same_enum.out
new file mode 100644
index 0000000000..5f53802c33
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_other_enum_cast_to_same_enum.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+0
+1
+0
+1
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_assign_same_enum.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_same_enum.gd
new file mode 100644
index 0000000000..2bfb318c3c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_same_enum.gd
@@ -0,0 +1,13 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+
+var class_var: MyEnum = MyEnum.ENUM_VALUE_1
+
+func test():
+ print(class_var)
+ class_var = MyEnum.ENUM_VALUE_2
+ print(class_var)
+
+ var local_var: MyEnum = MyEnum.ENUM_VALUE_1
+ print(local_var)
+ local_var = MyEnum.ENUM_VALUE_2
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_assign_same_enum.out b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_same_enum.out
new file mode 100644
index 0000000000..5f53802c33
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_same_enum.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+0
+1
+0
+1
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_is_treated_as_int.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_is_treated_as_int.gd
new file mode 100644
index 0000000000..7022d14566
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_is_treated_as_int.gd
@@ -0,0 +1,21 @@
+# Enum is equivalent to int for comparisons and operations.
+enum MyEnum {
+ ZERO,
+ ONE,
+ TWO,
+}
+
+enum OtherEnum {
+ ZERO,
+ ONE,
+ TWO,
+}
+
+func test():
+ print(MyEnum.ZERO == OtherEnum.ZERO)
+ print(MyEnum.ZERO == 1)
+ print(MyEnum.ZERO != OtherEnum.ONE)
+ print(MyEnum.ZERO != 0)
+
+ print(MyEnum.ONE + OtherEnum.TWO)
+ print(2 - MyEnum.ONE)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_is_treated_as_int.out b/modules/gdscript/tests/scripts/analyzer/features/enum_is_treated_as_int.out
new file mode 100644
index 0000000000..c8f34c11db
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_is_treated_as_int.out
@@ -0,0 +1,7 @@
+GDTEST_OK
+true
+false
+true
+false
+3
+1
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_type_is_treated_as_dictionary.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_type_is_treated_as_dictionary.gd
new file mode 100644
index 0000000000..885d70408a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_type_is_treated_as_dictionary.gd
@@ -0,0 +1,13 @@
+enum MyEnum {
+ ZERO,
+ ONE,
+ TWO,
+}
+
+func test():
+ for key in MyEnum.keys():
+ prints(key, MyEnum[key])
+
+ # https://github.com/godotengine/godot/issues/55491
+ for key in MyEnum:
+ prints(key, MyEnum[key])
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_type_is_treated_as_dictionary.out b/modules/gdscript/tests/scripts/analyzer/features/enum_type_is_treated_as_dictionary.out
new file mode 100644
index 0000000000..d29f53109c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_type_is_treated_as_dictionary.out
@@ -0,0 +1,7 @@
+GDTEST_OK
+ZERO 0
+ONE 1
+TWO 2
+ZERO 0
+ONE 1
+TWO 2
diff --git a/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.gd b/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.gd
new file mode 100644
index 0000000000..2be1024214
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.gd
@@ -0,0 +1,15 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+
+# Assigning int value to enum-typed variable without explicit cast causes a warning.
+# While it is valid it may be a mistake in the assignment.
+var class_var: MyEnum = 0
+
+func test():
+ print(class_var)
+ class_var = 1
+ print(class_var)
+
+ var local_var: MyEnum = 0
+ print(local_var)
+ local_var = 1
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.out b/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.out
new file mode 100644
index 0000000000..eef13bbff8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.out
@@ -0,0 +1,21 @@
+GDTEST_OK
+>> WARNING
+>> Line: 5
+>> INT_ASSIGNED_TO_ENUM
+>> Integer used when an enum value is expected. If this is intended cast the integer to the enum type.
+>> WARNING
+>> Line: 9
+>> INT_ASSIGNED_TO_ENUM
+>> Integer used when an enum value is expected. If this is intended cast the integer to the enum type.
+>> WARNING
+>> Line: 12
+>> INT_ASSIGNED_TO_ENUM
+>> Integer used when an enum value is expected. If this is intended cast the integer to the enum type.
+>> WARNING
+>> Line: 14
+>> INT_ASSIGNED_TO_ENUM
+>> Integer used when an enum value is expected. If this is intended cast the integer to the enum type.
+0
+1
+0
+1
diff --git a/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.gd b/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.gd
index 10780b5379..7b3c112fe9 100644
--- a/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.gd
+++ b/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.gd
@@ -1,3 +1,4 @@
+#debug-only
func test():
var node := Node.new()
var inside_tree = node.is_inside_tree
diff --git a/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.out b/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.out
index e585c374e2..fe48ade26b 100644
--- a/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.out
+++ b/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.out
@@ -2,5 +2,5 @@ GDTEST_RUNTIME_ERROR
>> SCRIPT ERROR
>> on function: test()
>> runtime/errors/callable_call_after_free_object.gd
->> 5
+>> 6
>> Attempt to call function 'null::is_inside_tree (Callable)' on a null instance.
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 51608273a1..0056fee7a4 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -50,13 +50,13 @@
#include "core/io/file_access.h"
#include "core/io/file_access_memory.h"
#include "core/io/json.h"
+#include "core/io/stream_peer.h"
#include "core/math/disjoint_set.h"
#include "core/math/vector2.h"
#include "core/variant/dictionary.h"
#include "core/variant/typed_array.h"
#include "core/variant/variant.h"
#include "core/version.h"
-#include "core/version_hash.gen.h"
#include "drivers/png/png_driver_common.h"
#include "editor/import/resource_importer_scene.h"
#include "scene/2d/node_2d.h"
@@ -6277,7 +6277,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
p_track.rotation_track.values.write[key_i] = rotation;
}
} else if (track_type == Animation::TYPE_VALUE) {
- if (path.find(":position") != -1) {
+ if (path.contains(":position")) {
p_track.position_track.times = times;
p_track.position_track.interpolation = gltf_interpolation;
@@ -6288,7 +6288,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
Vector3 position = p_animation->track_get_key_value(p_track_i, key_i);
p_track.position_track.values.write[key_i] = position;
}
- } else if (path.find(":rotation") != -1) {
+ } else if (path.contains(":rotation")) {
p_track.rotation_track.times = times;
p_track.rotation_track.interpolation = gltf_interpolation;
@@ -6299,7 +6299,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
Vector3 rotation_radian = p_animation->track_get_key_value(p_track_i, key_i);
p_track.rotation_track.values.write[key_i] = Quaternion(rotation_radian);
}
- } else if (path.find(":scale") != -1) {
+ } else if (path.contains(":scale")) {
p_track.scale_track.times = times;
p_track.scale_track.interpolation = gltf_interpolation;
@@ -6312,7 +6312,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
}
}
} else if (track_type == Animation::TYPE_BEZIER) {
- if (path.find("/scale") != -1) {
+ if (path.contains("/scale")) {
const int32_t keys = p_animation->track_get_key_time(p_track_i, key_count - 1) * BAKE_FPS;
if (!p_track.scale_track.times.size()) {
Vector<real_t> new_times;
@@ -6333,16 +6333,16 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
for (int32_t key_i = 0; key_i < keys; key_i++) {
Vector3 bezier_track = p_track.scale_track.values[key_i];
- if (path.find("/scale:x") != -1) {
+ if (path.contains("/scale:x")) {
bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
- } else if (path.find("/scale:y") != -1) {
+ } else if (path.contains("/scale:y")) {
bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
- } else if (path.find("/scale:z") != -1) {
+ } else if (path.contains("/scale:z")) {
bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
}
p_track.scale_track.values.write[key_i] = bezier_track;
}
- } else if (path.find("/position") != -1) {
+ } else if (path.contains("/position")) {
const int32_t keys = p_animation->track_get_key_time(p_track_i, key_count - 1) * BAKE_FPS;
if (!p_track.position_track.times.size()) {
Vector<real_t> new_times;
@@ -6359,11 +6359,11 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
for (int32_t key_i = 0; key_i < keys; key_i++) {
Vector3 bezier_track = p_track.position_track.values[key_i];
- if (path.find("/position:x") != -1) {
+ if (path.contains("/position:x")) {
bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
- } else if (path.find("/position:y") != -1) {
+ } else if (path.contains("/position:y")) {
bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
- } else if (path.find("/position:z") != -1) {
+ } else if (path.contains("/position:z")) {
bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
}
p_track.position_track.values.write[key_i] = bezier_track;
@@ -6384,7 +6384,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
continue;
}
String orig_track_path = animation->track_get_path(track_i);
- if (String(orig_track_path).find(":position") != -1) {
+ if (String(orig_track_path).contains(":position")) {
const Vector<String> node_suffix = String(orig_track_path).split(":position");
const NodePath path = node_suffix[0];
const Node *node = ap->get_parent()->get_node_or_null(path);
@@ -6400,7 +6400,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
gltf_animation->get_tracks().insert(node_index, track);
}
}
- } else if (String(orig_track_path).find(":rotation_degrees") != -1) {
+ } else if (String(orig_track_path).contains(":rotation_degrees")) {
const Vector<String> node_suffix = String(orig_track_path).split(":rotation_degrees");
const NodePath path = node_suffix[0];
const Node *node = ap->get_parent()->get_node_or_null(path);
@@ -6416,7 +6416,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
gltf_animation->get_tracks().insert(node_index, track);
}
}
- } else if (String(orig_track_path).find(":scale") != -1) {
+ } else if (String(orig_track_path).contains(":scale")) {
const Vector<String> node_suffix = String(orig_track_path).split(":scale");
const NodePath path = node_suffix[0];
const Node *node = ap->get_parent()->get_node_or_null(path);
@@ -6432,7 +6432,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
gltf_animation->get_tracks().insert(node_index, track);
}
}
- } else if (String(orig_track_path).find(":transform") != -1) {
+ } else if (String(orig_track_path).contains(":transform")) {
const Vector<String> node_suffix = String(orig_track_path).split(":transform");
const NodePath path = node_suffix[0];
const Node *node = ap->get_parent()->get_node_or_null(path);
@@ -6443,7 +6443,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
gltf_animation->get_tracks().insert(transform_track_i.key, track);
}
}
- } else if (String(orig_track_path).find(":blend_shapes/") != -1) {
+ } else if (String(orig_track_path).contains(":blend_shapes/")) {
const Vector<String> node_suffix = String(orig_track_path).split(":blend_shapes/");
const NodePath path = node_suffix[0];
const String suffix = node_suffix[1];
@@ -6499,7 +6499,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
}
tracks[mesh_index] = track;
}
- } else if (String(orig_track_path).find(":") != -1) {
+ } else if (String(orig_track_path).contains(":")) {
//Process skeleton
const Vector<String> node_suffix = String(orig_track_path).split(":");
const String node = node_suffix[0];
@@ -6529,7 +6529,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
gltf_animation->get_tracks()[node_i] = track;
}
}
- } else if (String(orig_track_path).find(":") == -1) {
+ } else if (!String(orig_track_path).contains(":")) {
ERR_CONTINUE(!ap->get_parent());
Node *godot_node = ap->get_parent()->get_node_or_null(orig_track_path);
for (const KeyValue<GLTFNodeIndex, Node *> &scene_node_i : state->scene_nodes) {
@@ -6654,8 +6654,8 @@ Error GLTFDocument::_serialize_version(Ref<GLTFState> state) {
Dictionary asset;
asset["version"] = version;
- String hash = VERSION_HASH;
- asset["generator"] = String(VERSION_FULL_NAME) + String("@") + (hash.length() == 0 ? String("unknown") : hash);
+ String hash = String(VERSION_HASH);
+ asset["generator"] = String(VERSION_FULL_NAME) + String("@") + (hash.is_empty() ? String("unknown") : hash);
state->json["asset"] = asset;
ERR_FAIL_COND_V(!asset.has("version"), Error::FAILED);
ERR_FAIL_COND_V(!state->json.has("asset"), Error::FAILED);
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index 67deedf839..6df7835855 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -813,6 +813,7 @@ void GridMap::_update_octants_callback() {
}
while (to_delete.front()) {
+ memdelete(octant_map[to_delete.front()->get()]);
octant_map.erase(to_delete.front()->get());
to_delete.pop_front();
}
diff --git a/modules/meshoptimizer/register_types.cpp b/modules/meshoptimizer/register_types.cpp
index 57a1b697be..597c12ed23 100644
--- a/modules/meshoptimizer/register_types.cpp
+++ b/modules/meshoptimizer/register_types.cpp
@@ -38,6 +38,9 @@ void register_meshoptimizer_types() {
SurfaceTool::simplify_with_attrib_func = meshopt_simplifyWithAttributes;
SurfaceTool::simplify_scale_func = meshopt_simplifyScale;
SurfaceTool::simplify_sloppy_func = meshopt_simplifySloppy;
+ SurfaceTool::generate_remap_func = meshopt_generateVertexRemap;
+ SurfaceTool::remap_vertex_func = meshopt_remapVertexBuffer;
+ SurfaceTool::remap_index_func = meshopt_remapIndexBuffer;
}
void unregister_meshoptimizer_types() {
@@ -45,4 +48,7 @@ void unregister_meshoptimizer_types() {
SurfaceTool::simplify_func = nullptr;
SurfaceTool::simplify_scale_func = nullptr;
SurfaceTool::simplify_sloppy_func = nullptr;
+ SurfaceTool::generate_remap_func = nullptr;
+ SurfaceTool::remap_vertex_func = nullptr;
+ SurfaceTool::remap_index_func = nullptr;
}
diff --git a/modules/modules_builders.py b/modules/modules_builders.py
index 2243162555..13d5a2075a 100644
--- a/modules/modules_builders.py
+++ b/modules/modules_builders.py
@@ -14,13 +14,10 @@ def generate_modules_enabled(target, source, env):
def generate_modules_tests(target, source, env):
import os
- import glob
with open(target[0].path, "w") as f:
- for name, path in env.module_list.items():
- headers = glob.glob(os.path.join(path, "tests", "*.h"))
- for h in headers:
- f.write('#include "%s"\n' % (os.path.normpath(h)))
+ for header in source:
+ f.write('#include "%s"\n' % (os.path.normpath(header.path)))
if __name__ == "__main__":
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 26436e3ec0..085ab9a467 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -3245,6 +3245,8 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) {
CRASH_COND(!valid);
#endif
+ GD_MONO_SCOPE_THREAD_ATTACH;
+
if (native) {
StringName native_name = NATIVE_GDMONOCLASS_NAME(native);
if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) {
@@ -3257,8 +3259,6 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) {
}
}
- GD_MONO_SCOPE_THREAD_ATTACH;
-
Callable::CallError unchecked_error;
return _create_instance(nullptr, 0, p_this, Object::cast_to<RefCounted>(p_this) != nullptr, unchecked_error);
}
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 2be588cac4..2de923c125 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -103,8 +103,6 @@ private:
bool valid = false;
bool reload_invalidated = false;
- bool builtin;
-
GDMonoClass *base = nullptr;
GDMonoClass *native = nullptr;
GDMonoClass *script_class = nullptr;
diff --git a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
index ce2b378623..7a4641dbbc 100644
--- a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
@@ -47,9 +47,13 @@ namespace GodotTools.OpenVisualStudio
if (dte == null)
{
// Open a new instance
+ dte = TryVisualStudioLaunch("VisualStudio.DTE.17.0");
- var visualStudioDteType = Type.GetTypeFromProgID("VisualStudio.DTE.16.0", throwOnError: true);
- dte = (DTE)Activator.CreateInstance(visualStudioDteType);
+ if (dte == null)
+ {
+ // Launch of VS 2022 failed, fallback to 2019
+ dte = TryVisualStudioLaunch("VisualStudio.DTE.16.0");
+ }
dte.UserControl = true;
@@ -133,6 +137,21 @@ namespace GodotTools.OpenVisualStudio
return 0;
}
+ private static DTE TryVisualStudioLaunch(string version)
+ {
+ try
+ {
+ var visualStudioDteType = Type.GetTypeFromProgID(version, throwOnError: true);
+ var dte = (DTE)Activator.CreateInstance(visualStudioDteType);
+
+ return dte;
+ }
+ catch (COMException)
+ {
+ return null;
+ }
+ }
+
private static DTE FindInstanceEditingSolution(string solutionPath)
{
if (GetRunningObjectTable(0, out IRunningObjectTable pprot) != 0)
@@ -164,7 +183,7 @@ namespace GodotTools.OpenVisualStudio
continue;
// The digits after the colon are the process ID
- if (!Regex.IsMatch(ppszDisplayName, "!VisualStudio.DTE.16.0:[0-9]"))
+ if (!Regex.IsMatch(ppszDisplayName, "!VisualStudio.DTE.1[6-7].0:[0-9]"))
continue;
if (pprot.GetObject(moniker[0], out object ppunkObject) == 0)
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
index 37e6a34977..ed758cc137 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
@@ -537,7 +537,6 @@ MONO_AOT_MODE_LAST = 1000,
{
var iosArchs = new[]
{
- "armv7",
"arm64"
};
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 1b4ab0ef4b..1de41821f9 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -690,11 +690,11 @@ void BindingsGenerator::_apply_prefix_to_enum_constants(BindingsGenerator::EnumI
continue;
}
- if (parts[curr_prefix_length][0] >= '0' && parts[curr_prefix_length][0] <= '9') {
+ if (is_digit(parts[curr_prefix_length][0])) {
// The name of enum constants may begin with a numeric digit when strip from the enum prefix,
// so we make the prefix for this constant one word shorter in those cases.
for (curr_prefix_length = curr_prefix_length - 1; curr_prefix_length > 0; curr_prefix_length--) {
- if (parts[curr_prefix_length][0] < '0' || parts[curr_prefix_length][0] > '9') {
+ if (!is_digit(parts[curr_prefix_length][0])) {
break;
}
}
diff --git a/modules/mono/editor_templates/CharacterBody2D/basic_movement.cs b/modules/mono/editor_templates/CharacterBody2D/basic_movement.cs
index 6e9547ce9b..b0ded3133f 100644
--- a/modules/mono/editor_templates/CharacterBody2D/basic_movement.cs
+++ b/modules/mono/editor_templates/CharacterBody2D/basic_movement.cs
@@ -6,7 +6,7 @@ using System;
public partial class _CLASS_ : _BASE_
{
public const float Speed = 300.0f;
- public const float JumpForce = -400.0f;
+ public const float JumpVelocity = -400.0f;
// Get the gravity from the project settings to be synced with RigidDynamicBody nodes.
public float gravity = (float)ProjectSettings.GetSetting("physics/2d/default_gravity");
@@ -21,7 +21,7 @@ public partial class _CLASS_ : _BASE_
// Handle Jump.
if (Input.IsActionJustPressed("ui_accept") && IsOnFloor())
- motionVelocity.y = JumpForce;
+ motionVelocity.y = JumpVelocity;
// Get the input direction and handle the movement/deceleration.
// As good practice, you should replace UI actions with custom gameplay actions.
diff --git a/modules/mono/editor_templates/CharacterBody3D/basic_movement.cs b/modules/mono/editor_templates/CharacterBody3D/basic_movement.cs
index 13be4bbcb1..d8c2f67ac8 100644
--- a/modules/mono/editor_templates/CharacterBody3D/basic_movement.cs
+++ b/modules/mono/editor_templates/CharacterBody3D/basic_movement.cs
@@ -6,7 +6,7 @@ using System;
public partial class _CLASS_ : _BASE_
{
public const float Speed = 5.0f;
- public const float JumpForce = 4.5f;
+ public const float JumpVelocity = 4.5f;
// Get the gravity from the project settings to be synced with RigidDynamicBody nodes.
public float gravity = (float)ProjectSettings.GetSetting("physics/3d/default_gravity");
@@ -21,7 +21,7 @@ public partial class _CLASS_ : _BASE_
// Handle Jump.
if (Input.IsActionJustPressed("ui_accept") && IsOnFloor())
- motionVelocity.y = JumpForce;
+ motionVelocity.y = JumpVelocity;
// Get the input direction and handle the movement/deceleration.
// As good practice, you should replace UI actions with custom gameplay actions.
diff --git a/modules/mono/editor_templates/VisualShaderNodeCustom/basic.cs b/modules/mono/editor_templates/VisualShaderNodeCustom/basic.cs
index 00fdc9968e..a1b93e7daa 100644
--- a/modules/mono/editor_templates/VisualShaderNodeCustom/basic.cs
+++ b/modules/mono/editor_templates/VisualShaderNodeCustom/basic.cs
@@ -55,11 +55,6 @@ public partial class VisualShaderNode_CLASS_ : _BASE_
return 0;
}
- public override string _GetGlobalCode(Shader.Mode mode)
- {
- return "";
- }
-
public override string _GetCode(Godot.Collections.Array inputVars, Godot.Collections.Array outputVars, Shader.Mode mode, VisualShader.Type type)
{
return "";
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
index eba0ea9a79..a1f058ffe5 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
@@ -266,7 +266,7 @@ namespace Godot
/// <returns>The capitalized string.</returns>
public static string Capitalize(this string instance)
{
- string aux = instance.Replace("_", " ").ToLower();
+ string aux = instance.CamelcaseToUnderscore(true).Replace("_", " ").Trim();
string cap = string.Empty;
for (int i = 0; i < aux.GetSliceCount(" "); i++)
@@ -284,6 +284,51 @@ namespace Godot
return cap;
}
+ private static string CamelcaseToUnderscore(this string instance, bool lowerCase)
+ {
+ string newString = string.Empty;
+ int startIndex = 0;
+
+ for (int i = 1; i < instance.Length; i++)
+ {
+ bool isUpper = char.IsUpper(instance[i]);
+ bool isNumber = char.IsDigit(instance[i]);
+
+ bool areNext2Lower = false;
+ bool isNextLower = false;
+ bool isNextNumber = false;
+ bool wasPrecedentUpper = char.IsUpper(instance[i - 1]);
+ bool wasPrecedentNumber = char.IsDigit(instance[i - 1]);
+
+ if (i + 2 < instance.Length)
+ {
+ areNext2Lower = char.IsLower(instance[i + 1]) && char.IsLower(instance[i + 2]);
+ }
+
+ if (i + 1 < instance.Length)
+ {
+ isNextLower = char.IsLower(instance[i + 1]);
+ isNextNumber = char.IsDigit(instance[i + 1]);
+ }
+
+ bool condA = isUpper && !wasPrecedentUpper && !wasPrecedentNumber;
+ bool condB = wasPrecedentUpper && isUpper && areNext2Lower;
+ bool condC = isNumber && !wasPrecedentNumber;
+ bool canBreakNumberLetter = isNumber && !wasPrecedentNumber && isNextLower;
+ bool canBreakLetterNumber = !isNumber && wasPrecedentNumber && (isNextLower || isNextNumber);
+
+ bool shouldSplit = condA || condB || condC || canBreakNumberLetter || canBreakLetterNumber;
+ if (shouldSplit)
+ {
+ newString += instance.Substring(startIndex, i - startIndex) + "_";
+ startIndex = i;
+ }
+ }
+
+ newString += instance.Substring(startIndex, instance.Length - startIndex);
+ return lowerCase ? newString.ToLower() : newString;
+ }
+
/// <summary>
/// Performs a case-sensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater.
/// </summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
index 1f5282e88f..fa7838633c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
@@ -512,24 +512,24 @@ namespace Godot
/// Returns the result of the spherical linear interpolation between
/// this vector and <paramref name="to"/> by amount <paramref name="weight"/>.
///
- /// Note: Both vectors must be normalized.
+ /// This method also handles interpolating the lengths if the input vectors have different lengths.
+ /// For the special case of one or both input vectors having zero length, this method behaves like [method lerp].
/// </summary>
- /// <param name="to">The destination vector for interpolation. Must be normalized.</param>
+ /// <param name="to">The destination vector for interpolation.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The resulting vector of the interpolation.</returns>
public Vector2 Slerp(Vector2 to, real_t weight)
{
-#if DEBUG
- if (!IsNormalized())
- {
- throw new InvalidOperationException("Vector2.Slerp: From vector is not normalized.");
+ real_t startLengthSquared = LengthSquared();
+ real_t endLengthSquared = to.LengthSquared();
+ if (startLengthSquared == 0.0 || endLengthSquared == 0.0) {
+ // Zero length vectors have no angle, so the best we can do is either lerp or throw an error.
+ return Lerp(to, weight);
}
- if (!to.IsNormalized())
- {
- throw new InvalidOperationException($"Vector2.Slerp: `{nameof(to)}` is not normalized.");
- }
-#endif
- return Rotated(AngleTo(to) * weight);
+ real_t startLength = Mathf.Sqrt(startLengthSquared);
+ real_t resultLength = Mathf.Lerp(startLength, Mathf.Sqrt(endLengthSquared), weight);
+ real_t angle = AngleTo(to);
+ return Rotated(angle * weight) * (resultLength / startLength);
}
/// <summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
index 433a5d9dc9..0faf13f8b7 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
@@ -549,25 +549,24 @@ namespace Godot
/// Returns the result of the spherical linear interpolation between
/// this vector and <paramref name="to"/> by amount <paramref name="weight"/>.
///
- /// Note: Both vectors must be normalized.
+ /// This method also handles interpolating the lengths if the input vectors have different lengths.
+ /// For the special case of one or both input vectors having zero length, this method behaves like [method lerp].
/// </summary>
- /// <param name="to">The destination vector for interpolation. Must be normalized.</param>
+ /// <param name="to">The destination vector for interpolation.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The resulting vector of the interpolation.</returns>
public Vector3 Slerp(Vector3 to, real_t weight)
{
-#if DEBUG
- if (!IsNormalized())
- {
- throw new InvalidOperationException("Vector3.Slerp: From vector is not normalized.");
+ real_t startLengthSquared = LengthSquared();
+ real_t endLengthSquared = to.LengthSquared();
+ if (startLengthSquared == 0.0 || endLengthSquared == 0.0) {
+ // Zero length vectors have no angle, so the best we can do is either lerp or throw an error.
+ return Lerp(to, weight);
}
- if (!to.IsNormalized())
- {
- throw new InvalidOperationException($"Vector3.Slerp: `{nameof(to)}` is not normalized.");
- }
-#endif
- real_t theta = AngleTo(to);
- return Rotated(Cross(to), theta * weight);
+ real_t startLength = Mathf.Sqrt(startLengthSquared);
+ real_t resultLength = Mathf.Lerp(startLength, Mathf.Sqrt(endLengthSquared), weight);
+ real_t angle = AngleTo(to);
+ return Rotated(Cross(to).Normalized(), angle * weight) * (resultLength / startLength);
}
/// <summary>
diff --git a/modules/ogg/ogg_packet_sequence.h b/modules/ogg/ogg_packet_sequence.h
index 7e56b14a24..73e3cb4fff 100644
--- a/modules/ogg/ogg_packet_sequence.h
+++ b/modules/ogg/ogg_packet_sequence.h
@@ -104,7 +104,7 @@ class OGGPacketSequencePlayback : public RefCounted {
mutable ogg_packet *packet;
- uint64_t data_version;
+ uint64_t data_version = 0;
mutable int64_t packetno = 0;
diff --git a/modules/raycast/raycast_occlusion_cull.cpp b/modules/raycast/raycast_occlusion_cull.cpp
index dd5eef8b2b..2e0d17fb28 100644
--- a/modules/raycast/raycast_occlusion_cull.cpp
+++ b/modules/raycast/raycast_occlusion_cull.cpp
@@ -270,13 +270,14 @@ void RaycastOcclusionCull::scenario_set_instance(RID p_scenario, RID p_instance,
OccluderInstance &instance = scenario.instances[p_instance];
+ bool changed = false;
+
if (instance.removed) {
instance.removed = false;
scenario.removed_instances.erase(p_instance);
+ changed = true; // It was removed and re-added, we might have missed some changes
}
- bool changed = false;
-
if (instance.occluder != p_occluder) {
Occluder *old_occluder = occluder_owner.get_or_null(instance.occluder);
if (old_occluder) {
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index e50a5337cb..c7511f587e 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -303,22 +303,6 @@ _FORCE_INLINE_ bool is_connected_to_prev(char32_t p_chr, char32_t p_pchr) {
return (prop != U_JT_RIGHT_JOINING) && (prop != U_JT_NON_JOINING) ? !is_ligature(p_pchr, p_chr) : false;
}
-_FORCE_INLINE_ bool is_control(char32_t p_char) {
- return (p_char <= 0x001f) || (p_char >= 0x007f && p_char <= 0x009F);
-}
-
-_FORCE_INLINE_ bool is_whitespace(char32_t p_char) {
- return (p_char == 0x0020) || (p_char == 0x00A0) || (p_char == 0x1680) || (p_char >= 0x2000 && p_char <= 0x200a) || (p_char == 0x202f) || (p_char == 0x205f) || (p_char == 0x3000) || (p_char == 0x2028) || (p_char == 0x2029) || (p_char >= 0x0009 && p_char <= 0x000d) || (p_char == 0x0085);
-}
-
-_FORCE_INLINE_ bool is_linebreak(char32_t p_char) {
- return (p_char >= 0x000a && p_char <= 0x000d) || (p_char == 0x0085) || (p_char == 0x2028) || (p_char == 0x2029);
-}
-
-_FORCE_INLINE_ bool is_underscore(char32_t p_char) {
- return (p_char == 0x005F);
-}
-
/*************************************************************************/
String TextServerAdvanced::interface_name = "ICU / HarfBuzz / Graphite";
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index dd520a2e40..182d2a02ad 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -44,30 +44,6 @@
#endif
/*************************************************************************/
-/* Character properties. */
-/*************************************************************************/
-
-_FORCE_INLINE_ bool is_control(char32_t p_char) {
- return (p_char <= 0x001f) || (p_char >= 0x007f && p_char <= 0x009F);
-}
-
-_FORCE_INLINE_ bool is_whitespace(char32_t p_char) {
- return (p_char == 0x0020) || (p_char == 0x00A0) || (p_char == 0x1680) || (p_char >= 0x2000 && p_char <= 0x200a) || (p_char == 0x202f) || (p_char == 0x205f) || (p_char == 0x3000) || (p_char == 0x2028) || (p_char == 0x2029) || (p_char >= 0x0009 && p_char <= 0x000d) || (p_char == 0x0085);
-}
-
-_FORCE_INLINE_ bool is_linebreak(char32_t p_char) {
- return (p_char >= 0x000a && p_char <= 0x000d) || (p_char == 0x0085) || (p_char == 0x2028) || (p_char == 0x2029);
-}
-
-_FORCE_INLINE_ bool is_punct(char32_t p_char) {
- return (p_char >= 0x0020 && p_char <= 0x002F) || (p_char >= 0x003A && p_char <= 0x0040) || (p_char >= 0x005B && p_char <= 0x005E) || (p_char == 0x0060) || (p_char >= 0x007B && p_char <= 0x007E) || (p_char >= 0x2000 && p_char <= 0x206F) || (p_char >= 0x3000 && p_char <= 0x303F);
-}
-
-_FORCE_INLINE_ bool is_underscore(char32_t p_char) {
- return (p_char == 0x005F);
-}
-
-/*************************************************************************/
String TextServerFallback::interface_name = "Fallback";
uint32_t TextServerFallback::interface_features = 0; // Nothing is supported.
diff --git a/modules/visual_script/editor/visual_script_editor.cpp b/modules/visual_script/editor/visual_script_editor.cpp
index b00b5a2ddd..5ea8eaff00 100644
--- a/modules/visual_script/editor/visual_script_editor.cpp
+++ b/modules/visual_script/editor/visual_script_editor.cpp
@@ -653,7 +653,6 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
List<int> ids;
script->get_node_list(&ids);
- StringName editor_icons = "EditorIcons";
for (int &E : ids) {
if (p_only_id >= 0 && p_only_id != E) {
@@ -2803,7 +2802,7 @@ void VisualScriptEditor::add_callback(const String &p_function, PackedStringArra
String name = p_args[i];
Variant::Type type = Variant::NIL;
- if (name.find(":") != -1) {
+ if (name.contains(":")) {
String tt = name.get_slice(":", 1);
name = name.get_slice(":", 0);
for (int j = 0; j < Variant::VARIANT_MAX; j++) {
diff --git a/modules/visual_script/editor/visual_script_editor.h b/modules/visual_script/editor/visual_script_editor.h
index f1b01aa6dc..b01732b2fd 100644
--- a/modules/visual_script/editor/visual_script_editor.h
+++ b/modules/visual_script/editor/visual_script_editor.h
@@ -173,10 +173,10 @@ class VisualScriptEditor : public ScriptEditorBase {
String member_name;
PortAction port_action;
- int port_action_node;
- int port_action_output;
+ int port_action_node = 0;
+ int port_action_output = 0;
Vector2 port_action_pos;
- int port_action_new_node;
+ int port_action_new_node = 0;
bool saved_pos_dirty = false;
@@ -196,7 +196,7 @@ class VisualScriptEditor : public ScriptEditorBase {
int _create_new_node_from_name(const String &p_text, const Vector2 &p_point);
void _selected_new_virtual_method(const String &p_text, const String &p_category, const bool p_connecting);
- int error_line;
+ int error_line = -1;
void _node_selected(Node *p_node);
void _center_on_node(int p_id);
@@ -267,12 +267,12 @@ class VisualScriptEditor : public ScriptEditorBase {
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
- int editing_id;
- int editing_input;
+ int editing_id = 0;
+ int editing_input = 0;
- bool can_swap;
- int data_disconnect_node;
- int data_disconnect_port;
+ bool can_swap = false;
+ int data_disconnect_node = 0;
+ int data_disconnect_port = 0;
void _default_value_changed();
void _default_value_edited(Node *p_button, int p_id, int p_input_port);
diff --git a/modules/visual_script/editor/visual_script_property_selector.cpp b/modules/visual_script/editor/visual_script_property_selector.cpp
index 1059d126bc..bba5410629 100644
--- a/modules/visual_script/editor/visual_script_property_selector.cpp
+++ b/modules/visual_script/editor/visual_script_property_selector.cpp
@@ -379,8 +379,6 @@ void VisualScriptPropertySelector::_bind_methods() {
}
VisualScriptPropertySelector::VisualScriptPropertySelector() {
- virtuals_only = false;
-
vbox = memnew(VBoxContainer);
add_child(vbox);
@@ -1116,14 +1114,14 @@ TreeItem *VisualScriptPropertySelector::SearchRunner::_create_class_item(TreeIte
String details = p_doc->name;
if (p_doc->category.begins_with("VisualScriptCustomNode/")) {
Vector<String> path = p_doc->name.split("/");
- icon = ui_service->get_theme_icon("VisualScript", "EditorIcons");
+ icon = ui_service->get_theme_icon(SNAME("VisualScript"), SNAME("EditorIcons"));
text_0 = path[path.size() - 1];
text_1 = "VisualScriptCustomNode";
what = "VisualScriptCustomNode";
details = "CustomNode";
} else if (p_doc->category.begins_with("VisualScriptNode/")) {
Vector<String> path = p_doc->name.split("/");
- icon = ui_service->get_theme_icon("VisualScript", "EditorIcons");
+ icon = ui_service->get_theme_icon(SNAME("VisualScript"), SNAME("EditorIcons"));
text_0 = path[path.size() - 1];
if (p_doc->category.begins_with("VisualScriptNode/deconstruct")) {
text_0 = "deconstruct " + text_0;
diff --git a/modules/visual_script/editor/visual_script_property_selector.h b/modules/visual_script/editor/visual_script_property_selector.h
index 3970c4473e..6b5112f1af 100644
--- a/modules/visual_script/editor/visual_script_property_selector.h
+++ b/modules/visual_script/editor/visual_script_property_selector.h
@@ -96,16 +96,16 @@ class VisualScriptPropertySelector : public ConfirmationDialog {
EditorHelpBit *help_bit;
- bool properties;
- bool visual_script_generic;
- bool connecting;
+ bool properties = false;
+ bool visual_script_generic = false;
+ bool connecting = false;
String selected;
Variant::Type type;
String base_type;
String base_script;
ObjectID script;
Object *instance;
- bool virtuals_only;
+ bool virtuals_only = false;
VBoxContainer *vbox;
protected:
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index fbdf3a654b..88445f2f98 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -1164,9 +1164,6 @@ void VisualScript::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_custom_signal", "name"), &VisualScript::remove_custom_signal);
ClassDB::bind_method(D_METHOD("rename_custom_signal", "name", "new_name"), &VisualScript::rename_custom_signal);
- //ClassDB::bind_method(D_METHOD("set_variable_info","name","info"),&VScript::set_variable_info);
- //ClassDB::bind_method(D_METHOD("get_variable_info","name"),&VScript::set_variable_info);
-
ClassDB::bind_method(D_METHOD("set_instance_base_type", "type"), &VisualScript::set_instance_base_type);
ClassDB::bind_method(D_METHOD("_set_data", "data"), &VisualScript::_set_data);
diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp
index 17a3566ed2..e8942b9788 100644
--- a/modules/visual_script/visual_script_expression.cpp
+++ b/modules/visual_script/visual_script_expression.cpp
@@ -377,13 +377,13 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
r_token.type = TK_ERROR;
return ERR_PARSE_ERROR;
}
- if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
+ if (!is_hex_digit(c)) {
_set_error("Malformed hex constant in string");
r_token.type = TK_ERROR;
return ERR_PARSE_ERROR;
}
char32_t v;
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
v = c - '0';
} else if (c >= 'a' && c <= 'f') {
v = c - 'a';
@@ -457,7 +457,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
break;
}
- if (cchar >= '0' && cchar <= '9') {
+ if (is_digit(cchar)) {
//a number
String num;
@@ -476,7 +476,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
while (true) {
switch (reading) {
case READING_INT: {
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
//pass
} else if (c == '.') {
reading = READING_DEC;
@@ -489,7 +489,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
} break;
case READING_DEC: {
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
} else if (c == 'e') {
reading = READING_EXP;
@@ -499,7 +499,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
} break;
case READING_EXP: {
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
exp_beg = true;
} else if ((c == '-' || c == '+') && !exp_sign && !exp_beg) {
@@ -532,11 +532,11 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
}
return OK;
- } else if ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_') {
+ } else if (is_ascii_char(cchar) || cchar == '_') {
String id;
bool first = true;
- while ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_' || (!first && cchar >= '0' && cchar <= '9')) {
+ while (is_ascii_char(cchar) || cchar == '_' || (!first && is_digit(cchar))) {
id += String::chr(cchar);
cchar = GET_CHAR();
first = false;
diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp
index f3594e5164..e7f4e542c1 100644
--- a/modules/visual_script/visual_script_nodes.cpp
+++ b/modules/visual_script/visual_script_nodes.cpp
@@ -2495,7 +2495,7 @@ static Node *_find_script_node(Node *p_edited_scene, Node *p_current_node, const
VisualScriptSceneNode::TypeGuess VisualScriptSceneNode::guess_output_type(TypeGuess *p_inputs, int p_output) const {
VisualScriptSceneNode::TypeGuess tg;
tg.type = Variant::OBJECT;
- tg.gdclass = "Node";
+ tg.gdclass = SNAME("Node");
#ifdef TOOLS_ENABLED
Ref<Script> script = get_visual_script();
@@ -2649,7 +2649,7 @@ VisualScriptNodeInstance *VisualScriptSceneTree::instantiate(VisualScriptInstanc
VisualScriptSceneTree::TypeGuess VisualScriptSceneTree::guess_output_type(TypeGuess *p_inputs, int p_output) const {
TypeGuess tg;
tg.type = Variant::OBJECT;
- tg.gdclass = "SceneTree";
+ tg.gdclass = SNAME("SceneTree");
return tg;
}
@@ -2766,11 +2766,11 @@ PropertyInfo VisualScriptSelf::get_input_value_port_info(int p_idx) const {
}
PropertyInfo VisualScriptSelf::get_output_value_port_info(int p_idx) const {
- String type_name;
+ StringName type_name;
if (get_visual_script().is_valid()) {
type_name = get_visual_script()->get_instance_base_type();
} else {
- type_name = "instance";
+ type_name = SNAME("instance");
}
return PropertyInfo(Variant::OBJECT, type_name);
@@ -2801,7 +2801,7 @@ VisualScriptNodeInstance *VisualScriptSelf::instantiate(VisualScriptInstance *p_
VisualScriptSelf::TypeGuess VisualScriptSelf::guess_output_type(TypeGuess *p_inputs, int p_output) const {
VisualScriptSceneNode::TypeGuess tg;
tg.type = Variant::OBJECT;
- tg.gdclass = "Object";
+ tg.gdclass = SNAME("Object");
Ref<Script> script = get_visual_script();
if (!script.is_valid()) {
diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp
index be1c75c354..bebb198126 100644
--- a/modules/websocket/wsl_client.cpp
+++ b/modules/websocket/wsl_client.cpp
@@ -163,22 +163,24 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
_peer = Ref<WSLPeer>(memnew(WSLPeer));
if (p_host.is_valid_ip_address()) {
- ip_candidates.clear();
- ip_candidates.push_back(IPAddress(p_host));
+ _ip_candidates.push_back(IPAddress(p_host));
} else {
- ip_candidates = IP::get_singleton()->resolve_hostname_addresses(p_host);
- }
-
- ERR_FAIL_COND_V(ip_candidates.is_empty(), ERR_INVALID_PARAMETER);
-
- String port = "";
- if ((p_port != 80 && !p_ssl) || (p_port != 443 && p_ssl)) {
- port = ":" + itos(p_port);
+ // Queue hostname for resolution.
+ _resolver_id = IP::get_singleton()->resolve_hostname_queue_item(p_host);
+ ERR_FAIL_COND_V(_resolver_id == IP::RESOLVER_INVALID_ID, ERR_INVALID_PARAMETER);
+ // Check if it was found in cache.
+ IP::ResolverStatus ip_status = IP::get_singleton()->get_resolve_item_status(_resolver_id);
+ if (ip_status == IP::RESOLVER_STATUS_DONE) {
+ _ip_candidates = IP::get_singleton()->get_resolve_item_addresses(_resolver_id);
+ IP::get_singleton()->erase_resolve_item(_resolver_id);
+ _resolver_id = IP::RESOLVER_INVALID_ID;
+ }
}
- Error err = ERR_BUG; // Should be at least one entry.
- while (ip_candidates.size() > 0) {
- err = _tcp->connect_to_host(ip_candidates.pop_front(), p_port);
+ // We assume OK while hostname resultion is pending.
+ Error err = _resolver_id != IP::RESOLVER_INVALID_ID ? OK : FAILED;
+ while (_ip_candidates.size()) {
+ err = _tcp->connect_to_host(_ip_candidates.pop_front(), p_port);
if (err == OK) {
break;
}
@@ -200,8 +202,11 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
}
_key = WSLPeer::generate_key();
- // TODO custom extra headers (allow overriding this too?)
String request = "GET " + p_path + " HTTP/1.1\r\n";
+ String port = "";
+ if ((p_port != 80 && !p_ssl) || (p_port != 443 && p_ssl)) {
+ port = ":" + itos(p_port);
+ }
request += "Host: " + p_host + port + "\r\n";
request += "Upgrade: websocket\r\n";
request += "Connection: Upgrade\r\n";
@@ -231,6 +236,30 @@ int WSLClient::get_max_packet_size() const {
}
void WSLClient::poll() {
+ if (_resolver_id != IP::RESOLVER_INVALID_ID) {
+ IP::ResolverStatus ip_status = IP::get_singleton()->get_resolve_item_status(_resolver_id);
+ if (ip_status == IP::RESOLVER_STATUS_WAITING) {
+ return;
+ }
+ // Anything else is either a candidate or a failure.
+ Error err = FAILED;
+ if (ip_status == IP::RESOLVER_STATUS_DONE) {
+ _ip_candidates = IP::get_singleton()->get_resolve_item_addresses(_resolver_id);
+ while (_ip_candidates.size()) {
+ err = _tcp->connect_to_host(_ip_candidates.pop_front(), _port);
+ if (err == OK) {
+ break;
+ }
+ }
+ }
+ IP::get_singleton()->erase_resolve_item(_resolver_id);
+ _resolver_id = IP::RESOLVER_INVALID_ID;
+ if (err != OK) {
+ disconnect_from_host();
+ _on_error();
+ return;
+ }
+ }
if (_peer->is_connected_to_host()) {
_peer->poll();
if (!_peer->is_connected_to_host()) {
@@ -251,7 +280,7 @@ void WSLClient::poll() {
_on_error();
break;
case StreamPeerTCP::STATUS_CONNECTED: {
- ip_candidates.clear();
+ _ip_candidates.clear();
Ref<StreamPeerSSL> ssl;
if (_use_ssl) {
if (_connection == _tcp) {
@@ -282,9 +311,9 @@ void WSLClient::poll() {
_do_handshake();
} break;
case StreamPeerTCP::STATUS_ERROR:
- while (ip_candidates.size() > 0) {
+ while (_ip_candidates.size() > 0) {
_tcp->disconnect_from_host();
- if (_tcp->connect_to_host(ip_candidates.pop_front(), _port) == OK) {
+ if (_tcp->connect_to_host(_ip_candidates.pop_front(), _port) == OK) {
return;
}
}
@@ -307,7 +336,7 @@ MultiplayerPeer::ConnectionStatus WSLClient::get_connection_status() const {
return CONNECTION_CONNECTED;
}
- if (_tcp->is_connected_to_host()) {
+ if (_tcp->is_connected_to_host() || _resolver_id != IP::RESOLVER_INVALID_ID) {
return CONNECTION_CONNECTING;
}
@@ -330,7 +359,12 @@ void WSLClient::disconnect_from_host(int p_code, String p_reason) {
memset(_resp_buf, 0, sizeof(_resp_buf));
_resp_pos = 0;
- ip_candidates.clear();
+ if (_resolver_id != IP::RESOLVER_INVALID_ID) {
+ IP::get_singleton()->erase_resolve_item(_resolver_id);
+ _resolver_id = IP::RESOLVER_INVALID_ID;
+ }
+
+ _ip_candidates.clear();
}
IPAddress WSLClient::get_connected_host() const {
diff --git a/modules/websocket/wsl_client.h b/modules/websocket/wsl_client.h
index 4839d7ab9b..d846e6be00 100644
--- a/modules/websocket/wsl_client.h
+++ b/modules/websocket/wsl_client.h
@@ -63,10 +63,11 @@ private:
String _key;
String _host;
- int _port;
- Array ip_candidates;
+ uint16_t _port;
+ Array _ip_candidates;
Vector<String> _protocols;
bool _use_ssl = false;
+ IP::ResolverID _resolver_id = IP::RESOLVER_INVALID_ID;
void _do_handshake();
bool _verify_headers(String &r_protocol);